Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8282091

TreeView Horizontal Scroll Bar in endless hide and show loop

    XMLWordPrintable

    Details

    • Subcomponent:
    • CPU:
      generic
    • OS:
      generic

      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


        Attachments

          Issue Links

            Activity

              People

              Assignee:
              pnarayanaswa Praveen Narayanaswamy
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: