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

ListView sometimes highlights wrong item

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: P4 P4
    • 8u20
    • 7u6
    • javafx
    • JavaFX 2.2b13 32-bit, Windows 7 64-bit

      See mailinglist post titled 'Little worried about the ListView/TreeView/TableView classes'.

      This occurs very rarely with the default ScrollBarSkin, but happens much more often with the provided Skin (that isn't 100% according to spec) -- it may help to debug the issue for the default ScrollBarSkin.

      What happens is that when scrolling through the ListView with cursor down and holding it at some point causes every Xth cell to be highlighted instead of the currently active cell. Play with the provided example and you should be able to reproduce it hopefully.


      package hs.mediasystem;

      import javafx.application.Application;
      import javafx.event.EventHandler;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.Label;
      import javafx.scene.control.ListCell;
      import javafx.scene.control.ListView;
      import javafx.scene.input.KeyCode;
      import javafx.scene.input.KeyEvent;
      import javafx.scene.layout.HBox;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;
      import javafx.util.Callback;

      public class ListTest extends Application {

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

        @Override
        public void start(Stage primaryStage) throws Exception {
          Scene scene = new Scene(new VBox() {{
            getChildren().add(
              new HBox() {{
                ListView<String> listView = new ListView<>();

                listView.setOnKeyPressed(new EventHandler<KeyEvent>() {
                  @Override
                  public void handle(KeyEvent event) {
                    if(event.getCode() == KeyCode.LEFT) {
                      System.out.println("Consuming cursor left!");
                      event.consume();
                    }
                    if(event.getCode() == KeyCode.RIGHT) {
                      System.out.println("Consuming cursor right!");
                      event.consume();
                    }
                  }
                });

                listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
                  private int cellCount;
                  @Override
                  public ListCell<String> call(ListView<String> param) {
                    System.out.println("New cell created " + cellCount++);
                    return new ListCell<String>() {
                      @Override
                      protected void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);

                        if(item != null) {
                          setText(item);
                        }
                      }
                    };
                  }
                });

                for(int i = 0; i < 1000; i++) {
                  listView.getItems().add("" + i);
                  if((i - 90) % 100 == 0) {
                    listView.getItems().add("Extra Long Item to get a ScrollBar at the Bottom of the ListView");
                  }
                }

                getChildren().add(new Button("1"));
                getChildren().add(new Button("2"));
                getChildren().add(new Button("3"));
                getChildren().add(listView);
                getChildren().add(new Button("4"));
                getChildren().add(new Button("5"));
                getChildren().add(new Button("6"));
              }}
            );
            getChildren().add(new Label("Tab to ListView, hold down cursor down until the bug occurs (may take a while and take multiple tries, usually at item 600)") {{
              setWrapText(true);
            }});
          }});

          scene.getStylesheets().add("bug.css");

          primaryStage.setScene(scene);
          primaryStage.show();
        }
      }

      bug.css


      .root {
        -c-base: rgb(173, 216, 230);
        
        -c-text: -c-base;
        -c-text-highlight: derive(-c-base, +75%);
        -c-text-focused: derive(-c-base, +75%);
        -c-text-unobtrusive: derive(-c-base, -30%);
        -c-text-watermark: rgba(173, 216, 230, 0.1);
        
        -c-shadow-highlight: derive(-c-base, -50%);
        
        -c-border-highlight: -c-text-highlight;
        -c-border-dialog: -c-text-highlight;
        -c-fill-dark: derive(-c-base, -75%);

        -c-star: yellow;
        -c-star-disabled: gray;
        
        -c-bg-transparent: rgba(0, 0, 0, 0.66);

        color-main: -c-base;
        
        color-focused-text: derive(color-main, 30%);

        color-content-background: derive(color-blue-80, -90%);

        color-blue: -c-base;
        color-blue-80: rgba(173, 216, 230, 0.8);
        color-blue-70: rgba(173, 216, 230, 0.7);
        color-blue-50: rgba(173, 216, 230, 0.5);
        color-blue-40: rgba(173, 216, 230, 0.4);
        color-blue-20: rgba(173, 216, 230, 0.2);
        color-blue-10: rgba(173, 216, 230, 0.1);
        color-white: rgb(255, 255, 255);
        
        color-black-50: rgba(0, 0, 0, 0.50);
        color-black-75: rgba(0, 0, 0, 0.75);
        
        -fx-font-family: "Arial";
        -fx-font-size: 16pt;
        -fx-font-weight: normal;
      }

      .scroll-pane {
        -fx-background-color: transparent;
      }

      /*
       * List & Tree styles
       */

      .list-cell:even, .tree-cell:even, .table-cell:even {
        -fx-background-color: transparent;
      }

      .list-cell:odd, .tree-cell:odd, .table-cell:odd {
        -fx-background-color: transparent;
      }

      .list-cell:focused, .tree-cell:focused {
        -fx-background-color: rgba(173, 216, 230, 0.3);
      }

      .list-cell, .tree-cell, .option-cell {
        -fx-background-color: transparent;

        -fx-border-color: linear-gradient(to right, transparent, color-blue-20 15%, color-blue-20 85%, transparent);
        -fx-border-width: 1;
        -fx-border-insets: 1;
      }

      .list-cell:empty, .tree-cell:empty {
        -fx-border-color: transparent;
        -fx-border-width: 1;
        -fx-border-insets: 1;
      }

      .list-cell:focused, .tree-cell:focused, .option-cell.focused {
        -fx-background-insets: 1, 1, 1;
        -fx-background-color: radial-gradient(center 25% 0%, radius 25%, color-blue 0%, transparent),
                              radial-gradient(center 75% 100%, radius 25%, color-blue 0%, transparent),
                              linear-gradient(to right, transparent, color-blue-20 15%, color-blue-20 85%, transparent);
        
        -fx-border-width: 1, 1, 1;
        -fx-border-color: radial-gradient(center 25% 0%, radius 25%, color-blue 0%, transparent),
                          radial-gradient(center 75% 100%, radius 25%, color-blue 0%, transparent),
                          linear-gradient(to right, transparent, color-blue-50 15%, color-blue-50 85%, transparent);
                          
      }

      .label {
        -fx-text-fill: black;
      }

      .list .list-view {
        -fx-border-width: 2;
        -fx-border-color: color-blue-20;
        -fx-border-radius: 10;
      }

      .scroll-bar {
        -fx-skin: "hs.mediasystem.ScrollBarSkinBad";
      }

      .scroll-bar .track {
        -fx-fill: derive(color-blue-50, -80%);
      }

      .scroll-bar .thumb {
        -fx-stroke-width: 2;
        -fx-stroke: derive(color-blue-50, -30%);
        -fx-stroke-type: inside;
        -fx-fill: derive(color-blue-50, -60%);
      }


      ScrollBarSkinBad:

      package hs.mediasystem;

      import javafx.beans.binding.Bindings;
      import javafx.beans.binding.NumberBinding;
      import javafx.geometry.Orientation;
      import javafx.scene.Group;
      import javafx.scene.Node;
      import javafx.scene.control.ScrollBar;
      import javafx.scene.control.Skin;
      import javafx.scene.shape.Rectangle;

      public class ScrollBarSkinBad implements Skin<ScrollBar> {

        private final ScrollBar scrollBar;

        public ScrollBarSkinBad(ScrollBar scrollBar) {
          this.scrollBar = scrollBar;
        }

        @Override
        public void dispose() {
        }

        @Override
        public Node getNode() {
          try {
            return new Group() {{
              getChildren().add(new Rectangle() {{
                getStyleClass().add("track");
                if(scrollBar.getOrientation() == Orientation.HORIZONTAL) {
                  widthProperty().bind(scrollBar.widthProperty());
                  setHeight(16);
                }
                else {
                  setWidth(16);
                  heightProperty().bind(scrollBar.heightProperty());
                }
              }});
              getChildren().add(new Rectangle() {{
                getStyleClass().add("thumb");

                NumberBinding range = Bindings.subtract(scrollBar.maxProperty(), scrollBar.minProperty());
                NumberBinding position = Bindings.divide(Bindings.subtract(scrollBar.valueProperty(), scrollBar.minProperty()), range);

                if(scrollBar.getOrientation() == Orientation.HORIZONTAL) {
                  setHeight(16);
                  widthProperty().bind(scrollBar.visibleAmountProperty().divide(range).multiply(scrollBar.widthProperty()));
                  xProperty().bind(Bindings.subtract(scrollBar.widthProperty(), widthProperty()).multiply(position));
                }
                else {
                  setWidth(16);
                  heightProperty().bind(scrollBar.visibleAmountProperty().divide(range).multiply(scrollBar.heightProperty()));
                  yProperty().bind(Bindings.subtract(scrollBar.heightProperty(), heightProperty()).multiply(position));
                }
              }});
            }};
          }
          catch(Exception e) {
            e.printStackTrace();
            throw e;
          }
        }

        @Override
        public ScrollBar getSkinnable() {
          return scrollBar;
        }
      }

            jgiles Jonathan Giles
            jhendrikx John Hendrikx
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: