0

Hi so I have tried many solutions online on how to get the Node(cell) that was clicked on in the Gridpane. The method I went with for getting the node is shown below. I have also tried iterating through all the nodes prior to this method. I have built the gridpane with scenebuilder and as you can see in the picture below the columns and row are clearly there. I am trying this solution How to get GridPane Row and Column IDs on Mouse Entered in each cell of Grid in JavaFX? however as I've mentioned above the values returned from the rows and columns are null.

@FXML
    private void mouseEntered(MouseEvent e) {
        Node source = (Node)e.getSource() ;
        System.out.println(source);
        Integer colIndex = userSelectionGrid.getColumnIndex(source);
        Integer rowIndex = userSelectionGrid.getRowIndex(source);
        if (colIndex == null && rowIndex == null) System.out.println("BOO");
        else
        System.out.printf("Mouse entered cell [%d, %d]%n", colIndex.intValue(), rowIndex.intValue());
    }

enter image description here

FXML containing this grid

<GridPane fx:id="userSelectionGrid" onMouseClicked="#mouseEntered" prefHeight="44.0" prefWidth="563.0">
         <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
         </columnConstraints>
         <rowConstraints>
            <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
         </rowConstraints>
         <children>
            <Label text="SAVE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="0">
               <font>
                  <Font name="System Bold" size="13.0" />
               </font>
            </Label>
            <Label text="CANCEL" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0">
               <font>
                  <Font name="System Bold" size="13.0" />
               </font>
            </Label>
            <Separator orientation="VERTICAL" GridPane.columnIndex="1" />
         </children>
         <VBox.margin>
            <Insets />
         </VBox.margin>
      </GridPane>
Community
  • 1
  • 1
Edward Lim
  • 763
  • 9
  • 31
  • Where hava you added this handler (Which `Node`)? – fabian Aug 27 '16 at 08:25
  • Im sorry Im still really new to this but I assume you are asking where I added the mouseEntered method in scenebuilder. Under the code section of the grid I added it in the onMouseClicked method – Edward Lim Aug 27 '16 at 17:38

1 Answers1

2

The source of the event is the Node where the handler is added, i.e. in this case the GridPane. You could add the handlers to the children of the GridPane for the source Node to be correct:

<GridPane fx:id="userSelectionGrid" prefHeight="44.0" prefWidth="563.0">
     <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
     </columnConstraints>
     <rowConstraints>
        <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
     </rowConstraints>
     <children>
        <Label text="SAVE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="0"  onMouseClicked="#mouseEntered">
           <font>
              <Font name="System Bold" size="13.0" />
           </font>
        </Label>
        <Label text="CANCEL" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0"  onMouseClicked="#mouseEntered">
           <font>
              <Font name="System Bold" size="13.0" />
           </font>
        </Label>
        <Separator orientation="VERTICAL" GridPane.columnIndex="1" />
     </children>
     <VBox.margin>
        <Insets />
     </VBox.margin>
</GridPane>

Note that you could also make this work with your original fxml. However there are a few things that you should consider:

  1. null is also a valid return value for GridPane.getRowIndex and GridPane.getColumnIndex this doesn't mean the node is not the child of a GridPane, it just means the value has not been changed from the default. This has the same effect as using the value 0.
  2. The condition

    if (colIndex == null && rowIndex == null)
    

    Is too strong to prevent a NullPointerException in the else part. This could occur e.g. if the Separator is clicked.

  3. The target of the event is the node that is actually clicked, which could be a Node in the Label's skin. From this node you could traverse to the parent node until the parent is the GridPane to find the correct child of the GridPane.
@FXML
private void mouseEntered(MouseEvent e) {
    Node target = (Node) e.getTarget();
    // traverse towards root until userSelectionGrid is the parent node
    if (target != userSelectionGrid) {
        Node parent;
        while ((parent = target.getParent()) != userSelectionGrid) {
            target = parent;
        }
    }
    Integer colIndex = userSelectionGrid.getColumnIndex(target);
    Integer rowIndex = userSelectionGrid.getRowIndex(target);
    if (colIndex == null || rowIndex == null) {
        System.out.println("BOO");
    } else {
        System.out.printf("Mouse entered cell [%d, %d]%n", colIndex.intValue(), rowIndex.intValue());
    }
}

Note that the Labels do not fill the full GridPane unless you set the preferred size larger than the GridPane constraints. This messes up the layout of the GridPane. A workaround would be placing Regions behind the Labels in the cells. Those can be resized with the cells without influence on the GridPane layout, which allows you to catch the event on those nodes:

<GridPane fx:id="userSelectionGrid" prefHeight="44.0" prefWidth="563.0" onMouseClicked="#mouseEntered">                 
    <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" fillWidth="true" />
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" fillWidth="true" />
    </columnConstraints>
    <rowConstraints>
        <RowConstraints minHeight="10.0" vgrow="SOMETIMES" fillHeight="true" />
    </rowConstraints>
    <children>
        <Region GridPane.columnIndex="1" GridPane.rowIndex="0"/>
        <Region GridPane.columnIndex="0" GridPane.rowIndex="0"/>
        <Label text="SAVE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="0">
            <font>
                <Font name="System Bold" size="13.0" />
            </font>
        </Label>
        <Label text="CANCEL" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0">
            <font>
                <Font name="System Bold" size="13.0" />
            </font>
        </Label>
        <Separator orientation="VERTICAL" GridPane.columnIndex="1" />
    </children>
    <VBox.margin>
        <Insets />
    </VBox.margin>
</GridPane>
fabian
  • 80,457
  • 12
  • 86
  • 114
  • hmm I seem to be missing something. I tried what you posed above and I believe I understand the logic behind it. However, what I got now is that only the labels within the individual grid cells are being recognized. What I am hoping to achieve is that if the user hits the cell in which the labels are contained in to do something. Instead of having to hit on the actual label itself. Please correct me if Im wrong, I started this about 5 days ago and have many more to learn but isn't this the hierachy for GridPanes as follows. Grid > Cells > "items in cells" – Edward Lim Aug 27 '16 at 21:06
  • @EdwardLim It's easiest to just add a resizeable node to the cell, since any way of resizing of the `Label`s to fill the complete cells easily messes up the `GridPane` layout. Added an example with `Region`s to the answer. – fabian Aug 27 '16 at 22:25
  • you are the man! thank you so much! much better then what i was going to do which was to wrap the label in an hbox. Cheers! – Edward Lim Aug 28 '16 at 03:23