From 3f0c142d3d7c9f672ede65d346496fd7f2342a20 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 11:56:44 +0800 Subject: [PATCH 01/13] Fix hide window startup issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2bfc9587c..192f8b481 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -638,8 +638,13 @@ namespace Flow.Launcher.ViewModel /// private async Task ChangeQueryTextAsync(string queryText, bool isReQuery = false) { - await Application.Current.Dispatcher.InvokeAsync(async () => + // Must check access so that we will not block the UI thread which cause window visibility issue + if (!Application.Current.Dispatcher.CheckAccess()) { + await Application.Current.Dispatcher.InvokeAsync(() => ChangeQueryText(queryText, isReQuery)); + return; + } + BackToQueryResults(); if (QueryText != queryText) @@ -656,7 +661,6 @@ namespace Flow.Launcher.ViewModel } QueryTextCursorMovedToEnd = true; - }); } public bool LastQuerySelected { get; set; } From 7d39c5259604cb7e83088f46b6959e2644db62cc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 12:00:23 +0800 Subject: [PATCH 02/13] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 62 ++++++++++++---- Flow.Launcher/ViewModel/MainViewModel.cs | 91 ++++++------------------ 2 files changed, 70 insertions(+), 83 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 25d9c5d2d..89d4f0847 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -161,24 +161,67 @@ namespace Flow.Launcher { if (_viewModel.MainWindowVisibilityStatus) { + // Set clock and search icon opacity + var opacity = _settings.UseAnimation ? 0.0 : 1.0; + ClockPanel.Opacity = opacity; + SearchIcon.Opacity = opacity; + + // Set clock and search icon visibility + ClockPanel.Visibility = string.IsNullOrEmpty(_viewModel.QueryText) ? Visibility.Visible : Visibility.Collapsed; + if (_viewModel.PluginIconSource != null) + { + SearchIcon.Opacity = 0.0; + } + else + { + _viewModel.SearchIconVisibility = Visibility.Visible; + } + + // Play sound effect before activing the window if (_settings.UseSound) { SoundPlay(); } + // Update position & Activate UpdatePosition(); - _viewModel.ResetPreview(); Activate(); - QueryTextBox.Focus(); - _settings.ActivateTimes++; + + // Reset preview + _viewModel.ResetPreview(); + + // Select last query if need if (!_viewModel.LastQuerySelected) { QueryTextBox.SelectAll(); _viewModel.LastQuerySelected = true; } + // Focus query box + QueryTextBox.Focus(); + + // Play window animation if (_settings.UseAnimation) + { WindowAnimation(); + } + + _settings.ActivateTimes++; + } + else + { + // Set clock and search icon opacity + var opacity = _settings.UseAnimation ? 0.0 : 1.0; + ClockPanel.Opacity = opacity; + SearchIcon.Opacity = opacity; + + // Set clock and search icon visibility + ClockPanel.Visibility = Visibility.Hidden; + _viewModel.SearchIconVisibility = Visibility.Hidden; + + // Force UI update + ClockPanel.UpdateLayout(); + SearchIcon.UpdateLayout(); } }); break; @@ -191,7 +234,6 @@ namespace Flow.Launcher Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); _viewModel.QueryTextCursorMovedToEnd = false; } - break; case nameof(MainViewModel.GameModeStatus): _notifyIcon.Icon = _viewModel.GameModeStatus @@ -280,8 +322,8 @@ namespace Flow.Launcher _settings.WindowLeft = Left; _settings.WindowTop = Top; - ClockPanel.Opacity = 0; - SearchIcon.Opacity = 0; + ClockPanel.Opacity = 0.0; + SearchIcon.Opacity = 0.0; // This condition stops extra hide call when animator is on, // which causes the toggling to occasional hide instead of show. @@ -291,7 +333,9 @@ namespace Flow.Launcher // This also stops the mainwindow from flickering occasionally after Settings window is opened // and always after Settings window is closed. if (_settings.UseAnimation) + { await Task.Delay(100); + } if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible) { @@ -765,12 +809,6 @@ namespace Flow.Launcher { _isArrowKeyPressed = true; - UpdatePosition(); - - var opacity = _settings.UseAnimation ? 0.0 : 1.0; - ClockPanel.Opacity = opacity; - SearchIcon.Opacity = opacity; - var clocksb = new Storyboard(); var iconsb = new Storyboard(); var easing = new CircleEase { EasingMode = EasingMode.EaseInOut }; diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 192f8b481..9e63244f4 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; @@ -645,22 +644,22 @@ namespace Flow.Launcher.ViewModel return; } - BackToQueryResults(); + BackToQueryResults(); - if (QueryText != queryText) - { - // re-query is done in QueryText's setter method - QueryText = queryText; - // set to false so the subsequent set true triggers - // PropertyChanged and MoveQueryTextToEnd is called - QueryTextCursorMovedToEnd = false; - } - else if (isReQuery) - { - await QueryAsync(isReQuery: true); - } + if (QueryText != queryText) + { + // re-query is done in QueryText's setter method + QueryText = queryText; + // set to false so the subsequent set true triggers + // PropertyChanged and MoveQueryTextToEnd is called + QueryTextCursorMovedToEnd = false; + } + else if (isReQuery) + { + await QueryAsync(isReQuery: true); + } - QueryTextCursorMovedToEnd = true; + QueryTextCursorMovedToEnd = true; } public bool LastQuerySelected { get; set; } @@ -1448,43 +1447,10 @@ namespace Flow.Launcher.ViewModel #pragma warning disable VSTHRD100 // Avoid async void methods - public async void Show() + public void Show() { - await Application.Current.Dispatcher.InvokeAsync(() => - { - if (Application.Current.MainWindow is MainWindow mainWindow) - { - // 📌 Remove DWM Cloak (Make the window visible normally) - Win32Helper.DWMSetCloakForWindow(mainWindow, false); - - // Clock and SearchIcon hide when show situation - var opacity = Settings.UseAnimation ? 0.0 : 1.0; - mainWindow.ClockPanel.Opacity = opacity; - mainWindow.SearchIcon.Opacity = opacity; - - // QueryText sometimes is null when it is just initialized - if (QueryText != null && QueryText.Length != 0) - { - mainWindow.ClockPanel.Visibility = Visibility.Collapsed; - } - else - { - mainWindow.ClockPanel.Visibility = Visibility.Visible; - } - - if (PluginIconSource != null) - { - mainWindow.SearchIcon.Opacity = 0; - } - else - { - SearchIconVisibility = Visibility.Visible; - } - - // 📌 Restore UI elements - //mainWindow.SearchIcon.Visibility = Visibility.Visible; - } - }, DispatcherPriority.Render); + // 📌 Remove DWM Cloak (Make the window visible normally) + Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, false); // Update WPF properties MainWindowVisibility = Visibility.Visible; @@ -1500,6 +1466,9 @@ namespace Flow.Launcher.ViewModel public async void Hide() { + // 📌 Apply DWM Cloak (Completely hide the window) + Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, true); + lastHistoryIndex = 1; if (ExternalPreviewVisible) @@ -1534,26 +1503,6 @@ namespace Flow.Launcher.ViewModel break; } - await Application.Current.Dispatcher.InvokeAsync(() => - { - if (Application.Current.MainWindow is MainWindow mainWindow) - { - // 📌 Set Opacity of icon and clock to 0 and apply Visibility.Hidden - var opacity = Settings.UseAnimation ? 0.0 : 1.0; - mainWindow.ClockPanel.Opacity = opacity; - mainWindow.SearchIcon.Opacity = opacity; - mainWindow.ClockPanel.Visibility = Visibility.Hidden; - SearchIconVisibility = Visibility.Hidden; - - // Force UI update - mainWindow.ClockPanel.UpdateLayout(); - mainWindow.SearchIcon.UpdateLayout(); - - // 📌 Apply DWM Cloak (Completely hide the window) - Win32Helper.DWMSetCloakForWindow(mainWindow, true); - } - }); - if (StartWithEnglishMode) { Win32Helper.RestorePreviousKeyboardLayout(); From de5803482c3509f11d5c911c1e888ee40fdbd848 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 12:33:07 +0800 Subject: [PATCH 03/13] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 89d4f0847..a44b3a305 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -101,11 +101,16 @@ namespace Flow.Launcher // Check first launch if (_settings.FirstLaunch) { + // Set First Launch to false _settings.FirstLaunch = false; + + // Set Backdrop Type to Acrylic for Windows 11 when First Launch. Default is None + if (Win32Helper.IsBackdropSupported()) _settings.BackdropType = BackdropTypes.Acrylic; + + // Save settings App.API.SaveAppAllSettings(); - /* Set Backdrop Type to Acrylic for Windows 11 when First Launch. Default is None. */ - if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000)) - _settings.BackdropType = BackdropTypes.Acrylic; + + // Show Welcome Window var WelcomeWindow = new WelcomeWindow(); WelcomeWindow.Show(); } @@ -206,6 +211,7 @@ namespace Flow.Launcher WindowAnimation(); } + // Update activate times _settings.ActivateTimes++; } else From 589fefce3c307db30c0cf404ee0352f8a31f8f87 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 13:50:36 +0800 Subject: [PATCH 04/13] Adjust blank lines --- Flow.Launcher/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index a44b3a305..9eb1271ec 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -948,6 +948,7 @@ namespace Flow.Launcher ClockPanel.BeginAnimation(OpacityProperty, fadeOut); } + // ✅ 4. When showing ClockPanel (apply fade-in animation) else if (shouldShowClock && ClockPanel.Visibility != Visibility.Visible && !_isClockPanelAnimating) { @@ -971,7 +972,6 @@ namespace Flow.Launcher } } - private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0) { if (style == null) From 40d2d58406a62d2d66f1ec49c329cf2040b24306 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:31:46 +0800 Subject: [PATCH 05/13] Fix InvalidOperationException --- Flow.Launcher/MainWindow.xaml.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 9eb1271ec..7b3814c87 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -296,7 +296,8 @@ namespace Flow.Launcher Notification.Uninstall(); // After plugins are all disposed, we can close the main window _canClose = true; - Close(); + // Use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event + Application.Current.Shutdown(); } } From b519313c25f8b74f8577c89f1a15d907bde961ba Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:32:03 +0800 Subject: [PATCH 06/13] Adjust log debug location --- Flow.Launcher/App.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 833c63ddf..1270d16bf 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -138,13 +138,13 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { - Log.SetLogLevel(_settings.LogLevel); - Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); + Log.SetLogLevel(_settings.LogLevel); + RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); From 8269304f4e48dc28bef83ef7a6d9568279bef214 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:45:52 +0800 Subject: [PATCH 07/13] Check disposed for result view update ending --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9e63244f4..6bfde42c5 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -212,7 +212,8 @@ namespace Flow.Launcher.ViewModel queue.Clear(); } - Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); + if (!_disposed) + Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); } void continueAction(Task t) From 91e2366c499cebae354f0aa3d0b62dbc3e4ad900 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:49:28 +0800 Subject: [PATCH 08/13] Revert "Adjust log debug location" This reverts commit b519313c25f8b74f8577c89f1a15d907bde961ba. --- Flow.Launcher/App.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 1270d16bf..833c63ddf 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -138,13 +138,13 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { + Log.SetLogLevel(_settings.LogLevel); + Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); - Log.SetLogLevel(_settings.LogLevel); - RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); From 8a08e03cf95173b8c6c84166190832e5199b4118 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 15:52:31 +0800 Subject: [PATCH 09/13] Bind to vm --- Flow.Launcher/MainWindow.xaml | 5 ++++- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 087b2e998..34b376cc9 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -290,7 +290,9 @@ + Opacity="{Binding ClockPanelOpacity}" + Style="{DynamicResource ClockPanel}" + Visibility="{Binding ClockPanelVisibility}"> diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6bfde42c5..1631ff7c3 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -760,7 +760,10 @@ namespace Flow.Launcher.ViewModel public event VisibilityChangedEventHandler VisibilityChanged; + public Visibility ClockPanelVisibility { get; set; } public Visibility SearchIconVisibility { get; set; } + public double ClockPanelOpacity { get; set; } = 1; + public double SearchIconOpacity { get; set; } = 1; public double MainWindowWidth { From 693ba52fbc3e83775840e2f5591d88372b4dff4b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 16:13:02 +0800 Subject: [PATCH 10/13] Fix first-time window flicker & clock panel flicker issue --- Flow.Launcher/MainWindow.xaml.cs | 53 +++++------------------ Flow.Launcher/ViewModel/MainViewModel.cs | 55 +++++++++++++++++++++--- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 7b3814c87..86b65386e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -166,22 +166,6 @@ namespace Flow.Launcher { if (_viewModel.MainWindowVisibilityStatus) { - // Set clock and search icon opacity - var opacity = _settings.UseAnimation ? 0.0 : 1.0; - ClockPanel.Opacity = opacity; - SearchIcon.Opacity = opacity; - - // Set clock and search icon visibility - ClockPanel.Visibility = string.IsNullOrEmpty(_viewModel.QueryText) ? Visibility.Visible : Visibility.Collapsed; - if (_viewModel.PluginIconSource != null) - { - SearchIcon.Opacity = 0.0; - } - else - { - _viewModel.SearchIconVisibility = Visibility.Visible; - } - // Play sound effect before activing the window if (_settings.UseSound) { @@ -214,21 +198,6 @@ namespace Flow.Launcher // Update activate times _settings.ActivateTimes++; } - else - { - // Set clock and search icon opacity - var opacity = _settings.UseAnimation ? 0.0 : 1.0; - ClockPanel.Opacity = opacity; - SearchIcon.Opacity = opacity; - - // Set clock and search icon visibility - ClockPanel.Visibility = Visibility.Hidden; - _viewModel.SearchIconVisibility = Visibility.Hidden; - - // Force UI update - ClockPanel.UpdateLayout(); - SearchIcon.UpdateLayout(); - } }); break; } @@ -329,8 +298,8 @@ namespace Flow.Launcher _settings.WindowLeft = Left; _settings.WindowTop = Top; - ClockPanel.Opacity = 0.0; - SearchIcon.Opacity = 0.0; + _viewModel.ClockPanelOpacity = 0.0; + _viewModel.SearchIconOpacity = 0.0; // This condition stops extra hide call when animator is on, // which causes the toggling to occasional hide instead of show. @@ -908,28 +877,28 @@ namespace Flow.Launcher var animationDuration = TimeSpan.FromMilliseconds(animationLength * 2 / 3); // ✅ Conditions for showing ClockPanel (No query input & ContextMenu, History are closed) - bool shouldShowClock = QueryTextBox.Text.Length == 0 && + var shouldShowClock = QueryTextBox.Text.Length == 0 && ContextMenu.Visibility != Visibility.Visible && History.Visibility != Visibility.Visible; // ✅ 1. When ContextMenu opens, immediately set Visibility.Hidden (force hide without animation) if (ContextMenu.Visibility == Visibility.Visible) { - ClockPanel.Visibility = Visibility.Hidden; - ClockPanel.Opacity = 0.0; // Set to 0 in case Opacity animation affects it + _viewModel.ClockPanelVisibility = Visibility.Hidden; + _viewModel.ClockPanelOpacity = 0.0; // Set to 0 in case Opacity animation affects it return; } // ✅ 2. When ContextMenu is closed, keep it Hidden if there's text in the query (remember previous state) if (ContextMenu.Visibility != Visibility.Visible && QueryTextBox.Text.Length > 0) { - ClockPanel.Visibility = Visibility.Hidden; - ClockPanel.Opacity = 0.0; + _viewModel.ClockPanelVisibility = Visibility.Hidden; + _viewModel.ClockPanelOpacity = 0.0; return; } // ✅ 3. When hiding ClockPanel (apply fade-out animation) - if ((!shouldShowClock) && ClockPanel.Visibility == Visibility.Visible && !_isClockPanelAnimating) + if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible && !_isClockPanelAnimating) { _isClockPanelAnimating = true; @@ -943,7 +912,7 @@ namespace Flow.Launcher fadeOut.Completed += (s, e) => { - ClockPanel.Visibility = Visibility.Hidden; // ✅ Completely hide after animation + _viewModel.ClockPanelVisibility = Visibility.Hidden; // ✅ Completely hide after animation _isClockPanelAnimating = false; }; @@ -951,13 +920,13 @@ namespace Flow.Launcher } // ✅ 4. When showing ClockPanel (apply fade-in animation) - else if (shouldShowClock && ClockPanel.Visibility != Visibility.Visible && !_isClockPanelAnimating) + else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible && !_isClockPanelAnimating) { _isClockPanelAnimating = true; Application.Current.Dispatcher.Invoke(() => { - ClockPanel.Visibility = Visibility.Visible; // ✅ Set Visibility to Visible first + _viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first var fadeIn = new DoubleAnimation { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 1631ff7c3..18ee2cb5b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; @@ -1453,8 +1454,30 @@ namespace Flow.Launcher.ViewModel public void Show() { - // 📌 Remove DWM Cloak (Make the window visible normally) - Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, false); + Application.Current.Dispatcher.Invoke(() => + { + if (Application.Current.MainWindow is MainWindow mainWindow) + { + // 📌 Remove DWM Cloak (Make the window visible normally) + Win32Helper.DWMSetCloakForWindow(mainWindow, false); + + // Set clock and search icon opacity + var opacity = Settings.UseAnimation ? 0.0 : 1.0; + ClockPanelOpacity = opacity; + SearchIconOpacity = opacity; + + // Set clock and search icon visibility + ClockPanelVisibility = string.IsNullOrEmpty(QueryText) ? Visibility.Visible : Visibility.Collapsed; + if (PluginIconSource != null) + { + SearchIconOpacity = 0.0; + } + else + { + SearchIconVisibility = Visibility.Visible; + } + } + }, DispatcherPriority.Render); // Update WPF properties MainWindowVisibility = Visibility.Visible; @@ -1470,9 +1493,6 @@ namespace Flow.Launcher.ViewModel public async void Hide() { - // 📌 Apply DWM Cloak (Completely hide the window) - Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, true); - lastHistoryIndex = 1; if (ExternalPreviewVisible) @@ -1507,11 +1527,36 @@ namespace Flow.Launcher.ViewModel break; } + Application.Current.Dispatcher.Invoke(() => + { + if (Application.Current.MainWindow is MainWindow mainWindow) + { + // Set clock and search icon opacity + var opacity = Settings.UseAnimation ? 0.0 : 1.0; + ClockPanelOpacity = opacity; + SearchIconOpacity = opacity; + + // Set clock and search icon visibility + ClockPanelVisibility = Visibility.Hidden; + SearchIconVisibility = Visibility.Hidden; + + // Force UI update + mainWindow.ClockPanel.UpdateLayout(); + mainWindow.SearchIcon.UpdateLayout(); + + // 📌 Apply DWM Cloak (Completely hide the window) + Win32Helper.DWMSetCloakForWindow(mainWindow, true); + } + }, DispatcherPriority.Render); + if (StartWithEnglishMode) { Win32Helper.RestorePreviousKeyboardLayout(); } + // Delay for a while to make sure clock will not flicker + await Task.Delay(50); + // Update WPF properties //MainWindowOpacity = 0; MainWindowVisibilityStatus = false; From 82909cd9fa81e3f8855bfe8c0f5c1ddda390c756 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 21:39:25 +0800 Subject: [PATCH 11/13] Fix application null exception & Remove unused MainWindowOpacity --- Flow.Launcher/MainWindow.xaml | 1 - Flow.Launcher/ViewModel/MainViewModel.cs | 15 +++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 34b376cc9..533819d17 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -24,7 +24,6 @@ Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Loaded="OnLoaded" LocationChanged="OnLocationChanged" - Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" PreviewKeyDown="OnKeyDown" PreviewKeyUp="OnKeyUp" PreviewMouseMove="OnPreviewMouseMove" diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 18ee2cb5b..c69fc4484 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -753,8 +753,7 @@ namespace Flow.Launcher.ViewModel public Visibility ProgressBarVisibility { get; set; } public Visibility MainWindowVisibility { get; set; } - public double MainWindowOpacity { get; set; } = 1; - + // This is to be used for determining the visibility status of the mainwindow instead of MainWindowVisibility // because it is more accurate and reliable representation than using Visibility as a condition check public bool MainWindowVisibilityStatus { get; set; } = true; @@ -1454,9 +1453,11 @@ namespace Flow.Launcher.ViewModel public void Show() { + // Invoke on UI thread Application.Current.Dispatcher.Invoke(() => { - if (Application.Current.MainWindow is MainWindow mainWindow) + // When application is exitting, the Application.Current will be null + if (Application.Current?.MainWindow is MainWindow mainWindow) { // 📌 Remove DWM Cloak (Make the window visible normally) Win32Helper.DWMSetCloakForWindow(mainWindow, false); @@ -1481,10 +1482,10 @@ namespace Flow.Launcher.ViewModel // Update WPF properties MainWindowVisibility = Visibility.Visible; - MainWindowOpacity = 1; MainWindowVisibilityStatus = true; VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); + // Switch keyboard layout if (StartWithEnglishMode) { Win32Helper.SwitchToEnglishKeyboardLayout(true); @@ -1527,9 +1528,11 @@ namespace Flow.Launcher.ViewModel break; } + // Invoke on UI thread Application.Current.Dispatcher.Invoke(() => { - if (Application.Current.MainWindow is MainWindow mainWindow) + // When application is exitting, the Application.Current will be null + if (Application.Current?.MainWindow is MainWindow mainWindow) { // Set clock and search icon opacity var opacity = Settings.UseAnimation ? 0.0 : 1.0; @@ -1549,6 +1552,7 @@ namespace Flow.Launcher.ViewModel } }, DispatcherPriority.Render); + // Switch keyboard layout if (StartWithEnglishMode) { Win32Helper.RestorePreviousKeyboardLayout(); @@ -1558,7 +1562,6 @@ namespace Flow.Launcher.ViewModel await Task.Delay(50); // Update WPF properties - //MainWindowOpacity = 0; MainWindowVisibilityStatus = false; MainWindowVisibility = Visibility.Collapsed; VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false }); From 494e947ccc4134bcf6709ae570ee5c61a7d9fb83 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 21:42:40 +0800 Subject: [PATCH 12/13] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 47 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 86b65386e..d8c05971a 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -241,7 +241,7 @@ namespace Flow.Launcher }; // QueryTextBox.Text change detection (modified to only work when character count is 1 or higher) - QueryTextBox.TextChanged += (sender, e) => UpdateClockPanelVisibility(); + QueryTextBox.TextChanged += (s, e) => UpdateClockPanelVisibility(); // Detecting ContextMenu.Visibility changes DependencyPropertyDescriptor @@ -351,7 +351,6 @@ namespace Flow.Launcher _viewModel.LoadContextMenuCommand.Execute(null); e.Handled = true; } - break; case Key.Left: if (!_viewModel.QueryResultsSelected() && QueryTextBox.CaretIndex == 0) @@ -359,7 +358,6 @@ namespace Flow.Launcher _viewModel.EscCommand.Execute(null); e.Handled = true; } - break; case Key.Back: if (specialKeyState.CtrlPressed) @@ -378,7 +376,6 @@ namespace Flow.Launcher } } } - break; default: break; @@ -864,8 +861,11 @@ namespace Flow.Launcher private void UpdateClockPanelVisibility() { if (QueryTextBox == null || ContextMenu == null || History == null || ClockPanel == null) + { return; + } + // ✅ Initialize animation length & duration var animationLength = _settings.AnimationSpeed switch { AnimationSpeeds.Slow => 560, @@ -873,7 +873,6 @@ namespace Flow.Launcher AnimationSpeeds.Fast => 160, _ => _settings.CustomAnimationLength }; - var animationDuration = TimeSpan.FromMilliseconds(animationLength * 2 / 3); // ✅ Conditions for showing ClockPanel (No query input & ContextMenu, History are closed) @@ -890,15 +889,21 @@ namespace Flow.Launcher } // ✅ 2. When ContextMenu is closed, keep it Hidden if there's text in the query (remember previous state) - if (ContextMenu.Visibility != Visibility.Visible && QueryTextBox.Text.Length > 0) + else if (QueryTextBox.Text.Length > 0) { _viewModel.ClockPanelVisibility = Visibility.Hidden; _viewModel.ClockPanelOpacity = 0.0; return; } + // ✅ Prevent multiple animations + if (_isClockPanelAnimating) + { + return; + } + // ✅ 3. When hiding ClockPanel (apply fade-out animation) - if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible && !_isClockPanelAnimating) + if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible) { _isClockPanelAnimating = true; @@ -920,32 +925,32 @@ namespace Flow.Launcher } // ✅ 4. When showing ClockPanel (apply fade-in animation) - else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible && !_isClockPanelAnimating) + else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible) { _isClockPanelAnimating = true; - Application.Current.Dispatcher.Invoke(() => + _viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first + + var fadeIn = new DoubleAnimation { - _viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first + From = 0.0, + To = 1.0, + Duration = animationDuration, + FillBehavior = FillBehavior.HoldEnd + }; - var fadeIn = new DoubleAnimation - { - From = 0.0, - To = 1.0, - Duration = animationDuration, - FillBehavior = FillBehavior.HoldEnd - }; + fadeIn.Completed += (s, e) => _isClockPanelAnimating = false; - fadeIn.Completed += (s, e) => _isClockPanelAnimating = false; - ClockPanel.BeginAnimation(OpacityProperty, fadeIn); - }, DispatcherPriority.Render); + ClockPanel.BeginAnimation(OpacityProperty, fadeIn); } } private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0) { if (style == null) + { return defaultOpacity; + } foreach (Setter setter in style.Setters.Cast()) { @@ -961,7 +966,9 @@ namespace Flow.Launcher private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness) { if (style == null) + { return defaultThickness; + } foreach (Setter setter in style.Setters.Cast()) { From c64e16d086366d712bb33f23485250c852934775 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 21:46:23 +0800 Subject: [PATCH 13/13] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index d8c05971a..0fd802f1e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -111,8 +111,8 @@ namespace Flow.Launcher App.API.SaveAppAllSettings(); // Show Welcome Window - var WelcomeWindow = new WelcomeWindow(); - WelcomeWindow.Show(); + var welcomeWindow = new WelcomeWindow(); + welcomeWindow.Show(); } // Hide window if need