using CursorLang.Core.Interop; using CursorLang.Core.Models; using CursorLang.Core.Services; namespace CursorLang.Core.Tests.Services; /// /// The placement maths for the tooltip. This is the easiest place to get a sign /// or half a size wrong, and on screen such a mistake is only visible by eye. /// public sealed class PopupLayoutTests { // The anchor: 100..140 horizontally, 200..220 vertically private static readonly PopupWindowNative.Rect Anchor = new() { Left = 100, Top = 200, Right = 140, Bottom = 220, }; private const int Offset = 10; private const int Width = 30; private const int Height = 16; [Fact] public void Bottom_right_offsets_the_tooltip_from_the_bottom_right_corner() { PopupWindowNative.Point point = Near(AnchorSide.BottomRight); Assert.Equal(140 + 10, point.X); Assert.Equal(220 + 10, point.Y); } [Fact] public void Bottom_left_fits_the_tooltip_to_the_left_of_the_anchor() { PopupWindowNative.Point point = Near(AnchorSide.BottomLeft); Assert.Equal(100 - 10 - Width, point.X); Assert.Equal(220 + 10, point.Y); } [Fact] public void Top_right_fits_the_tooltip_above_the_anchor() { PopupWindowNative.Point point = Near(AnchorSide.TopRight); Assert.Equal(140 + 10, point.X); Assert.Equal(200 - 10 - Height, point.Y); } [Fact] public void Top_left_fits_the_tooltip_both_left_of_and_above_the_anchor() { PopupWindowNative.Point point = Near(AnchorSide.TopLeft); Assert.Equal(100 - 10 - Width, point.X); Assert.Equal(200 - 10 - Height, point.Y); } // At the sides the tooltip lines up with the middle of the anchor [Fact] public void On_the_right_the_tooltip_lines_up_with_the_anchor() { PopupWindowNative.Point point = Near(AnchorSide.Right); Assert.Equal(140 + 10, point.X); Assert.Equal(200 + ((20 - Height) / 2), point.Y); } [Fact] public void On_the_left_the_tooltip_lines_up_with_the_anchor() { PopupWindowNative.Point point = Near(AnchorSide.Left); Assert.Equal(100 - 10 - Width, point.X); Assert.Equal(200 + ((20 - Height) / 2), point.Y); } // A tooltip taller than the input field: the middle is measured from the // anchor, not from zero [Fact] public void At_the_side_a_tooltip_taller_than_the_anchor_rises_above_it() { PopupWindowNative.Point point = PopupLayout.NearAnchor(Anchor, AnchorSide.Right, Offset, Width, height: 40); Assert.Equal(200 + ((20 - 40) / 2), point.Y); Assert.True(point.Y < Anchor.Top); } [Fact] public void The_cursor_anchors_as_a_rectangle_of_zero_size() { PopupWindowNative.Rect anchor = PopupLayout.AsAnchor(new PopupWindowNative.Point { X = 50, Y = 60 }); Assert.Equal(50, anchor.Left); Assert.Equal(50, anchor.Right); Assert.Equal(60, anchor.Top); Assert.Equal(60, anchor.Bottom); } [Fact] public void At_the_cursor_both_sides_are_measured_from_the_same_point() { PopupWindowNative.Rect cursor = PopupLayout.AsAnchor(new PopupWindowNative.Point { X = 50, Y = 60 }); PopupWindowNative.Point bottomRight = PopupLayout.NearAnchor(cursor, AnchorSide.BottomRight, Offset, Width, Height); PopupWindowNative.Point topLeft = PopupLayout.NearAnchor(cursor, AnchorSide.TopLeft, Offset, Width, Height); Assert.Equal(60, bottomRight.X); Assert.Equal(70, bottomRight.Y); Assert.Equal(50 - 10 - Width, topLeft.X); Assert.Equal(60 - 10 - Height, topLeft.Y); } // A monitor to the left of the primary one gives negative coordinates — that is normal [Fact] public void Negative_coordinates_of_a_neighbouring_monitor_are_allowed() { var anchor = new PopupWindowNative.Rect { Left = -800, Top = -200, Right = -800, Bottom = -200 }; PopupWindowNative.Point point = PopupLayout.NearAnchor(anchor, AnchorSide.BottomRight, Offset, Width, Height); Assert.Equal(-790, point.X); Assert.Equal(-190, point.Y); } [Fact] public void A_zero_offset_puts_the_tooltip_flush_against_the_anchor() { PopupWindowNative.Point point = PopupLayout.NearAnchor(Anchor, AnchorSide.BottomRight, offset: 0, Width, Height); Assert.Equal(Anchor.Right, point.X); Assert.Equal(Anchor.Bottom, point.Y); } [Theory] [InlineData(AnchorSide.TopLeft)] [InlineData(AnchorSide.TopRight)] [InlineData(AnchorSide.Left)] [InlineData(AnchorSide.Right)] [InlineData(AnchorSide.BottomLeft)] [InlineData(AnchorSide.BottomRight)] public void No_side_is_left_behind(AnchorSide side) { // The sides are handled by a switch expression with a fallback branch: // each of them has to get its own place, not the shared "bottom right" PopupWindowNative.Point point = Near(side); PopupWindowNative.Point bottomRight = Near(AnchorSide.BottomRight); if (side != AnchorSide.BottomRight) { Assert.True(point.X != bottomRight.X || point.Y != bottomRight.Y); } } public static TheoryData ScreenCases() => new() { // A work area of 0..1000 horizontally and 0..800 vertically, margin 20 { ScreenPosition.TopLeft, 20, 20 }, { ScreenPosition.Top, (1000 - Width) / 2, 20 }, { ScreenPosition.TopRight, 1000 - 20 - Width, 20 }, { ScreenPosition.Center, (1000 - Width) / 2, (800 - Height) / 2 }, { ScreenPosition.BottomLeft, 20, 800 - 20 - Height }, { ScreenPosition.Bottom, (1000 - Width) / 2, 800 - 20 - Height }, { ScreenPosition.BottomRight, 1000 - 20 - Width, 800 - 20 - Height }, }; [Theory] [MemberData(nameof(ScreenCases))] public void The_place_on_the_monitor_is_measured_from_the_work_area( ScreenPosition position, int expectedX, int expectedY) { var work = new PopupWindowNative.Rect { Left = 0, Top = 0, Right = 1000, Bottom = 800 }; PopupWindowNative.Point point = PopupLayout.OnScreen(work, position, margin: 20, Width, Height); Assert.Equal(expectedX, point.X); Assert.Equal(expectedY, point.Y); } // The work area of a second monitor does not start at zero, and the taskbar // takes its bottom away — the place is measured from those bounds [Fact] public void The_place_on_a_neighbouring_monitor_is_measured_from_its_own_bounds() { var work = new PopupWindowNative.Rect { Left = 1920, Top = 0, Right = 3520, Bottom = 860 }; PopupWindowNative.Point point = PopupLayout.OnScreen(work, ScreenPosition.BottomRight, margin: 20, Width, Height); Assert.Equal(3520 - 20 - Width, point.X); Assert.Equal(860 - 20 - Height, point.Y); } [Fact] public void In_the_centre_of_the_monitor_the_margin_is_ignored() { var work = new PopupWindowNative.Rect { Left = 0, Top = 0, Right = 1000, Bottom = 800 }; PopupWindowNative.Point withMargin = PopupLayout.OnScreen(work, ScreenPosition.Center, 20, Width, Height); PopupWindowNative.Point withoutMargin = PopupLayout.OnScreen(work, ScreenPosition.Center, 0, Width, Height); Assert.Equal(withoutMargin.X, withMargin.X); Assert.Equal(withoutMargin.Y, withMargin.Y); } private static PopupWindowNative.Point Near(AnchorSide side) => PopupLayout.NearAnchor(Anchor, side, Offset, Width, Height); [Theory] [InlineData(16, 1.0, 16)] [InlineData(16, 1.25, 20)] [InlineData(16, 1.5, 24)] [InlineData(16, 2.0, 32)] [InlineData(0, 2.0, 0)] [InlineData(20.4, 1.0, 20)] [InlineData(20.6, 1.0, 21)] public void WPF_units_turn_into_pixels_by_the_scale(double units, double scale, int expected) { Assert.Equal(expected, PopupLayout.ToPixels(units, scale)); } }