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

TableView: contextMenu of cell must be triggered by keyboard

XMLWordPrintable

      Below is an example that has a contextMenu set on cell level. It can be opened by right click, but not by keyboard popup trigger (on win that's shift-f10)

      To reproduce, run and
      - right click a cell to see the contextMenu appear
      - do anything to hide again
      - press the keyboard popup trigger
      - expected: contextMenu showing again
      - actual: nothing happens

      (just for comparison focus the button and see the keyboard popup trigger working as expected)

      Didn't test the other virtualized controls, might misbehave as well

      The example:

      import javafx.application.Application;
      import javafx.beans.property.Property;
      import javafx.beans.property.SimpleStringProperty;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.ContextMenu;
      import javafx.scene.control.MenuItem;
      import javafx.scene.control.TableCell;
      import javafx.scene.control.TableColumn;
      import javafx.scene.control.TableView;
      import javafx.scene.control.cell.PropertyValueFactory;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      /**
       * per-cell contextMenu must be triggered by keyboard
       * (f.i. shift-f10 on win)
       *
       */
      public class TableViewContextMenu extends Application {
          @Override
          public void start(Stage stage) throws Exception {

              TableView<Person> table = new TableView<>();

              TableColumn<Person,String> firstNameCol = new TableColumn<>("First Name");
              firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
              ContextMenu cellMenu = new ContextMenu(new MenuItem("cellItem"));
              firstNameCol.setCellFactory(p -> new PlainTableCell(cellMenu));
              table.getColumns().addAll(firstNameCol);
              table.setItems(data);

              Button button = new Button("dummy for comparison");
              button.setContextMenu(new ContextMenu(new MenuItem("buttonItem")));
              VBox pane = new VBox(table, button);
              Scene scene = new Scene(pane);
              stage.setScene(scene);
              stage.show();
          }

          /**
           * C&P of default tableCell in TableColumn + contextMenu
           */
          public static class PlainTableCell<S, T> extends TableCell<S, T> {
              
              public PlainTableCell(ContextMenu menu) {
                  setContextMenu(menu);
              }
              @Override protected void updateItem(T item, boolean empty) {
                  if (item == getItem()) return;

                  super.updateItem(item, empty);

                  if (item == null) {
                      super.setText(null);
                      super.setGraphic(null);
                  } else if (item instanceof Node) {
                      super.setText(null);
                      super.setGraphic((Node)item);
                  } else {
                      super.setText(item.toString());
                      super.setGraphic(null);
                  }
              }

          }

          public static class Person {
              private final SimpleStringProperty firstName;
              private final SimpleStringProperty lastName;
              private Person(String fName, String lName) {
                  this.firstName = new SimpleStringProperty(fName);
                  this.lastName = new SimpleStringProperty(lName);
              }
              public String getFirstName() {return firstName.get();}
              public void setFirstName(String fName) {firstName.set(fName);}
              public Property<String> firstNameProperty() { return firstName; }
              public String getLastName() {return lastName.get();}
              public void setLastName(String fName) {lastName.set(fName);}
              public Property<String> lastNameProperty() { return lastName; }
          }

          ObservableList<Person> data = FXCollections.observableArrayList(
                  new Person("Jacob", "Smith"),
                  new Person("Isabella", "JohnsoJn"),
                  new Person("Ethan", "Williams"),
                  new Person("Emma", "Jones"),
                  new Person("Michael", "Brown")
          );


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

            Unassigned Unassigned
            fastegal Jeanette Winzenburg
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Imported: