diff --git a/DesktopClock/MainWindow.xaml.cs b/DesktopClock/MainWindow.xaml.cs
index 4bbdd93..5f1a91c 100644
--- a/DesktopClock/MainWindow.xaml.cs
+++ b/DesktopClock/MainWindow.xaml.cs
@@ -224,6 +224,8 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e)
{
UpdateTimeString();
+ TryReassertTopmost();
+
TryShiftPixels();
TryPlaySound();
@@ -271,6 +273,21 @@ private void TryPlaySound()
}
}
+ ///
+ /// Puts the clock back above the taskbar, which buries it whenever the shell raises itself.
+ ///
+ ///
+ /// Checking every second rather than reacting to a foreground event is deliberate: the shell also raises itself without changing the foreground, as an auto-hide taskbar does, and reasserting mid-switch gets undone anyway.
+ ///
+ private void TryReassertTopmost()
+ {
+ Dispatcher.Invoke(() =>
+ {
+ if (Settings.Default.Topmost && IsVisible && WindowState != WindowState.Minimized)
+ this.ReassertTopmost();
+ });
+ }
+
private void TryShiftPixels()
{
if (!Settings.Default.BurnInMitigation || DateTimeOffset.Now.Second != 0)
diff --git a/DesktopClock/Utilities/WindowUtil.cs b/DesktopClock/Utilities/WindowUtil.cs
index 76cc5e0..6a059e1 100644
--- a/DesktopClock/Utilities/WindowUtil.cs
+++ b/DesktopClock/Utilities/WindowUtil.cs
@@ -14,8 +14,11 @@ public static class WindowUtil
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOZORDER = 0x0004;
+ private const uint SWP_NOACTIVATE = 0x0010;
private const uint SWP_FRAMECHANGED = 0x0020;
+ private static readonly IntPtr HWND_TOPMOST = new(-1);
+
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hwnd, int index);
@@ -83,6 +86,23 @@ public static void ApplyWindowVisibility(this Window window, bool showInTaskbar,
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
+ ///
+ /// Moves the window back to the top of the topmost band so it covers the taskbar again.
+ ///
+ ///
+ /// Topmost windows all share one band, so the shell raising itself leaves the clock buried inside it.
+ /// Assigning again wouldn't help because WPF only reapplies the z-order when the value changes.
+ ///
+ public static void ReassertTopmost(this Window window)
+ {
+ var hwnd = new WindowInteropHelper(window).Handle;
+ if (hwnd == IntPtr.Zero)
+ return;
+
+ // SWP_NOZORDER is deliberately left out because moving within the band is the whole point, and SWP_NOACTIVATE keeps focus wherever the user left it.
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+
///
/// Returns the extended style that matches the requested taskbar and Alt+Tab behavior.
///