Re: Decreasing seams in scaled windows

Neil <[email protected]> Sun, 24 May 2026 20:34:49 -0700 (PDT)
Newsgroups gmane.comp.lib.scintilla.devel
Message-ID <[email protected]>
------=_Part_208204_1285727470.1779680089082
Content-Type: multipart/alternative; 
	boundary="----=_Part_208205_1740363535.1779680089082"

------=_Part_208205_1740363535.1779680089082
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

There were some more problems on current Wayland with the popups reporting=
=20
a devicePixelRatioF of 2.0 when the system scale was 1.5 or 1.33. Its=20
possible this is an initialization issue with windows not knowing an=20
accurate scaling factor before they receive a set scale message. It was=20
fixed by using the devicePixelRatioF of the main Scintilla window in the=20
attached patch.

Mitchell:

I like the indicators that I see without the patch. Having multiple new=20
scale-specific indicators doesn=E2=80=99t sound ideal.


It's more likely you will be able to replace Diagonal with a single new=20
DiagonalProportionalToDescender (or similar name) that implements a=20
particular style of scaling diagonals. If there are different opinions on=
=20
the best technique to use then there could be more than one Diagonal*. A=20
constraint here is that downstream Windows projects have been scaling for 6=
=20
years and shouldn't have their appearance modified in ways that they may=20
not like or want.

I increased my margin width using your new patch, but the markers remained=
=20
the same, small size. Do I need to implement some sort of client-side=20
scaling?


It is likely you will have to perform some scaling yourself.

I did run into a strange issue with a Scintilla view that is normally 1=20
line high is now 2 lines high. However, I set the height using the=20
SCI_TEXTHEIGHT, so I suspect Scintilla is reporting a new scaled height.=20
Either I have to divide by two, or Scintilla (or its Qt platform) needs to=
=20
do another computation somewhere.


I'm not sure. There are really two scales working in the code: graphics and=
=20
windowing with window coordinates not being scaled.

I would like some sort of control to disable scaling and keep the default=
=20
drawing Scintilla Qt is doing on my Retina and 4K display (I assume macOS=
=20
is doing 200% scaling for both). When I have time, I can enable scaling and=
=20
then identify and report any issues I find, as I am now. Right now it feels=
=20
like I=E2=80=99m going to run into a lot of small things over time rather t=
han all=20
at once. I=E2=80=99d be more comfortable with the fallback to disable and k=
eep the=20
current drawing.


I have been working on this but scaling occurs in many places and the=20
'scaled' control property needs to be distributed and kept current.

Neil

--=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/c4101367-bd2c-4159-97f9-5b21b496912en%40googlegroups.com.

------=_Part_208205_1740363535.1779680089082
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

There were some more problems on current Wayland with the popups reporting =
a=C2=A0devicePixelRatioF of 2.0 when the system scale was 1.5 or 1.33. Its =
possible this is an initialization issue with windows not knowing an accura=
te scaling factor before they receive a set scale message. It was fixed by =
using the=C2=A0devicePixelRatioF=C2=A0of the main Scintilla window in the a=
ttached patch.<br /><br /><div><div dir=3D"auto">Mitchell:</div><div dir=3D=
"auto"><br /></div><blockquote style=3D"margin: 0px 0px 0px 0.8ex; border-l=
eft: 1px solid rgb(204, 204, 204); padding-left: 1ex;">I like the indicator=
s that I see without the patch. Having multiple new scale-specific indicato=
rs doesn=E2=80=99t sound ideal.</blockquote><div><br /></div><div>It's more=
 likely you will be able to replace Diagonal with a single new DiagonalProp=
ortionalToDescender (or similar name) that implements a particular style of=
 scaling diagonals. If there are different opinions on the best technique t=
