JavaFX HiDPI layout bugs
Daniel Peintner
daniel.peintner at gmail.com
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.
see https://github.com/controlsfx/controlsfx/issues/1235
The sample with buttons works fine while PropertySheet does not work as
expected.
-- Daniel
On Fri, Jun 19, 2020 at 3:52 PM Sam Hutchins <
sam.hutchins at screamingfrog.co.uk> 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("glass.win.uiScale", "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());
> alert.show();
> });
> final var root = createTestUi();
> root.getChildren().add(button);
> primaryStage.setScene(new Scene(root, 400, 600));
> primaryStage.show();
> }
> 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