From 1a75066fda02e14546eff83ab2e3eac68323ecb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 12 Sep 2025 09:20:12 +0200 Subject: [PATCH] When computing the size of a widget always return values rounded up When converting pixels to point there can be depending on the zoom level be a fractional result. Currently in all cases these result is converted into an integer using `Math.round()` that will make values `+/-0.5` resulting in small values to be round towards a smaller value. While it is maybe valid for a _location_, when using points to express a _dimension_ this is not okay as it will result in the reported (integer) value to be to small leading to errors when the SWT API is then used after performing additional computations maybe. This now makes the following adjustments: 1. Introduce a rounding mode that allows different ways of rounding and adds as a first step ROUND (the previous default) and UP (for always round towards the largest integer) 2. Adjust the `Control` class to decide what mode is best in what situation. See - https://github.com/eclipse-platform/eclipse.platform.swt/pull/2381 - https://github.com/eclipse-platform/eclipse.platform.swt/issues/2166 --- .../common/org/eclipse/swt/graphics/Point.java | 5 +++-- .../org/eclipse/swt/internal/RoundingMode.java | 17 +++++++++++++++++ .../org/eclipse/swt/internal/Win32DPIUtils.java | 9 +++++++-- .../win32/org/eclipse/swt/widgets/Control.java | 11 +++++++---- 4 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/RoundingMode.java diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Point.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Point.java index b1a026ca92c..4afb5256959 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Point.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Point.java @@ -16,6 +16,7 @@ import java.io.*; +import org.eclipse.swt.internal.*; import org.eclipse.swt.widgets.*; /** @@ -135,8 +136,8 @@ public OfFloat(int x, int y) { super(x, y); } - public OfFloat(float x, float y) { - super(Math.round(x), Math.round(y)); + public OfFloat(float x, float y, RoundingMode mode) { + super(mode.round(x), mode.round(y)); this.residualX = x - this.x; this.residualY = y - this.y; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/RoundingMode.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/RoundingMode.java new file mode 100644 index 00000000000..1f2774d2272 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/RoundingMode.java @@ -0,0 +1,17 @@ +package org.eclipse.swt.internal; +/** +* @noreference This class is not intended to be referenced by clients +*/ +public enum RoundingMode { + ROUND, UP; + +int round(float x) { + if (this == ROUND) { + return Math.round(x); + } + if (this == UP) { + return (int) Math.ceil(x); + } + return (int) x; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java index fb27d92d90d..bfbfc9b0a1e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java @@ -114,12 +114,17 @@ public static float pixelToPoint(Drawable drawable, float size, int zoom) { } public static Point pixelToPoint(Point point, int zoom) { + //TODO actually all callers should explicitly state what mode the want! + return pixelToPoint(point, zoom, RoundingMode.ROUND); + } + + public static Point pixelToPoint(Point point, int zoom, RoundingMode mode) { if (zoom == 100 || point == null) return point; Point.OfFloat fPoint = FloatAwareGeometryFactory.createFrom(point); float scaleFactor = DPIUtil.getScalingFactor(zoom); float scaledX = fPoint.getX() / scaleFactor; float scaledY = fPoint.getY() / scaleFactor; - return new Point.OfFloat(scaledX, scaledY); + return new Point.OfFloat(scaledX, scaledY, mode); } public static Point pixelToPoint(Drawable drawable, Point point, int zoom) { @@ -225,7 +230,7 @@ public static Point pointToPixel(Point point, int zoom) { float scaleFactor = DPIUtil.getScalingFactor(zoom); float scaledX = fPoint.getX() * scaleFactor; float scaledY = fPoint.getY() * scaleFactor; - return new Point.OfFloat(scaledX, scaledY); + return new Point.OfFloat(scaledX, scaledY, RoundingMode.ROUND); } public static Point pointToPixel(Drawable drawable, Point point, int zoom) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java index 709f8aada02..a666b49ec71 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java @@ -620,7 +620,9 @@ public Point computeSize (int wHint, int hHint, boolean changed){ int zoom = getZoom(); wHint = (wHint != SWT.DEFAULT ? Win32DPIUtils.pointToPixel(wHint, zoom) : wHint); hHint = (hHint != SWT.DEFAULT ? Win32DPIUtils.pointToPixel(hHint, zoom) : hHint); - return Win32DPIUtils.pixelToPoint(computeSizeInPixels(wHint, hHint, changed), zoom); + //We should never return a size that is to small, RoundingMode.UP ensures we at worst case report + //a size that is a bit too large by half a point + return Win32DPIUtils.pixelToPoint(computeSizeInPixels(wHint, hHint, changed), zoom, RoundingMode.UP); } Point computeSizeInPixels (int wHint, int hHint, boolean changed) { @@ -1372,7 +1374,8 @@ public Object getLayoutData () { */ public Point getLocation () { checkWidget (); - return Win32DPIUtils.pixelToPoint(getLocationInPixels(), getZoom()); + //For a location the closest point values is okay + return Win32DPIUtils.pixelToPoint(getLocationInPixels(), getZoom(), RoundingMode.ROUND); } Point getLocationInPixels () { @@ -1528,7 +1531,7 @@ public Shell getShell () { */ public Point getSize (){ checkWidget (); - return Win32DPIUtils.pixelToPoint(getSizeInPixels (), getZoom()); + return Win32DPIUtils.pixelToPoint(getSizeInPixels (), getZoom(), RoundingMode.UP); } Point getSizeInPixels () { @@ -4030,7 +4033,7 @@ public Point toControl (int x, int y) { checkWidget (); Point displayPointInPixels = getDisplay().translateToDisplayCoordinates(new Point(x, y)); final Point controlPointInPixels = toControlInPixels(displayPointInPixels.x, displayPointInPixels.y); - return Win32DPIUtils.pixelToPoint(controlPointInPixels, getZoom()); + return Win32DPIUtils.pixelToPoint(controlPointInPixels, getZoom(), RoundingMode.ROUND); } Point toControlInPixels (int x, int y) {