using CursorLang.Core.Interop; using CursorLang.Core.Models; namespace CursorLang.Core.Services; /// /// Computes the screen point to show the popup at. /// /// /// 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. /// internal static class PopupLayout { /// /// The popup position next to the anchor point — the cursor or the caret. /// The cursor arrives here as a rectangle of zero size. /// 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 }; } /// /// The popup position in the given corner of the monitor work area. /// 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 }; } /// A rectangle of zero size at a point — the mouse cursor as an anchor. internal static PopupWindowNative.Rect AsAnchor(PopupWindowNative.Point point) => new() { Left = point.X, Top = point.Y, Right = point.X, Bottom = point.Y, }; /// WPF units into physical pixels of a monitor with the given scale. internal static int ToPixels(double wpfUnits, double scale) => (int)Math.Round(wpfUnits * scale); }