Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions DesktopClock/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ private void SystemClockTimer_SecondChanged(object sender, EventArgs e)
{
UpdateTimeString();

TryReassertTopmost();

TryShiftPixels();

TryPlaySound();
Expand Down Expand Up @@ -271,6 +273,21 @@ private void TryPlaySound()
}
}

/// <summary>
/// Puts the clock back above the taskbar, which buries it whenever the shell raises itself.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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)
Expand Down
20 changes: 20 additions & 0 deletions DesktopClock/Utilities/WindowUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}

/// <summary>
/// Moves the window back to the top of the topmost band so it covers the taskbar again.
/// </summary>
/// <remarks>
/// Topmost windows all share one band, so the shell raising itself leaves the clock buried inside it.
/// Assigning <see cref="Window.Topmost"/> again wouldn't help because WPF only reapplies the z-order when the value changes.
/// </remarks>
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);
}

/// <summary>
/// Returns the extended style that matches the requested taskbar and Alt+Tab behavior.
/// </summary>
Expand Down