Search code examples
javajavafxmouseeventtilegridpane

Javafx - Determine position of cursor on right click relative to gridpane


I have a 40x40 gridpane, displaying a map. Currently, i have the following methods listening for a right click:

    //detect right click + display menu, select if you want to place item 1 or item 2
    final ContextMenu cm = new ContextMenu();
    cm.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() 
    {
        @Override
        public void handle(MouseEvent event) 
        {
            if (event.getButton() == MouseButton.SECONDARY) 
                event.consume();

        }
    });
    cm.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            System.out.println("Following item selected: "+
                    ((MenuItem)event.getTarget()).getText());
        }
    });
    //two placeable items
    MenuItem item1Place = new MenuItem("Item 1");
    MenuItem item2Place = new MenuItem("Item 2");
    cm.getItems().addAll(item1Place, item2Place);

    primaryStage.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() 
    {
        @Override
        public void handle(MouseEvent e)
        {
            if (e.getButton() == MouseButton.SECONDARY) 
            {
              cm.show(primaryStage, e.getScreenX(), e.getScreenY());
            } 
        }
    });

What I'd like to implement is the following:

If a menu item is selected after a right click, i'd like to save the position of the right click in a variable
e.g. [32,12] if right click was 32 blocks of the gridpane across, and 12 blocks up.


Any suggestions on how to go about doing this?
Thanks in advance

// first edit code entered below. I realise the indentation is very off however fixing it seemed to turn it back to plain text
placeItem1.setOnAction(evt -> System.out.println("Item 1 placed ["+col+", "+row+"]"));
                placeItem2.setOnAction(evt -> System.out.println(Item 2 placed["+col+", "+row+"]"));
                menu.show(cell, e.getScreenX(), e.getScreenY());
            });
            root.add(cell, x, y);

Solution

  • Add a listener to each cell in the grid, instead of adding one mouse listener to the stage. The proper listener to use for context menu handling is a contextMenuRequested handler:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.ContextMenu;
    import javafx.scene.control.MenuItem;
    import javafx.scene.input.MouseButton;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.Pane;
    import javafx.stage.Stage;
    
    public class ClicksInGridPane extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            GridPane root = new GridPane();
    
            final int NUM_ROWS = 40 ;
            final int NUM_COLS = 40 ;
    
            ContextMenu menu = new ContextMenu();
            MenuItem menuItem1 = new MenuItem("Item 1");
            MenuItem menuItem2 = new MenuItem("Item 2");
            menu.getItems().addAll(menuItem1, menuItem2);
    
            for (int x = 0 ; x < NUM_COLS ; x++) {
                for (int y = 0 ; y < NUM_ROWS ; y++) {
                    Pane cell = new Pane();
                    cell.setPrefSize(20, 20);
                    // add style just to draw grid:
                    cell.setStyle("-fx-background-color: black, white; -fx-background-insets: 0, 0 0 1 1;");
    
                    // context menu listener:
                    final int col = x ;
                    final int row = y ;
    
                    cell.setOnContextMenuRequested(e -> {
                        menuItem1.setOnAction(evt -> System.out.println("Item 1 selected in cell ["+col+", "+row+"]"));
                        menuItem2.setOnAction(evt -> System.out.println("Item 2 selected in cell ["+col+", "+row+"]"));
                        menu.show(cell, e.getScreenX(), e.getScreenY());
                    });
    
                    root.add(cell, x, y);
                }
            }
    
            primaryStage.setScene(new Scene(root));
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }