This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using CursorLang.Core.Interop;
|
||||
|
||||
namespace CursorLang.Agent.Interop;
|
||||
|
||||
/// <summary>
|
||||
/// Plain GDI: a font, a brush, a rounded region and text on a device context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// GDI rather than GDI+ on purpose. <c>System.Drawing.Common</c> would make the drawing
|
||||
/// code shorter, but it is a separate assembly with a native GDI+ library behind it, and
|
||||
/// how little this process weighs is the entire reason it exists apart from the window.
|
||||
/// Everything the popup needs — one rounded rectangle and one line of text — GDI can do
|
||||
/// on its own, and it draws the text with ClearType, exactly as Windows does everywhere else.
|
||||
/// </remarks>
|
||||
internal static class GdiNative
|
||||
{
|
||||
internal const int TRANSPARENT = 1;
|
||||
|
||||
internal const uint DT_SINGLELINE = 0x00000020;
|
||||
internal const uint DT_CENTER = 0x00000001;
|
||||
internal const uint DT_VCENTER = 0x00000004;
|
||||
internal const uint DT_CALCRECT = 0x00000400;
|
||||
internal const uint DT_NOPREFIX = 0x00000800;
|
||||
internal const uint DT_NOCLIP = 0x00000100;
|
||||
|
||||
private const int DEFAULT_CHARSET = 1;
|
||||
private const int OUT_TT_PRECIS = 4;
|
||||
private const int CLIP_DEFAULT_PRECIS = 0;
|
||||
private const int CLEARTYPE_QUALITY = 5;
|
||||
private const int DEFAULT_PITCH = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The face the popup is written in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// WPF is asked for "Segoe UI" at FontWeight SemiBold and resolves that to the
|
||||
/// seguisb.ttf face. To GDI that face is a family of its own — "Segoe UI Semibold" —
|
||||
/// and asking for the "Segoe UI" family at weight 600 lands on Bold instead, which
|
||||
/// is visibly heavier. So the family is named outright and the weight is left to the
|
||||
/// mapper: the family has one member and no synthetic emboldening happens.
|
||||
/// </remarks>
|
||||
private const string SemiBoldFace = "Segoe UI Semibold";
|
||||
|
||||
private const int FW_DONTCARE = 0;
|
||||
|
||||
/// <summary>
|
||||
/// A font of the given size in physical pixels.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The size in the settings is in WPF units, that is 1/96 inch, while GDI counts
|
||||
/// pixels — hence the multiplication by the monitor scale. The height is negative:
|
||||
/// that asks for the em size rather than the cell height, which is what a font size
|
||||
/// means everywhere else.
|
||||
/// </remarks>
|
||||
internal static IntPtr CreateFont(double wpfFontSize, double scale)
|
||||
{
|
||||
var height = (int)Math.Round(wpfFontSize * scale);
|
||||
|
||||
return CreateFontW(
|
||||
-height, 0, 0, 0, FW_DONTCARE,
|
||||
false, false, false,
|
||||
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH,
|
||||
SemiBoldFace);
|
||||
}
|
||||
|
||||
/// <summary>The size of a single line of text with the font selected into the context.</summary>
|
||||
internal static Size MeasureText(IntPtr deviceContext, string text)
|
||||
{
|
||||
var bounds = default(PopupWindowNative.Rect);
|
||||
DrawText(deviceContext, text, text.Length, ref bounds,
|
||||
DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX);
|
||||
|
||||
return new Size(bounds.Right - bounds.Left, bounds.Bottom - bounds.Top);
|
||||
}
|
||||
|
||||
/// <summary>A colour as GDI wants it: 0x00BBGGRR, the alpha carried elsewhere.</summary>
|
||||
internal static uint ToColorRef(Color color) =>
|
||||
(uint)(color.R | (color.G << 8) | (color.B << 16));
|
||||
|
||||
[DllImport("gdi32.dll", CharSet = CharSet.Unicode, EntryPoint = "CreateFontW")]
|
||||
private static extern IntPtr CreateFontW(int cHeight, int cWidth, int cEscapement, int cOrientation,
|
||||
int cWeight, bool bItalic, bool bUnderline, bool bStrikeOut,
|
||||
int iCharSet, int iOutPrecision, int iClipPrecision, int iQuality, int iPitchAndFamily,
|
||||
string pszFaceName);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern IntPtr CreateSolidBrush(uint color);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int w, int h);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern bool DeleteObject(IntPtr ho);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern int SetBkMode(IntPtr hdc, int mode);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern uint SetTextColor(IntPtr hdc, uint color);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int FillRect(IntPtr hdc, ref PopupWindowNative.Rect lprc, IntPtr hbr);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DrawTextW")]
|
||||
internal static extern int DrawText(IntPtr hdc, string lpchText, int cchText,
|
||||
ref PopupWindowNative.Rect lprc, uint format);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern IntPtr GetDC(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern IntPtr BeginPaint(IntPtr hWnd, out PaintStruct lpPaint);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern bool EndPaint(IntPtr hWnd, ref PaintStruct lpPaint);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct PaintStruct
|
||||
{
|
||||
public IntPtr hdc;
|
||||
public bool fErase;
|
||||
public PopupWindowNative.Rect rcPaint;
|
||||
public bool fRestore;
|
||||
public bool fIncUpdate;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||
public byte[] rgbReserved;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user