/// <summary>Object created with this ctor can draw and measure.</summary> /// <param name="hdc">Device context handle. <b>Dispose</b> will not release it.</param> /// <param name="dpi"></param> public GdiTextRenderer(IntPtr hdc, int dpi) { _dpi = dpi; _dc = hdc; _oldFont = Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi)); _oldAlign = 0xffffffff; Api.SetBkMode(_dc, 1); _oldColor = Api.SetTextColor(_dc, _color = 0); }
void _WmMousemove(POINT pc) { if (!_paintedOnce) { return; } //format text to draw below magnifier string text; using (new StringBuilder_(out var s)) { var ic = _flags & (ICFlags.Image | ICFlags.Color | ICFlags.Rectangle); if (ic == 0) { ic = ICFlags.Image | ICFlags.Color; } bool canColor = ic.Has(ICFlags.Color); if (canColor) { var color = _img.GetPixel(pc.x, pc.y).ToArgb() & 0xffffff; s.Append("Color #").Append(color.ToString("X6")).Append('\n'); } if (ic == ICFlags.Color) { s.Append("Click to capture color.\n"); } else if (ic == ICFlags.Rectangle) { s.Append("Mouse-drag to capture rectangle.\n"); } else if (!canColor) { s.Append("Mouse-drag to capture image.\n"); } else { s.Append("Mouse-drag to capture image,\nor Ctrl+click to capture color.\n"); } s.Append("More: right-click"); //" cancel: key Esc\n retry: key F3 ... F3" text = s.ToString(); } var font = NativeFont_.RegularCached(_dpi); int magnWH = Dpi.Scale(200, _dpi) / 10 * 10; //width and height of the magnified image without borders etc if (_textSize == default) { using (var tr = new FontDC_(font)) _textSize = tr.MeasureDT(text, TFFlags.NOPREFIX); } int width = Math.Max(magnWH, _textSize.width) + 2, height = magnWH + 4 + _textSize.height; using var mb = new MemoryBitmap(width, height); var dc = mb.Hdc; using var wdc = new WindowDC_(_w); //draw frames and color background. Also erase magnifier, need when near screen edges. Api.FillRect(dc, (0, 0, width, height), Api.GetStockObject(4)); //BLACK_BRUSH //copy from captured screen image to magnifier image. Magnify 5 times. int k = magnWH / 10; Api.StretchBlt(dc, 1, 1, magnWH, magnWH, wdc, pc.x - k, pc.y - k, k * 2, k * 2, Api.SRCCOPY); //draw red crosshair k = magnWH / 2; using (var pen = new Pen_(0xff)) { pen.DrawLine(dc, (k, 1), (k, magnWH + 1)); pen.DrawLine(dc, (1, k), (magnWH + 1, k)); } //draw text below magnifier var rc = new RECT(1, magnWH + 2, _textSize.width, _textSize.height); Api.SetTextColor(dc, 0x32CD9A); //Color.YellowGreen Api.SetBkMode(dc, 1); var oldFont = Api.SelectObject(dc, font); Api.DrawText(dc, text, ref rc, TFFlags.NOPREFIX); Api.SelectObject(dc, oldFont); //set magninifier position far from cursor var pm = new POINT(4, 4); _w.MapScreenToClient(ref pm); int xMove = magnWH * 3; if (_magnMoved) { pm.Offset(xMove, 0); } var rm = new RECT(pm.x, pm.y, width, height); rm.Inflate(magnWH / 2, magnWH / 2); if (rm.Contains(pc)) { Api.InvalidateRect(_w, (pm.x, pm.y, width, height)); _magnMoved ^= true; pm.Offset(_magnMoved ? xMove : -xMove, 0); } Api.BitBlt(wdc, pm.x, pm.y, width, height, dc, 0, 0, Api.SRCCOPY); }
void _Render(IntPtr dc, RECT rUpdate) { Api.FillRect(dc, rUpdate, (IntPtr)(Api.COLOR_BTNFACE + 1)); using var g = Graphics.FromHdc(dc); g.InterpolationMode = InterpolationMode.HighQualityBicubic; var font = NativeFont_.RegularCached(_dpi); using var soFont = new GdiSelectObject_(dc, font); Api.SetBkMode(dc, 1); int textColor = Api.GetSysColor(Api.COLOR_BTNTEXT), textColorDisabled = Api.GetSysColor(Api.COLOR_GRAYTEXT); rUpdate.Offset(0, _scroll.Offset); for (int i = _scroll.Pos; i < _a.Count; i++) { var b = _a[i]; if (b.rect.bottom <= rUpdate.top) { continue; } if (b.rect.top >= rUpdate.bottom) { break; } var r = _ItemRect(b); //g.DrawRectangleInset(Pens.BlueViolet, r); if (b.IsSeparator) { r.Inflate(-_z.textPaddingX / 4, 0); //r.left+=_z.check+_z.image; r.top += (r.Height - _z.sepLine) / 2; r.Height = _z.sepLine; if (_z.theme != default) { Api.DrawThemeBackground(_z.theme, dc, 15, 0, r); } else { Api.DrawEdge(dc, ref r, Api.EDGE_ETCHED, Api.BF_TOP); } } else { if (b.BackgroundColor != default) { using (var brush = new SolidBrush((Color)b.BackgroundColor)) g.FillRectangle(brush, r); } if (i == _iHot) { if ((textColor == 0 || b.TextColor != default) && b.BackgroundColor == default) { using (var brush = new SolidBrush(0xC0DCF3.ToColor_())) g.FillRectangle(brush, r); } using (var pen = new Pen(0x90C8F6.ToColor_(), _z.bBorder)) g.DrawRectangleInset(pen, r); } r.Inflate(-_z.bBorder, -_z.bBorder); var r2 = r; r.left += _z.check; if (b.image2 != null) { g.DrawImage(b.image2, r.left + _z.textPaddingY, r.top + (r.Height - _z.image) / 2, _z.image, _z.image); } r.left += _z.image + _z.textPaddingX; r.right -= _z.textPaddingX + _z.submenu + _z.submenuMargin; r.top += _z.textPaddingY; r.bottom -= _z.textPaddingY; if (b.Hotkey != null) { Api.SetTextColor(dc, textColorDisabled); var rh = r; rh.left += _z.xHotkeyStart; Api.DrawText(dc, b.Hotkey, ref rh, c_tffHotkey); } Api.SetTextColor(dc, b.TextColor != default ? b.TextColor.ToBGR() : (b.IsDisabled ? textColorDisabled : textColor)); if (!b.Text.NE()) { if (b.FontBold) { Api.SelectObject(dc, NativeFont_.BoldCached(_dpi)); } r.Width = _z.xTextEnd; Api.DrawText(dc, b.Text, ref r, _TfFlags(b)); if (b.FontBold) { Api.SelectObject(dc, font); } } if (b.IsSubmenu) { _DrawControl(_z.zSubmenu, 16, b.IsDisabled ? 2 : 1, "➜", r2.right - _z.submenu, r.top, _z.submenu, r.Height); } if (b.IsChecked) { _DrawControl(_z.zCheck, 11, b.checkType == 1 ? (b.IsDisabled ? 2 : 1) : (b.IsDisabled ? 4 : 3), b.checkType == 1 ? "✔" : "●", r2.left, r.top, _z.check, r.Height); } void _DrawControl(SIZE z, int part, int state, string c, int x, int y, int width, int height) { if (_z.theme != default && z != default) { RECT r = new(x, r2.top + (r2.Height - z.height) / 2, z.width, z.height); Api.DrawThemeBackground(_z.theme, dc, part, state, r); } else { RECT r = new(x, y, width, height); Api.DrawText(dc, c, ref r, TFFlags.CENTER); //cannot use DrawFrameControl(DFC_MENU, DFCS_MENUARROW etc), it draws with white background and too small when high DPI } } } } }
/// <summary> /// Selects standard UI font for specified DPI. /// </summary> /// <param name="dpi"></param> public FontDC_(DpiOf dpi) : this(NativeFont_.RegularCached(dpi)) { }
SIZE _Measure(int maxWidth) { SIZE R = default; _z?.Dispose(); _z = new _Metrics(this); int buttonPlusX = (_z.bBorder + _z.textPaddingX) * 2 + _z.check + _z.image + _z.submenu + _z.submenuMargin; int buttonPlusY = (_z.bBorder + _z.textPaddingY) * 2 + 1; int maxTextWidth = maxWidth - buttonPlusX; var font = NativeFont_.RegularCached(_dpi); using var dc = new FontDC_(font); int lineHeight = dc.MeasureEP(" ").height + buttonPlusY; int maxHotkey = 0; if (_z.hasHotkeys) { foreach (var b in _a) { if (b.Hotkey == null) { continue; } int wid = dc.MeasureDT(b.Hotkey, c_tffHotkey).width; maxHotkey = Math.Max(maxHotkey, Math.Min(wid, maxTextWidth / 2)); } } int hotkeyPlus = lineHeight * 3 / 2; //space between text and hotkey if (maxHotkey > 0) { maxTextWidth -= maxHotkey += hotkeyPlus; } int y = 0; for (int i = 0; i < _a.Count; i++) { //note: to support multiline, wrap, underlines and tabs we have to use DrawText, not TextOut. // DrawText(DT_CALCRECT) is slow. Very slow compared with GetTextExtentPoint32. Eg 100-300 ms for 1000 items. Depends on text length. var b = _a[i]; SIZE z; if (b.IsSeparator) { z = new(0, _z.separator); } else { var s = b.Text; if (!s.NE()) { if (b.FontBold) { Api.SelectObject(dc, NativeFont_.BoldCached(_dpi)); } z = dc.MeasureDT(s, _TfFlags(b), maxTextWidth); z.width = Math.Min(z.width, maxTextWidth); if (b.FontBold) { Api.SelectObject(dc, font); } _z.xTextEnd = Math.Max(_z.xTextEnd, z.width); z.width += buttonPlusX; z.height += buttonPlusY; } else { z = new(0, lineHeight); } } b.rect = new(0, y, z.width, z.height); y += z.height; R.width = Math.Max(R.width, z.width); R.height = Math.Max(R.height, y); } if (maxHotkey > 0) { R.width += maxHotkey; _z.xHotkeyStart = _z.xTextEnd + hotkeyPlus; } foreach (var b in _a) { b.rect.right = R.width; } return(R); }