This discussion is archived
14 Replies Latest reply: Nov 20, 2012 11:58 AM by famedoro RSS

How to progate an event to a children using JavaFx2

famedoro Newbie
Currently Being Moderated
I have a code like this:
public void handle(MouseEvent event) {
        //if (event.getEventType() == MouseEvent.MOUSE_CLICKED) {
        if ( ( event.getEventType() == MouseEvent.MOUSE_PRESSED )&&
             (event.isPrimaryButtonDown()) ) {
                x1 = x2 = event.getX();
                y1 = y2 = event.getY();
                line = new BoundLine(pane, event, x1, y1,  x2, y2);
                pane.getChildren().add(line);                    
                //line.getEventDispatcher().dispatchEvent(event, null);
        }
   }
Is it possibile to propagate immediately the "MouseEvent event" to the javafx.scene.shape.Line (line in the code) object ? I've tried line.getEventDispatcher().dispatchEvent(event, null); but I've got this message error:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35) at testjavafxapplication.LinesEditCtx02$MouseHandler.handle(LinesEditCtx02.java:243) at testjavafxapplication.LinesEditCtx02$MouseHandler.handle(LinesEditCtx02.java:105) at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170) at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53) at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33) at javafx.event.Event.fireEvent(Event.java:171) at javafx.scene.Scene$MouseHandler.process(Scene.java:3324) at javafx.scene.Scene$MouseHandler.process(Scene.java:3164) at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3119) at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1559) at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2261) at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:228) at com.sun.glass.ui.View.handleMouseEvent(View.java:528) at com.sun.glass.ui.View.notifyMouse(View.java:922) at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method) at com.sun.glass.ui.gtk.GtkApplication$3$1.run(GtkApplication.java:82) at java.lang.Thread.run(Thread.java:722)

