1

I have a javafx8 app with a complex scene. It consists of a splitpane where, in the top segment I have a stackpane with multitple levels (children). The rear most is a background that would only need redraws on resizing, another with gridlines, lat/long. Another for drawing and, finally, another with a sliding composite control. When the composite (buttons, labels, comboboxes, and textfields, in the last child of the stackframe, it properly intercepted and acted on mouse and keyboard events. However, with it on top no events were getting propagated to the child panes below. I switched the order and now the buttons and axis control work but nothing now gets propagated to the composite control below it. Buttons fail to see the mouse action.

This seems similar to Stackpane - MouseClick to be listened by both its children but it's not real clear that it is nor is it clear that has a correct answer.

I simplified my scenario with a very simple test, source to follow. In this I have a StackPane with two children, both VBox, one if which with top left alignment, the other with top right. Only the second button, top right responds to being pressed.

Why. The reason I have my app with multiple panes is performance. Both drawing actions are expensive and freq and I didn't want to have to constantly redraw static or near static layouts.

fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<StackPane fx:id="containerPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mouseeventdemo.MouseEventDemoController">
   <children>
      <VBox fx:id="container1" prefHeight="200.0" prefWidth="100.0">
         <children>
            <Button fx:id="leftButton" mnemonicParsing="false" onAction="#leftButtonDown" text="left button" />
         </children>
      </VBox>
      <VBox fx:id="container2" alignment="TOP_RIGHT" prefHeight="200.0" prefWidth="100.0">
         <children>
            <Button fx:id="rightButton" mnemonicParsing="false" onAction="#rightButtonDown" text="Right Button" />
         </children>
      </VBox>
   </children>
</StackPane>

Controller:

package mouseeventdemo;
/**
 * Sample Skeleton for 'FXMLDocument.fxml' Controller Class
 */

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;

public class MouseEventDemoController {

    @FXML // ResourceBundle that was given to the FXMLLoader
    private ResourceBundle resources;

    @FXML // URL location of the FXML file that was given to the FXMLLoader
    private URL location;

    @FXML // fx:id="containerPane"
    private StackPane containerPane; // Value injected by FXMLLoader

    @FXML // fx:id="container1"
    private VBox container1; // Value injected by FXMLLoader

    @FXML // fx:id="leftButton"
    private Button leftButton; // Value injected by FXMLLoader

    @FXML // fx:id="container2"
    private VBox container2; // Value injected by FXMLLoader

    @FXML // fx:id="rightButton"
    private Button rightButton; // Value injected by FXMLLoader

    @FXML
    void leftButtonDown(ActionEvent event) {
        System.out.println("leftButtonPressed");
    }

    @FXML
    void rightButtonDown(ActionEvent event) {
        System.out.println("rightButtonPressed");
    }

    @FXML // This method is called by the FXMLLoader when initialization is complete
    void initialize() {
        assert containerPane != null : "fx:id=\"containerPane\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
        assert container1 != null : "fx:id=\"container1\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
        assert leftButton != null : "fx:id=\"leftButton\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
        assert container2 != null : "fx:id=\"container2\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
        assert rightButton != null : "fx:id=\"rightButton\" was not injected: check your FXML file 'FXMLDocument.fxml'.";

    }
}

main

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mouseeventdemo;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

/**
 *
 * @author walt
 */
public class MouseEventDemo extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

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

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

In my app the rear most pane is a background canvas. It need be drawn but once and redrawn when the window is resized.

The next is a canvas that had lat/long grids. It need only be redrawn when the window size changes or either the vertical or horizontal scales change.

The next is the main drawing canvas in the following

<vbox>
<hbox>
   drawing canvas
   vertical scale
</hbox>
   horizontal scale
</vbox>

The next carries one or more flagpoles where the vertical pole is a slide rule like reticle. These need to be dragable. If this layer is on top it slides perfectly but the two scales fail to get focus. If the top two panes are reversed, the scales work perfectly but the flagpole objects never receive events.

The two button example duplicates very simply what I see.

Thanks!

enter image description here

Community
  • 1
  • 1
Walt Corey
  • 718
  • 7
  • 12

1 Answers1

2

In the eventhandling of javafx only the topmost node (in your example container2) under the mouseposition will be determined as the target for the mouse click event, that's why container1 doesn't receive the event.

The target node is the end node in the event dispatch chain. When you click on leftButton, the mouse clicked event travels from the root node of your scene graph to the target node(container2), and back if it doesn't get consumed. Container1 as not being on the dispatch chain, won't receive the event: http://docs.oracle.com/javafx/2/events/processing.htm

You can try to use container.setPickOnBounds(false)

jns
  • 6,017
  • 2
  • 23
  • 28
  • I added a screen shot of the app. The fxml should be the correct one for that screenshot. If not, that wasn't the point of this question. – Walt Corey Apr 15 '16 at 17:19
  • To partially restate the problem, it appears both on Linux and Windows only controls in the top pane appear to receive events. That two button app is a minimum app to demonstrate the issue. Right mouse works, left mouse does not. If the children were loaded in the other order, left button would work, right button would not. – Walt Corey Apr 15 '16 at 17:36
  • while I am willing to give you credit for having answered this. I don't understand why. I did find http://stackoverflow.com/questions/24607969/mouse-events-get-ignored-on-the-underlying-layer. which means this is actually a dup.Of the books I have they don't differentiate a stack pane any differently than any other pane beyond you can have multiple layers. In fact, books on event processing they say events traversal and consuming events that events traverse through children from the top through to the bottom until they hit a consume(). Where is this actually explained? – Walt Corey Apr 15 '16 at 21:51
  • Thanks! So why wouldn't that be the default for more than just textfields? In composite scenes there will always be containers for layout, i.e. HBox inside VBox and multiple whatevers inside it? As I said. My actual scene is incredibly complex and for the sake of brevity I wanted to come up with a minimal 'for instance'. What I tried to do was individually turn off setInBounds one at a time to see what was blocking the traversal of events. Out of frustration I ended up turning it off everywhere. – Walt Corey Apr 16 '16 at 03:11