o use then there could be more than one Diagonal*. A constraint here is tha=
t downstream=C2=A0Windows projects have been scaling for 6 years and should=
n't have their appearance modified in ways that they may not like or want.<=
/div><div><br /></div><blockquote style=3D"margin: 0px 0px 0px 0.8ex; borde=
r-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">I increased my ma=
rgin width using your new patch, but the markers remained the same, small s=
ize. Do I need to implement some sort of client-side scaling?</blockquote><=
div><br /></div><div>It is likely you will have to perform some scaling you=
rself.</div><div><br /></div><blockquote style=3D"margin: 0px 0px 0px 0.8ex=
; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">I did run =
into a strange issue with a Scintilla view that is normally 1 line high is =
now 2 lines high. However, I set the height using the SCI_TEXTHEIGHT, so I =
suspect Scintilla is reporting a new scaled height. Either I have to divide=
 by two, or Scintilla (or its Qt platform) needs to do another computation =
somewhere.</blockquote><div><br /></div><div>I'm not sure. There are really=
 two scales working in the code: graphics and windowing with window coordin=
ates not being scaled.</div><div><br /></div><blockquote style=3D"margin: 0=
px 0px 0px 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: =
1ex;">I would like some sort of control to disable scaling and keep the def=
ault drawing Scintilla Qt is doing on my Retina and 4K display (I assume ma=
cOS is doing 200% scaling for both). When I have time, I can enable scaling=
 and then identify and report any issues I find, as I am now. Right now it =
feels like I=E2=80=99m going to run into a lot of small things over time ra=
ther than all at once. I=E2=80=99d be more comfortable with the fallback to=
 disable and keep the current drawing.</blockquote><div><br /></div><div>I =
have been working on this but scaling occurs in many places and the 'scaled=
' control property needs to be distributed and kept current.</div><div><br =
/></div><div>Neil</div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;scintilla-interest&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:[email protected]">=
[email protected]</a>.<br />
To view this discussion visit <a href=3D"https://groups.google.com/d/msgid/=
scintilla-interest/c4101367-bd2c-4159-97f9-5b21b496912en%40googlegroups.com=
?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.com/d/msgid/=
scintilla-interest/c4101367-bd2c-4159-97f9-5b21b496912en%40googlegroups.com=
</a>.<br />

------=_Part_208205_1740363535.1779680089082--

------=_Part_208204_1285727470.1779680089082
Content-Type: text/plain; charset=US-ASCII; name=QtScaleA.patch
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename=QtScaleA.patch
X-Attachment-Id: 6d617180-a9d1-4e24-aad6-70fd1fad39a6
Content-ID: <6d617180-a9d1-4e24-aad6-70fd1fad39a6>

diff -r c105229352ea qt/ScintillaEditBase/PlatQt.cpp
--- a/qt/ScintillaEditBase/PlatQt.cpp	Sun May 24 21:24:48 2026 +1000
+++ b/qt/ScintillaEditBase/PlatQt.cpp	Mon May 25 13:10:40 2026 +1000
@@ -229,6 +229,11 @@
 	return std::make_unique<SurfaceImpl>(width, height, mode);
 }
 
+std::unique_ptr<Surface> SurfaceImpl_AllocatePixMap(int width, int height, SurfaceMode mode)
+{
+	return std::make_unique<SurfaceImpl>(width, height, mode);
+}
+
 void SurfaceImpl::SetMode(SurfaceMode mode_)
 {
 	mode = mode_;
@@ -309,7 +314,7 @@
 
 int SurfaceImpl::DeviceHeightFont(int points)
 {
-	return points;
+	return points * device->devicePixelRatioF();
 }
 
 void SurfaceImpl::LineDraw(Point start, Point end, Stroke stroke)
@@ -796,6 +801,9 @@
 		painter->setRenderHint(QPainter::TextAntialiasing, true);
 
 		painter->setRenderHint(QPainter::Antialiasing, true);
+
+		const qreal scale = device->devicePixelRatioF();
+		painter->scale(1.0 / scale, 1.0 / scale);
 	}
 
 	return painter;
