diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 60ae046e3..4169f3534 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -16,6 +16,7 @@ using Microsoft.Win32; using Flow.Launcher.Plugin; using System.Windows.Threading; using TextBox = System.Windows.Controls.TextBox; +using System.Threading.Tasks; namespace Flow.Launcher.Core.Resource { @@ -45,6 +46,8 @@ namespace Flow.Launcher.Core.Resource #endregion + #region Constructor + public Theme(IPublicAPI publicAPI, Settings settings) { _api = publicAPI; @@ -71,6 +74,8 @@ namespace Flow.Launcher.Core.Resource _oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath); } + #endregion + #region Theme Resources private void MakeSureThemeDirectoriesExist() @@ -274,7 +279,7 @@ namespace Flow.Launcher.Core.Resource //if (_settings.UseDropShadowEffect) // AddDropShadowEffectToCurrentTheme(); //Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled); - SetBlurForWindow(); + _ = SetBlurForWindowAsync(); } catch (DirectoryNotFoundException) { @@ -348,7 +353,7 @@ namespace Flow.Launcher.Core.Resource windowBorderStyle.Setters.Add(effectSetter); - UpdateResourceDictionary(dict); + UpdateResourceDictionary(dict); } public void RemoveDropShadowEffectFromCurrentTheme() @@ -406,19 +411,22 @@ namespace Flow.Launcher.Core.Resource #region Blur Handling - public void RefreshFrame() + /// + /// Refreshes the frame to apply the current theme settings. + /// + public async Task RefreshFrameAsync() { - Application.Current.Dispatcher.Invoke(() => + await Application.Current.Dispatcher.InvokeAsync(async () => { // Remove OS minimizing/maximizing animation // Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3); - + // The timing of adding the shadow effect should vary depending on whether the theme is transparent. if (BlurEnabled) { AutoDropShadow(); } - SetBlurForWindow(); + await SetBlurForWindowAsync(); if (!BlurEnabled) { @@ -427,53 +435,12 @@ namespace Flow.Launcher.Core.Resource }, DispatcherPriority.Normal); } - private void AutoDropShadow() - { - SetWindowCornerPreference("Default"); - RemoveDropShadowEffectFromCurrentTheme(); - if (_settings.UseDropShadowEffect) - { - if (BlurEnabled && Win32Helper.IsBackdropSupported()) - { - SetWindowCornerPreference("Round"); - } - else - { - SetWindowCornerPreference("Default"); - AddDropShadowEffectToCurrentTheme(); - } - } - else - { - if (BlurEnabled && Win32Helper.IsBackdropSupported()) - { - SetWindowCornerPreference("Default"); - } - else - { - RemoveDropShadowEffectFromCurrentTheme(); - } - } - } - - private static void SetWindowCornerPreference(string cornerType) - { - Application.Current.Dispatcher.Invoke(() => - { - Window mainWindow = Application.Current.MainWindow; - if (mainWindow == null) - return; - - Win32Helper.DWMSetCornerPreferenceForWindow(mainWindow, cornerType); - }, DispatcherPriority.Normal); - } - /// /// Sets the blur for a window via SetWindowCompositionAttribute /// - public void SetBlurForWindow() + public async Task SetBlurForWindowAsync() { - Application.Current.Dispatcher.Invoke(() => + await Application.Current.Dispatcher.InvokeAsync(async () => { var dict = GetThemeResourceDictionary(_settings.Theme); if (dict == null) @@ -523,6 +490,44 @@ namespace Flow.Launcher.Core.Resource }, DispatcherPriority.Normal); } + private void AutoDropShadow() + { + SetWindowCornerPreference("Default"); + RemoveDropShadowEffectFromCurrentTheme(); + if (_settings.UseDropShadowEffect) + { + if (BlurEnabled && Win32Helper.IsBackdropSupported()) + { + SetWindowCornerPreference("Round"); + } + else + { + SetWindowCornerPreference("Default"); + AddDropShadowEffectToCurrentTheme(); + } + } + else + { + if (BlurEnabled && Win32Helper.IsBackdropSupported()) + { + SetWindowCornerPreference("Default"); + } + else + { + RemoveDropShadowEffectFromCurrentTheme(); + } + } + } + + private static void SetWindowCornerPreference(string cornerType) + { + Window mainWindow = Application.Current.MainWindow; + if (mainWindow == null) + return; + + Win32Helper.DWMSetCornerPreferenceForWindow(mainWindow, cornerType); + } + // Get Background Color from WindowBorderStyle when there not color for BG. // for theme has not "LightBG" or "DarkBG" case. private Color GetWindowBorderStyleBackground() @@ -571,133 +576,127 @@ namespace Flow.Launcher.Core.Resource { if (bgColor == null) return; - Application.Current.Dispatcher.Invoke(() => + // Copy the existing WindowBorderStyle + var previewStyle = new Style(typeof(Border)); + if (Application.Current.Resources.Contains("WindowBorderStyle")) { - // Copy the existing WindowBorderStyle - var previewStyle = new Style(typeof(Border)); - if (Application.Current.Resources.Contains("WindowBorderStyle")) + if (Application.Current.Resources["WindowBorderStyle"] is Style originalStyle) { - if (Application.Current.Resources["WindowBorderStyle"] is Style originalStyle) + foreach (var setter in originalStyle.Setters.OfType()) { - foreach (var setter in originalStyle.Setters.OfType()) - { - previewStyle.Setters.Add(new Setter(setter.Property, setter.Value)); - } + previewStyle.Setters.Add(new Setter(setter.Property, setter.Value)); } } + } - // Apply background color (remove transparency in color) - // WPF does not allow the use of an acrylic brush within the window's internal area, - // so transparency effects are not applied to the preview. - Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B); - previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor))); + // Apply background color (remove transparency in color) + // WPF does not allow the use of an acrylic brush within the window's internal area, + // so transparency effects are not applied to the preview. + Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B); + previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor))); - // The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues). - // The non-blur theme retains the previously set WindowBorderStyle. - if (BlurEnabled) - { - previewStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(5))); - previewStyle.Setters.Add(new Setter(Border.BorderThicknessProperty, new Thickness(1))); - } - Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle; - }, DispatcherPriority.Render); + // The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues). + // The non-blur theme retains the previously set WindowBorderStyle. + if (BlurEnabled) + { + previewStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(5))); + previewStyle.Setters.Add(new Setter(Border.BorderThicknessProperty, new Thickness(1))); + } + Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle; } private void ColorizeWindow() { - Application.Current.Dispatcher.Invoke(() => + var dict = GetThemeResourceDictionary(_settings.Theme); + if (dict == null) return; + + var mainWindow = Application.Current.MainWindow; + if (mainWindow == null) return; + + // Check if the theme supports blur + bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b; + + // SystemBG value check (Auto, Light, Dark) + string systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto + + // Check the user's ColorScheme setting + string colorScheme = _settings.ColorScheme; + + // Check system dark mode setting (read AppsUseLightTheme value) + int themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1); + bool isSystemDark = themeValue == 0; + + // Final decision on whether to use dark mode + bool useDarkMode = false; + + // If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value + if (systemBG == "Dark") { - var dict = GetThemeResourceDictionary(_settings.Theme); - if (dict == null) return; + useDarkMode = true; // Dark + } + else if (systemBG == "Light") + { + useDarkMode = false; // Light + } + else if (systemBG == "Auto") + { + // If systemBG is "Auto", decide based on ColorScheme + if (colorScheme == "Dark") + useDarkMode = true; + else if (colorScheme == "Light") + useDarkMode = false; + else + useDarkMode = isSystemDark; // Auto (based on system setting) + } - var mainWindow = Application.Current.MainWindow; - if (mainWindow == null) return; + // Apply DWM Dark Mode + Win32Helper.DWMSetDarkModeForWindow(mainWindow, useDarkMode); - // Check if the theme supports blur - bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b; + Color LightBG; + Color DarkBG; - // SystemBG value check (Auto, Light, Dark) - string systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto + // Retrieve LightBG value (fallback to WindowBorderStyle background color if not found) + try + { + LightBG = dict.Contains("LightBG") ? (Color)dict["LightBG"] : GetWindowBorderStyleBackground(); + } + catch (Exception) + { + LightBG = GetWindowBorderStyleBackground(); + } - // Check the user's ColorScheme setting - string colorScheme = _settings.ColorScheme; + // Retrieve DarkBG value (fallback to LightBG if not found) + try + { + DarkBG = dict.Contains("DarkBG") ? (Color)dict["DarkBG"] : LightBG; + } + catch (Exception) + { + DarkBG = LightBG; + } - // Check system dark mode setting (read AppsUseLightTheme value) - int themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1); - bool isSystemDark = themeValue == 0; + // Select background color based on ColorScheme and SystemBG + Color selectedBG = useDarkMode ? DarkBG : LightBG; + ApplyPreviewBackground(selectedBG); - // Final decision on whether to use dark mode - bool useDarkMode = false; + bool isBlurAvailable = hasBlur && Win32Helper.IsBackdropSupported(); // Windows 11 미만이면 hasBlur를 강제 false - // If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value - if (systemBG == "Dark") + if (!isBlurAvailable) + { + mainWindow.Background = Brushes.Transparent; + } + else + { + // Only set the background to transparent if the theme supports blur + if (_settings.BackdropType == BackdropTypes.Mica || _settings.BackdropType == BackdropTypes.MicaAlt) { - useDarkMode = true; // Dark - } - else if (systemBG == "Light") - { - useDarkMode = false; // Light - } - else if (systemBG == "Auto") - { - // If systemBG is "Auto", decide based on ColorScheme - if (colorScheme == "Dark") - useDarkMode = true; - else if (colorScheme == "Light") - useDarkMode = false; - else - useDarkMode = isSystemDark; // Auto (based on system setting) - } - - // Apply DWM Dark Mode - Win32Helper.DWMSetDarkModeForWindow(mainWindow, useDarkMode); - - Color LightBG; - Color DarkBG; - - // Retrieve LightBG value (fallback to WindowBorderStyle background color if not found) - try - { - LightBG = dict.Contains("LightBG") ? (Color)dict["LightBG"] : GetWindowBorderStyleBackground(); - } - catch (Exception) - { - LightBG = GetWindowBorderStyleBackground(); - } - - // Retrieve DarkBG value (fallback to LightBG if not found) - try - { - DarkBG = dict.Contains("DarkBG") ? (Color)dict["DarkBG"] : LightBG; - } - catch (Exception) - { - DarkBG = LightBG; - } - - // Select background color based on ColorScheme and SystemBG - Color selectedBG = useDarkMode ? DarkBG : LightBG; - ApplyPreviewBackground(selectedBG); - - bool isBlurAvailable = hasBlur && Win32Helper.IsBackdropSupported(); // Windows 11 미만이면 hasBlur를 강제 false - - if (!isBlurAvailable) - { - mainWindow.Background = Brushes.Transparent; + mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); } else { - // Only set the background to transparent if the theme supports blur - if (_settings.BackdropType == BackdropTypes.Mica || _settings.BackdropType == BackdropTypes.MicaAlt) - { - mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); - } - else - { - mainWindow.Background = new SolidColorBrush(selectedBG); - } + mainWindow.Background = new SolidColorBrush(selectedBG); } - }, DispatcherPriority.Normal); + } } private static bool IsBlurTheme() @@ -712,6 +711,10 @@ namespace Flow.Launcher.Core.Resource #endregion + #region Classes + public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null); + + #endregion } } diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index c6cf55e64..b8ae4bc85 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -62,10 +62,7 @@ namespace Flow.Launcher _settings = settings; InitializeComponent(); - // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 - InitializePosition(); - InitializePosition(); - + InitSoundEffects(); DataObject.AddPastingHandler(QueryTextBox, OnPaste); this.Loaded += (_, _) => @@ -185,11 +182,10 @@ namespace Flow.Launcher { } - private void OnLoaded(object sender, RoutedEventArgs _) + private async void OnLoaded(object sender, RoutedEventArgs _) { // Refresh frame - ThemeManager.Instance.RefreshFrame(); - + await ThemeManager.Instance.RefreshFrameAsync(); // MouseEventHandler PreviewMouseMove += MainPreviewMouseMove; CheckFirstLaunch(); @@ -833,7 +829,6 @@ namespace Flow.Launcher public void HideStartup() { - UpdatePosition(); //_viewModel.MainWindowOpacity = 0.2; /*Fix Render Blinking */ if (_settings.HideOnStartup) { diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index cd51e760e..1036d321c 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -53,7 +53,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel OnPropertyChanged(nameof(IsDropShadowEnabled)); OnPropertyChanged(nameof(DropShadowEffect)); - ThemeManager.Instance.RefreshFrame(); + _ = ThemeManager.Instance.RefreshFrameAsync(); } } @@ -144,7 +144,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel _ => ThemeManagerForColorSchemeSwitch.Current.ApplicationTheme }; Settings.ColorScheme = value; - ThemeManager.Instance.RefreshFrame(); + _ = ThemeManager.Instance.RefreshFrameAsync(); } } @@ -225,7 +225,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel { public void ApplyBackdrop() { - ThemeManager.Instance.SetBlurForWindow(); + _ = ThemeManager.Instance.SetBlurForWindowAsync(); } } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs index 13928563d..93d65cb0a 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs @@ -39,7 +39,7 @@ namespace Flow.Launcher.Plugin.Sys _settings.UseDropShadowEffect = true; } - _theme.RefreshFrame(); + _ = _theme.RefreshFrameAsync(); } }