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 1/8] 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 2bfc9587c6e..192f8b48194 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -638,8 +638,13 @@ public void ChangeQueryText(string queryText, bool isReQuery = false) /// 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 @@ await Application.Current.Dispatcher.InvokeAsync(async () => } 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 2/8] 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 25d9c5d2dbe..89d4f0847e5 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -161,24 +161,67 @@ private async void OnLoaded(object sender, RoutedEventArgs _) { 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 @@ private async void OnLoaded(object sender, RoutedEventArgs _) Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); _viewModel.QueryTextCursorMovedToEnd = false; } - break; case nameof(MainViewModel.GameModeStatus): _notifyIcon.Icon = _viewModel.GameModeStatus @@ -280,8 +322,8 @@ private async void OnDeactivated(object sender, EventArgs e) _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 @@ private async void OnDeactivated(object sender, EventArgs e) // 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 @@ private void WindowAnimation() { _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 192f8b48194..9e63244f429 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -10,7 +10,6 @@ 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 @@ private async Task ChangeQueryTextAsync(string queryText, bool isReQuery = false 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 @@ public bool ShouldIgnoreHotkeys() #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 @@ await Application.Current.Dispatcher.InvokeAsync(() => 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 @@ public async void Hide() 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 3/8] 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 89d4f0847e5..a44b3a3050e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -101,11 +101,16 @@ private async void OnLoaded(object sender, RoutedEventArgs _) // 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 @@ private async void OnLoaded(object sender, RoutedEventArgs _) 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 4/8] 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 a44b3a3050e..9eb1271ecbf 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -948,6 +948,7 @@ private void UpdateClockPanelVisibility() ClockPanel.BeginAnimation(OpacityProperty, fadeOut); } + // ✅ 4. When showing ClockPanel (apply fade-in animation) else if (shouldShowClock && ClockPanel.Visibility != Visibility.Visible && !_isClockPanelAnimating) { @@ -971,7 +972,6 @@ private void UpdateClockPanelVisibility() } } - 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 5/8] 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 9eb1271ecbf..7b3814c874c 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -296,7 +296,8 @@ private async void OnClosing(object sender, CancelEventArgs e) 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 6/8] 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 833c63ddff8..1270d16bf21 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -138,13 +138,13 @@ private async void OnStartup(object sender, StartupEventArgs e) { 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 7/8] 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 9e63244f429..6bfde42c51a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -212,7 +212,8 @@ async Task UpdateActionAsync() 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 8/8] 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 1270d16bf21..833c63ddff8 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -138,13 +138,13 @@ private async void OnStartup(object sender, StartupEventArgs e) { 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();