diff --git a/App/App.csproj b/App/App.csproj
index eb1517e..c514219 100644
--- a/App/App.csproj
+++ b/App/App.csproj
@@ -16,7 +16,7 @@
10.0.22000.0
PerMonitorV2
AnyCPU;x64;ARM64;x86
- 2.1.3
+ 2.1.4
https://github.com/soleon/Percentage
https://github.com/soleon/Percentage?tab=MIT-1-ov-file
Icon.png
diff --git a/App/App.xaml.cs b/App/App.xaml.cs
index 037884f..59fb44a 100644
--- a/App/App.xaml.cs
+++ b/App/App.xaml.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -34,6 +34,7 @@ public partial class App
internal const bool DefaultIsAutoBatteryNormalColour = true;
internal const int DefaultRefreshSeconds = 60;
internal const bool DefaultTrayIconFontBold = false;
+ internal const int DefaultTrayIconFontSize = 16;
internal const bool DefaultTrayIconFontUnderline = false;
internal const string Id = "f05f920a-c997-4817-84bd-c54d87e40625";
private static Exception _trayIconUpdateError;
@@ -69,30 +70,11 @@ public App()
ToastNotificationManagerCompat.OnActivated += OnToastNotificationActivatedAsync;
}
- internal static MainWindow ActivateMainWindow()
- {
- var window = Current.Windows.OfType().FirstOrDefault();
- if (window != null)
- {
- window.Activate();
- return window;
- }
-
- window = new MainWindow();
- window.Show();
- return window;
- }
-
internal static Exception GetTrayIconUpdateError()
{
return _trayIconUpdateError;
}
- internal static NotifyIconWindow GetTrayIconWindow()
- {
- return Current.Windows.OfType().FirstOrDefault();
- }
-
private static void HandleException(object exception)
{
var version = VersionExtensions.GetAppVersion();
@@ -158,17 +140,15 @@ private void OnToastNotificationActivatedAsync(ToastNotificationActivatedEventAr
{
var arguments = ToastArguments.Parse(toastArgs.Argument);
if (!arguments.TryGetActionArgument(out var action))
- {
// When there's no action from toast notification activation, this is most likely triggered by users
// clicking the entire notification instead of an individual button.
// Show the details view in this case.
- Dispatcher.InvokeAsync(() => ActivateMainWindow().NavigateToPage());
- }
-
+ Dispatcher.InvokeAsync(() => this.ActivateMainWindow().NavigateToPage());
+
switch (action)
{
case ToastNotificationExtensions.Action.ViewDetails:
- Dispatcher.InvokeAsync(() => ActivateMainWindow().NavigateToPage());
+ Dispatcher.InvokeAsync(() => this.ActivateMainWindow().NavigateToPage());
break;
case ToastNotificationExtensions.Action.DisableBatteryNotification:
if (!arguments.TryGetNotificationTypeArgument(out var type)) break;
@@ -188,12 +168,14 @@ private void OnToastNotificationActivatedAsync(ToastNotificationActivatedEventAr
break;
case ToastNotificationExtensions.NotificationType.None:
default:
- throw new ArgumentOutOfRangeException(nameof(type), type, $"{type} is not supported.");
+ throw new InvalidEnumArgumentException(nameof(type), (int)type,
+ typeof(ToastNotificationExtensions.NotificationType));
}
break;
default:
- throw new ArgumentOutOfRangeException(nameof(action), action, $"{action} is not supported.");
+ throw new InvalidEnumArgumentException(nameof(action), (int)action,
+ typeof(ToastNotificationExtensions.Action));
}
}
}
\ No newline at end of file
diff --git a/App/Extensions/ApplicationExtensions.cs b/App/Extensions/ApplicationExtensions.cs
new file mode 100644
index 0000000..fe56e1d
--- /dev/null
+++ b/App/Extensions/ApplicationExtensions.cs
@@ -0,0 +1,26 @@
+using System.Linq;
+using System.Windows;
+
+namespace Percentage.App.Extensions;
+
+internal static class ApplicationExtensions
+{
+ internal static MainWindow ActivateMainWindow(this Application app)
+ {
+ var window = app.Windows.OfType().FirstOrDefault();
+ if (window != null)
+ {
+ window.Activate();
+ return window;
+ }
+
+ window = new MainWindow();
+ window.Show();
+ return window;
+ }
+
+ internal static NotifyIconWindow GetTrayIconWindow(this Application app)
+ {
+ return app.Windows.OfType().FirstOrDefault();
+ }
+}
\ No newline at end of file
diff --git a/App/Extensions/ExternalProcessExtensions.cs b/App/Extensions/ExternalProcessExtensions.cs
index b49ee3c..e606c4c 100644
--- a/App/Extensions/ExternalProcessExtensions.cs
+++ b/App/Extensions/ExternalProcessExtensions.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Diagnostics;
-using System.Reflection;
-using Windows.ApplicationModel;
+using System.Diagnostics;
namespace Percentage.App.Extensions;
@@ -17,6 +14,11 @@ internal static void OpenFeedbackLocation()
StartShellExecutedProgress("https://github.com/soleon/Percentage/issues");
}
+ internal static void OpenPowerSettings()
+ {
+ StartShellExecutedProgress("ms-settings:powersleep");
+ }
+
internal static void OpenSourceCodeLocation()
{
StartShellExecutedProgress("https://github.com/soleon/Percentage");
@@ -27,9 +29,15 @@ internal static void ShowRatingView()
StartShellExecutedProgress("ms-windows-store://review/?ProductId=9PCKT2B7DZMW");
}
- internal static void OpenPowerSettings()
+ internal static void SleepDevice()
{
- StartShellExecutedProgress("ms-settings:powersleep");
+ // Parameter 0,0,0 for "SetSuspendState" native function:
+ // 0: no hibernation
+ // 0: deprecated
+ // 0: allow wake-up events
+ // See documentation for "powerprof":
+ // https://learn.microsoft.com/windows/win32/api/powrprof/nf-powrprof-setsuspendstate
+ Process.Start("rundll32.exe", "powrprof.dll,SetSuspendState 0,0,0");
}
private static void StartShellExecutedProgress(string fileName)
diff --git a/App/Extensions/NotifyIconExtensions.cs b/App/Extensions/NotifyIconExtensions.cs
index 40a45b6..954bf6f 100644
--- a/App/Extensions/NotifyIconExtensions.cs
+++ b/App/Extensions/NotifyIconExtensions.cs
@@ -9,7 +9,7 @@ namespace Percentage.App.Extensions;
internal static class NotifyIconExtensions
{
- private const double NotifyIconSize = 16;
+ private const double DefaultNotifyIconSize = 16;
internal static void SetIcon(this NotifyIcon notifyIcon, FrameworkElement textBlock)
{
@@ -18,8 +18,8 @@ internal static void SetIcon(this NotifyIcon notifyIcon, FrameworkElement textBl
// Use the desired size to work out the appropriate margin so that the element can be centre aligned in the
// tray icon's 16-by-16 region.
- textBlock.Margin = new Thickness((NotifyIconSize - textBlock.DesiredSize.Width) / 2,
- (NotifyIconSize - textBlock.DesiredSize.Height) / 2, 0, 0);
+ textBlock.Margin = new Thickness((DefaultNotifyIconSize - textBlock.DesiredSize.Width) / 2,
+ (DefaultNotifyIconSize - textBlock.DesiredSize.Height) / 2, 0, 0);
// Measure again for the correct desired size with the margin.
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
@@ -28,14 +28,29 @@ internal static void SetIcon(this NotifyIcon notifyIcon, FrameworkElement textBl
// Render the element with the correct DPI scale.
var dpiScale = VisualTreeHelper.GetDpi(textBlock);
var renderTargetBitmap = new RenderTargetBitmap(
- (int)Math.Round(NotifyIconSize * dpiScale.DpiScaleX, MidpointRounding.AwayFromZero),
- (int)Math.Round(NotifyIconSize * dpiScale.DpiScaleY, MidpointRounding.AwayFromZero),
+ (int)Math.Round(DefaultNotifyIconSize * dpiScale.DpiScaleX, MidpointRounding.AwayFromZero),
+ (int)Math.Round(DefaultNotifyIconSize * dpiScale.DpiScaleY, MidpointRounding.AwayFromZero),
dpiScale.PixelsPerInchX,
dpiScale.PixelsPerInchY,
PixelFormats.Default);
renderTargetBitmap.Render(textBlock);
- notifyIcon.Icon = renderTargetBitmap;
+ // There's a chance that some native exception may be thrown when setting the icon's image.
+ // Catch any exception here and retry a few times then fail silently with logs.
+ for (var i = 0; i < 5; i++)
+ try
+ {
+ notifyIcon.Icon = renderTargetBitmap;
+ App.SetTrayIconUpdateError(null);
+ break;
+ }
+ catch (Exception e)
+ {
+ if (i == 4)
+ // Retried maximum number of times.
+ // Log error and continue.
+ App.SetTrayIconUpdateError(e);
+ }
}
internal static void SetBatteryFullIcon(this NotifyIcon notifyIcon)
diff --git a/App/NotifyIconWindow.xaml b/App/NotifyIconWindow.xaml
index d2dfdcc..a1eaca1 100644
--- a/App/NotifyIconWindow.xaml
+++ b/App/NotifyIconWindow.xaml
@@ -27,6 +27,10 @@
Click="OnSystemSettingsMenuItemClick"
Icon="{ui:SymbolIcon Power20}"
ToolTip="Open Windows system power and battery settings" />
+
();
+ Application.Current.ActivateMainWindow().NavigateToPage();
}
private void OnAppSettingsMenuItemClick(object sender, RoutedEventArgs e)
{
- App.ActivateMainWindow().NavigateToPage();
+ Application.Current.ActivateMainWindow().NavigateToPage();
}
private void OnDetailsMenuItemClick(object sender, RoutedEventArgs e)
{
- App.ActivateMainWindow().NavigateToPage();
+ Application.Current.ActivateMainWindow().NavigateToPage();
}
private void OnExitMenuItemClick(object sender, RoutedEventArgs e)
@@ -67,7 +67,7 @@ private void OnLoaded(object sender, RoutedEventArgs args)
{
Visibility = Visibility.Collapsed;
- if (!Default.HideAtStartup) App.ActivateMainWindow().NavigateToPage();
+ if (!Default.HideAtStartup) Application.Current.ActivateMainWindow().NavigateToPage();
// Debounce all calls to update battery status.
// This should be the only place that calls the UpdateBatteryStatus method.
@@ -109,7 +109,12 @@ private void OnLoaded(object sender, RoutedEventArgs args)
private void OnNotifyIconLeftDoubleClick(NotifyIcon sender, RoutedEventArgs e)
{
- App.ActivateMainWindow().NavigateToPage();
+ Application.Current.ActivateMainWindow().NavigateToPage();
+ }
+
+ private void OnSleepMenuItemClick(object sender, RoutedEventArgs e)
+ {
+ ExternalProcessExtensions.SleepDevice();
}
private void OnSystemSettingsMenuItemClick(object sender, RoutedEventArgs e)
@@ -170,22 +175,7 @@ private void SetNotifyIconText(string text, Brush foreground)
if (Default.TrayIconFontUnderline) textBlock.TextDecorations = TextDecorations.Underline;
- // There's a chance that some native exception may be thrown when setting the icon's image.
- // Catch any exception here and retry a few times then fail silently with logs.
- for (var i = 0; i < 5; i++)
- try
- {
- NotifyIcon.SetIcon(textBlock);
- App.SetTrayIconUpdateError(null);
- break;
- }
- catch (Exception e)
- {
- if (i == 4)
- // Retried maximum number of times.
- // Log error and continue.
- App.SetTrayIconUpdateError(e);
- }
+ NotifyIcon.SetIcon(textBlock);
}
private void UpdateBatteryStatus()
diff --git a/App/Pages/DetailsPage.xaml.cs b/App/Pages/DetailsPage.xaml.cs
index 5a83f05..3a03633 100644
--- a/App/Pages/DetailsPage.xaml.cs
+++ b/App/Pages/DetailsPage.xaml.cs
@@ -1,4 +1,5 @@
using System.Windows;
+using Percentage.App.Extensions;
namespace Percentage.App.Pages;
@@ -12,6 +13,6 @@ public DetailsPage()
private void OnRefreshButtonClick(object sender, RoutedEventArgs e)
{
BatteryInformation.RequestUpdate();
- App.GetTrayIconWindow().RequestBatteryStatusUpdate();
+ Application.Current.GetTrayIconWindow().RequestBatteryStatusUpdate();
}
}
\ No newline at end of file
diff --git a/App/Pages/SettingsPage.xaml.cs b/App/Pages/SettingsPage.xaml.cs
index a3e44e5..6508389 100644
--- a/App/Pages/SettingsPage.xaml.cs
+++ b/App/Pages/SettingsPage.xaml.cs
@@ -117,6 +117,7 @@ private void OnResetButtonClick(object sender, RoutedEventArgs e)
Default.TrayIconFontFamily = App.DefaultTrayIconFontFamily;
Default.TrayIconFontBold = App.DefaultTrayIconFontBold;
Default.TrayIconFontUnderline = App.DefaultTrayIconFontUnderline;
+ Default.TrayIconFontSize = App.DefaultTrayIconFontSize;
Default.BatteryCriticalNotificationValue = App.DefaultBatteryCriticalNotificationValue;
Default.BatteryLowNotificationValue = App.DefaultBatteryLowNotificationValue;
Default.BatteryHighNotificationValue = App.DefaultBatteryHighNotificationValue;
diff --git a/Pack/Package.appxmanifest b/Pack/Package.appxmanifest
index f49d85c..6853644 100644
--- a/Pack/Package.appxmanifest
+++ b/Pack/Package.appxmanifest
@@ -12,7 +12,7 @@
+ Version="2.1.4.0" />
Battery Percentage Icon