From 47bfaae288bd20bcbbc33615c1de982c527a1856 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 23 Aug 2026 17:47:58 +1000 Subject: [PATCH] Re-measure the character cell when the display scaling changes The canvas lays everything out in character cells and measures one, in pixels, from a Graphics - which is per display. The measurement was kept for the life of the control and only the footer and the scrollbar were rescaled on a DPI change, so dragging the window to a display at 150% left the framework drawing the same eleven point glyphs half again as large on the old row pitch and gutter: rows overlapping, labels clipped, and a body row count that did not describe what was on screen. The other way round left gaps. The measurement is thrown away on OnDpiChangedAfterParent, and a dragged splitter is now remembered in cells rather than pixels, which is what the constant beside it always said it was for: the column holds the same number of characters on the new display rather than the same number of pixels. No test. Observing it takes a second display at a different scale factor, which nothing in the suite can produce; the pixel snapshots cover the arithmetic at one scale. --- src/DiffEngineViewer.Windows/ViewerCanvas.cs | 59 +++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/DiffEngineViewer.Windows/ViewerCanvas.cs b/src/DiffEngineViewer.Windows/ViewerCanvas.cs index fc8b926a..3473d377 100644 --- a/src/DiffEngineViewer.Windows/ViewerCanvas.cs +++ b/src/DiffEngineViewer.Windows/ViewerCanvas.cs @@ -59,10 +59,20 @@ sealed class ViewerCanvas : Control Screen? screen; /// - /// Zero until first asked for, because the default is counted in cells and a cell can only be - /// measured once a Graphics exists. + /// The queue column as the reader last dragged it, in cells. Zero until they do, which is what + /// answers for. + /// + /// Cells rather than pixels so that it survives a change of display scaling: the column then + /// holds the same number of characters on the new one rather than the same number of pixels. + /// /// - int queueWidth; + int queueCells; + + /// + /// Measured once, from a Graphics, and thrown away when the display scaling changes: the same + /// point size is a different number of pixels there. + /// + Size cell; bool dragging; @@ -110,16 +120,31 @@ Size Cell { get { - if (field.IsEmpty) + if (cell.IsEmpty) { using var graphics = CreateGraphics(); - field = MonoFont.Cell(graphics, font); + cell = MonoFont.Cell(graphics, font); } - return field; + return cell; } } + /// + /// Everything drawn here is laid out in character cells, and a cell is measured in pixels from + /// a Graphics, which is per display. Dragging the window to a display with different scaling + /// left that measurement behind: the framework drew the same eleven point glyphs half again as + /// large while the row pitch, the gutter and the queue column stayed where they were - rows + /// overlapping, labels clipped, and a body row count that did not match what was on screen. + /// The other way round left gaps. + /// + protected override void OnDpiChangedAfterParent(EventArgs e) + { + base.OnDpiChangedAfterParent(e); + cell = Size.Empty; + Invalidate(); + } + int BodyTop => padding + (Cell.Height + gap) * 2 + gap * 2; @@ -127,18 +152,8 @@ Size Cell /// Clamped on every read rather than only when dragged, so shrinking the window narrows the /// column instead of leaving the panes with nothing. /// - int QueueWidth - { - get - { - if (queueWidth == 0) - { - queueWidth = Cell.Width * defaultQueueCells; - } - - return Clamp(queueWidth); - } - } + int QueueWidth => + Clamp(Cell.Width * (queueCells == 0 ? defaultQueueCells : queueCells)); int Clamp(int value) { @@ -446,10 +461,12 @@ protected override void OnMouseMove(MouseEventArgs e) base.OnMouseMove(e); if (dragging) { - var width = Clamp(e.X - padding - gap / 2); - if (width != queueWidth) + // Clamped as a width, then held as cells, so the drag stops where it always stopped + // and what is remembered is a number of characters + var cells = Math.Max(1, Clamp(e.X - padding - gap / 2) / Cell.Width); + if (cells != queueCells) { - queueWidth = width; + queueCells = cells; Invalidate(); }