lightweight variant (#1)
Reviewed-on: #1 Co-authored-by: Aleksandr Neychev <alexnejchev73@gmail.com>
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using CursorLang.Core.Interop;
|
||||
|
||||
namespace CursorLang.Agent.Interop;
|
||||
|
||||
/// <summary>
|
||||
/// Plain GDI: a font, text, and an off-screen bitmap to draw them into.
|
||||
/// </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);
|
||||
|
||||
/// <summary>
|
||||
/// A 32-bit surface to draw the popup into before anyone can see it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Top-down — a negative height — so that the first row of <paramref name="bits"/>
|
||||
/// is the top row of the picture and the alpha fixing up afterwards can walk the
|
||||
/// memory straight through.
|
||||
/// </remarks>
|
||||
internal static IntPtr CreateSurface(IntPtr deviceContext, int width, int height, out IntPtr bits)
|
||||
{
|
||||
var header = new BitmapInfoHeader
|
||||
{
|
||||
biSize = Marshal.SizeOf<BitmapInfoHeader>(),
|
||||
biWidth = width,
|
||||
biHeight = -height,
|
||||
biPlanes = 1,
|
||||
biBitCount = 32,
|
||||
biCompression = BI_RGB,
|
||||
};
|
||||
|
||||
return CreateDIBSection(deviceContext, ref header, DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
|
||||
}
|
||||
|
||||
private const uint BI_RGB = 0;
|
||||
private const uint DIB_RGB_COLORS = 0;
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
internal static extern bool DeleteDC(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
private static extern IntPtr CreateDIBSection(IntPtr hdc, ref BitmapInfoHeader header, uint usage,
|
||||
out IntPtr bits, IntPtr section, uint offset);
|
||||
|
||||
[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", 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);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct BitmapInfoHeader
|
||||
{
|
||||
public int biSize;
|
||||
public int biWidth;
|
||||
public int biHeight;
|
||||
public short biPlanes;
|
||||
public short biBitCount;
|
||||
public uint biCompression;
|
||||
public uint biSizeImage;
|
||||
public int biXPelsPerMeter;
|
||||
public int biYPelsPerMeter;
|
||||
public uint biClrUsed;
|
||||
public uint biClrImportant;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user