RFR: 8264591: HBox/VBox child widths pixel-snap to wrong value [v7]
John Hendrikx
jhendrikx at openjdk.org
Sat Apr 22 09:42:07 UTC 2023
On Tue, 3 Jan 2023 06:31:37 GMT, Michael Strauß <mstrauss at openjdk.org> wrote:
>> The children of HBox/VBox don't always pixel-snap to the same value as the container itself when a render scale other than 1 is used. This can lead to a visual glitch where the content bounds don't line up with the container bounds. In this case, the container will extend beyond its content, or vice versa.
>>
>> The following program shows the problem for HBox:
>>
>> Region r1 = new Region();
>> Region r2 = new Region();
>> Region r3 = new Region();
>> Region r4 = new Region();
>> Region r5 = new Region();
>> Region r6 = new Region();
>> r1.setBackground(new Background(new BackgroundFill(Color.GREY, null, null)));
>> r2.setBackground(new Background(new BackgroundFill(Color.DARKGRAY, null, null)));
>> r3.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
>> r4.setBackground(new Background(new BackgroundFill(Color.GREY, null, null)));
>> r5.setBackground(new Background(new BackgroundFill(Color.DARKGRAY, null, null)));
>> r6.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
>> r1.setPrefWidth(25.3);
>> r2.setPrefWidth(25.3);
>> r3.setPrefWidth(25.4);
>> r4.setPrefWidth(25.3);
>> r5.setPrefWidth(25.3);
>> r6.setPrefWidth(25.4);
>> r1.setMaxHeight(30);
>> r2.setMaxHeight(30);
>> r3.setMaxHeight(30);
>> r4.setMaxHeight(30);
>> r5.setMaxHeight(30);
>> r6.setMaxHeight(30);
>> HBox hbox1 = new HBox(r1, r2, r3, r4, r5, r6);
>> hbox1.setBackground(new Background(new BackgroundFill(Color.RED, null, null)));
>> hbox1.setPrefHeight(40);
>>
>> r1 = new Region();
>> r2 = new Region();
>> r3 = new Region();
>> r4 = new Region();
>> r5 = new Region();
>> r6 = new Region();
>> r1.setBackground(new Background(new BackgroundFill(Color.GREY, null, null)));
>> r2.setBackground(new Background(new BackgroundFill(Color.DARKGRAY, null, null)));
>> r3.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
>> r4.setBackground(new Background(new BackgroundFill(Color.GREY, null, null)));
>> r5.setBackground(new Background(new BackgroundFill(Color.DARKGRAY, null, null)));
>> r6.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
>> r1.setPrefWidth(25.3);
>> r2.setPrefWidth(25.3);
>> r3.setPrefWidth(25.4);
>> r4.setPrefWidth(25.3);
>> r5.setPrefWidth(25.3);
>> r6.setPrefWidth(25.4);
>> r1.setMaxHeight(30);
>> r2.setMaxHeight(30);
>> r3.setMaxHeight(30);
>> r4.setMaxHeight(30);
>> r5.setMaxHeight(30);
>> r6.setMaxHeight(30);
>> HBox hbox2 = new HBox(r1, r2, r3, r4, r5, r6);
>> hbox2.setBackground(new Background(new BackgroundFill(Color.RED, null, null)));
>> hbox2.setPrefHeight(40);
>> hbox2.setPrefWidth(152);
>>
>> VBox root = new VBox(new HBox(hbox1), new HBox(hbox2));
>> root.setSpacing(20);
>> Scene scene = new Scene(root, 500, 250);
>>
>> primaryStage.setScene(scene);
>> primaryStage.show();
>>
>>
>> Here's a before-and-after comparison of the program above after applying the fix. Note that 'before', the backgrounds of the container (red) and its content (black) don't align perfectly. The render scale is 175% in this example.
>> 
>>
>> I've filed a bug report and will change the title of the GitHub issue as soon as it's posted.
>
> Michael Strauß has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 12 commits:
>
> - changes per review
> - Merge branch 'master' into fixes/box-snap-to-pixel
>
> # Conflicts:
> # modules/javafx.graphics/src/test/java/test/javafx/scene/layout/HBoxTest.java
> # modules/javafx.graphics/src/test/java/test/javafx/scene/layout/VBoxTest.java
> - Merge branch 'openjdk:master' into fixes/box-snap-to-pixel
> - revert snappedSum
> - don't call snappedSum in hot loop
> - Improved code documentation
> - Merge branch 'master' into fixes/box-snap-to-pixel
> - changed some method names, make test config a local class
> - added documentation, improved method names
> - Merge branch 'master' into fixes/box-snap-to-pixel
> - ... and 2 more: https://git.openjdk.org/jfx/compare/a35c3bf7...fbbc273a
I think a single pass algorithm should be possible.
The fractional screen scale should make no difference here as you can treat sizes (after scaling when snapping is on) as integer values. It is then a matter of assigning an integer number to each of the children.
What is clear to me however is that the algorithm is sufficiently complex that it warrants direct unit testing. Verifying adjustments with manual testing is inevitably going to miss regressions as there are a lot of cases to take into account.
I've been testing out a separate function to distribute space with unit tests to verify its behavior, and it is quite similar to what already exists requiring only a single pass. The problem with the current algorithm is more that it is tightly integrated making it hard to test, not that it can't be made to function correctly.
-------------
PR Comment: https://git.openjdk.org/jfx/pull/445#issuecomment-1518579770
More information about the openjfx-dev
mailing list