2015-08-26 5 views
9

Sto provando a inserire alcuni componenti all'interno di uno ScrollPane. Questi componenti dovrebbero avere la possibilità di essere spostati attraverso questo riquadro con il mouse (fare clic e trascinare). Lo stesso ScrollPane è pannable e zoomable.Selezionare e spostare un nodo in un riquadro Pannable/Zoomable

Ora se ne seleziono uno e lo trascino in una nuova posizione, il mouse è più veloce del componente se ho eseguito lo zoom indietro. Quando ingrandito, il componente viene spostato più velocemente del movimento del mouse.

Se non ingrandito, funziona finché non raggiungo una determinata posizione in cui la padella ScrollPane esegue automaticamente la panoramica.

Deve fare qualcosa con le coordinate determinate dei nodi. Qualcuno ha un'idea di cosa devo aggiungere per farlo funzionare correttamente?

mia classe controller: class

public class MainWindowController implements Initializable { 

    private final double SCALE_DELTA = 1.1; 
    private final StackPane zoomPane = new StackPane(); 
    private Group group = new Group(); 

    @FXML 
    private ScrollPane scrollPane; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
     Node node1 = new Node("Test"); 
     Node node2 = new Node("Test2", 100, 200); 

     group.getChildren().addAll(node1, node2); 
     zoomPane.getChildren().add(group); 
     Group scrollContent = new Group(zoomPane); 
     scrollPane.setContent(scrollContent); 
     scrollPane.viewportBoundsProperty().addListener((ObservableValue<? extends Bounds> observable, 
       Bounds oldValue, Bounds newValue) -> { 
        zoomPane.setMinSize(newValue.getWidth(), newValue.getHeight()); 
       }); 

     zoomPane.setOnScroll(
       (ScrollEvent event) -> { 
        event.consume(); 
        if (event.getDeltaY() == 0) { 
         return; 
        } 
        double scaleFactor = (event.getDeltaY() > 0) ? SCALE_DELTA : 1/SCALE_DELTA; 
        Point2D scrollOffset = figureScrollOffset(scrollContent, scrollPane); 
        group.setScaleX(group.getScaleX() * scaleFactor); 
        group.setScaleY(group.getScaleY() * scaleFactor); 
        repositionScroller(scrollContent, scrollPane, scaleFactor, scrollOffset); 
       } 
     ); 

     group.getChildren() 
       .add(new Node("Test3", 500, 500)); 

    } 

    private Point2D figureScrollOffset(javafx.scene.Node scrollContent, ScrollPane scroller) { 
     double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth(); 
     double hScrollProportion = (scroller.getHvalue() - scroller.getHmin())/(scroller.getHmax() - scroller.getHmin()); 
     double scrollXOffset = hScrollProportion * Math.max(0, extraWidth); 
     double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight(); 
     double vScrollProportion = (scroller.getVvalue() - scroller.getVmin())/(scroller.getVmax() - scroller.getVmin()); 
     double scrollYOffset = vScrollProportion * Math.max(0, extraHeight); 
     return new Point2D(scrollXOffset, scrollYOffset); 
    } 

    private void repositionScroller(javafx.scene.Node scrollContent, ScrollPane scroller, double scaleFactor, Point2D scrollOffset) { 
     double scrollXOffset = scrollOffset.getX(); 
     double scrollYOffset = scrollOffset.getY(); 
     double extraWidth = scrollContent.getLayoutBounds().getWidth() - scroller.getViewportBounds().getWidth(); 
     if (extraWidth > 0) { 
      double halfWidth = scroller.getViewportBounds().getWidth()/2; 
      double newScrollXOffset = (scaleFactor - 1) * halfWidth + scaleFactor * scrollXOffset; 
      scroller.setHvalue(scroller.getHmin() + newScrollXOffset * (scroller.getHmax() - scroller.getHmin())/extraWidth); 
     } else { 
      scroller.setHvalue(scroller.getHmin()); 
     } 
     double extraHeight = scrollContent.getLayoutBounds().getHeight() - scroller.getViewportBounds().getHeight(); 
     if (extraHeight > 0) { 
      double halfHeight = scroller.getViewportBounds().getHeight()/2; 
      double newScrollYOffset = (scaleFactor - 1) * halfHeight + scaleFactor * scrollYOffset; 
      scroller.setVvalue(scroller.getVmin() + newScrollYOffset * (scroller.getVmax() - scroller.getVmin())/extraHeight); 
     } else { 
      scroller.setHvalue(scroller.getHmin()); 
     } 
    } 
} 

