From d8271cf2041c8f9c541718a6e7e8929d6e30d9e7 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Sun, 8 Mar 2026 17:12:02 -0500 Subject: [PATCH] Keep DesktopClock out of the taskbar after Alt+Tab activation --- DesktopClock.Tests/WindowUtilTests.cs | 47 +++++++++++++++++++++++++++ DesktopClock/MainWindow.xaml.cs | 26 ++++++++++----- DesktopClock/Utilities/WindowUtil.cs | 32 +++++++++++------- 3 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 DesktopClock.Tests/WindowUtilTests.cs diff --git a/DesktopClock.Tests/WindowUtilTests.cs b/DesktopClock.Tests/WindowUtilTests.cs new file mode 100644 index 0000000..2135c9c --- /dev/null +++ b/DesktopClock.Tests/WindowUtilTests.cs @@ -0,0 +1,47 @@ +namespace DesktopClock.Tests; + +public class WindowUtilTests +{ + private const int WsExTransparent = 0x00000020; + private const int WsExToolWindow = 0x00000080; + private const int WsExAppWindow = 0x00040000; + + [Fact] + public void GetWindowVisibilityExtendedStyle_HideFromAltTab_SetsToolWindowAndClearsAppWindow() + { + var style = WsExTransparent | WsExAppWindow; + + var updatedStyle = WindowUtil.GetWindowVisibilityExtendedStyle( + style, + showInTaskbar: true, + hideFromAltTab: true); + + Assert.Equal(WsExTransparent | WsExToolWindow, updatedStyle); + } + + [Fact] + public void GetWindowVisibilityExtendedStyle_ShowInTaskbar_SetsAppWindowAndClearsToolWindow() + { + var style = WsExTransparent | WsExToolWindow; + + var updatedStyle = WindowUtil.GetWindowVisibilityExtendedStyle( + style, + showInTaskbar: true, + hideFromAltTab: false); + + Assert.Equal(WsExTransparent | WsExAppWindow, updatedStyle); + } + + [Fact] + public void GetWindowVisibilityExtendedStyle_HideTaskbarWithoutHidingFromAltTab_ClearsBothFlags() + { + var style = WsExTransparent | WsExToolWindow | WsExAppWindow; + + var updatedStyle = WindowUtil.GetWindowVisibilityExtendedStyle( + style, + showInTaskbar: false, + hideFromAltTab: false); + + Assert.Equal(WsExTransparent, updatedStyle); + } +} diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs index 82186f3..963d75b 100644 --- a/DesktopClock/MainWindow.xaml.cs +++ b/DesktopClock/MainWindow.xaml.cs @@ -46,8 +46,7 @@ public MainWindow() _settingsPropertyChanged = (s, e) => Dispatcher.Invoke(() => Settings_PropertyChanged(s, e)); Settings.Default.PropertyChanged += _settingsPropertyChanged; - // Not done through binding due to what's explained in the comment in WindowUtil.HideFromScreen(). - ShowInTaskbar = Settings.Default.ShowInTaskbar; + ApplyWindowVisibilitySettings(); // Restore the last displayed text so the window starts near its previous size. CurrentTimeOrCountdownString = Settings.Default.LastDisplay; @@ -84,6 +83,7 @@ public void HideForNow() } this.HideFromScreen(); + ApplyWindowVisibilitySettings(); } /// @@ -119,6 +119,12 @@ protected override void OnClosed(EventArgs e) base.OnClosed(e); } + protected override void OnActivated(EventArgs e) + { + base.OnActivated(e); + ApplyWindowVisibilitySettings(); + } + private void ConfigureTrayIcon() { if (_trayIcon == null) @@ -155,12 +161,8 @@ private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) break; case nameof(Settings.Default.ShowInTaskbar): - ShowInTaskbar = Settings.Default.ShowInTaskbar; - this.SetHiddenFromAltTab(Settings.Default.HideFromAltTab); - break; - case nameof(Settings.Default.HideFromAltTab): - this.SetHiddenFromAltTab(Settings.Default.HideFromAltTab); + ApplyWindowVisibilitySettings(); break; case nameof(Settings.Default.ClickThrough): @@ -303,7 +305,7 @@ private void Window_SourceInitialized(object sender, EventArgs e) // Apply click-through setting. this.SetClickThrough(Settings.Default.ClickThrough); - this.SetHiddenFromAltTab(Settings.Default.HideFromAltTab); + ApplyWindowVisibilitySettings(); UpdateTimeString(); _systemClockTimer.Start(); @@ -315,6 +317,7 @@ private void Window_SourceInitialized(object sender, EventArgs e) { _trayIcon?.ShowNotification("Started hidden", "Use the tray icon to show it"); this.HideFromScreen(); + ApplyWindowVisibilitySettings(); } // Show the window now that it's finished loading. @@ -380,6 +383,13 @@ private void Window_StateChanged(object sender, EventArgs e) _systemClockTimer.Start(); EfficiencyModeUtilities.SetEfficiencyMode(false); } + + ApplyWindowVisibilitySettings(); + } + + private void ApplyWindowVisibilitySettings() + { + this.ApplyWindowVisibility(Settings.Default.ShowInTaskbar, Settings.Default.HideFromAltTab); } private void Window_KeyDown(object sender, KeyEventArgs e) diff --git a/DesktopClock/Utilities/WindowUtil.cs b/DesktopClock/Utilities/WindowUtil.cs index 4c46595..76cc5e0 100644 --- a/DesktopClock/Utilities/WindowUtil.cs +++ b/DesktopClock/Utilities/WindowUtil.cs @@ -66,27 +66,35 @@ public static void SetClickThrough(this Window window, bool clickThrough) } /// - /// Hides or shows the window in Alt+Tab. + /// Applies the window's taskbar and Alt+Tab visibility. /// - public static void SetHiddenFromAltTab(this Window window, bool hideFromAltTab) + public static void ApplyWindowVisibility(this Window window, bool showInTaskbar, bool hideFromAltTab) { + window.ShowInTaskbar = showInTaskbar; + var hwnd = new WindowInteropHelper(window).Handle; if (hwnd == IntPtr.Zero) return; var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); - if (hideFromAltTab) - { - extendedStyle |= WS_EX_TOOLWINDOW; - extendedStyle &= ~WS_EX_APPWINDOW; - } - else - { - extendedStyle |= WS_EX_APPWINDOW; - extendedStyle &= ~WS_EX_TOOLWINDOW; - } + extendedStyle = GetWindowVisibilityExtendedStyle(extendedStyle, showInTaskbar, hideFromAltTab); SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle); SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } + + /// + /// Returns the extended style that matches the requested taskbar and Alt+Tab behavior. + /// + public static int GetWindowVisibilityExtendedStyle(int extendedStyle, bool showInTaskbar, bool hideFromAltTab) + { + extendedStyle &= ~(WS_EX_TOOLWINDOW | WS_EX_APPWINDOW); + + if (hideFromAltTab) + return extendedStyle | WS_EX_TOOLWINDOW; + + return showInTaskbar + ? extendedStyle | WS_EX_APPWINDOW + : extendedStyle; + } }