Thanks in advance for your help.
  • 1. Re: How to progate an event to a children using JavaFx2
    dscarminiabielefeld Newbie
    Currently Being Moderated
    Hi,
    please use the
    Code-Tags
    it's really hard to figure out what you tried to do there. What do you mean with propagate? Can't really say what you want to know.
  • 2. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    My goal is to pass mouse event from parent (pane) to children (line) immediately after children is created.

    Example:

    1) left mouse click anywhere on the chart
    2) while keep pressed, from pane is create first point of a line
    3) still pressed and drag a line is then created: this means that mouse event must be transferred from pane to line
  • 3. Re: How to progate an event to a children using JavaFx2
    James_D Guru
    Currently Being Moderated
    I think you can achieve what you want without "propagating the event to the line". For example:
    import javafx.application.Application;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Pane;
    import javafx.scene.shape.Line;
    import javafx.stage.Stage;
    
    
    public class LineDrawing extends Application {
    
      @Override
      public void start(Stage primaryStage) throws Exception {
        final Pane root = new Pane();
        final ObjectProperty<Line> lineBeingDrawn = new SimpleObjectProperty<Line>();
        
        root.setOnMousePressed(new EventHandler<MouseEvent>() {
          @Override public void handle(MouseEvent event) {
            final double x = event.getX();
            final double y = event.getY();
            Line line = new Line(x, y, x, y);
            lineBeingDrawn.set(line);
            root.getChildren().add(line);        
          }
        });
        
        root.setOnMouseDragged(new EventHandler<MouseEvent>() {
          @Override public void handle(MouseEvent event) {
            Line line = lineBeingDrawn.get();
            if (line != null) {
              final double x = event.getX();
              final double y = event.getY();
              line.setEndX(x);
              line.setEndY(y);
            }
          }
        });
        
        root.setOnMouseReleased(new EventHandler<MouseEvent>() {
          @Override public void handle(MouseEvent event) {
            lineBeingDrawn.set(null);
          }
        });
    
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
      }
    
      public static void main(String[] args) {launch(args);}
    
    }
  • 4. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    Thanks for your response,
    but my target is in create on object that extends the line features in order to be independent, without any need to use parent panel (pane) for its use (events and more)
  • 5. Re: How to progate an event to a children using JavaFx2
    James_D Guru
    Currently Being Moderated
    It's not clear what you're trying to achieve here. It sounds like you are trying to use a (non?)-pattern which isn't really supported by JavaFX.

    "Propagating events to nodes" is usually a sign that your design is wrong. The whole event handling mechanism is really just there so that you have the opportunity to execute code when something happens (usually because of user input). Programmatically sending an event to a node is usually (if not always) redundant; the point of having an event occur on a node is so that you can execute the code in a handler, so why not just invoke the same logic you want the handler to invoke directly? In other words, you're already in a context where you know you want to do something, you don't need the event handling model to notify you of this.

    I think what you're trying to say here is that you want your Line subclass to respond to the events to change it's size and position, without reference to its containing pane. In general, you can do this kind of thing by having it register the appropriate listeners. The problem in this case is that the event is not occurring on the line; you're not dragging the mouse within the line itself, you're dragging it in it's containing pane. The coordinates of the end points are coordinates relative to the parent; they simply don't make sense solely in the context of the line, without knowing about the parent.

    That said, you could have the line observe its parent property and behave appropriately. That would encapsulate everything inside the Line subclass; it necessarily still has to know about its parent (because the coordinates are, after all, coordinates defined by the parent). The Line already has access to its parent anyway, though, through the parentProperty(). So maybe something like this is what you want?
    import javafx.application.Application;
    import javafx.beans.property.BooleanProperty;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.event.EventHandler;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Pane;
    import javafx.scene.shape.Line;
    import javafx.stage.Stage;
    
    
    public class LineDrawing extends Application {
    
      @Override
      public void start(Stage primaryStage) throws Exception {
        final Pane root = new Pane();
        
        root.setOnMousePressed(new EventHandler<MouseEvent>() {
          @Override public void handle(MouseEvent event) {
            final double x = event.getX();
            final double y = event.getY();
            final BoundLine line = new BoundLine(x, y);
            line.setDrawing(true);
            root.getChildren().add(line);        
          }
        });
        
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
      }
    
      public static void main(String[] args) {launch(args);}
    
      public static class BoundLine extends Line {
        
        private final BooleanProperty drawing = new SimpleBooleanProperty();
        private final EventHandler<MouseEvent> parentDragListener = new EventHandler<MouseEvent>() {
          @Override
          public void handle(MouseEvent event) {
            if (drawing.get()) {
              double x = event.getX();
              double y = event.getY();
              setEndX(x);
              setEndY(y);
            }
          }      
        };
        private final EventHandler<MouseEvent> parentReleaseListener = new EventHandler<MouseEvent>() {
          @Override
          public void handle(MouseEvent event) {
            drawing.set(false);
          }
        };
        
        public BoundLine(double initialX, double initialY) {
          super(initialX, initialY, initialX, initialY);
          parentProperty().addListener(new ChangeListener<Parent>() {
            @Override
            public void changed(ObservableValue<? extends Parent> observable,
                Parent oldValue, Parent newValue) {
              if (oldValue != null) {
                oldValue.removeEventHandler(MouseEvent.MOUSE_DRAGGED, parentDragListener);
                oldValue.removeEventHandler(MouseEvent.MOUSE_RELEASED, parentReleaseListener);
              }
              if (newValue != null) {
                newValue.addEventHandler(MouseEvent.MOUSE_DRAGGED, parentDragListener);
                newValue.addEventHandler(MouseEvent.MOUSE_RELEASED, parentReleaseListener);
              }
            }        
          });
          
        }
        
        public BooleanProperty drawingProperty() {
          return drawing ;
        }
        public boolean isDrawing() {
          return drawing.get();
        }
        public void setDrawing(boolean drawing) {
          this.drawing.set(drawing);
        }
      }
    }
  • 6. Re: How to progate an event to a children using JavaFx2
    James_D Guru
    Currently Being Moderated
    By the way, I should say here that I would never actually do this; it just seemed to be what you are asking for.

    The Node classes and subclasses (including the custom BoundLine in this example) are part of the "view". The code that changes the model (data to which the view refers in order to display it) should be managed in a controller. Here I have deliberately put controller code in the view, which is not good practice.
  • 7. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    Hi James,

    as you've clearly understood, my target is in subclassing the Line class in order to get something similar to the code below: i.e. click and drag on parent and line gets drawn:
    import com.sun.javafx.stage.WindowEventDispatcher;
    import javafx.application.Application;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
     
    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.BooleanProperty;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.event.*;
    import javafx.geometry.Side;
    import javafx.scene.*;
    import javafx.scene.chart.CategoryAxis;
    import javafx.scene.chart.LineChart;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.chart.XYChart;
    import javafx.scene.control.ContextMenu;
    import javafx.scene.control.Label;
    import javafx.scene.control.MenuItem;
    import javafx.scene.input.ContextMenuEvent;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.*;
    import javafx.stage.Stage;
    import javafx.stage.WindowEvent;
     
    public class LinesEditCtx02 extends Application {
     
        Path path;
     
        public static void main(String[] args) {
            launch(args);
        }
     
        @Override
        public void start(Stage stage) {
     
            final CategoryAxis xAxis = new CategoryAxis();
            final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
            yAxis.setTickUnit(1);
            yAxis.setPrefWidth(35);
            yAxis.setMinorTickCount(10);
            yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
                @Override
                public String toString(Number object) {
                    String label;
                    label = String.format("%7.2f", object.floatValue());
                    return label;
                }
            });
            final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
     
            lineChart.setCreateSymbols(false);
            lineChart.setAlternativeRowFillVisible(false);
            lineChart.setLegendVisible(false);
     
            XYChart.Series series1 = new XYChart.Series();
     
            series1.getData().add(new XYChart.Data("Jan", 1));
            series1.getData().add(new XYChart.Data("Feb", 4));
            series1.getData().add(new XYChart.Data("Mar", 2.5));
            series1.getData().add(new XYChart.Data("Apr", 5));
            series1.getData().add(new XYChart.Data("May", 6));
            series1.getData().add(new XYChart.Data("Jun", 8));
            series1.getData().add(new XYChart.Data("Jul", 12));
            series1.getData().add(new XYChart.Data("Aug", 8));
            series1.getData().add(new XYChart.Data("Sep", 11));
            series1.getData().add(new XYChart.Data("Oct", 13));
            series1.getData().add(new XYChart.Data("Nov", 10));
            series1.getData().add(new XYChart.Data("Dec", 20));
     
            BorderPane bp = new BorderPane();
            bp.setCenter(lineChart);
            Scene scene = new Scene(bp, 800, 600);
            lineChart.setAnimated(false);
            lineChart.getData().addAll(series1);
     
            LinesEditCtx02.MouseHandler mh = new LinesEditCtx02.MouseHandler(bp);
            bp.setOnMouseClicked(mh);
            bp.setOnMouseMoved(mh);
     
            stage.setScene(scene);
     
            path = new Path();
            path.setStrokeWidth(1);
            path.setStroke(Color.BLACK);
     
            scene.setOnMouseDragged(mh);
            scene.setOnMousePressed(mh);
            bp.getChildren().add(path);
            stage.setScene(scene);
            stage.show();
        }
     
        class MouseHandler implements EventHandler< MouseEvent> {
     
            private Line line;
            private Pane pane;
            private double x1, y1, x2, y2;
     
            public MouseHandler(Pane pane) {
                this.pane = pane;
            }
     
            @Override
            public void handle(MouseEvent event) {
                //if (event.getEventType() == MouseEvent.MOUSE_CLICKED) {
                if ( ( event.getEventType() == MouseEvent.MOUSE_PRESSED )&&
                     (event.isPrimaryButtonDown()) ) {
                        x1 = x2 = event.getX();
                        y1 = y2 = event.getY();
                        line = new BoundLine(pane, event, x1, y1,  x2, y2);
                        pane.getChildren().add(line);                    
                        line.getEventDispatcher().dispatchEvent(event, null);
    
                    }                
            }
        }   
       
        private static final class BoundLine extends Line {        
    
            final EventDispatcher blockEventDispatcher = new EventDispatcher() {
               @Override public Event dispatchEvent(Event event, EventDispatchChain tail) {
                   // block the event from being passed down to children
                   return event;
               }
            };
            // a draggable anchor displayed around a point.
            class Anchor extends Circle { 
                    Anchor(Color color, DoubleProperty x, DoubleProperty y) {
                    super(x.get(), y.get(), 10);
                    setFill(color.deriveColor(1, 1, 1, 0.5));
                    setStroke(color);
                    setStrokeWidth(2);
                    setStrokeType(StrokeType.OUTSIDE);
    
                    x.bind(centerXProperty());
                    y.bind(centerYProperty());
                    enableDrag();
                    }
                    private void enableDrag() {                    
                        setOnMousePressed(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {          
                            getScene().setCursor(Cursor.MOVE);
                            mouseEvent.consume();
                            }
                        });
                        setOnMouseReleased(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                            getScene().setCursor(Cursor.HAND);
                            mouseEvent.consume();
                            }
                        });
                        setOnMouseDragged(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                                double newX = mouseEvent.getX();
                                if (newX > 0 && newX < getScene().getWidth()) {
                                    setCenterX(newX);
                                }  
                                double newY = mouseEvent.getY();
                                if (newY > 0 && newY < getScene().getHeight()) {
                                    setCenterY(newY);
                                }
                                mouseEvent.consume();
                            }
                        });
                        setOnMouseEntered(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                            if (!mouseEvent.isPrimaryButtonDown()) {
                                getScene().setCursor(Cursor.HAND);
                                mouseEvent.consume();
                            }
                            }
                        });
                        setOnMouseExited(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                            if (!mouseEvent.isPrimaryButtonDown()) {
                                getScene().setCursor(Cursor.DEFAULT);
                                mouseEvent.consume();
                            }
                            }
                        });
                    }
            }
            BoundLine(Pane pane, final MouseEvent mouseEvent, double d, double d1, double d2, double d3) {
                super(d, d1, d2, d3);
                Anchor endAnchor    = new Anchor(Color.PALEGREEN, startXProperty(), startYProperty());
                pane.getChildren().add(endAnchor);
            }
        
        }   
    }
    How to solve the exception thrown?
  • 8. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    Hi James,
    your code works properly, and it is just what I want to achieve: why you woudln't do this way? and what could be the best mode to get same result?

    Thanks.
  • 9. Re: How to progate an event to a children using JavaFx2
    James_D Guru
    Currently Being Moderated
    The Null Pointer Exception is presumably coming from passing in null as the EventDispatchChain to the dispatchEvent(...) method.

    I really wouldn't try to do it like this. You already have a reference to the pane passed into the constructor in your BoundLine class. Define a flag in the Anchor which determines if it's currently being dragged or not. Register a listener for mouse drags on the pane (you can still do this in the Anchor class, since you're already passing a reference to the pane into the BoundLine constructor): if the anchor is being dragged update its coordinates there. Initialize the dragged flag to true, set it to false if the mouse is released and set it to true again if the mouse is pressed on the anchor.

    If you really want to manually dispatching the event to the new line, it looks like you need a non-null EventDispatchChain; I don't know how you implement this though.
  • 10. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    Hi James,
    thank you very much for your suggestions: can you please give me an example so I can implement in my project?
    My best regards

    Francesco
  • 11. Re: How to progate an event to a children using JavaFx2
    James_D Guru
    Currently Being Moderated
    Hmm. This works: it's not the cleanest but it does the job. The trick is to activate the anchors when the mouse is pressed on the anchor; to move the activated anchor when the mouse is dragged on the pane, and to deactivate (all) anchors when the mouse is released on the pane. Probably needs a bit of tidying up but it will give you a start.
    import javafx.application.Application;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
     
    import javafx.beans.property.DoubleProperty;
    import javafx.scene.*;
    import javafx.scene.chart.CategoryAxis;
    import javafx.scene.chart.LineChart;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.chart.XYChart;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.*;
     
    public class LinesEditCtx02 extends Application {
     
        Path path;
     
        public static void main(String[] args) {
            launch(args);
        }
     
        @Override
        public void start(Stage stage) {
     
            final CategoryAxis xAxis = new CategoryAxis();
            final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
            yAxis.setTickUnit(1);
            yAxis.setPrefWidth(35);
            yAxis.setMinorTickCount(10);
            yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
                @Override
                public String toString(Number object) {
                    String label;
                    label = String.format("%7.2f", object.floatValue());
                    return label;
                }
            });
            final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
     
            lineChart.setCreateSymbols(false);
            lineChart.setAlternativeRowFillVisible(false);
            lineChart.setLegendVisible(false);
     
            XYChart.Series series1 = new XYChart.Series();
     
            series1.getData().add(new XYChart.Data("Jan", 1));
            series1.getData().add(new XYChart.Data("Feb", 4));
            series1.getData().add(new XYChart.Data("Mar", 2.5));
            series1.getData().add(new XYChart.Data("Apr", 5));
            series1.getData().add(new XYChart.Data("May", 6));
            series1.getData().add(new XYChart.Data("Jun", 8));
            series1.getData().add(new XYChart.Data("Jul", 12));
            series1.getData().add(new XYChart.Data("Aug", 8));
            series1.getData().add(new XYChart.Data("Sep", 11));
            series1.getData().add(new XYChart.Data("Oct", 13));
            series1.getData().add(new XYChart.Data("Nov", 10));
            series1.getData().add(new XYChart.Data("Dec", 20));
     
            BorderPane bp = new BorderPane();
            bp.setCenter(lineChart);
            Scene scene = new Scene(bp, 800, 600);
            lineChart.setAnimated(false);
            lineChart.getData().addAll(series1);
     
            LinesEditCtx02.MouseHandler mh = new LinesEditCtx02.MouseHandler(bp);
            bp.setOnMouseClicked(mh);
            bp.setOnMouseMoved(mh);
     
            stage.setScene(scene);
     
            path = new Path();
            path.setStrokeWidth(1);
            path.setStroke(Color.BLACK);
     
            scene.setOnMouseDragged(mh);
            scene.setOnMousePressed(mh);
            bp.getChildren().add(path);
            stage.setScene(scene);
            stage.show();
        }
     
        class MouseHandler implements EventHandler< MouseEvent> {
     
            private Line line;
            private Pane pane;
            private double x1, y1, x2, y2;
     
            public MouseHandler(Pane pane) {
                this.pane = pane;
            }
     
            @Override
            public void handle(MouseEvent event) {
                //if (event.getEventType() == MouseEvent.MOUSE_CLICKED) {
                if ( ( event.getEventType() == MouseEvent.MOUSE_PRESSED )&&
                     (event.isPrimaryButtonDown()) ) {
                        x1 = x2 = event.getX();
                        y1 = y2 = event.getY();
                        line = new BoundLine(pane, event, x1, y1,  x2, y2);
                        pane.getChildren().add(line);                    
    //                    line.getEventDispatcher().dispatchEvent(event, null);
     
                    }                
            }
        }   
       
        private static final class BoundLine extends Line {        
     
           // a draggable anchor displayed around a point.
            class Anchor extends Circle { 
                  private boolean dragging ;
                    Anchor(Color color, DoubleProperty x, DoubleProperty y) {
                    super(x.get(), y.get(), 10);
                    setFill(color.deriveColor(1, 1, 1, 0.5));
                    setStroke(color);
                    setStrokeWidth(2);
                    setStrokeType(StrokeType.OUTSIDE);
     
                    x.bind(centerXProperty());
                    y.bind(centerYProperty());
                    enableDrag();
                    }
                    private void enableDrag() {
                        dragging = true ;
                        setOnMousePressed(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {      
                            getScene().setCursor(Cursor.MOVE);
                            dragging = true ;
                            mouseEvent.consume();
                            }
                        });
                        pane.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                            getScene().setCursor(Cursor.HAND);
                            dragging = false ;
                            }
                        });
                       pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                                double newX = mouseEvent.getX();
                                if (newX > 0 && newX < getScene().getWidth() && dragging) {
                                    setCenterX(newX);
                                }  
                                double newY = mouseEvent.getY();
                                if (newY > 0 && newY < getScene().getHeight() && dragging) {
                                    setCenterY(newY);
                                }
                                mouseEvent.consume();
                            }
                        });
                        setOnMouseEntered(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                            if (!mouseEvent.isPrimaryButtonDown()) {
                                getScene().setCursor(Cursor.HAND);
                                mouseEvent.consume();
                            }
                            }
                        });
                        setOnMouseExited(new EventHandler<MouseEvent>() {
                            @Override public void handle(MouseEvent mouseEvent) {
                            if (!mouseEvent.isPrimaryButtonDown()) {
                                getScene().setCursor(Cursor.DEFAULT);
                                mouseEvent.consume();
                            }
                            }
                        });
                    }
            }
            
            private Pane pane ;
            BoundLine(Pane pane, final MouseEvent mouseEvent, double d, double d1, double d2, double d3) {
                super(d, d1, d2, d3);
                
                this.pane = pane ;
                
                final Anchor endAnchor    = new Anchor(Color.PALEGREEN, startXProperty(), startYProperty());
    
                pane.getChildren().add(endAnchor);
            }
    
        }   
    }
    Edited by: James_D on Nov 19, 2012 11:38 AM (to fix the explosion of unnecessary imports)
  • 12. Re: How to progate an event to a children using JavaFx2
    James_D Guru
    Currently Being Moderated
    One last comment (it's a slow day: can you tell?). I think I now understand what you want to do:

    1. Create a class that nicely encapsulates a "draggable line", managing the mouse events to move the ends around and displaying "anchors" that the user can easily click on.
    2. Have a pane in which the user can click and drag to create new draggable lines and add them to the display.
    3. Since the code is similar for moving an end of an existing line, and moving the end of a newly created line, use the mouse drag handler associated with an existing line to manage the newly created line.

    The issue you ran into was that the end of the newly created line is not receiving the mouse drag notification. This is because (I think) the mouse was never pressed on that anchor. Hence your request to propagate the mouse press to the newly created line.

    I think the cleanest approach is to abandon aim 3. In a sense, aims 1 and 2 are somewhat different (edit an existing line, create a new line) so it's not too unreasonable to use different code. In fact, when you get into the details, there is a subtle difference. When you're creating a new line, the end of the line should be exactly at the point of the mouse drag event. When you're adjusting an existing line, the end of the line should be adjusted by the offset between the original end of the line and the actual mouse click (which, since your anchor has non-zero radius, may be a few pixels different). So managing the "create a new line" and "edit an existing line" separately helps this.

    Creating a new line is really something the pane should handle (there's no existing line to handle it...); managing an existing line is something the line should do itself.

    Here's my shot at this (just in a BorderPane, no line chart, but you can fix that easily). There's some extra functionality I haven't used in the DraggableLine (double property for the radius of the anchors, anchorsVisible boolean property for toggling their display) and a hook for css by adding a style class to the anchors.

    DraggableLine.java
    package draggablelines;
    
    import javafx.beans.property.BooleanProperty;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.beans.property.SimpleDoubleProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.event.EventHandler;
    import javafx.geometry.Point2D;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.shape.Circle;
    import javafx.scene.shape.Line;
    
    public class DraggableLine {
         private Line line ;
         private Circle startAnchor ;
         private Circle endAnchor ;
         private Group group ;
         
         private DoubleProperty anchorRadius ;
         private BooleanProperty anchorsVisible ;
         
         public DraggableLine(double startX, double startY, double endX, double endY, double anchorRadius) {
              this.line = new Line(startX, startY, endX, endY);
              this.anchorRadius = new SimpleDoubleProperty(anchorRadius);
              this.anchorsVisible = new SimpleBooleanProperty(true);
              startAnchor = createAnchor(line.startXProperty(), line.startYProperty());
              endAnchor = createAnchor(line.endXProperty(), line.endYProperty());
              this.group = new Group();
              group.getChildren().addAll(line, startAnchor, endAnchor);
         }
         
         public DraggableLine(double startX, double startY, double endX, double endY) {
              this(startX, startY, endX, endY, 5);
         }
         
         private Circle createAnchor(DoubleProperty x, DoubleProperty y) {
              final Circle anchor = new Circle();
              anchor.centerXProperty().bindBidirectional(x);
              anchor.centerYProperty().bindBidirectional(y);
              anchor.radiusProperty().bind(anchorRadius);
              anchor.visibleProperty().bind(anchorsVisible);
              anchor.getStyleClass().add("draggable-line-anchor");
              
              final ObjectProperty<Point2D> mousePressPoint = new SimpleObjectProperty<Point2D>();
              anchor.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
                   @Override
                   public void handle(MouseEvent event) {
                        mousePressPoint.set(new Point2D(event.getX(), event.getY()));
                        event.consume();
                   }
              });
              anchor.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
                   @Override
                   public void handle(MouseEvent event) {
                        if (mousePressPoint.get() != null) {
                             double deltaX = event.getX()-mousePressPoint.get().getX();
                             double deltaY = event.getY()-mousePressPoint.get().getY();
                             mousePressPoint.set(new Point2D(event.getX(), event.getY()));
                             double oldCenterX = anchor.getCenterX() ;
                             double oldCenterY = anchor.getCenterY();
                             anchor.setCenterX(oldCenterX+deltaX);
                             anchor.setCenterY(oldCenterY+deltaY);
                             event.consume();
                        }
                   }
              });
              anchor.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
                   @Override
                   public void handle(MouseEvent event) {
                        mousePressPoint.set(null) ;
                        event.consume();
                   }
              });
              return anchor ;
         }
         
         public Node view() {
              return group ;
         }
         
         public DoubleProperty anchorRadiusProperty() {
              return anchorRadius ;
         }
         
         public double getAnchorRadius() {
              return anchorRadius.get();
         }
         
         public void setAnchorRadius(double radius) {
              anchorRadius.set(radius);
         }
         
         public BooleanProperty anchorsVisibleProperty() {
              return anchorsVisible ;
         }
         
         public boolean isAnchorsVisible() {
              return anchorsVisible.get();
         }
         
         public void setAnchorsVisible(boolean visible) {
              anchorsVisible.set(visible);
         }
         
         public double getStartX() {
              return line.getStartX();
         }
         
         public void setStartX(double x) {
              line.setStartX(x);
         }
         
         public double getStartY() {
              return line.getStartY();
         }
         
         public void setStartY(double y) {
              line.setStartY(y);
         }
         
         public double getEndX() {
              return line.getEndX();
         }
         
         public void setEndX(double x) {
              line.setEndX(x);
         }
         
         public double getEndY() {
              return line.getEndY();
         }
         
         public void setEndY(double y) {
              line.setEndY(y);
         }     
    }
    And
    DraggableLineTest.java
    package draggablelines;
    
    import javafx.application.Application;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.Pane;
    import javafx.scene.shape.Line;
    import javafx.stage.Stage;
    
    public class DraggableLineTest extends Application {
    
         @Override
         public void start(Stage primaryStage) throws Exception {
              final BorderPane root = new BorderPane();
              final Pane pane = new Pane();
              final ObjectProperty<Line> newLine = new SimpleObjectProperty<Line>();
              root.setCenter(pane);
              pane.setOnMousePressed(new EventHandler<MouseEvent>() {
                   @Override
                   public void handle(MouseEvent event) {
                        double x = event.getX();
                        double y = event.getY();
                        Line line = new Line(x, y, x, y);
                        pane.getChildren().add(line);
                        newLine.set(line);
                   }
              });
              pane.setOnMouseDragged(new EventHandler<MouseEvent>() {
                   @Override
                   public void handle(MouseEvent event) {
                        Line line = newLine.get();
                        if (line != null) {
                             line.setEndX(event.getX());
                             line.setEndY(event.getY());
                        }
                   }
              });
              pane.setOnMouseReleased(new EventHandler<MouseEvent>() {
                   @Override
                   public void handle(MouseEvent event) {
                        Line line = newLine.get();
                        if (line != null) {
                             pane.getChildren().remove(line);
                             pane.getChildren().add(new DraggableLine(line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY()).view());
                        }
                        newLine.set(null);
                   }
              });
              primaryStage.setScene(new Scene(root, 600, 600));
              primaryStage.show();
         }
    
         public static void main(String[] args) {launch(args);}
    }
  • 13. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    Dear James,
    I want to really thank you very much for your quick reply, I will try your code as soon as possibile.
    With my best regards,
    Francesco

    I would be happy to contact you via email, mine is frn.alfano on gmail.com
  • 14. Re: How to progate an event to a children using JavaFx2
    famedoro Newbie
    Currently Being Moderated
    I have tried your code and it works prefectly !
    Is it possible to attach an event to group made of Line and Anchor, or at line only in order to intercept the cancel key keyboard press to remove the whole group?
    I have tried this:
                     final EventHandler<KeyEvent> keyEventHandler =
                        new EventHandler<KeyEvent>() {
                    @Override
                            public void handle(final KeyEvent keyEvent) {
                                    System.out.println("code "+keyEvent.getCode());
                                    keyEvent.consume();                            
                            }
                        };
                    
                    group.setOnKeyPressed(keyEventHandler);
                    group.setOnKeyReleased(keyEventHandler);
                    
                    group.addEventHandler(KeyEvent.ANY, new EventHandler<KeyEvent>() {
                        @Override
                        public void handle(final KeyEvent keyEvent) 
                        {
                            System.out.println("here");
                            System.out.println("code "+keyEvent.getCode());
                        }
                    });
    but I can't get the event working
    Thank you very much-

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points