Il nodo:

public class Node extends Parent { 

    private NodeStatus status = NodeStatus.OK; 
    private final Image okImage = new Image(getClass().getResourceAsStream("/images/MasterOK.png")); 
    private ImageView image = new ImageView(okImage); 
    private final Text label = new Text(); 
    private final Font font = Font.font("Courier", 20); 
    double orgSceneX, orgSceneY; 
    double layoutX, layoutY; 

    public Node(String labelText) { 
     this(labelText, 0, 0); 
    } 

    public Node(String labelText, double x, double y) { 
     label.setText(labelText); 
     label.setFont(font); 
     label.setLayoutX(okImage.getWidth() + 10); 
     label.setLayoutY(okImage.getHeight()/2 + 10); 
     getChildren().add(image); 
     getChildren().add(label); 
     setLayoutX(x); 
     setLayoutY(y); 
     setCursor(Cursor.MOVE); 
     setOnMousePressed(new EventHandler<MouseEvent>() { 

      @Override 
      public void handle(MouseEvent t) { 
       orgSceneX = t.getSceneX(); 
       orgSceneY = t.getSceneY(); 
       layoutX = getLayoutX(); 
       layoutY = getLayoutY(); 
      } 
     }); 
     setOnMouseDragged(new EventHandler<MouseEvent>() { 

      @Override 
      public void handle(MouseEvent t) { 
       setLayoutX(layoutX + t.getSceneX() - orgSceneX); 
       setLayoutY(layoutY + t.getSceneY() - orgSceneY); 
      } 
     }); 

    } 

    public NodeStatus getStatus() { 
     return status; 
    } 

    public void setStatus(NodeStatus status) { 
     this.status = status; 
    } 

} 

class Delta { 

    double x, y; 
} 

e la fxml:

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

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

<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="cqsmonitor.MainWindowController"> 
    <children> 
     <Pane layoutX="666.0" layoutY="14.0" prefHeight="572.0" prefWidth="114.0" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0"> 
      <children> 
       <TextField layoutY="30.0" prefHeight="25.0" prefWidth="114.0" /> 
       <Label layoutY="12.0" text="Search:" /> 
       <ChoiceBox layoutY="90.0" prefHeight="25.0" prefWidth="114.0" /> 
       <Label layoutY="73.0" text="View:" /> 
      </children> 
     </Pane> 
     <ScrollPane fx:id="scrollPane" layoutX="14.0" layoutY="14.0" pannable="true" prefHeight="571.0" prefWidth="644.0" AnchorPane.bottomAnchor="15.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="142.0" AnchorPane.topAnchor="14.0"> 
     </ScrollPane> 
    </children> 
</AnchorPane> 

risposta

3

Dal momento che nessuno ha risposto ancora, ecco qualche codice. Non voglio scavare nel tuo e reinventare la ruota.

È possibile spostare i nodi trascinando con il pulsante sinistro del mouse, ridimensionare il riquadro con la rotellina del mouse, eseguire una panoramica del riquadro con il pulsante destro del mouse. Non è necessario alcun ScrollPane. Tuttavia, se vuoi ScrollBars, puoi sempre aggiungerli se preferisci.

Il codice:

import javafx.application.Application; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.SimpleDoubleProperty; 
import javafx.event.EventHandler; 
import javafx.scene.Group; 
import javafx.scene.Node; 
import javafx.scene.Scene; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.canvas.GraphicsContext; 
import javafx.scene.control.Label; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.input.ScrollEvent; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Circle; 
import javafx.scene.shape.Rectangle; 
import javafx.stage.Stage; 

