Re: Decreasing seams in scaled windows
John Ehresman <[email protected]> Tue, 16 Jun 2026 13:04:41 +0100
| Newsgroups | gmane.comp.lib.scintilla.devel |
|---|---|
| Message-ID | <[email protected]> |
--Apple-Mail=_191C2469-BECF-4493-A070-191172D8E0F5 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="UTF-8" I was working with your prior patch yesterday and have updated to the new p= atch. I have two patches and the first modifies the Qt widget method genera= tion 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) = pixel units. Our code uses scintilla through these methods; we=E2=80=99re b= eginning to migrate to using the float variants; the integer methods work w= ith the expected limitations. The second patch uses full repaints on macOS because I intermittently see a= one pixel of selection color when I select and deselect lines (though I ca= n trigger it pretty easily). I=E2=80=99ve only tested on macOS so far so th= is may be a problem on win32 as well; I=E2=80=99m using a QT_SCALE_FACTOR= =3D8.5 to test. The patch also fixes the initial tiny text problem and a sc= roll by lines bug. I=E2=80=99m going to see if I can figure out where the one pixel line comes= from. Thanks, 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/2726E62A-72FA-4022-9F68-C9CAAB4620FE%40wingware.com. --Apple-Mail=_191C2469-BECF-4493-A070-191172D8E0F5 Content-Disposition: attachment; filename=QtScale-pixels-method-gen.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="QtScale-pixels-method-gen.patch" Content-Transfer-Encoding: 7bit diff -r 7d5e2d6cf92c -r b191544ae99b include/Scintilla.iface --- a/include/Scintilla.iface Tue Jun 16 11:23:34 2026 +0100 +++ b/include/Scintilla.iface Tue Jun 16 11:38:17 2026 +0100 @@ -210,11 +210,11 @@ set void SetTabDrawMode=2699(TabDrawMode tabDrawMode,) # Find the position from a point within the window. -fun position PositionFromPoint=2022(int x, int y) +fun position PositionFromPoint=2022(pixels x, pixels y) # Find the position from a point within the window but return # INVALID_POSITION if not close to text. -fun position PositionFromPointClose=2023(int x, int y) +fun position PositionFromPointClose=2023(pixels x, pixels y) # Set caret to start of a line and ensure it is visible. fun void GotoLine=2024(line line,) @@ -272,10 +272,10 @@ get int GetTabWidth=2121(,) # Set the minimum visual width of a tab. -set void SetTabMinimumWidth=2724(int pixels,) +set void SetTabMinimumWidth=2724(pixels pixels,) # Get the minimum visual width of a tab. -get int GetTabMinimumWidth=2725(,) +get pixels GetTabMinimumWidth=2725(,) # Clear explicit tabstops on a line. fun void ClearTabStops=2675(line line,) @@ -439,7 +439,7 @@ set void MarkerSetBackSelectedTranslucent=2296(int markerNumber, colouralpha back) # Set the width of strokes used in .01 pixels so 50 = 1/2 pixel width. -set void MarkerSetStrokeWidth=2297(int markerNumber, int hundredths) +set void MarkerSetStrokeWidth=2297(int markerNumber, pixels hundredths) # Enable/disable highlight for current folding block (smallest one that contains the caret) fun void MarkerEnableHighlight=2293(bool enabled,) @@ -498,10 +498,10 @@ get MarginType GetMarginTypeN=2241(int margin,) # Set the width of a margin to a width expressed in pixels. -set void SetMarginWidthN=2242(int margin, int pixelWidth) +set void SetMarginWidthN=2242(int margin, pixels pixelWidth) # Retrieve the width of a margin in pixels. -get int GetMarginWidthN=2243(int margin,) +get pixels GetMarginWidthN=2243(int margin,) # Set a mask that determines which markers are displayed in a margin. set void SetMarginMaskN=2244(int margin, int mask) @@ -1009,10 +1009,10 @@ get IndicFlag IndicGetFlags=2685(int indicator,) # Set the stroke width of an indicator in hundredths of a pixel. -set void IndicSetStrokeWidth=2751(int indicator, int hundredths) +set void IndicSetStrokeWidth=2751(int indicator, pixels hundredths) # Retrieve the stroke width of an indicator. -get int IndicGetStrokeWidth=2752(int indicator,) +get pixels IndicGetStrokeWidth=2752(int indicator,) # Set the foreground colour of all whitespace and whether to use this setting. fun void SetWhitespaceFore=2084(bool useSetting, colour fore) @@ -1021,10 +1021,10 @@ fun void SetWhitespaceBack=2085(bool useSetting, colour back) # Set the size of the dots used to mark space characters. -set void SetWhitespaceSize=2086(int size,) +set void SetWhitespaceSize=2086(pixels size,) # Get the size of the dots used to mark space characters. -get int GetWhitespaceSize=2087(,) +get pixels GetWhitespaceSize=2087(,) # Used to hold extra styling information for each line. set void SetLineState=2092(line line, int state) @@ -1049,11 +1049,11 @@ # Retrieve the caret line frame width. # Width = 0 means this option is disabled. -get int GetCaretLineFrame=2704(,) +get pixels GetCaretLineFrame=2704(,) # Display the caret line framed. # Set width != 0 to enable this option and width = 0 to disable it. -set void SetCaretLineFrame=2705(int width,) +set void SetCaretLineFrame=2705(pixels width,) # Set a style to be changeable or not (read only). # Experimental feature, currently buggy. @@ -1373,16 +1373,16 @@ set void AllocateLines=2089(line lines,) # Sets the size in pixels of the left margin. -set void SetMarginLeft=2155(, int pixelWidth) +set void SetMarginLeft=2155(, pixels pixelWidth) # Returns the size in pixels of the left margin. -get int GetMarginLeft=2156(,) +get pixels GetMarginLeft=2156(,) # Sets the size in pixels of the right margin. -set void SetMarginRight=2157(, int pixelWidth) +set void SetMarginRight=2157(, pixels pixelWidth) # Returns the size in pixels of the right margin. -get int GetMarginRight=2158(,) +get pixels GetMarginRight=2158(,) # Is the document different from when it was last saved? get bool GetModify=2159(,) @@ -1410,10 +1410,10 @@ get bool GetSelectionHidden=2088(,) # Retrieve the x value of the point in the window where a position is displayed. -fun int PointXFromPosition=2164(, position pos) +fun pixels PointXFromPosition=2164(, position pos) # Retrieve the y value of the point in the window where a position is displayed. -fun int PointYFromPosition=2165(, position pos) +fun pixels PointYFromPosition=2165(, position pos) # Retrieve the line containing a position. fun line LineFromPosition=2166(position pos,) @@ -1496,10 +1496,10 @@ get bool GetOvertype=2187(,) # Set the width of the insert mode caret. -set void SetCaretWidth=2188(int pixelWidth,) +set void SetCaretWidth=2188(pixels pixelWidth,) # Returns the width of the insert mode caret. -get int GetCaretWidth=2189(,) +get pixels GetCaretWidth=2189(,) # Sets the position that starts the target which is used for updating the # document without affecting the scroll position. @@ -1595,7 +1595,7 @@ set void CallTipSetForeHlt=2207(colour fore,) # Enable use of STYLE_CALLTIP and set call tip tab size in pixels. -set void CallTipUseStyle=2212(int tabSize,) +set void CallTipUseStyle=2212(pixels tabSize,) # Set position of calltip, above or below text. set void CallTipSetPosition=2213(bool above,) @@ -1813,10 +1813,10 @@ get WrapVisualLocation GetWrapVisualFlagsLocation=2463(,) # Set the start indent for wrapped lines. -set void SetWrapStartIndent=2464(int indent,) +set void SetWrapStartIndent=2464(pixels indent,) # Retrive the start indent for wrapped lines. -get int GetWrapStartIndent=2465(,) +get pixels GetWrapStartIndent=2465(,) enu WrapIndentMode=SC_WRAPINDENT_ val SC_WRAPINDENT_FIXED=0 @@ -1845,10 +1845,10 @@ get LineCache GetLayoutCache=2273(,) # Sets the document width assumed for scrolling. -set void SetScrollWidth=2274(int pixelWidth,) +set void SetScrollWidth=2274(pixels pixelWidth,) # Retrieve the document width assumed for scrolling. -get int GetScrollWidth=2275(,) +get pixels GetScrollWidth=2275(,) # Sets whether the maximum width line displayed is used to set scroll width. set void SetScrollWidthTracking=2516(bool tracking,) @@ -1859,7 +1859,7 @@ # Measure the pixel width of some text in a particular style. # NUL terminated text argument. # Does not handle tab or control characters. -fun int TextWidth=2276(int style, string text) +fun pixels TextWidth=2276(int style, string text) # Sets the scroll range so that maximum scroll position has # the last line at the bottom of the view (default). @@ -1871,7 +1871,7 @@ get bool GetEndAtLastLine=2278(,) # Retrieve the height of a particular line of text in pixels. -fun int TextHeight=2279(line line,) +fun pixels TextHeight=2279(line line,) # Show or hide the vertical scroll bar. set void SetVScrollBar=2280(bool visible,) @@ -1933,7 +1933,7 @@ # Split the lines in the target into lines that are less wide than pixelWidth # where possible. -fun void LinesSplit=2289(int pixelWidth,) +fun void LinesSplit=2289(pixels pixelWidth,) # Set one of the colours used as a chequerboard pattern in the fold margin fun void SetFoldMarginColour=2290(bool useSetting, colour back) @@ -2351,10 +2351,10 @@ fun void DelLineRight=2396(,) # Set the xOffset (ie, horizontal scroll position). -set void SetXOffset=2397(int xOffset,) +set void SetXOffset=2397(pixels xOffset,) # Get the xOffset (ie, horizontal scroll position). -get int GetXOffset=2398(,) +get pixels GetXOffset=2398(,) # Set the last x chosen value to be the caret x position. fun void ChooseCaretX=2399(,) @@ -2388,7 +2388,7 @@ # Set the way the caret is kept visible when going sideways. # The exclusion zone is given in pixels. -fun void SetXCaretPolicy=2402(CaretPolicy caretPolicy, int caretSlop) +fun void SetXCaretPolicy=2402(CaretPolicy caretPolicy, pixels caretSlop) # Set the way the line the caret is on is kept visible. # The exclusion zone is given in lines. @@ -2756,16 +2756,16 @@ get Alpha IndicGetOutlineAlpha=2559(int indicator,) # Set extra ascent for each line -set void SetExtraAscent=2525(int extraAscent,) +set void SetExtraAscent=2525(pixels extraAscent,) # Get extra ascent for each line -get int GetExtraAscent=2526(,) +get pixels GetExtraAscent=2526(,) # Set extra descent for each line -set void SetExtraDescent=2527(int extraDescent,) +set void SetExtraDescent=2527(pixels extraDescent,) # Get extra descent for each line -get int GetExtraDescent=2528(,) +get pixels GetExtraDescent=2528(,) # Which symbol was defined for markerNumber with MarkerDefine fun MarkerSymbol MarkerSymbolDefined=2529(int markerNumber,) @@ -2865,11 +2865,11 @@ fun void AddUndoAction=2560(int token, UndoFlags flags) # Find the position of a character from a point within the window. -fun position CharPositionFromPoint=2561(int x, int y) +fun position CharPositionFromPoint=2561(pixels x, pixels y) # Find the position of a character from a point within the window. # Return INVALID_POSITION if not close to text. -fun position CharPositionFromPointClose=2562(int x, int y) +fun position CharPositionFromPointClose=2562(pixels x, pixels y) # Set whether switching to rectangular mode while selecting with the mouse is allowed. set void SetMouseSelectionRectangularSwitch=2668(bool mouseSelectionRectangularSwitch,) @@ -2917,7 +2917,7 @@ fun void AddSelection=2573(position caret, position anchor) # Find the selection index for a point. -1 when not at a selection. -fun int SelectionFromPoint=2474(int x, int y) +fun int SelectionFromPoint=2474(pixels x, pixels y) # Drop one selection fun void DropSelectionN=2671(int selection,) diff -r 7d5e2d6cf92c -r b191544ae99b qt/ScintillaEdit/WidgetGen.py --- a/qt/ScintillaEdit/WidgetGen.py Tue Jun 16 11:23:34 2026 +0100 +++ b/qt/ScintillaEdit/WidgetGen.py Tue Jun 16 11:38:17 2026 +0100 @@ -44,6 +44,7 @@ "colour": "int", "colouralpha": "int", "keymod": "int", + "pixels": "int", "string": "const char *", "stringresult": "const char *", "cells": "const char *", @@ -58,7 +59,7 @@ return s understoodTypes = ["", "void", "int", "bool", "position", "line", "pointer", - "colour", "colouralpha", "keymod", "string", "stringresult", "cells"] + "colour", "colouralpha", "keymod", "pixels", "string", "stringresult", "cells"] def understoodType(t): return t in understoodTypes or Face.IsEnumeration(t) @@ -76,22 +77,102 @@ understandAllTypes = False return understandAllTypes -def arguments(v, stringResult, options): +def isPixel(t): + return t == "pixels" + +def hasPixels(v): + return isPixel(v["ReturnType"]) or isPixel(v["Param1Type"]) or isPixel(v["Param2Type"]) + +def floatSuffix(options): + return "F" if options["qtStyle"] else "_f" + +def argCppType(t, floatVariant): + if floatVariant and isPixel(t): + return "double" + a = cppAlias(t) + if a == "int": + a = "sptr_t" + return a + +def returnCppType(v, stringResult, floatVariant): + if stringResult: + return "QByteArray" + if floatVariant and isPixel(v["ReturnType"]): + return "double" + r = cppAlias(v["ReturnType"]) + if r == "int": + r = "sptr_t" + return r + +def arguments(v, stringResult, options, floatVariant): ret = "" - p1Type = cppAlias(v["Param1Type"]) - if p1Type == "int": - p1Type = "sptr_t" + p1Type = argCppType(v["Param1Type"], floatVariant) if p1Type: ret = ret + p1Type + " " + normalisedName(v["Param1Name"], options) - p2Type = cppAlias(v["Param2Type"]) - if p2Type == "int": - p2Type = "sptr_t" + p2Type = argCppType(v["Param2Type"], floatVariant) if p2Type and not stringResult: if p1Type: ret = ret + ", " ret = ret + p2Type + " " + normalisedName(v["Param2Name"], options) return ret +def sendArg(ptype, pname, options): + # Expression passed to send() for one parameter slot. + if not cppAlias(ptype): + return "0" + name = normalisedName(pname, options) if pname else "0" + if isPixel(ptype): + # Caller passes logical pixels; the engine expects device pixels. + return "qRound64(" + name + " * sciScale)" + if "*" in cppAlias(ptype): + return "(sptr_t)" + name + return name + +def declaration(name, v, feat, options, floatVariant): + constDeclarator = " const" if feat == "get" else "" + stringResult = v["Param2Type"] == "stringresult" + returnType = returnCppType(v, stringResult, floatVariant) + suffix = floatSuffix(options) if floatVariant else "" + return ("\t" + returnType + " " + normalisedName(name, options, feat) + suffix + "(" + + arguments(v, stringResult, options, floatVariant) + + ")" + constDeclarator + ";") + +def definition(name, v, feat, options, floatVariant): + out = [] + constDeclarator = " const" if feat == "get" else "" + featureDefineName = "SCI_" + name.upper() + stringResult = v["Param2Type"] == "stringresult" + returnType = returnCppType(v, stringResult, floatVariant) + suffix = floatSuffix(options) if floatVariant else "" + out.append(returnType + " ScintillaEdit::" + normalisedName(name, options, feat) + suffix + "(" + + arguments(v, stringResult, options, floatVariant) + + ")" + constDeclarator + " {") + if hasPixels(v): + # Scale factor the engine is using: the "ScintillaScale" viewport + # property (set by ScintillaQt::SetScaleProperty) is the device-pixel + # ratio under SCALE_TECHNIQUE_PIXEL_ALIGNED and 0.0 otherwise. + out.append(" const double sciScaleProp = viewport()->property(\"ScintillaScale\").toDouble();") + out.append(" const double sciScale = sciScaleProp ? sciScaleProp : 1.0;") + returnStatement = "return " if returnType != "void" else "" + if stringResult: + out.append(" " + returnStatement + "TextReturner(" + featureDefineName + ", " + + sendArg(v["Param1Type"], v["Param1Name"], options) + ");") + else: + call = ("send(" + featureDefineName + ", " + + sendArg(v["Param1Type"], v["Param1Name"], options) + ", " + + sendArg(v["Param2Type"], v["Param2Name"], options) + ")") + if isPixel(v["ReturnType"]): + # Engine returns device pixels; hand back logical pixels. + if floatVariant: + out.append(" " + returnStatement + call + " / sciScale;") + else: + out.append(" " + returnStatement + "qRound64(" + call + " / sciScale);") + else: + out.append(" " + returnStatement + call + ";") + out.append("}") + out.append("") + return out + def printHFile(f, options): out = [] for name in f.order: @@ -100,16 +181,9 @@ feat = v["FeatureType"] if feat in ["fun", "get", "set"]: if checkTypes(name, v): - constDeclarator = " const" if feat == "get" else "" - returnType = cppAlias(v["ReturnType"]) - if returnType == "int": - returnType = "sptr_t" - stringResult = v["Param2Type"] == "stringresult" - if stringResult: - returnType = "QByteArray" - out.append("\t" + returnType + " " + normalisedName(name, options, feat) + "(" + - arguments(v, stringResult, options)+ - ")" + constDeclarator + ";") + out.append(declaration(name, v, feat, options, False)) + if hasPixels(v): + out.append(declaration(name, v, feat, options, True)) return out def methodNames(f, options): @@ -129,49 +203,9 @@ feat = v["FeatureType"] if feat in ["fun", "get", "set"]: if checkTypes(name, v): - constDeclarator = " const" if feat == "get" else "" - featureDefineName = "SCI_" + name.upper() - returnType = cppAlias(v["ReturnType"]) - if returnType == "int": - returnType = "sptr_t" - stringResult = v["Param2Type"] == "stringresult" - if stringResult: - returnType = "QByteArray" - returnStatement = "" - if returnType != "void": - returnStatement = "return " - out.append(returnType + " ScintillaEdit::" + normalisedName(name, options, feat) + "(" + - arguments(v, stringResult, options) + - ")" + constDeclarator + " {") - returns = "" - if stringResult: - returns += " " + returnStatement + "TextReturner(" + featureDefineName + ", " - if "*" in cppAlias(v["Param1Type"]): - returns += "(sptr_t)" - if v["Param1Name"]: - returns += normalisedName(v["Param1Name"], options) - else: - returns += "0" - returns += ");" - else: - returns += " " + returnStatement + "send(" + featureDefineName + ", " - if "*" in cppAlias(v["Param1Type"]): - returns += "(sptr_t)" - if v["Param1Name"]: - returns += normalisedName(v["Param1Name"], options) - else: - returns += "0" - returns += ", " - if "*" in cppAlias(v["Param2Type"]): - returns += "(sptr_t)" - if v["Param2Name"]: - returns += normalisedName(v["Param2Name"], options) - else: - returns += "0" - returns += ");" - out.append(returns) - out.append("}") - out.append("") + out += definition(name, v, feat, options, False) + if hasPixels(v): + out += definition(name, v, feat, options, True) return out def gtkNames(): --Apple-Mail=_191C2469-BECF-4493-A070-191172D8E0F5 Content-Disposition: attachment; filename=QtScale-platform-fixes.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="QtScale-platform-fixes.patch" Content-Transfer-Encoding: 7bit diff -r 7d5e2d6cf92c -r daa799cb6b38 qt/ScintillaEditBase/PlatQt.cpp --- a/qt/ScintillaEditBase/PlatQt.cpp Tue Jun 16 11:23:34 2026 +0100 +++ b/qt/ScintillaEditBase/PlatQt.cpp Tue Jun 16 11:38:17 2026 +0100 @@ -935,15 +935,17 @@ if (wid) { const qreal scale = ScaleOfWindow(wid); if (scale) { -#if !defined(Q_OS_WIN) && !defined(Q_OS_APPLE) - // Using X11 or Wayland, likely Linux but may be a BSD or similar +#if !defined(Q_OS_WIN) + // Using X11, Wayland, or macOS. if (scale != 1.0 && scale != 2.0) { // Fractional scaling leaves repaint debris, so redraw completely window(wid)->update(); return; } #endif - rc = rc / scale; + // Use QRectF::toAlignedRect to get int rect that contains float rect + window(wid)->update(QRectFFromPRect(rc / scale).toAlignedRect()); + return; } window(wid)->update(QRectFromPRect(rc)); } diff -r 7d5e2d6cf92c -r daa799cb6b38 qt/ScintillaEditBase/ScintillaQt.cpp --- a/qt/ScintillaEditBase/ScintillaQt.cpp Tue Jun 16 11:23:34 2026 +0100 +++ b/qt/ScintillaEditBase/ScintillaQt.cpp Tue Jun 16 11:38:17 2026 +0100 @@ -277,6 +277,11 @@ void ScintillaQt::ScrollText(Sci::Line linesToMove) { int dy = vs.lineHeight * (linesToMove); + if (IsPixelAlignedScale()) { + // lineHeight is in device pixels, viewport()->scroll() needs logical pixels + const qreal scale = window(wMain.GetID())->devicePixelRatioF(); + dy = qRound(dy / scale); + } scrollArea->viewport()->scroll(0, dy); } @@ -780,6 +785,8 @@ case Message::SetScaleTechnique: scaleTechnique = static_cast<ScaleTechnique>(wParam); + // Scale property needs to be set before InvalidateStyleRedraw() + SetScaleProperty(); InvalidateStyleRedraw(); break; --Apple-Mail=_191C2469-BECF-4493-A070-191172D8E0F5 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="UTF-8" > On Jun 16, 2026, at 12:56=E2=80=AFAM, Neil Hodgson <scintilladotorg@gmail= .com> wrote: >=20 > An updated version is available that fixes problems with scrolling and > position+size of calltips and autocompletion. >=20 > A ScaleTechnique property was added with values > SCALE_TECHNIQUE_DEFAULT=3D0, SCALE_TECHNIQUE_PIXEL_ALIGNED=3D1 controlled > with a SCI_SETSCALETECHNIQUE API. >=20 > The ScaleTechnique property may get additional enumeration values or > modifying bit flags with future refinements. This feature is marked > provisional so may change. >=20 > While the calltips and autocompletion lists are positioned under the > caret, they do not always flip above the caret when there is no room > below. This is because scaling is not consistently taken into account > when checking the bottom of the screen or window. >=20 > Autocompletion lists were made slightly wider as text could be > truncated with ... in some cases. This is from converting coordinates > that are scaled by a fraction to integers and there may be other > instances with similar issues. >=20 > The patch changes the location of the output shared libraries to be in > scintilla/bin as they were often in > scintilla/qt/ScintillaEditBase/bin. Newer versions of qmake (or Qt > Creator) seemed to use a different base or current directory when > building and this caused problems for downstream projects like Haven > that were coded to previous behaviour. >=20 > Haven was updated as https://www.scintilla.org/HavenScale4.zip to > match the changes in the patch. >=20 > Patch attached. >=20 > This may be committed to the main repository for the next release, so > anyone who thinks it may cause problems or want changes should speak > up now. >=20 > Neil >=20 > --=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= email to [email protected]. > To view this discussion visit https://groups.google.com/d/msgid/scintilla= -interest/CACWkrTgsnwcr_8C53NfpBfFptT%2B5dURNTE3vHWJpG2i6MumVhQ%40mail.gmai= l.com. > <QtScaleH.patch> --=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/2726E62A-72FA-4022-9F68-C9CAAB4620FE%40wingware.com. --Apple-Mail=_191C2469-BECF-4493-A070-191172D8E0F5--