Re: Decreasing seams in scaled windows

John Ehresman <[email protected]> Fri, 26 Jun 2026 11:05:31 +0100
Newsgroups gmane.comp.lib.scintilla.devel
Message-ID <[email protected]>
--Apple-Mail=_51AF8940-1472-4FDF-A076-02B5C56F6102
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="UTF-8"

Neil,

It seems to be working for me on Wayland. I=E2=80=99m attaching my 2nd try =
at a patch to add a pixels type to the .iface file =E2=80=94 the pixels def=
initions are now in comments beneath the original definitions and there=E2=
=80=99s support in Face.py to read them. This should let other tools that u=
se the .iface file and scripts to continue to work until they are updated.

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/80D3F69C-5C27-40C2-A5B8-2337B4360C71%40wingware.com.

--Apple-Mail=_51AF8940-1472-4FDF-A076-02B5C56F6102
Content-Disposition: attachment;
	filename=pixel-iface-decls.diff
Content-Type: application/octet-stream;
	x-unix-mode=0664;
	name="pixel-iface-decls.diff"
Content-Transfer-Encoding: 7bit

diff -r e780c4c8e3c5 include/Scintilla.iface
--- a/include/Scintilla.iface	Thu Jun 25 05:31:03 2026 +0100
+++ b/include/Scintilla.iface	Thu Jun 25 21:25:03 2026 +0100
@@ -66,6 +66,7 @@
 ##     keymod -> integer containing key in low half and modifiers in high half
 ##     formatrange
 ##     formatrangefull
+##     pixels -> intptr_t pixel positions and sizes (experimental: used in ## line following definition)
 ## Enumeration types always start with a capital letter
 ## Types no longer used:
 ##     findtextex -> searchrange
@@ -211,10 +212,12 @@
 
 # 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,)
@@ -273,9 +276,11 @@
 
 # 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,)
@@ -440,6 +445,7 @@
 
 # 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,)
@@ -499,9 +505,11 @@
 
 # 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)
@@ -1010,9 +1018,11 @@
 
 # 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)
@@ -1022,9 +1032,11 @@
 
 # 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)
@@ -1050,10 +1062,12 @@
 # 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.
@@ -1374,15 +1388,19 @@
 
 # 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(,)
@@ -1411,9 +1429,11 @@
 
 # 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,)
@@ -1497,9 +1517,11 @@
 
 # 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.
@@ -1596,6 +1618,7 @@
 
 # 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,)
@@ -1814,9 +1837,11 @@
 
 # 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
@@ -1846,9 +1871,11 @@
 
 # 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,)
@@ -1860,6 +1887,7 @@
 # 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).
@@ -1872,6 +1900,7 @@
 
 # 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,)
@@ -1934,6 +1963,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)
@@ -2352,9 +2382,11 @@
 
 # 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(,)
@@ -2389,6 +2421,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.
@@ -2757,15 +2790,19 @@
 
 # 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,)
@@ -2866,10 +2903,12 @@
 
 # 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,)
@@ -2918,6 +2957,7 @@
 
 # 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 e780c4c8e3c5 qt/ScintillaEdit/WidgetGen.py
--- a/qt/ScintillaEdit/WidgetGen.py	Thu Jun 25 05:31:03 2026 +0100
+++ b/qt/ScintillaEdit/WidgetGen.py	Thu Jun 25 21:25:03 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,104 @@
 		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):
+	# The float (_f) twin takes logical pixels as a double; the int variant
+	# takes the plain integer alias.
+	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. A pixels slot takes
+	# logical pixels from the caller; the engine expects device pixels.
+	if not cppAlias(ptype):
+		return "0"
+	name = normalisedName(pname, options) if pname else "0"
+	if isPixel(ptype):
+		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):
+		# The "ScintillaScale" viewport property (set by ScintillaQt::SetScaleProperty)
+		# is the device-pixel ratio under SCALE_TECHNIQUE_PIXEL_ALIGNED and 0.0
+		# otherwise; treat 0.0 as 1.0 so the conversion is a no-op when unscaled.
+		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 +183,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 +205,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():
@@ -199,7 +235,9 @@
 def readInterface(cleanGenerated):
 	f = Face.Face()
 	if not cleanGenerated:
