diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index 06db073a12..071c782901 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -17,6 +17,7 @@ using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; using Microsoft.PowerToys.Settings.UI.SerializationContext; using Microsoft.PowerToys.Settings.UI.Services; +using Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard; using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml; @@ -35,6 +36,8 @@ namespace Microsoft.PowerToys.Settings.UI private ScoobeWindow scoobeWindow; + private ShortcutConflictWindow shortcutConflictWindow; + private enum Arguments { PTPipeName = 1, @@ -336,10 +339,10 @@ namespace Microsoft.PowerToys.Settings.UI return settingsWindow; } - public static bool IsOobeOrScoobeOpen() + public static bool IsSecondaryWindowOpen() { var app = (App)Current; - return app.oobeWindow != null || app.scoobeWindow != null; + return app.oobeWindow != null || app.scoobeWindow != null || app.shortcutConflictWindow != null; } public void OpenScoobe() @@ -384,6 +387,25 @@ namespace Microsoft.PowerToys.Settings.UI } } + public void OpenShortcutConflictWindow() + { + if (shortcutConflictWindow == null) + { + shortcutConflictWindow = new ShortcutConflictWindow(); + + shortcutConflictWindow.Closed += (_, _) => + { + shortcutConflictWindow = null; + }; + + shortcutConflictWindow.Activate(); + } + else + { + WindowHelpers.BringToForeground(shortcutConflictWindow.GetWindowHandle()); + } + } + public static Type GetPage(string settingWindow) { switch (settingWindow) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictControl.xaml.cs index d7806f17ea..1bb42d8f15 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictControl.xaml.cs @@ -2,10 +2,7 @@ // 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 System.Collections.Generic; using System.ComponentModel; -using System.Linq; using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard; @@ -154,11 +151,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls ConflictCount = this.ConflictCount, }); - // Create and show the new window instead of dialog - var conflictWindow = new ShortcutConflictWindow(); - - // Show the window - conflictWindow.Activate(); + ((App)App.Current)!.OpenShortcutConflictWindow(); } } } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs index 672b3b51a4..ec6aac76d1 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/ShortcutConflictWindow.xaml.cs @@ -2,16 +2,13 @@ // 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 CommunityToolkit.WinUI.Controls; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts; using Microsoft.PowerToys.Settings.UI.Services; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.PowerToys.Settings.UI.Views; -using Microsoft.UI; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -26,7 +23,11 @@ namespace Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard public ShortcutConflictWindow() { + App.ThemeService.ThemeChanged += OnThemeChanged; + App.ThemeService.ApplyTheme(); + var settingsUtils = SettingsUtils.Default; + ViewModel = new ShortcutConflictViewModel( settingsUtils, SettingsRepository.GetInstance(settingsUtils), @@ -50,6 +51,11 @@ namespace Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard ViewModel.OnPageLoaded(); } + private void OnThemeChanged(object sender, ElementTheme theme) + { + WindowHelper.SetTheme(this, theme); + } + private void CenterOnScreen() { var displayArea = DisplayArea.GetFromWindowId(this.AppWindow.Id, DisplayAreaFallback.Nearest); @@ -127,6 +133,14 @@ namespace Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard private void WindowEx_Closed(object sender, WindowEventArgs args) { ViewModel?.Dispose(); + + var mainWindow = App.GetSettingsWindow(); + if (mainWindow != null) + { + mainWindow.CloseHiddenWindow(); + } + + App.ThemeService.ThemeChanged -= OnThemeChanged; } private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs index ba053e1124..488c68a3ae 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs @@ -9,7 +9,6 @@ using System.Linq; using CommunityToolkit.WinUI; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.Services; using Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard; @@ -300,9 +299,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls // Close the current shortcut dialog shortcutDialog.Hide(); - // Create and show the ShortcutConflictWindow - var conflictWindow = new ShortcutConflictWindow(); - conflictWindow.Activate(); + ((App)App.Current)!.OpenShortcutConflictWindow(); } private void UpdateKeyVisualStyles() diff --git a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs index 20834e8f44..ba417ad066 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs @@ -162,7 +162,7 @@ namespace Microsoft.PowerToys.Settings.UI var hWnd = WindowNative.GetWindowHandle(this); WindowHelper.SerializePlacement(hWnd); - if (!App.IsOobeOrScoobeOpen()) + if (!App.IsSecondaryWindowOpen()) { App.ClearSettingsWindow(); } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs index c697199249..3e4d122379 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs @@ -39,6 +39,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; Loaded += (s, e) => ViewModel.OnPageLoaded(); + Unloaded += (s, e) => ViewModel?.Dispose(); } public void RefreshEnabledState() diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs index 418d6b5964..6301465996 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs @@ -55,6 +55,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // Flag to prevent toggle operations during sorting to avoid race conditions. private bool _isSorting; + private bool _isDisposed; private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData(); @@ -132,8 +133,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private void OnSettingsChanged(GeneralSettings newSettings) { + if (_isDisposed) + { + return; + } + dispatcher.TryEnqueue(() => { + if (_isDisposed) + { + return; + } + generalSettingsConfig = newSettings; // Update local field and notify UI if sort order changed @@ -149,8 +160,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e) { + if (_isDisposed) + { + return; + } + dispatcher.TryEnqueue(() => { + if (_isDisposed) + { + return; + } + var allConflictData = e.Conflicts; foreach (var inAppConflict in allConflictData.InAppConflicts) { @@ -363,6 +384,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels /// public void ModuleEnabledChangedOnSettingsPage() { + if (_isDisposed) + { + return; + } + // Ignore if this was triggered by a UI change that we're already handling. if (_isUpdatingFromUI) { @@ -391,6 +417,17 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels /// private void RefreshShortcutModules() { + if (_isDisposed) + { + return; + } + + if (!dispatcher.HasThreadAccess) + { + _ = dispatcher.TryEnqueue(DispatcherQueuePriority.Normal, RefreshShortcutModules); + return; + } + ShortcutModules.Clear(); ActionModules.Clear(); @@ -804,6 +841,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels public override void Dispose() { + if (_isDisposed) + { + return; + } + + _isDisposed = true; base.Dispose(); if (_settingsRepository != null) {