/** 
* The canvas which holds all of the nodes of the application. 
*/ 
class PannableCanvas extends Pane { 

    DoubleProperty myScale = new SimpleDoubleProperty(1.0); 

    public PannableCanvas() { 

     setPrefSize(600, 600); 
     setStyle("-fx-background-color: lightgrey; -fx-border-color: blue;"); 

     // add scale transform 
     scaleXProperty().bind(myScale); 
     scaleYProperty().bind(myScale); 

     // logging 
     addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { 
      System.out.println( 
        "canvas event: " + (((event.getSceneX() - getBoundsInParent().getMinX())/getScale()) + ", scale: " + getScale()) 
        ); 
      System.out.println("canvas bounds: " + getBoundsInParent()); 
       }); 

    } 

    /** 
    * Add a grid to the canvas, send it to back 
    */ 
    public void addGrid() { 

     double w = getBoundsInLocal().getWidth(); 
     double h = getBoundsInLocal().getHeight(); 

     // add grid 
     Canvas grid = new Canvas(w, h); 

     // don't catch mouse events 
     grid.setMouseTransparent(true); 

     GraphicsContext gc = grid.getGraphicsContext2D(); 

     gc.setStroke(Color.GRAY); 
     gc.setLineWidth(1); 

     // draw grid lines 
     double offset = 50; 
     for(double i=offset; i < w; i+=offset) { 
      // vertical 
      gc.strokeLine(i, 0, i, h); 
      // horizontal 
      gc.strokeLine(0, i, w, i); 
     } 

     getChildren().add(grid); 

     grid.toBack(); 
    } 

    public double getScale() { 
     return myScale.get(); 
    } 

    /** 
    * Set x/y scale 
    * @param myScale 
    */ 
    public void setScale(double scale) { 
     myScale.set(scale); 
    } 

    /** 
    * Set x/y pivot points 
    * @param x 
    * @param y 
    */ 
    public void setPivot(double x, double y) { 
     setTranslateX(getTranslateX()-x); 
     setTranslateY(getTranslateY()-y); 
    } 
} 


/** 
* Mouse drag context used for scene and nodes. 
*/ 
class DragContext { 

    double mouseAnchorX; 
    double mouseAnchorY; 

    double translateAnchorX; 
    double translateAnchorY; 

} 

/** 
* Listeners for making the nodes draggable via left mouse button. Considers if parent is zoomed. 
*/ 
class NodeGestures { 

    private DragContext nodeDragContext = new DragContext(); 

    PannableCanvas canvas; 

    public NodeGestures(PannableCanvas canvas) { 
     this.canvas = canvas; 

    } 

    public EventHandler<MouseEvent> getOnMousePressedEventHandler() { 
     return onMousePressedEventHandler; 
    } 

    public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() { 
     return onMouseDraggedEventHandler; 
    } 

    private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() { 

     public void handle(MouseEvent event) { 

      // left mouse button => dragging 
      if(!event.isPrimaryButtonDown()) 
       return; 

      nodeDragContext.mouseAnchorX = event.getSceneX(); 
      nodeDragContext.mouseAnchorY = event.getSceneY(); 

      Node node = (Node) event.getSource(); 

      nodeDragContext.translateAnchorX = node.getTranslateX(); 
      nodeDragContext.translateAnchorY = node.getTranslateY(); 

     } 

    }; 

    private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() { 
     public void handle(MouseEvent event) { 

      // left mouse button => dragging 
      if(!event.isPrimaryButtonDown()) 
       return; 

      double scale = canvas.getScale(); 

      Node node = (Node) event.getSource(); 

      node.setTranslateX(nodeDragContext.translateAnchorX + ((event.getSceneX() - nodeDragContext.mouseAnchorX)/scale)); 
      node.setTranslateY(nodeDragContext.translateAnchorY + ((event.getSceneY() - nodeDragContext.mouseAnchorY)/scale)); 

      event.consume(); 

     } 
    }; 
} 

/** 
* Listeners for making the scene's canvas draggable and zoomable 
*/ 
class SceneGestures { 

    private static final double MAX_SCALE = 10.0d; 
    private static final double MIN_SCALE = .1d; 

