138 lines
5.4 KiB
C#
138 lines
5.4 KiB
C#
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;
|
|
}
|
|
}
|