@@ -854,6 +862,8 @@
 
 void Window::SetPositionRelative(PRectangle rc, const Window *relativeTo)
 {
+	const qreal scale = window(relativeTo->GetID())->devicePixelRatioF();
+	rc = rc / scale;
 	QPoint oPos = window(relativeTo->wid)->mapToGlobal(QPoint(0,0));
 	int ox = oPos.x();
 	int oy = oPos.y();
@@ -884,7 +894,9 @@
 PRectangle Window::GetClientPosition() const
 {
 	// The client position is the window position
-	return GetPosition();
+	const qreal scale = wid ? window(wid)->devicePixelRatioF() : 1.0;
+	const PRectangle rc = GetPosition();
+	return rc * scale;
 }
 
 void Window::Show(bool show)
@@ -901,8 +913,15 @@
 
 void Window::InvalidateRectangle(PRectangle rc)
 {
-	if (wid)
-		window(wid)->update(QRectFromPRect(rc));
+	if (wid) {
+		//const qreal scale = window(wid)->devicePixelRatioF();
+		// Avoids aliasing leaving a partial bottom pixel
+		//rc = rc / scale;
+		//rc.top -= 20;
+		//rc.bottom += 2;
+		//window(wid)->update(QRectFromPRect(rc));
+		window(wid)->update();
+	}
 }
 
 void Window::SetCursor(Cursor curs)
@@ -999,6 +1018,7 @@
 	int visibleRows{5};
 	QMap<int,QPixmap> images;
 	float imageScale{1.0};
+	QWidget *owner = nullptr;
 };
 ListBoxImpl::ListBoxImpl() noexcept = default;
 
@@ -1012,6 +1032,7 @@
 	unicodeMode = unicodeMode_;
 
 	QWidget *qparent = static_cast<QWidget *>(parent.GetID());
+	owner = qparent;
 	ListWidget *list = new ListWidget(qparent);
 
 #if defined(Q_OS_WIN)
@@ -1054,7 +1075,11 @@
 	ListWidget *list = GetWidget();
 	const FontAndCharacterSet *pfacs = AsFontAndCharacterSet(font);
 	if (pfacs && pfacs->pfont) {
-		list->setFont(*(pfacs->pfont));
+		QFont fontDeScaled = *(pfacs->pfont);
+		const qreal scale = window(owner)->devicePixelRatioF();
+		qreal pointSize = fontDeScaled.pointSizeF() / scale;
+		fontDeScaled.setPointSizeF(pointSize);
+		list->setFont(fontDeScaled);
 	}
 }
 void ListBoxImpl::SetAverageCharWidth(int /*width*/) {}
@@ -1084,7 +1109,8 @@
 		width += style->pixelMetric(QStyle::PM_ScrollBarExtent);
 	}
 
-	return PRectangle(0, 0, width, height);
+	const qreal scale = window(wid)->devicePixelRatioF();
+	return PRectangle(0, 0, width, height) * scale;
 }
 int ListBoxImpl::CaretFromEdge()
 {
diff -r c105229352ea qt/ScintillaEditBase/PlatQt.h
--- a/qt/ScintillaEditBase/PlatQt.h	Sun May 24 21:24:48 2026 +1000
+++ b/qt/ScintillaEditBase/PlatQt.h	Mon May 25 13:10:40 2026 +1000
@@ -164,6 +164,8 @@
 	QPainter *GetPainter();
 };
 
+std::unique_ptr<Surface> SurfaceImpl_AllocatePixMap(int width, int height, SurfaceMode mode);
+
 }
 
 #endif
diff -r c105229352ea qt/ScintillaEditBase/ScintillaEditBase.cpp
--- a/qt/ScintillaEditBase/ScintillaEditBase.cpp	Sun May 24 21:24:48 2026 +1000
+++ b/qt/ScintillaEditBase/ScintillaEditBase.cpp	Mon May 25 13:10:40 2026 +1000
@@ -127,6 +127,12 @@
 	} else if (event->type() == QEvent::Hide) {
 		setMouseTracking(false);
 		result = QAbstractScrollArea::event(event);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
+	} else if (event->type() == QEvent::DevicePixelRatioChange) {
+		// Reset cached data
+		sqt->InvalidateStyleRedraw();
+		result = QAbstractScrollArea::event(event);
+#endif
 	} else {
 		result = QAbstractScrollArea::event(event);
 	}
