Re: Decreasing seams in scaled windows
John Ehresman <[email protected]> Wed, 17 Jun 2026 20:30:50 +0100
| Newsgroups | gmane.comp.lib.scintilla.devel |
|---|---|
| Message-ID | <[email protected]> |
--Apple-Mail=_8F0F36E6-E47F-4EAB-8BAC-CC936DA0FB6D Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="UTF-8" > On Jun 17, 2026, at 4:38=E2=80=AFAM, Neil Hodgson <scintilladotorg@gmail.= com> wrote: >> to add *F() variants of methods that take or return pixel values =E2=80= =94 these mimic how Qt works by always working in (possibly fractional) pix= el units. > ... > Since this could cause failures in these projects, I'd like to provide > some transition support. Perhaps by publishing a script that converts > 'int' into 'pixels' in Scintilla.iface where appropriate so projects > could update on their own schedule then force the update in a release > or two. The list could be in WidgetGen.py for now until .iface is updated or put te= mporary comments in the .iface file. > There will be decisions on how to expose this: does ScintillaTypes.h > get a 'using Pixels =3D intptr_t;' with ScintillaCall.h then specifying > 'Pixels' in methods? Is the documentation updated or left with 'int'? Making it a C++ alias might be good =E2=80=94 but I rarely work with C++ th= ese days so I=E2=80=99m not the one to ask. >> The second patch uses full repaints on macOS because I intermittently se= e a one pixel of selection color when I select and deselect lines (though I= can trigger it pretty easily). >=20 > Yes, I have seen this too and do not understand it. It=E2=80=99s apparently a bug with the Qt backing store on macOS. There are= 2+ backing stores and when a paint with a clip is done, the portions outsi= de the clip can be copied from another backing store and the portions in th= e clip drawn by the widget. The problem seems to be that the copy from anot= her backing store is done with logical pixel rects that are rounded to devi= ce pixels and the clip is applied in device pixels =E2=80=94 which can lead= to 1+ device pixel lines that aren=E2=80=99t filled by either so they reta= in whatever values they had originally. What seems to work is enlarging the= invalidated rect so it has coordinates that are multiples of device / logi= cal pixels =E2=80=94 if there are 3 device pixels per logical pixels, the c= oordinates need to be multiples of 3 so there aren=E2=80=99t any rounding g= aps. Attached is a patch that seems to work =E2=80=94 note that on some ratios, = it will revert to a full redraw. I have only tried this on macOS but plan t= o try it on the other platforms soon. John --=20 You received this message because you are subscribed to the Google Groups "= scintilla-interest" group. To unsubscribe from this group and stop receiving emails from it, send an e= mail to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/scintilla-i= nterest/8A65646B-CBAD-469B-B42B-FBC16EB8AFE4%40wingware.com. --Apple-Mail=_8F0F36E6-E47F-4EAB-8BAC-CC936DA0FB6D Content-Disposition: attachment; filename=QtScale-PlatQt.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="QtScale-PlatQt.patch" Content-Transfer-Encoding: 7bit diff -r 7d5e2d6cf92c qt/ScintillaEditBase/PlatQt.cpp --- a/qt/ScintillaEditBase/PlatQt.cpp Tue Jun 16 11:23:34 2026 +0100 +++ b/qt/ScintillaEditBase/PlatQt.cpp Wed Jun 17 16:30:58 2026 +0100 @@ -223,6 +223,40 @@ return variant.toDouble(); } +#ifdef Q_OS_APPLE +// Denominator of QT_SCALE_FACTOR when it is a simple fraction (1 for an integer +// factor), else 0. The macOS backing scale is 2 (Retina) or 1, so +// QT_SCALE_FACTOR = devicePixelRatioF / that. +static int ScaleFactorDenominator(WindowID wid) +{ + const double scale = ScaleOfWindow(wid); + // Divide out the backing scale: a fractional factor hides as an integer + // devicePixelRatioF (1.5x Retina -> 3.0); only sf shows the fraction. + const double backing = scale >= 2.0 ? 2.0 : 1.0; + const double sf = scale / backing; + for (int q = 1; q <= 8; ++q) + if (std::fabs(sf * q - std::round(sf * q)) < 1e-4) + return q; + return 0; +} + +// Smallest rect containing rc whose edges lie on a q-pixel grid. +static QRect SnapToGrid(QRect rc, int q) +{ + auto floorTo = [](int v, int q) { + int r = v % q; + if (r < 0) + r += q; + return v - r; + }; + const int l = floorTo(rc.left(), q); + const int t = floorTo(rc.top(), q); + const int r = floorTo(rc.right() + q, q) - 1; // ceil exclusive edge + const int b = floorTo(rc.bottom() + q, q) - 1; + return QRect(QPoint(l, t), QPoint(r, b)); +} +#endif + double ScaleToMultiply(WindowID wid) { const qreal scale = ScaleOfWindow(wid); @@ -943,7 +977,27 @@ return; } #endif - rc = rc / scale; + // Logical update rect containing the device rect. + QRect upd = QRectFFromPRect(rc / scale).toAlignedRect(); +#ifdef Q_OS_APPLE + // Work around Qt Cocoa logical -> device pixel rounding bug by + // enlarging the rect with SnapToGrid() + const int qSnap = ScaleFactorDenominator(wid); + if (qSnap == 0) { + window(wid)->update(); + return; + } + if (qSnap > 1) { + upd = SnapToGrid(upd, qSnap); + const QSize sz = window(wid)->size(); + if (upd.width() >= sz.width() && upd.height() >= sz.height()) { + window(wid)->update(); + return; + } + } +#endif + window(wid)->update(upd); + return; } window(wid)->update(QRectFromPRect(rc)); } --Apple-Mail=_8F0F36E6-E47F-4EAB-8BAC-CC936DA0FB6D Content-Type: text/plain; charset="UTF-8" -- You received this message because you are subscribed to the Google Groups "scintilla-interest" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/scintilla-interest/8A65646B-CBAD-469B-B42B-FBC16EB8AFE4%40wingware.com. --Apple-Mail=_8F0F36E6-E47F-4EAB-8BAC-CC936DA0FB6D--