-		f.ReadFromFile("../../include/Scintilla.iface")
+		# pickUpPixels lets Face apply the '## ... pixels ...' annotations so the
+		# pixel slots come back typed as "pixels"; the int + _f twins follow.
+		f.ReadFromFile("../../include/Scintilla.iface", pickUpPixels=True)
 	return f
 
 def main(argv):
diff -r e780c4c8e3c5 scripts/Face.py
--- a/scripts/Face.py	Thu Jun 25 05:31:03 2026 +0100
+++ b/scripts/Face.py	Thu Jun 25 21:25:03 2026 +0100
@@ -67,12 +67,17 @@
 		self.events = {}
 		self.aliases = {}
 
-	def ReadFromFile(self, name):
+	def ReadFromFile(self, name, pickUpPixels=False):
 		currentCategory = ""
 		currentComment = []
 		currentCommentFinished = 0
 		file = open(name)
 		for line in file:
+			if pickUpPixels:
+				stripped = line.strip()
+				if stripped.startswith("## "):
+					self.ApplyPixelComment(stripped[3:])
+					continue
 			line = sanitiseLine(line)
 			if line:
 				if line[0] == "#":
@@ -145,3 +150,24 @@
 						self.aliases[name] = value
 						currentComment = []
 		file.close()
+
+	def ApplyPixelComment(self, featureVal):
+		# A '## <fun|get|set ... pixels ...>' comment below a message re-types
+		# the named feature's pixels slots, so a caller passing pickUpPixels=True
+		# sees them. Other consumers ignore it, since sanitiseLine strips '##'.
+		featureType, _, rest = featureVal.partition(" ")
+		if featureType not in ("fun", "get", "set") or not rest:
+			return
+		try:
+			retType, name, value, param1, param2 = decodeFunction(rest)
+		except ValueError:
+			return
+		feature = self.features.get(name)
+		if feature is None:
+			return
+		if retType == "pixels":
+			feature["ReturnType"] = "pixels"
+		if decodeParam(param1)[0] == "pixels":
+			feature["Param1Type"] = "pixels"
+		if decodeParam(param2)[0] == "pixels":
+			feature["Param2Type"] = "pixels"

--Apple-Mail=_51AF8940-1472-4FDF-A076-02B5C56F6102
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="UTF-8"



> On Jun 25, 2026, at 1:30=E2=80=AFAM, Neil Hodgson <scintilladotorg@gmail.=
com> wrote:
>=20
> John:
>=20
>> I think it makes sense to commit this and then I=E2=80=99ll send you pat=
ches in the next few days to go on top of this.
>> One change to maybe make now is to ifdef out the full repaint for Waylan=
d if SCINTILLA_QT_BACKINGSTORE_FIXED is defined.
>=20
> OK. Committed.
>=20
> Some other minor changes made:
> Replaced deprecated Q_OS_OSX with Q_OS_MACOS to fix warning.
> Moved output shared library to always be in scintilla/bin.
> Reformatted 'else'.
>=20
> The committed changes can be examined either in the repository
>=20
> hg clone http://hg.code.sf.net/p/scintilla/code scintilla
>=20
> or from
>=20
> https://www.scintilla.org/scite.zip Source
>=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/CACWkrTjbZTUng00J_p3yRL6Yb0R-Tsnru_QXD%2B0yiQPOXeh6ZA%40mail.gmai=
l.com.

--=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/80D3F69C-5C27-40C2-A5B8-2337B4360C71%40wingware.com.

--Apple-Mail=_51AF8940-1472-4FDF-A076-02B5C56F6102--