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