Search code examples
javajavafxtableviewcontextmenukeypress

Show ContextMenu via ENTER key on selected row 2nd column in TableView (JavaFX)


until now I have only managed to make the contextMenu appear within the frame of the TableView once I press the ENTER button with this code contextMenu.show(tableView, Side.RIGHT, 0, 0);. But this way the contextmenu pops up only in a static position.

1. Howto get x y position of selected row 2nd column

I would like the contextmenu appear dynamically, that is whenever the user press ENTER key on a selected row, the contextmenu will show in the selected row on its 2nd column: please look at the given picture below.enter image description here

Is there something like this

contextMenu.show(tableView, x-SelectedRow2ndCol, y-SelectedRow2ndCol);

2. Howto Contextmenu shows wholly in visible area

For instance if the selected row is the last row of the TableView and thus its position is at the lowest part of the screen, still the contextmenu will pop up all its items above the last row.


Solution

  • lookupAll can be used to get all TableRows from the TableView. Find the selected one and get the child TableCell with a tableColumn matching the column. This gets you the TableCell. Show the ContextMenu using the show(Node anchor, Side side, double dx, double dy) method. This also takes care of keeping the menu on screen.

    From the javadoc:

    If there is not enough room, the menu is moved to the opposite side and the offset is not applied.

    Example of a key listener on TableView:

    ContextMenu contextMenu = ...
    
    TableColumn secondColumn = tableView.getColumns().get(1);
    
    tableView.setOnKeyReleased(evt -> {
        if (evt.getCode() == KeyCode.ENTER) {
            Set<Node> rows = tableView.lookupAll(".table-row-cell");
            Optional<Cell> n = rows.stream().map(r -> (Cell) r).filter(Cell::isSelected).findFirst();
    
            if (n.isPresent()) {
                Optional<Node> node = n.get().getChildrenUnmodifiable().stream()
                        .filter(c -> c instanceof TableCell && ((TableCell) c).getTableColumn() == secondColumn)
                        .findFirst();
    
                if (node.isPresent()) {
                    Node cell = node.get();
                    Bounds b = cell.getLayoutBounds();
                    contextMenu.show(cell, Side.BOTTOM, b.getWidth() / 2, b.getHeight() / -2);
                }
            }
        }
    });
    

    Note that this doesn't work, if there is no selected table row visible.