diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java index 87892d1b6a..4b96d17d49 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java @@ -104,11 +104,11 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() { button.setBounds(new Rectangle(0, 47, 200, 47)); assertEquals("Control::setBounds(Rectangle) doesn't scale up correctly", - new Rectangle(0, 82, 350, 83), button.getBoundsInPixels()); + new Rectangle(0, 82, 350, 82), button.getBoundsInPixels()); button.setBounds(0, 47, 200, 47); assertEquals("Control::setBounds(int, int, int, int) doesn't scale up correctly", - new Rectangle(0, 82, 350, 83), button.getBoundsInPixels()); + new Rectangle(0, 82, 350, 82), button.getBoundsInPixels()); } @ParameterizedTest 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 93b896b4e1..3cf454d47c 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 @@ -1227,7 +1227,7 @@ int getBorderWidthInPixels () { */ public Rectangle getBounds (){ checkWidget (); - return Win32DPIUtils.pixelToPoint(getBoundsInPixels (), computeGetBoundsZoom()); + return boundsToPointsViaShellCoordinates(getBoundsInPixels (), computeGetBoundsZoom()); } Rectangle getBoundsInPixels () { @@ -1594,7 +1594,8 @@ public Shell getShell () { */ public Point getSize (){ checkWidget (); - return Win32DPIUtils.pixelToPointAsSize(getSizeInPixels (), computeGetBoundsZoom()); + Rectangle.OfFloat bounds = Rectangle.OfFloat.from(getBounds()); + return new Point.OfFloat(bounds.getWidth(), bounds.getHeight()); } Point getSizeInPixels () { @@ -3335,14 +3336,71 @@ public void setBounds (Rectangle rect) { checkWidget (); if (rect == null) error (SWT.ERROR_NULL_ARGUMENT); int zoom = computeBoundsZoom(); - Rectangle boundsInPixels = Win32DPIUtils.pointToPixel(rect, zoom); + Rectangle boundsInPixels = boundsToPixelsViaShellCoordinates(rect, zoom); fitInParentBounds(boundsInPixels, zoom); setBoundsInPixels(boundsInPixels); } +/** + * Converts bounds to pixels via the shell coordinate system, such that the + * coordinates for every control are rounded in the same. Otherwise, child and + * parent controls may not fit as their coordinates are relative to different + * coordinate systems (the ones with their individual parent as origin), such + * that applied rounding leads to different values for the actually same + * coordinates. One consequence when not doing this is that child controls with + * x and y set to 0 and width and height set to the parent's bounds may be + * larger than the parent. + */ +private Rectangle boundsToPixelsViaShellCoordinates(Rectangle bounds, int zoom) { + Point.OfFloat parentOffsetToShell = calculateParentOffsetToShell(); + Point.OfFloat parentOffsetToShellInPixels = Point.OfFloat + .from(Win32DPIUtils.pointToPixelAsSize(parentOffsetToShell, zoom)); + Rectangle.OfFloat offsetRectangle = new Rectangle.OfFloat(bounds.x + parentOffsetToShell.getX(), + bounds.y + parentOffsetToShell.getY(), bounds.width, bounds.height); + Rectangle.OfFloat offsetRectangleInPixels = Rectangle.OfFloat + .from(Win32DPIUtils.pointToPixel(offsetRectangle, zoom)); + int xInPixels = offsetRectangleInPixels.x - parentOffsetToShellInPixels.x; + int yInPixels = offsetRectangleInPixels.y - parentOffsetToShellInPixels.y; + int widthInPixels = offsetRectangleInPixels.width; + int heightInPixels = offsetRectangleInPixels.height; + return new Rectangle(xInPixels, yInPixels, widthInPixels, heightInPixels); +} + +private Point.OfFloat calculateParentOffsetToShell() { + float parentX = 0; + float parentY = 0; + Control parent = getParent(); + while (parent != null & !(parent instanceof Shell)) { + Rectangle.OfFloat parentLocation = Rectangle.OfFloat.from(parent.getBounds()); + parentX += parentLocation.getX(); + parentY += parentLocation.getY(); + parent = parent.getParent(); + } + return new Point.OfFloat(parentX, parentY); +} + +/** + * See {@link #boundsToPixelsViaShellCoordinates(Rectangle, int)} as inverse + * operation + */ +private Rectangle boundsToPointsViaShellCoordinates(Rectangle boundsInPixels, int zoom) { + Point.OfFloat parentOffsetToShell = calculateParentOffsetToShell(); + Point.OfFloat parentOffsetToShellInPixels = Point.OfFloat + .from(Win32DPIUtils.pointToPixelAsSize(parentOffsetToShell, zoom)); + Rectangle.OfFloat offsetRectangleInPixels = new Rectangle.OfFloat( + boundsInPixels.x + parentOffsetToShellInPixels.getX(), + boundsInPixels.y + parentOffsetToShellInPixels.getY(), boundsInPixels.width, boundsInPixels.height); + Rectangle.OfFloat offsetRectangle = Rectangle.OfFloat + .from(Win32DPIUtils.pixelToPoint(offsetRectangleInPixels, zoom)); + float x = offsetRectangle.getX() - parentOffsetToShell.getX(); + float y = offsetRectangle.getY() - parentOffsetToShellInPixels.getY(); + float width = offsetRectangle.getWidth(); + float height = offsetRectangle.getHeight(); + return new Rectangle.OfFloat(x, y, width, height); +} /** - * Cope with limited invertibility of pixel/point conversions. + * Cope with limited invertability of pixel/point conversions. *

* Example: 125% monitor, layout fills composite with single child *