230 lines
7.9 KiB
C#
230 lines
7.9 KiB
C#
using CursorLang.Interop;
|
|
using CursorLang.Models;
|
|
using CursorLang.Services;
|
|
|
|
namespace CursorLang.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<ScreenPosition, int, int> 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));
|
|
}
|
|
}
|