using System.Windows; using System.Windows.Interop; using CursorLang.Core.Interop; using CursorLang.Settings.Interop; using CursorLang.Settings.Services; using CursorLang.Settings.Tests.Infrastructure; namespace CursorLang.Settings.Tests.Services; /// /// Placing the settings window: the centre maths and bringing the window back /// into the work area. /// public sealed class MainWindowPlacementTests { private static readonly PopupWindowNative.Rect Work = new() { Left = 0, Top = 0, Right = 1000, Bottom = 800, }; private static readonly PopupWindowNative.Rect Bounds = new() { Left = 0, Top = 0, Right = 400, Bottom = 300, }; [Fact] public void The_centre_follows_the_window_size_and_the_work_area() { PopupWindowNative.Point point = MainWindowPlacement.Center(Bounds, Work); Assert.Equal((1000 - 400) / 2, point.X); Assert.Equal((800 - 300) / 2, point.Y); } // The work area of a second monitor does not start at zero [Fact] public void The_centre_of_a_neighbouring_monitor_is_measured_from_its_left_edge() { var work = new PopupWindowNative.Rect { Left = 1920, Top = 100, Right = 3520, Bottom = 1000 }; PopupWindowNative.Point point = MainWindowPlacement.Center(Bounds, work); Assert.Equal(1920 + ((1600 - 400) / 2), point.X); Assert.Equal(100 + ((900 - 300) / 2), point.Y); } [Fact] public void A_window_inside_the_work_area_stays_where_it_is() { var position = new PopupWindowNative.Point { X = 120, Y = 90 }; PopupWindowNative.Point clamped = MainWindowPlacement.Clamp(position, Bounds, Work); Assert.Equal(120, clamped.X); Assert.Equal(90, clamped.Y); } [Fact] public void A_window_past_the_right_edge_is_pulled_back_in() { var position = new PopupWindowNative.Point { X = 900, Y = 700 }; PopupWindowNative.Point clamped = MainWindowPlacement.Clamp(position, Bounds, Work); Assert.Equal(1000 - 400, clamped.X); Assert.Equal(800 - 300, clamped.Y); } [Fact] public void A_window_past_the_left_and_top_edges_is_pulled_back_in() { var position = new PopupWindowNative.Point { X = -500, Y = -400 }; PopupWindowNative.Point clamped = MainWindowPlacement.Clamp(position, Bounds, Work); Assert.Equal(Work.Left, clamped.X); Assert.Equal(Work.Top, clamped.Y); } // The window height matches its content and on a short monitor exceeds the // work area. The title bar matters more than the bottom of the window [Fact] public void A_window_taller_than_the_work_area_is_pinned_to_its_top() { var tall = new PopupWindowNative.Rect { Left = 0, Top = 0, Right = 400, Bottom = 900 }; var position = new PopupWindowNative.Point { X = 0, Y = 300 }; PopupWindowNative.Point clamped = MainWindowPlacement.Clamp(position, tall, Work); Assert.Equal(Work.Top, clamped.Y); } [Fact] public void A_window_wider_than_the_work_area_is_pinned_to_its_left_edge() { var wide = new PopupWindowNative.Rect { Left = 0, Top = 0, Right = 1200, Bottom = 300 }; var position = new PopupWindowNative.Point { X = 400, Y = 0 }; PopupWindowNative.Point clamped = MainWindowPlacement.Clamp(position, wide, Work); Assert.Equal(Work.Left, clamped.X); } [Theory] [InlineData(0, 0, 0, 0, true)] [InlineData(0, 0, 100, 0, true)] [InlineData(0, 0, 0, 100, true)] [InlineData(100, 100, 100, 200, true)] [InlineData(0, 0, 1, 1, false)] [InlineData(-100, -100, 100, 100, false)] public void An_area_without_width_or_height_counts_as_empty( int left, int top, int right, int bottom, bool expected) { var rect = new PopupWindowNative.Rect { Left = left, Top = top, Right = right, Bottom = bottom }; Assert.Equal(expected, MainWindowPlacement.IsEmpty(rect)); } [Fact] public void The_first_time_in_a_session_the_window_lands_centred_on_the_active_monitor() { Sta.Run(() => { var placement = new MainWindowPlacement(); using var window = new TestWindow(); (PopupWindowNative.Rect before, _) = PopupWindowNative.GetActiveMonitorWorkArea(); placement.Apply(window); (PopupWindowNative.Rect work, _) = PopupWindowNative.GetActiveMonitorWorkArea(); if (!before.Equals(work)) { // The user moved to another monitor right during the check Assert.Skip("The active monitor changed while the check was running"); } PopupWindowNative.Rect bounds = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; // Pixel precision: the window goes exactly where it was computed to go PopupWindowNative.Point expected = MainWindowPlacement.Clamp( MainWindowPlacement.Center(bounds, work), bounds, work); Assert.Equal(expected.X, bounds.Left); Assert.Equal(expected.Y, bounds.Top); }); } [Fact] public void The_window_returns_where_the_user_moved_it() { Sta.Run(() => { var placement = new MainWindowPlacement(); using var window = new TestWindow(); placement.Attach(window); placement.Apply(window); // Move the window the way the user does it with the mouse PopupWindowNative.Rect centered = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; PopupWindowNative.MoveTo(window.Handle, centered.Left + 40, centered.Top + 30); window.RaiseLocationChanged(); // Showing the window again — it has to stay where it was left placement.Apply(window); PopupWindowNative.Rect after = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; Assert.Equal(centered.Left + 40, after.Left); Assert.Equal(centered.Top + 30, after.Top); }); } // While we move the window ourselves its position must not drift from repeats [Fact] public void Placing_again_does_not_move_the_window() { Sta.Run(() => { var placement = new MainWindowPlacement(); using var window = new TestWindow(); placement.Attach(window); placement.Apply(window); PopupWindowNative.Rect first = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; placement.Apply(window); placement.Apply(window); PopupWindowNative.Rect third = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; Assert.Equal(first.Left, third.Left); Assert.Equal(first.Top, third.Top); }); } [Fact] public void A_minimised_window_is_not_placed() { Sta.Run(() => { var placement = new MainWindowPlacement(); using var window = new TestWindow(); PopupWindowNative.MoveTo(window.Handle, 7, 9); window.WindowState = WindowState.Minimized; placement.Apply(window); PopupWindowNative.Rect bounds = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; Assert.Equal(7, bounds.Left); Assert.Equal(9, bounds.Top); }); } [Fact] public void A_window_without_a_handle_is_not_placed() { Sta.Run(() => { var placement = new MainWindowPlacement(); var window = new Window { Width = 200, Height = 150 }; // There must be no exception: the window does not exist yet, // so there is nothing to place placement.Apply(window); Assert.Equal(IntPtr.Zero, new WindowInteropHelper(window).Handle); }); } // The place of the window lives in memory only: the set of monitors may be // different by the next run [Fact] public void Every_placement_starts_its_session_afresh() { Sta.Run(() => { using var window = new TestWindow(); var first = new MainWindowPlacement(); first.Attach(window); first.Apply(window); PopupWindowNative.Rect centered = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; PopupWindowNative.MoveTo(window.Handle, centered.Left + 60, centered.Top + 60); window.RaiseLocationChanged(); // A new placement knows nothing of the earlier move and centres the window again var second = new MainWindowPlacement(); second.Apply(window); PopupWindowNative.Rect bounds = WindowPlacementNative.TryGetBounds(window.Handle)!.Value; Assert.Equal(centered.Left, bounds.Left); Assert.Equal(centered.Top, bounds.Top); }); } /// /// A window with a ready handle that never appears on screen: placement /// works with the system bounds, and a created window is enough for those. /// private sealed class TestWindow : Window, IDisposable { internal TestWindow() { Width = 400; Height = 300; ShowInTaskbar = false; WindowStartupLocation = WindowStartupLocation.Manual; Handle = new WindowInteropHelper(this).EnsureHandle(); } internal IntPtr Handle { get; } /// Reports a move the way WPF does after the user acts. internal void RaiseLocationChanged() => OnLocationChanged(EventArgs.Empty); public void Dispose() => Close(); } }