Skip to content
Merged
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
30 changes: 30 additions & 0 deletions DesktopClock.Tests/WindowUtilTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
using System.Windows.Input;

namespace DesktopClock.Tests;

public class WindowUtilTests
{
[Theory]
[InlineData(Key.Left, ModifierKeys.None, -1, 0)]
[InlineData(Key.Right, ModifierKeys.None, 1, 0)]
[InlineData(Key.Up, ModifierKeys.None, 0, -1)]
[InlineData(Key.Down, ModifierKeys.None, 0, 1)]
[InlineData(Key.Left, ModifierKeys.Shift, -10, 0)]
[InlineData(Key.Right, ModifierKeys.Shift, 10, 0)]
[InlineData(Key.Up, ModifierKeys.Shift, 0, -10)]
[InlineData(Key.Down, ModifierKeys.Shift, 0, 10)]
public void GetKeyboardNudge_ArrowKeys_MoveByStep(Key key, ModifierKeys modifiers, double expectedX, double expectedY)
{
var nudge = WindowUtil.GetKeyboardNudge(key, modifiers);

Assert.Equal(expectedX, nudge.X);
Assert.Equal(expectedY, nudge.Y);
}

[Theory]
[InlineData(Key.Enter)]
[InlineData(Key.A)]
[InlineData(Key.Space)]
public void GetKeyboardNudge_OtherKeys_DoNotMove(Key key)
{
var nudge = WindowUtil.GetKeyboardNudge(key, ModifierKeys.None);

Assert.Equal(default, nudge);
}

private const int WsExTransparent = 0x00000020;
private const int WsExToolWindow = 0x00000080;
private const int WsExAppWindow = 0x00040000;
Expand Down
24 changes: 24 additions & 0 deletions DesktopClock/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,30 @@ private void Window_KeyDown(object sender, KeyEventArgs e)
break;
}
}
else if (Keyboard.Modifiers is ModifierKeys.None or ModifierKeys.Shift)
{
NudgeWindow(e);
}
}

/// <summary>
/// Moves the clock with the arrow keys as a precise, mouse-free alternative to dragging.
/// </summary>
private void NudgeWindow(KeyEventArgs e)
{
// The keyboard respects the same lock as the mouse.
if (!Settings.Default.DragToMove)
return;

var nudge = WindowUtil.GetKeyboardNudge(e.Key, Keyboard.Modifiers);
if (nudge == default)
return;

Left += nudge.X;
Top += nudge.Y;

PixelShifter?.UpdateBasePosition(this);
e.Handled = true;
}

private void OpenUrl(string url)
Expand Down
28 changes: 28 additions & 0 deletions DesktopClock/Utilities/WindowUtil.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;

namespace DesktopClock;

public static class WindowUtil
{
/// <summary>
/// How far one arrow key press moves the window.
/// </summary>
public const double NudgeStep = 1;

/// <summary>
/// How far an arrow key press moves the window while Shift is held.
/// </summary>
public const double LargeNudgeStep = 10;

/// <summary>
/// Returns how far an arrow key should move the window, or zero for other keys.
/// </summary>
public static Vector GetKeyboardNudge(Key key, ModifierKeys modifiers)
{
var step = modifiers == ModifierKeys.Shift ? LargeNudgeStep : NudgeStep;

return key switch
{
Key.Left => new Vector(-step, 0),
Key.Right => new Vector(step, 0),
Key.Up => new Vector(0, -step),
Key.Down => new Vector(0, step),
_ => default,
};
}

private const int GWL_EXSTYLE = -20;
private const int WS_EX_TRANSPARENT = 0x00000020;
private const int WS_EX_TOOLWINDOW = 0x00000080;
Expand Down