[Run] Switch to WPF UI theme manager (#30520)

* Switch to WPF UI theme manager

* fix

* add theme manager

* fix

* fix

* update error icon

* moved image initialization

* cleanup
This commit is contained in:
Davide Giacometti 2023-12-21 13:56:48 +01:00 committed by GitHub
parent e73e73fa6c
commit ae21b0dc09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 123 deletions

View File

@ -50,15 +50,7 @@ public partial class OCROverlay : Window
InitializeComponent();
// workaround for #30177
try
{
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, Wpf.Ui.Controls.WindowBackdropType.None);
}
catch (Exception ex)
{
Logger.LogError($"Exception in SystemThemeWatcher.Watch, issue 30177. {ex.Message}");
}
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, Wpf.Ui.Controls.WindowBackdropType.None);
PopulateLanguageMenu();
}

View File

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Linq;
using Common.UI;
using ImageResizer.ViewModels;
using ManagedCommon;
using Microsoft.Win32;
using Wpf.Ui.Controls;
using AppResources = ImageResizer.Properties.Resources;
@ -31,15 +30,7 @@ namespace ImageResizer.Views
WindowBackdropType = WindowBackdropType.None;
}
// workaround for #30177
try
{
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, WindowBackdropType);
}
catch (Exception ex)
{
Logger.LogError($"Exception in SystemThemeWatcher.Watch, issue 30177. {ex.Message}");
}
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, WindowBackdropType);
}
public IEnumerable<string> OpenPictureFiles()

View File

