Jaman
Jaman

Reputation: 125

Java FX nested event handler I want to block the parent handler when child handler is called

I have a fxml Mouse event handler method where every time I click a Pane a pop up dialog box appears on the screen asking for information. When OK is clicked on the dialog, a circle with text is added to a stack pane and the stack pane is then added to the pane where the user clicked.

However I am trying to implement a event handler where I can move the stack pane around by dragging the stack pane with the mouse. My event handler works but every time I finish dragging the stack pane the pop up dialog pops up. I do not want the pop up dialog to pop up when moving the circle how would I change my code to do this is there a way of blocking the pop up handler?

Thank you.

My Handler method:

    @FXML
    private void handleAddVertex(MouseEvent event) {

        boolean okClicked = main.showAddVertexPopUp(this);

        if(okClicked) {

            String vertexText = "";

            if(getSelectedDataChoice().equals("Integer")) {

                vertexText = dataModel.getListOfIntVertices().get(dataModel.getListOfIntVertices().size() - 1).toString();

            }else if(getSelectedDataChoice().equals("Double")){

                vertexText = dataModel.getListOfDoubleVertices().get(dataModel.getListOfDoubleVertices().size() - 1).toString();

            }else {

                vertexText = dataModel.getListOfStringVertices().get(dataModel.getListOfStringVertices().size() - 1).toString();

            }

            EventHandler<MouseEvent> circleOnMousePressedEventHandler = 
                    new EventHandler<MouseEvent>() {

                    @Override
                    public void handle(MouseEvent t) {
                        orgSceneX = t.getSceneX();
                        orgSceneY = t.getSceneY();
                        orgTranslateX = ((StackPane)(t.getSource())).getTranslateX();
                        orgTranslateY = ((StackPane)(t.getSource())).getTranslateY();
                    }
                };

                EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = 
                    new EventHandler<MouseEvent>() {

                    @Override
                    public void handle(MouseEvent t) {
                        double offsetX = t.getSceneX() - orgSceneX;
                        double offsetY = t.getSceneY() - orgSceneY;
                        double newTranslateX = orgTranslateX + offsetX;
                        double newTranslateY = orgTranslateY + offsetY;

                        ((StackPane)(t.getSource())).setTranslateX(newTranslateX);
                        ((StackPane)(t.getSource())).setTranslateY(newTranslateY);
                    }
                };

            double x = event.getX();
            double y = event.getY();

            Circle vertex = new Circle(x, y, 20, Color.WHITE);
            vertex.setStroke(Color.BLACK);

            Text text = new Text (vertexText);

            StackPane stack = new StackPane();

            stack.getChildren().addAll(vertex, text);
            stack.setLayoutX(x);
            stack.setLayoutY(y);

            stack.setOnMousePressed(circleOnMousePressedEventHandler);
            stack.setOnMouseDragged(circleOnMouseDraggedEventHandler);

            centerPane.getChildren().add(stack);

        }   


    }

Upvotes: 1

Views: 1625

Answers (1)

Slaw
Slaw

Reputation: 46255

To stop an Event from propagating you use Event.consume().

Marks this Event as consumed. This stops its further propagation.

From your description, it appears handleAddVertex is a MOUSE_CLICKED handler. You'll have to add another EventHandler to the newly created StackPane that consumes MOUSE_CLICKED events.

stack.setOnMouseClicked(Event::consume);

This will stop the MOUSE_CLICKED event from bubbling up to the Node which has the handleAddVertex handler.


For more information on event processing in JavaFX, see JavaFX: Handling Events.


Here's a small example where you can see the difference between consuming and not consuming the event:

import java.util.function.Predicate;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Main extends Application {

  private CheckBox consumeClickEventsCheckBox;

  @Override
  public void start(Stage primaryStage) throws Exception {
    consumeClickEventsCheckBox = new CheckBox("Consume click events");
    consumeClickEventsCheckBox.setSelected(true);

    HBox top = new HBox(consumeClickEventsCheckBox);
    top.setPadding(new Insets(10));
    top.setAlignment(Pos.CENTER);

    Pane center = new Pane();
    center.setBorder(new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, null, null)));
    center.setOnMouseClicked(this::handleAddCircle);

    Rectangle clip = new Rectangle();
    clip.widthProperty().bind(center.widthProperty());
    clip.heightProperty().bind(center.heightProperty());
    center.setClip(clip);

    BorderPane root = new BorderPane(center);
    root.setTop(top);

    Scene scene = new Scene(root, 600, 400);
    primaryStage.setScene(scene);
    primaryStage.show();
  }

  private void handleAddCircle(MouseEvent event) {
    event.consume();

    Alert confirm = new Alert(AlertType.CONFIRMATION);
    confirm.initOwner(((Node) event.getSource()).getScene().getWindow());
    confirm.setHeaderText(null);
    confirm.setContentText("Do you want to add a circle here?");

    if (confirm.showAndWait().filter(Predicate.isEqual(ButtonType.OK)).isPresent()) {
      Circle circle = new Circle(event.getX(), event.getY(), 25);
      circle.setOnMousePressed(this::handleCirclePressed);
      circle.setOnMouseDragged(this::handleCircleDragged);
      circle.setOnMouseClicked(this::handleCircleClicked);

      ((Pane) event.getSource()).getChildren().add(circle);
    }
  }

  private Point2D origin;

  private void handleCirclePressed(MouseEvent event) {
    event.consume();
    origin = new Point2D(event.getX(), event.getY());
  }

  private void handleCircleDragged(MouseEvent event) {
    event.consume();
    Circle circle = (Circle) event.getSource();
    circle.setTranslateX(circle.getTranslateX() + event.getX() - origin.getX());
    circle.setTranslateY(circle.getTranslateY() + event.getY() - origin.getY());
  }

  /*
   * Will consume the MOUSE_CLICKED event only if the CheckBox is selected. You can test
   * the behavior of consuming the event by toggling the CheckBox.
   */
  private void handleCircleClicked(MouseEvent event) {
    if (consumeClickEventsCheckBox.isSelected()) {
      event.consume();
    }
  }

}

Upvotes: 2

Related Questions