@@ -318,7 +324,7 @@
 
 void ScintillaEditBase::mousePressEvent(QMouseEvent *event)
 {
-	const Point pos = PointFromQPoint(event->pos());
+	const Point pos = PointFromQPoint(event->pos()) * devicePixelRatioF();
 
 	emit buttonPressed(event);
 
@@ -347,7 +353,8 @@
 
 void ScintillaEditBase::mouseReleaseEvent(QMouseEvent *event)
 {
-	const QPoint point = event->pos();
+	const QPoint point = event->pos() * devicePixelRatioF();
+
 	if (event->button() == Qt::LeftButton)
 		sqt->ButtonUpWithModifiers(PointFromQPoint(point), TimeOfEvent(time), ModifiersOfKeyboard());
 
@@ -367,7 +374,7 @@
 
 void ScintillaEditBase::mouseMoveEvent(QMouseEvent *event)
 {
-	const Point pos = PointFromQPoint(event->pos());
+	const Point pos = PointFromQPoint(event->pos()) * devicePixelRatioF();
 
 	const bool shift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
 	const bool ctrl  = QApplication::keyboardModifiers() & Qt::ControlModifier;
diff -r c105229352ea qt/ScintillaEditBase/ScintillaQt.cpp
--- a/qt/ScintillaEditBase/ScintillaQt.cpp	Sun May 24 21:24:48 2026 +1000
+++ b/qt/ScintillaEditBase/ScintillaQt.cpp	Mon May 25 13:10:40 2026 +1000
@@ -809,13 +809,22 @@
 
 void ScintillaQt::PartialPaint(const PRectangle &rect)
 {
-	rcPaint = rect;
+	QWidget *widget = static_cast<QWidget *>(wMain.GetID());
+	const qreal scale = widget->devicePixelRatioF();
+
+	rcPaint = rect * scale;
 	paintState = PaintState::painting;
 	PRectangle rcClient = GetClientRectangle();
 	paintingAllText = rcPaint.Contains(rcClient);
 
+	// Create Bitmap of exactly window size then blit after Paint call
+	const int w = static_cast<int>(rcClient.Width() * scale);
+	const int h = static_cast<int>(rcClient.Height() * scale);
+	std::unique_ptr<Surface> surfPix = SurfaceImpl_AllocatePixMap(w, h, CurrentSurfaceMode());
+	PRectangle rcScaled = PRectangle::FromInts(0,0,w,h);
+	Paint(surfPix.get(), rcScaled);
 	AutoSurface surfacePaint(this);
-	Paint(surfacePaint, rcPaint);
+	surfacePaint->Copy(rcScaled, Point(0,0), *surfPix);
 	surfacePaint->Release();
 
 	if (paintState == PaintState::abandoned) {
diff -r c105229352ea src/Geometry.h
--- a/src/Geometry.h	Sun May 24 21:24:48 2026 +1000
+++ b/src/Geometry.h	Mon May 25 13:10:40 2026 +1000
@@ -45,6 +45,10 @@
 		return Point(x - other.x, y - other.y);
 	}
 
+	constexpr Point operator*(XYPOSITION multiplier) const noexcept {
+		return Point(x * multiplier, y * multiplier);
+	}
+
 	// Other automatically defined methods (assignment, copy constructor, destructor) are fine
 };
 
@@ -101,6 +105,15 @@
 		return (rc.left == left) && (rc.right == right) &&
 			(rc.top == top) && (rc.bottom == bottom);
 	}
+	
+	constexpr PRectangle operator*(XYPOSITION multiplier) const noexcept {
+		return PRectangle(left * multiplier, top * multiplier, right * multiplier, bottom * multiplier);
+	}
+
+	constexpr PRectangle operator/(XYPOSITION divisor) const noexcept {
+		return PRectangle(left / divisor, top / divisor, right / divisor, bottom / divisor);
+	}
+
 	constexpr bool Contains(Point pt) const noexcept {
 		return (pt.x >= left) && (pt.x <= right) &&
 			(pt.y >= top) && (pt.y <= bottom);

------=_Part_208204_1285727470.1779680089082--