    private DragContext sceneDragContext = new DragContext(); 

    PannableCanvas canvas; 

    public SceneGestures(PannableCanvas canvas) { 
     this.canvas = canvas; 
    } 

    public EventHandler<MouseEvent> getOnMousePressedEventHandler() { 
     return onMousePressedEventHandler; 
    } 

    public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() { 
     return onMouseDraggedEventHandler; 
    } 

    public EventHandler<ScrollEvent> getOnScrollEventHandler() { 
     return onScrollEventHandler; 
    } 

    private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() { 

     public void handle(MouseEvent event) { 

      // right mouse button => panning 
      if(!event.isSecondaryButtonDown()) 
       return; 

      sceneDragContext.mouseAnchorX = event.getSceneX(); 
      sceneDragContext.mouseAnchorY = event.getSceneY(); 

      sceneDragContext.translateAnchorX = canvas.getTranslateX(); 
      sceneDragContext.translateAnchorY = canvas.getTranslateY(); 

     } 

    }; 

    private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() { 
     public void handle(MouseEvent event) { 

      // right mouse button => panning 
      if(!event.isSecondaryButtonDown()) 
       return; 

      canvas.setTranslateX(sceneDragContext.translateAnchorX + event.getSceneX() - sceneDragContext.mouseAnchorX); 
      canvas.setTranslateY(sceneDragContext.translateAnchorY + event.getSceneY() - sceneDragContext.mouseAnchorY); 

      event.consume(); 
     } 
    }; 

    /** 
    * Mouse wheel handler: zoom to pivot point 
    */ 
    private EventHandler<ScrollEvent> onScrollEventHandler = new EventHandler<ScrollEvent>() { 

     @Override 
     public void handle(ScrollEvent event) { 

      double delta = 1.2; 

      double scale = canvas.getScale(); // currently we only use Y, same value is used for X 
      double oldScale = scale; 

      if (event.getDeltaY() < 0) 
       scale /= delta; 
      else 
       scale *= delta; 

      scale = clamp(scale, MIN_SCALE, MAX_SCALE); 

      double f = (scale/oldScale)-1; 

      double dx = (event.getSceneX() - (canvas.getBoundsInParent().getWidth()/2 + canvas.getBoundsInParent().getMinX())); 
      double dy = (event.getSceneY() - (canvas.getBoundsInParent().getHeight()/2 + canvas.getBoundsInParent().getMinY())); 

      canvas.setScale(scale); 

      // note: pivot value must be untransformed, i. e. without scaling 
      canvas.setPivot(f*dx, f*dy); 

      event.consume(); 

     } 

    }; 


    public static double clamp(double value, double min, double max) { 

     if(Double.compare(value, min) < 0) 
      return min; 

     if(Double.compare(value, max) > 0) 
      return max; 

     return value; 
    } 
} 



/** 
* An application with a zoomable and pannable canvas. 
*/ 
public class ZoomAndScrollApplication extends Application { 
    public static void main(String[] args) { 
     launch(args); 
    } 

