96 lines
3.5 KiB
C#
96 lines
3.5 KiB
C#
using CursorLang.Core.Interop;
|
|
using CursorLang.Core.Models;
|
|
|
|
namespace CursorLang.Core.Services;
|
|
|
|
/// <summary>
|
|
/// Computes the screen point to show the popup at.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// There is nothing but arithmetic here: where the cursor is, where the caret is and
|
|
/// what the monitor bounds are is figured out by the window itself — it has a handle
|
|
/// of its own for that. The computation is kept apart because it is exactly the place
|
|
/// where a sign or half a size is easy to get wrong, and this way it can be checked
|
|
/// without a single window on screen.
|
|
///
|
|
/// All the values are in physical pixels: monitors have different scaling, and
|
|
/// converting to WPF units halfway would mean rounding twice.
|
|
/// </remarks>
|
|
internal static class PopupLayout
|
|
{
|
|
/// <summary>
|
|
/// The popup position next to the anchor point — the cursor or the caret.
|
|
/// The cursor arrives here as a rectangle of zero size.
|
|
/// </summary>
|
|
internal static PopupWindowNative.Point NearAnchor(
|
|
PopupWindowNative.Rect anchor,
|
|
AnchorSide side,
|
|
int offset,
|
|
int width,
|
|
int height)
|
|
{
|
|
int toLeftOf = anchor.Left - offset - width;
|
|
int toRightOf = anchor.Right + offset;
|
|
int above = anchor.Top - offset - height;
|
|
int below = anchor.Bottom + offset;
|
|
|
|
// For the "left" and "right" sides the popup lines up with the anchor point
|
|
int middle = anchor.Top + (((anchor.Bottom - anchor.Top) - height) / 2);
|
|
|
|
(int x, int y) = side switch
|
|
{
|
|
AnchorSide.TopLeft => (toLeftOf, above),
|
|
AnchorSide.TopRight => (toRightOf, above),
|
|
AnchorSide.Left => (toLeftOf, middle),
|
|
AnchorSide.Right => (toRightOf, middle),
|
|
AnchorSide.BottomLeft => (toLeftOf, below),
|
|
_ => (toRightOf, below),
|
|
};
|
|
|
|
return new PopupWindowNative.Point { X = x, Y = y };
|
|
}
|
|
|
|
/// <summary>
|
|
/// The popup position in the given corner of the monitor work area.
|
|
/// </summary>
|
|
internal static PopupWindowNative.Point OnScreen(
|
|
PopupWindowNative.Rect work,
|
|
ScreenPosition position,
|
|
int margin,
|
|
int width,
|
|
int height)
|
|
{
|
|
int left = work.Left + margin;
|
|
int right = work.Right - margin - width;
|
|
int top = work.Top + margin;
|
|
int bottom = work.Bottom - margin - height;
|
|
int centerX = work.Left + ((work.Right - work.Left - width) / 2);
|
|
int centerY = work.Top + ((work.Bottom - work.Top - height) / 2);
|
|
|
|
(int x, int y) = position switch
|
|
{
|
|
ScreenPosition.TopLeft => (left, top),
|
|
ScreenPosition.Top => (centerX, top),
|
|
ScreenPosition.TopRight => (right, top),
|
|
ScreenPosition.BottomLeft => (left, bottom),
|
|
ScreenPosition.Bottom => (centerX, bottom),
|
|
ScreenPosition.BottomRight => (right, bottom),
|
|
_ => (centerX, centerY),
|
|
};
|
|
|
|
return new PopupWindowNative.Point { X = x, Y = y };
|
|
}
|
|
|
|
/// <summary>A rectangle of zero size at a point — the mouse cursor as an anchor.</summary>
|
|
internal static PopupWindowNative.Rect AsAnchor(PopupWindowNative.Point point) => new()
|
|
{
|
|
Left = point.X,
|
|
Top = point.Y,
|
|
Right = point.X,
|
|
Bottom = point.Y,
|
|
};
|
|
|
|
/// <summary>WPF units into physical pixels of a monitor with the given scale.</summary>
|
|
internal static int ToPixels(double wpfUnits, double scale) => (int)Math.Round(wpfUnits * scale);
|
|
}
|