JDK-8282091 : TreeView Horizontal Scroll Bar in endless hide and show loop
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: openjfx11,openjfx17,openjfx18
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2022-02-17
  • Updated: 2022-03-24
  • Resolved: 2022-02-18
Related Reports
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
Multiple

Debian stable:

5.10.0-11-amd64 #1 SMP Debian 5.10.92-1 (2022-01-18) x86_64 GNU/Linux
OpenJDK 64-Bit Server VM (build 11.0.14+9-post-Debian-1deb11u1, mixed mode, sharing)

Rocky Linux 8
4.18.0-348.12.2.el8_5.x86_64 #1 SMP Wed Jan 19 17:53:40 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
OpenJDK 64-Bit Server VM 18.9 (build 11.0.14+9-LTS, mixed mode, sharing)

A DESCRIPTION OF THE PROBLEM :
A TreeView with a custom but simple Cell as content can cause the horizontal scroll pane to twitch endlessly when the entry that is "too wide" that is causing the scroll pane to be shown is right at the bottom of the TreeView.

Please see the screen recording for a demonstration: <LINK>

Minimal reproduction source code below.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Having a TreeView with a resizeable height. Having custom Cells that have a Pane (HBox) as content. Having an entry that will cause the TreeView to show the scroll bar at the lower part of the tree view (please see Video if unclear).

Then when the TreeView height is reduced just so much that the entry that causes the scroll bar to be shown is barely visible and the tree view was focussed just before that action or after the action that caused the resize. It will cause the scroll pane to be shown and hidden an unlimited number of times.
Having the mouse cursor on the window or outsize of the cursor can make a difference, please try what works.

In the code example you may use the "-" or "+" to resize the TreeView but resizing the whole window in order to manipulate the TreeView height will also work.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The scroll bar is shown or not shown in a stable manor.
ACTUAL -
The scroll bar twitches, it is shown or hidden in an endless loop.

---------- BEGIN SOURCE ----------

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TreeViewScrollApp extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Tree cell scroll jiggle example");

        TreeView<String> treeView = new TreeView<>(new TreeItem<>("root"));
        treeView.setCellFactory(c -> new CustomTreeCell()); // It seems to be important to have a custom cell

        // Add some entries to allow our "wide" entry to be only! at the bottom of the list
        for (int i = 0; i < 18; i++) {
            treeView.getRoot().getChildren().addAll(new TreeItem<>("some entry " + i));
        }

        // Pick some item at the lower part of the visible area and add a sub item
        final TreeItem<String> expandableTreeItem = treeView.getRoot().getChildren().get(6);
        expandableTreeItem.getChildren().addAll(new TreeItem<>("some longer entry that causes scrollbar"));
        expandableTreeItem.getChildren().addAll(new TreeItem<>("some entry"));

        // Expand it so that the total width will cause a hor scroll pane
        expandableTreeItem.setExpanded(true);

        // Like in my real app but maybe those settings are not that relevant
        treeView.setShowRoot(false);
        treeView.setEditable(false);
        treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

        // Important! We need to find a height that will cause the expanded item to be shown barely. Using the right
        // height and the focusing the tree view will the horizontal scroll bar to be shown and hidden indefinitely. I
        // suppose the mechanism to calculate the need for a scroll bar is in a loop.
        //
        // Good value on my system, use + / - buttons and then focus tree view to trigger the behavior
        // alternatively resizing the height of the window causes the same thing and allows for a good precision
        treeView.setMaxHeight(185.1);

        // Buttons are just there to adjust the tree view height until you get the error
        Button btnMinus = new Button();
        btnMinus.setText("-");
        btnMinus.setOnAction(evt -> treeView.setMaxHeight(treeView.getMaxHeight() - 0.1));

        Button btnMinus2 = new Button();
        btnMinus2.setText("--");
        btnMinus2.setOnAction(evt -> treeView.setMaxHeight(treeView.getMaxHeight() - 1.));

        Button btnAdd = new Button();
        btnAdd.setText("+");
        btnAdd.setOnAction(evt -> treeView.setMaxHeight(treeView.getMaxHeight() + 0.1));

        Button btnAdd2 = new Button();
        btnAdd2.setText("++");
        btnAdd2.setOnAction(evt -> treeView.setMaxHeight(treeView.getMaxHeight() + 1.));

        // Just for some debugging
        Label prefHeightLabel = new Label();
        prefHeightLabel.textProperty().bind(Bindings.createStringBinding(() -> {
            return "Tree View Max Height: " + treeView.maxHeightProperty().get();
        }, treeView.maxHeightProperty()));

        HBox root = new HBox(
                treeView,
                new VBox(
                        btnAdd,
                        btnAdd2,
                        btnMinus,
                        btnMinus2,
                        prefHeightLabel,
                        new Label("Resizing and then focussing the tree view is important.")));
        primaryStage.setScene(new Scene(root, 800, 600));

        primaryStage.show();

        // Wait 1s to trigger the actions that will reproduce the issue – might work, but maybe manual adjustment
        // of the height value is necessary on your system
        new Thread(() -> {

            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Focus and click on button to resize it, like a human being (alternatively resize the whole app until the
            // tree view is shrunk
            //Platform.runLater(() -> btnMinus.requestFocus());
            
            
            // Focus the tree view seems to be a vital part, also try moving the mouse, entering and leaving the window
            Platform.runLater(() -> {
                primaryStage.requestFocus();
                btnMinus.requestFocus();
                btnMinus.fire();
                treeView.requestFocus();
            });
            
        }).start();
    }

    private static class CustomTreeCell extends TreeCell<String> {

        private final Label name = new Label();

        private final HBox pain = new HBox(name);

        @Override
        public void updateItem(final String item, final boolean empty) {
            super.updateItem(item, empty);
            this.setText(null);

            // Only "workaround" I found: Make sure the scroll pane is shown all the time
            // since it is not possible to set it directly, make sure the content has a
            // size that triggers the scoll pane.
            // pain.setPrefSize(220, 20);

            if (empty || (item == null)) {
                name.setText("");
                setGraphic(null);
            } else {
                name.setText(item);
                
                // I could mitigate the problem by settings a fixed with to the pane – but this is not practical
                // since the correct text width is nothing the app knows
//                final int w = item.length() * 10; // some rough estimate
//                pain.setPrefWidth(w);
//                pain.setMaxWidth(w);
//                pain.setMinWidth(w);
                
                // Set a "flexible" pane as content
                setGraphic(pain);
            }
        }

    }

}

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

CUSTOMER SUBMITTED WORKAROUND :
Setting a fixed size for the cell seems to work but is not always practical.

It also works to show the horizontal scoll bar all the time. But unfortunately there is no option to force it to be shown, this has to be worked around again by setting the width of all items very large.


FREQUENCY : often



Comments
Duplicate of JDK-8255436 Test Results: ========== 8u321: Pass <-8 Not applicable
18-02-2022