    @Override 
    public void start(Stage stage) { 

     Group group = new Group(); 

     // create canvas 
     PannableCanvas canvas = new PannableCanvas(); 

     // we don't want the canvas on the top/left in this example => just 
     // translate it a bit 
     canvas.setTranslateX(100); 
     canvas.setTranslateY(100); 

     // create sample nodes which can be dragged 
     NodeGestures nodeGestures = new NodeGestures(canvas); 

     Label label1 = new Label("Draggable node 1"); 
     label1.setTranslateX(10); 
     label1.setTranslateY(10); 
     label1.addEventFilter(MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler()); 
     label1.addEventFilter(MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler()); 

     Label label2 = new Label("Draggable node 2"); 
     label2.setTranslateX(100); 
     label2.setTranslateY(100); 
     label2.addEventFilter(MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler()); 
     label2.addEventFilter(MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler()); 

     Label label3 = new Label("Draggable node 3"); 
     label3.setTranslateX(200); 
     label3.setTranslateY(200); 
     label3.addEventFilter(MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler()); 
     label3.addEventFilter(MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler()); 

     Circle circle1 = new Circle(300, 300, 50); 
     circle1.setStroke(Color.ORANGE); 
     circle1.setFill(Color.ORANGE.deriveColor(1, 1, 1, 0.5)); 
     circle1.addEventFilter(MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler()); 
     circle1.addEventFilter(MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler()); 

     Rectangle rect1 = new Rectangle(100,100); 
     rect1.setTranslateX(450); 
     rect1.setTranslateY(450); 
     rect1.setStroke(Color.BLUE); 
     rect1.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.5)); 
     rect1.addEventFilter(MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler()); 
     rect1.addEventFilter(MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler()); 

     canvas.getChildren().addAll(label1, label2, label3, circle1, rect1); 

     group.getChildren().add(canvas); 

     // create scene which can be dragged and zoomed 
     Scene scene = new Scene(group, 1024, 768); 

     SceneGestures sceneGestures = new SceneGestures(canvas); 
     scene.addEventFilter(MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler()); 
     scene.addEventFilter(MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler()); 
     scene.addEventFilter(ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler()); 

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

     canvas.addGrid(); 

    } 
} 

fatemi sapere se questo non ti aiuta affatto, allora io cancellare il post. Non sono interessato alla taglia.

+0

Grazie. Ci darò un'occhiata. –

+0

Funziona fuori dalla scatola. Proverò a implementarlo nel mio codice e a postare un aggiornamento una volta che avrò il tempo di realizzarlo. –

+0

Questo è stato molto utile, grazie! Ho usato il tuo esempio e ho aggiunto il mio nodo (un gruppo) su di esso. Anche lo zoom funziona perfettamente! Ho una domanda però: E 'possibile ingrandire il riquadro, senza zoomare i propri figli e mantenere le loro posizioni all'interno del riquadro? Pensa a un indicatore di Google Maps.Quando si ingrandisce, il marcatore non diventa più grande. Come potrei ottenere quello? – user3804769

0

mi limiterò a inserire una domanda di riassumere la soluzione al mio problema:

Il problema perché i miei nodi in cui ritardo era perché l'avevano alcun indizio circa il ridimensionamento che potrebbe avere verificato nel riquadro di un livello sopra. Quindi, come soluzione finale ho mescolato il mio codice con la risposta di Roland. se qualcuno ha in mente ulteriori ottimizzazioni, per favore pubblicale.

classe Controller:

public class MainWindowController implements Initializable { 

    private final Group group = new Group(); 
    private static final double MAX_SCALE = 10.0d; 
    private static final double MIN_SCALE = .1d; 

    @FXML 
    private ScrollPane scrollPane; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
     ZoomableCanvas canvas = new ZoomableCanvas(); 
     MasterNode node1 = new MasterNode("Test"); 
     MasterNode node2 = new MasterNode("Test", 100, 200); 
     canvas.getChildren().add(node1); 
     canvas.getChildren().add(node2); 
     group.getChildren().add(canvas); 
     scrollPane.setContent(group); 
     scrollPane.addEventHandler(ScrollEvent.ANY, new EventHandler<ScrollEvent>() { 

      @Override 
      public void handle(ScrollEvent event) { 
       double delta = 1.2; 

       double scale = canvas.getScale(); // currently we only use Y, same value is used for X 
       double oldScale = scale; 

       if (event.getDeltaY() < 0) { 
        scale /= delta; 
       } else { 
        scale *= delta; 
       } 

       scale = clamp(scale, MIN_SCALE, MAX_SCALE); 

       double f = (scale/oldScale) - 1; 

       double dx = (event.getSceneX() - (canvas.getBoundsInParent().getWidth()/2 + canvas.getBoundsInParent().getMinX())); 
       double dy = (event.getSceneY() - (canvas.getBoundsInParent().getHeight()/2 + canvas.getBoundsInParent().getMinY())); 

       canvas.setScale(scale); 

       // note: pivot value must be untransformed, i. e. without scaling 
       canvas.setPivot(f * dx, f * dy); 

       event.consume(); 
      } 
     }); 

    } 

    private double clamp(double value, double min, double max) { 

     if (Double.compare(value, min) < 0) { 
      return min; 
     } 

     if (Double.compare(value, max) > 0) { 
      return max; 
     } 

     return value; 
    } 
} 

