JDK-8199592 : Control labels truncated at certain DPI scaling levels
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 9,10,openjfx11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows
  • CPU: generic
  • Submitted: 2018-03-13
  • Updated: 2020-12-23
  • Resolved: 2020-11-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
openjfx11.0.10Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows 10.0.16299.248

A DESCRIPTION OF THE PROBLEM :
In a simple self-sizing scene, some or all control labels are incorrectly truncated with an ellipsis at DPI levels between 100% and 200%. On 125% and 150%, this appears to be fixed by an explicit call to sizeToScene which however should not be necessary, and was not necessary in Java SE 8u60. On 175%, which is also one of the standard Windows 10 scaling levels, the bug persists regardless of calling sizeToScene.

The bug did not appear in several real applications I checked. It seems that increased layout complexity eventually suppresses the bug. There may be an initial text measuring error that eventually gets corrected by additional layout passes.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile below test case and run using -Dglass.win.uiScale=125%, 150%, or 175%. Add parameter "sizeToScene" for calling that method explicitly.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
All control labels should always be fully displayed, as the scene is entirely self-sizing without external constraints.
ACTUAL -
On DPI levels of 125%, 150%, or 175% some or all control labels are truncated with an ellipsis. Explicitly calling sizeToScene fixes this for 125% and 150% but not for 175% where all control labels are still truncated.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javafx.application.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class ControlLabelTest extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        final HBox box = new HBox();
        box.setAlignment(Pos.CENTER);
        box.setPadding(new Insets(8));
        box.setSpacing(8);

        for (int i = 0; i < 4; i++)
            box.getChildren().add(new CheckBox("Check"));
        stage.setScene(new Scene(box));
       
        if (getParameters().getUnnamed().contains("sizeToScene"))
            stage.sizeToScene();

        stage.show();
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Explicitly calling sizeToScene appears to suppress the bug at DPI levels other than 175%. Otherwise, only an explicit minimum size for the labeled control works reliably.


Comments
Changeset: a394fabd Author: Jose Pereda <jpereda@openjdk.org> Date: 2020-11-24 11:59:06 +0000 URL: https://github.com/openjdk/jfx/commit/a394fabd
24-11-2020

After some debugging, it seems the root cause of this issue is related to the scene being initialised (Scene::initPeer, Scene::preferredSize) before the UI scale is set, so Window::renderScaleX/Y still have the default 1.0 value.
02-11-2020

A new JBS issue has been filed to tackle the snap issue: JDK-8255415 That will partially solve this current issue, but Stage::sizeToScene is still required. To get the CheckBox test case fully working, without the need of sizeToScene, there is an issue related to the incorrect UI scale used to perform initial calculations of preferred sizes when the Scene is not added to the Window, and therefore SnapXX uses scale 1.0 (see Region). When the scene is added to the window, and the correct UI scale is used (for instance, 1.75), the preferred size is not recalculated, keeping the old wrong values.
26-10-2020

While trying to find the root cause of this issue, I noticed that CheckBoxSkin::layoutChildren has the following: final CheckBox checkBox = getSkinnable(); final double boxWidth = snapSizeX(box.prefWidth(-1)); ... final double labelWidth = Math.min(..., w - snapSizeX(boxWidth)); (Notice the double call to snapSizeX) For a given UI scale like 1.75, box.prefWidth starts in 16.0 (11.0 + 5.0 padding, UI scale 1.0). Then ui scale is set and produces a value for box.prefWidth of 16.42857142857143 ( snapped 11.42857142857143 + 5.0 padding), then we snap it to get the boxWidth (16.571428571428573) and finally we snap it again to get the labelWidth (17.142857142857142). As we can see, doing several nested snap calls, produces different results. This leads to the question: should snap methods called over and over again should return the same result? In other words, is this expected to be true: snapSizeX(snapSizeX(a)) == snapSizeX(a) ? If that is not the case, we should review every possible case where we accidentally call them twice, like this one in CheckBoxSkin::layoutChildren. Simply removing the snapSizeX call in labelWidth solves the issue for the attached test case. But if that is the case and nested snap calls should give the same value, we have to find why that is failing for some cases. The snapSize methods ultimately call Region::scaledCeil that returns: Math.ceil(value * scale) / scale Therefore, the following results: a = 16.42857142857143 snap(a) = 16.571428571428573 snap(snap(a)) = 17.142857142857142 can be obtained from: a = 16.42857142857143, scale = 1.75 a * scale = 28.750000000000004, Math.ceil(a * scale) = 29.0 b = snap(a) = 16.571428571428573 b * scale = 29.000000000000004, Math.ceil(b * scale) = 30.0 snap(b) = 17.142857142857142 Notice the precision error in b * scale that causes the jump from the 29 to the 30 "scaled" pixel. Do we need to truncate those operations?
28-09-2020

Prem please take a look this is targeted for 11
26-06-2018

I can reproduce this bug on Windows 7 as well.
16-03-2018

Set System scaling to 100% Issue is reproducible when used command line property -Dglass.win.uiScale with 125%, 150%, 175% values and it's a regression introduced in 9+114. When used runtime parameter 'sizeToScene', issue is not reproducible for scaling less than 175%. WIndows 10, 64-bit JDK results --------------------------------------- 8u172 : Pass 9+113 : Pass 9+114 : Fail 9.0.4 : Fail 11-ea+3 : Fail ------------------------ PFA screenshots - Truncated_label.png & Regression_9+114.png
14-03-2018