JavaFX HiDPI layout bugs

Daniel Peintner daniel.peintner at
Mon Jun 22 09:07:34 UTC 2020

Hi Sam, all,

FYI: I have been encountering a similar behaviour but I have been *wrongly*
thinking it relates to ControlsFX PropertySheet which uses labels also.

The sample with buttons works fine while PropertySheet does not work as

-- Daniel

On Fri, Jun 19, 2020 at 3:52 PM Sam Hutchins <
sam.hutchins at> wrote:

> Hi,
> I've been doing some investigation into a layout bug in JavaFX on Windows
> with non-integer scaling values. I think it's related to JDK-8199592, and
> I've put a small example that will reproduce these layout bugs at the end
> of this email. The most obvious layout error is truncation of labels in
> many controls. I've used CheckBoxes in this example, but the truncation
> errors are apparent in any labeled control. I suspect the issue affects
> other controls too (and probably the entire layout), but in more subtle
> ways. With scaling values of 1.25 or 1.5, the label truncation only happens
> in the dialog box. At 1.75 the issue is apparent in both windows.
> I _think_ the issue is a combination of caching and invalid calculations.
> I stepped through the `layoutLabelInArea(x, y, w, h, alignment)` method in
> LabeledSkinBase and found that 3 layout passes take place.
> In the first and second passes, all the layout calculations appear to be
> done without taking scaling into account.
> The 3rd pass appears to be when a scene is involved, and the snap* methods
> start having an impact. It looks like some values are scaled appropriately,
> but others are still using the pre-scene cached values. In particular, the
> call to `leftLabelPadding()` returns 5 for the first 2 passes, but 5.6 in
> the 3rd (at 1.25 scaling). The value of `w` doesn't change though, and that
> .6 increase in padding causes the label to be truncated. When you interact
> with the CheckBox to trigger a 4th layout, the value of `w` is increased by
> .6, and the layout appears to work as expected.
> I've hacked some of the caching out of `Parent` by adding a call to
> clearSizeCache() to the start of the `prefWidth(height)` and
> `prefHeight(width)` methods, which forces JavaFX to re-compute everything
> on every call. This resolves the issues for simple layouts at 1.25 and 1.5
> scaling (but not 1.75).
> This doesn't resolve issues in more complicated layouts, however, as many
> subclasses of `Parent` have caching of their own. For example, controls in
> GridPanes will still exhibit this behaviour. I've hacked some caching out
> of that by removing the early return in the `computePrefHeights(widths)`
> and `computePrefWidths(heights)` methods, which resolves it for the
> GridPane case. I imagine there are many other instances where these cached
> values aren't correctly invalidated.
> I think there's also a fundamental issue with the layout calculations with
> non-integer scaling, as the layout is _always_ wrong at 1.75. I suspect
> that the layout calculations are always wrong, but the error at lower
> scaling values isn't enough to cause visible issues after pixel snapping.
> I'm not really sure where to go from here, as I'm not at all familiar with
> how and when JavaFX invalidates its layout calculations. If anyone can
> point me at some other threads to pull, I'd be grateful.
> Thanks,
> Sam
> Code to reproduce:
> import javafx.application.Application;
> import javafx.scene.Scene;
> import javafx.scene.control.Alert;
> import javafx.scene.control.Button;
> import javafx.scene.control.ButtonType;
> import javafx.scene.control.CheckBox;
> import javafx.scene.layout.GridPane;
> import javafx.scene.layout.VBox;
> import javafx.stage.Stage;
> public class LayoutBug
> {
>     public static void main(String[] args)
>     {
>         System.setProperty("", "1.5");
>         Application.launch(MainWindow.class);
>     }
>     public static class MainWindow extends Application
>     {
>         public void start(final Stage primaryStage)
>         {
>             final var button = new Button("Show Dialog");
>             button.setOnAction(e ->
>             {
>                 final var alert = new Alert(Alert.AlertType.NONE);
>                 alert.initOwner(primaryStage);
>                 alert.getButtonTypes().add(ButtonType.OK);
>                 alert.getDialogPane().setContent(createTestUi());
>       ;
>             });
>             final var root = createTestUi();
>             root.getChildren().add(button);
>             primaryStage.setScene(new Scene(root, 400, 600));
>   ;
>         }
>         private VBox createTestUi()
>         {
>             final var gridPane = new GridPane();
>             gridPane.addRow(0, new CheckBox("Checkbox in gridpane"));
>             return new VBox(10,
>                 gridPane,
>                 new CheckBox("Checkbox outside gridpane"));
>         }
>     }
> }

More information about the openjfx-dev mailing list