La tela:

public class ZoomableCanvas extends Pane { 

    DoubleProperty scale = new SimpleDoubleProperty(1.0); 

    public ZoomableCanvas() { 
     scaleXProperty().bind(scale); 
     scaleYProperty().bind(scale); 


     getChildren().addListener((Change<? extends javafx.scene.Node> c) -> { 
      while (c.next()) { 
       if (c.wasAdded()) { 
        for (Node child : c.getAddedSubList()) { 
         ((MasterNode) child).scaleProperty.bind(scale); 
        } 
       } 
       if (c.wasRemoved()) { 
        for (Node child : c.getRemoved()) { 
         ((MasterNode) child).scaleProperty.unbind(); 
        } 
       } 
      } 
     }); 
    } 

    public double getScale() { 
     return scale.get(); 
    } 

    public void setScale(double scale) { 
     this.scale.set(scale); 
    } 

    public void setPivot(double x, double y) { 
     setTranslateX(getTranslateX() - x); 
     setTranslateY(getTranslateY() - y); 
    } 
} 

Node:

public class MasterNode extends Parent { 

    private NodeStatus status = NodeStatus.OK; 
    private final Image okImage = new Image(getClass().getResourceAsStream("/images/MasterOK.png")); 
    private ImageView image = new ImageView(okImage); 
    private final Text label = new Text(); 
    private final Font font = Font.font("Courier", 20); 
    private DragContext nodeDragContext = new DragContext(); 
    public DoubleProperty scaleProperty = new SimpleDoubleProperty(1.0); 
    double orgSceneX, orgSceneY; 
    double layoutX, layoutY; 

    public MasterNode(String labelText) { 
     this(labelText, 0, 0); 
    } 

    public MasterNode(String labelText, double x, double y) { 
     scaleXProperty().bind(scaleProperty); 
     scaleYProperty().bind(scaleProperty); 
     label.setText(labelText); 
     label.setFont(font); 
     label.setLayoutX(okImage.getWidth() + 10); 
     label.setLayoutY(okImage.getHeight()/2 + 10); 
     getChildren().add(image); 
     getChildren().add(label); 
     setLayoutX(x); 
     setLayoutY(y); 
     setCursor(Cursor.MOVE); 
     setOnMousePressed(new EventHandler<MouseEvent>() { 

      @Override 
      public void handle(MouseEvent event) { 
       // left mouse button => dragging 
       if (!event.isPrimaryButtonDown()) { 
        return; 
       } 

       nodeDragContext.setMouseAnchorX(event.getSceneX()); 
       nodeDragContext.setMouseAnchorY(event.getSceneY()); 

       Node node = (Node) event.getSource(); 

       nodeDragContext.setTranslateAnchorX(node.getTranslateX()); 
       nodeDragContext.setTranslateAnchorY(node.getTranslateY()); 

      } 
     }); 
     setOnMouseDragged(new EventHandler<MouseEvent>() { 

      @Override 
      public void handle(MouseEvent event) { 
       // left mouse button => dragging 
       if (!event.isPrimaryButtonDown()) { 
        return; 
       } 
       Node node = (Node) event.getSource(); 
       node.setTranslateX(nodeDragContext.getTranslateAnchorX() + ((event.getSceneX() - nodeDragContext.getMouseAnchorX())/getScale())); 
       node.setTranslateY(nodeDragContext.getTranslateAnchorY() + ((event.getSceneY() - nodeDragContext.getMouseAnchorY())/getScale())); 
       event.consume(); 
      } 
     }); 

    } 

    public double getScale() { 
     return scaleProperty.get(); 
    } 

    public void setScale(double scale) { 
     scaleProperty.set(scale); 
    } 

    public NodeStatus getStatus() { 
     return status; 
    } 

    public void setStatus(NodeStatus status) { 
     this.status = status; 
    } 

}