@ -2,13 +2,11 @@
x:Class="PowerLauncher.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:theming="clr-namespace:Common.UI;assembly=PowerToys.Common.UI"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
ShutdownMode="OnMainWindowClose"
Startup="OnStartup">
<Application.Resources>
<ResourceDictionary>
<theming:CustomLibraryThemeProvider x:Key="{x:Static theming:CustomLibraryThemeProvider.DefaultInstance}" />
<ResourceDictionary.MergedDictionaries>
<ui:ThemesDictionary Theme="Dark" />
<ui:ControlsDictionary />

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@ -9,7 +9,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using Common.UI;
using interop;
using ManagedCommon;
using Microsoft.PowerLauncher.Telemetry;
@ -23,7 +22,6 @@ using Wox.Infrastructure.Image;
using Wox.Infrastructure.UserSettings;
using Wox.Plugin;
using Wox.Plugin.Logger;
using Wpf.Ui.Appearance;
using Stopwatch = Wox.Infrastructure.Stopwatch;
namespace PowerLauncher
@ -82,7 +80,7 @@ namespace PowerLauncher
{
application.InitializeComponent();
NativeEventWaiter.WaitForEventLoop(
Common.UI.NativeEventWaiter.WaitForEventLoop(
Constants.RunExitEvent(),
() =>
{
@ -122,8 +120,7 @@ namespace PowerLauncher
RegisterAppDomainExceptions();
RegisterDispatcherUnhandledException();
_themeManager = new ThemeManager(this);
ImageLoader.Initialize(_themeManager.GetCurrentTheme());
ImageLoader.Initialize();
_settingsVM = new SettingWindowViewModel();
_settings = _settingsVM.Settings;
@ -136,6 +133,7 @@ namespace PowerLauncher
_mainVM = new MainViewModel(_settings, NativeThreadCTS.Token);
_mainWindow = new MainWindow(_settings, _mainVM, NativeThreadCTS.Token);
_themeManager = new ThemeManager(_settings, _mainWindow);
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet, _themeManager);
_settingsReader = new SettingsReader(_settings, _themeManager);
_settingsReader.ReadSettings();
@ -152,10 +150,6 @@ namespace PowerLauncher
_settingsReader.ReadSettingsOnChange();
_themeManager.ThemeChanged += OnThemeChanged;
OnThemeChanged(_settings.Theme, _settings.Theme);
textToLog.AppendLine("End PowerToys Run startup ---------------------------------------------------- ");
bootTime.Stop();
@ -214,48 +208,6 @@ namespace PowerLauncher
};
}
/// <summary>
/// Callback when windows theme is changed.
/// </summary>
/// <param name="oldTheme">Previous Theme</param>
/// <param name="newTheme">Current Theme</param>
private void OnThemeChanged(Theme oldTheme, Theme newTheme)
{
// If OS theme is high contrast, don't change theme.
if (SystemParameters.HighContrast)
{
return;
}
ApplicationTheme theme = ApplicationTheme.Unknown;
switch (newTheme)
{
case Theme.Dark:
theme = ApplicationTheme.Dark; break;
case Theme.Light:
theme = ApplicationTheme.Light; break;
case Theme.HighContrastWhite:
case Theme.HighContrastBlack:
case Theme.HighContrastOne:
case Theme.HighContrastTwo:
theme = ApplicationTheme.HighContrast; break;
default:
break;
}
_mainWindow?.Dispatcher.Invoke(() =>
{
if (theme != ApplicationTheme.Unknown)
{
ApplicationThemeManager.Apply(theme);
}
});
ImageLoader.UpdateIconPath(newTheme);
_mainVM.Query();
}
/// <summary>
/// let exception throw as normal is better for Debug
/// </summary>
@ -302,19 +254,12 @@ namespace PowerLauncher
Log.Info("Start PowerToys Run Exit---------------------------------------------------- ", GetType());
if (disposing)
{
if (_themeManager != null)
{
_themeManager.ThemeChanged -= OnThemeChanged;
}
API?.SaveAppAllSettings();
PluginManager.Dispose();
// Dispose needs to be called on the main Windows thread, since some resources owned by the thread need to be disposed.
_mainWindow?.Dispatcher.Invoke(Dispose);
API?.Dispose();
_mainVM?.Dispose();
_themeManager?.Dispose();
}
Log.Info("End PowerToys Run Exit ---------------------------------------------------- ", GetType());

View File

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using ManagedCommon;
using Microsoft.Win32;
using Wpf.Ui.Appearance;
namespace PowerLauncher.Helper
{
public static class ThemeExtensions
{
public static Theme ToTheme(this ApplicationTheme applicationTheme)
{
return applicationTheme switch
{
ApplicationTheme.Dark => Theme.Dark,
ApplicationTheme.Light => Theme.Light,
ApplicationTheme.HighContrast => GetHighContrastBaseType(),
_ => Theme.Light,
};
}
private static Theme GetHighContrastBaseType()
{
string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty);
theme = theme.Split('\\').Last().Split('.').First().ToString();
switch (theme)
{
case "hc1":
return Theme.HighContrastOne;
case "hc2":
return Theme.HighContrastTwo;
case "hcwhite":
return Theme.HighContrastWhite;
case "hcblack":
return Theme.HighContrastBlack;
default:
return Theme.HighContrastOne;
}
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using ManagedCommon;
using Wox.Infrastructure.Image;
using Wox.Infrastructure.UserSettings;
using Wpf.Ui.Appearance;
namespace PowerLauncher.Helper
{
public class ThemeManager : IDisposable
{
private readonly PowerToysRunSettings _settings;
private readonly MainWindow _mainWindow;
private Theme _currentTheme;
private bool _disposed;
public Theme CurrentTheme => _currentTheme;
public event Common.UI.ThemeChangedHandler ThemeChanged;
public ThemeManager(PowerToysRunSettings settings, MainWindow mainWindow)
{
_settings = settings;
_mainWindow = mainWindow;
_currentTheme = ApplicationThemeManager.GetAppTheme().ToTheme();
SetTheme(false);
ApplicationThemeManager.Changed += ApplicationThemeManager_Changed;
}
public void SetTheme(bool fromSettings)
{
if (_settings.Theme == Theme.Light)
{
_currentTheme = Theme.Light;
_mainWindow?.Dispatcher.Invoke(() => ApplicationThemeManager.Apply(ApplicationTheme.Light, _mainWindow.WindowBackdropType));
}
else if (_settings.Theme == Theme.Dark)
{
_currentTheme = Theme.Dark;
_mainWindow?.Dispatcher.Invoke(() => ApplicationThemeManager.Apply(ApplicationTheme.Dark, _mainWindow.WindowBackdropType));
}
else if (fromSettings)
{
_mainWindow?.Dispatcher.Invoke(ApplicationThemeManager.ApplySystemTheme);
}
ImageLoader.UpdateIconPath(_currentTheme);
// oldTheme isn't used
ThemeChanged?.Invoke(_currentTheme, _currentTheme);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void ApplicationThemeManager_Changed(ApplicationTheme currentApplicationTheme, System.Windows.Media.Color systemAccent)
{
var newTheme = currentApplicationTheme.ToTheme();
if (_currentTheme == newTheme)
{
return;
}
_currentTheme = newTheme;
SetTheme(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
ApplicationThemeManager.Changed -= ApplicationThemeManager_Changed;
}
_disposed = true;
}
}
}

View File

@ -24,6 +24,7 @@ using PowerLauncher.ViewModel;
using Wox.Infrastructure.UserSettings;
using Wox.Plugin;
using Wox.Plugin.Interfaces;
using Wpf.Ui.Appearance;
using CancellationToken = System.Threading.CancellationToken;
using Image = Wox.Infrastructure.Image;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
@ -65,15 +66,7 @@ namespace PowerLauncher
WindowBackdropType = Wpf.Ui.Controls.WindowBackdropType.None;
}
// workaround for #30217
try
{
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, WindowBackdropType);
}
catch (Exception ex)
{
Log.Exception("Exception in SystemThemeWatcher.Watch, issue 30217.", ex, GetType());
}
SystemThemeWatcher.Watch(this, WindowBackdropType);
_firstDeleteTimer.Elapsed += CheckForFirstDelete;
_firstDeleteTimer.Interval = 1000;
@ -803,11 +796,7 @@ namespace PowerLauncher
{
if (disposing)
{
if (_firstDeleteTimer != null)
{
_firstDeleteTimer.Dispose();
}
_firstDeleteTimer?.Dispose();
_hwndSource?.Dispose();
}

View File

@ -80,7 +80,6 @@
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" />
<PackageReference Include="ModernWpfUI" />
<PackageReference Include="ScipBe.Common.Office.OneNote" />
<PackageReference Include="System.Reactive" />
<PackageReference Include="System.Data.OleDb" />

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@ -6,18 +6,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Common.UI;
using ManagedCommon;
using Microsoft.Toolkit.Uwp.Notifications;
using PowerLauncher.Helper;
using PowerLauncher.Plugin;
using PowerLauncher.ViewModel;
using Windows.UI.Notifications;
using Wox.Infrastructure;
using Wox.Infrastructure.Image;
using Wox.Plugin;
@ -32,7 +26,7 @@ namespace Wox
private readonly ThemeManager _themeManager;
private bool _disposed;
public event ThemeChangedHandler ThemeChanged;
public event Common.UI.ThemeChangedHandler ThemeChanged;
public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, Alphabet alphabet, ThemeManager themeManager)
{
@ -108,7 +102,7 @@ namespace Wox
public Theme GetCurrentTheme()
{
return _themeManager.GetCurrentTheme();
return _themeManager.CurrentTheme;
}
public void Dispose()

View File

@ -9,7 +9,6 @@ using System.IO.Abstractions;
using System.Linq;
using System.Threading;
using System.Windows.Input;
using Common.UI;
using global::PowerToys.GPOWrapper;
using Microsoft.PowerToys.Settings.UI.Library;
using PowerLauncher.Helper;
@ -44,9 +43,6 @@ namespace PowerLauncher
var overloadSettings = _settingsUtils.GetSettingsOrDefault<PowerLauncherSettings>(PowerLauncherSettings.ModuleName);
UpdateSettings(overloadSettings);
_settingsUtils.SaveSettings(overloadSettings.ToJsonString(), PowerLauncherSettings.ModuleName);
// Apply theme at startup
_themeManager.ChangeTheme(_settings.Theme, true);
}
public void CreateSettingsIfNotExists()
@ -161,7 +157,7 @@ namespace PowerLauncher
if (_settings.Theme != overloadSettings.Properties.Theme)
{
_settings.Theme = overloadSettings.Properties.Theme;
_themeManager.ChangeTheme(_settings.Theme, true);
_themeManager.SetTheme(true);
}
if (_settings.StartupPosition != overloadSettings.Properties.Position)

View File

@ -31,7 +31,7 @@ namespace Wox.Infrastructure.Image
private static IImageHashGenerator _hashGenerator;
public static string ErrorIconPath { get; set; }
public static string ErrorIconPath { get; set; } = Constant.LightThemedErrorIcon;
private static readonly string[] ImageExtensions =
{
@ -54,7 +54,7 @@ namespace Wox.Infrastructure.Image
return fs.Read(buffer, 0, buffer.Length) == buffer.Length && pngSignature.SequenceEqual(buffer);
}
public static void Initialize(Theme theme)
public static void Initialize()
{
_hashGenerator = new ImageHashGenerator();
@ -86,7 +86,6 @@ namespace Wox.Infrastructure.Image
}
}
UpdateIconPath(theme);
Task.Run(() =>
{
Stopwatch.Normal("ImageLoader.Initialize - Preload images cost", async () =>

View File

@ -5,7 +5,6 @@
using System;
using System.ComponentModel;
using System.Windows;
using ManagedCommon;
using Wpf.Ui.Controls;
using Point = PowerAccent.Core.Point;
using Size = PowerAccent.Core.Size;
@ -40,15 +39,7 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
{
InitializeComponent();
// workaround for #30177
try
{
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this);
}
catch (Exception ex)
{
Logger.LogError($"Exception in SystemThemeWatcher.Watch, issue 30177. {ex.Message}");
}
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this);
Application.Current.MainWindow.ShowActivated = false;
Application.Current.MainWindow.Topmost = true;