JavaFX : Draggable node (Label ) horizontally only not vertically - javafx

Scenario : I want to drag (Drag gable Node) i.e labels(series of labels) in horizontal direction inside JFXPanel and it should be fixed vertically ....
I am referring Link .....but i am not able to restrict user to drag vertically so someone help me....the labels should be drag horizontal only not vertically.

Here's an example. I commented out the Y translating, so that you can drag the label only horizontally.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class DragNodesHorizontally extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
// create label
Label label = new Label( "Drag me");
label.relocate(100,100);
// make label draggable
MouseGestures mg = new MouseGestures();
mg.makeDraggable( label);
root.getChildren().addAll( label);
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
}
public static class MouseGestures {
class DragContext {
double x;
double y;
}
DragContext dragContext = new DragContext();
public void makeDraggable( Node node) {
node.setOnMousePressed( onMousePressedEventHandler);
node.setOnMouseDragged( onMouseDraggedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = event -> {
Node node = ((Node) (event.getSource()));
dragContext.x = node.getTranslateX() - event.getSceneX();
dragContext.y = node.getTranslateY() - event.getSceneY();
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = event -> {
Node node = ((Node) (event.getSource()));
node.setTranslateX( dragContext.x + event.getSceneX());
// node.setTranslateY( dragContext.y + event.getSceneY()); // uncomment this if you want x/y dragging
};
}
}
In the link you posted, you could as well set offsetY to 0.

Related

How to stop transition when checkbox is unchecked javafx

So I've made a checkbox that applies a scale transition to a rectangle when checked. But the problem is that the transition keeps going even after I uncheck the checkbox. Any ideas on how to make it stop after un-checking?
checkbox.setOnAction(e -> {
ScaleTransition scaleT = new ScaleTransition(Duration.seconds(5), rectangle);
scaleT.setAutoReverse(true);
scaleT.setCycleCount(Timeline.INDEFINITE);
scaleT.setToX(2);
scaleT.setToY(2);
scaleT.play();
});
To control the animation, you need to define the transistion(with INDEFINITE cycle count) outside the CheckBox listener/action. Then you can just play/pause the animation as you required.
Below is the quick demo:
import javafx.animation.ScaleTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ScaleTransitionDemo extends Application {
#Override
public void start(Stage stage) {
Shape rectangle = new Rectangle(50, 50, Color.BLUE);
ScaleTransition transition = new ScaleTransition(Duration.seconds(1), rectangle);
transition.setDuration(Duration.seconds(1));
transition.setAutoReverse(true);
transition.setCycleCount(Timeline.INDEFINITE);
transition.setToX(3);
transition.setToY(3);
CheckBox checkBox = new CheckBox("Animate");
checkBox.selectedProperty().addListener((obs, old, selected) -> {
if (selected) {
transition.play();
} else {
transition.pause();
}
});
StackPane pane = new StackPane(rectangle);
VBox.setVgrow(pane, Priority.ALWAYS);
VBox root = new VBox(20, checkBox, pane);
root.setPadding(new Insets(10));
Scene scene = new Scene(root, 300, 300);
stage.setScene(scene);
stage.setTitle("Scale transition");
stage.show();
}
public static void main(String[] args) {
launch();
}
}
checking whether checkbox is selected or not with .isSelected() method . In this approach , scaled node will back to xy = 1 scale if checkbox is unchecked , but it will be disabled until transition ends .You can adjust setDuration . I've changed it just for gif recording. This is a single class javafx app you can try .
App.java
public class App extends Application {
#Override
public void start(Stage stage) {
Shape rectangle = new Rectangle(50, 50, Color.BLUE);
ScaleTransition scaleT = new ScaleTransition(Duration.seconds(1), rectangle);
CheckBox checkBox = new CheckBox("scale");
checkBox.setOnAction(e -> {
if (checkBox.isSelected()) {
scaleT.setDuration(Duration.seconds(1));
scaleT.setAutoReverse(true);
scaleT.setCycleCount(Timeline.INDEFINITE);
scaleT.setToX(2);
scaleT.setToY(2);
scaleT.play();
} else {
scaleT.setDuration(scaleT.getCurrentTime());
scaleT.stop();
scaleT.setCycleCount(1);
scaleT.setToX(1);
scaleT.setToY(1);
scaleT.play();
checkBox.setDisable(true);
scaleT.setOnFinished((t) -> {
checkBox.setDisable(false);
});
}
});
var scene = new Scene(new HBox(50, rectangle, checkBox), 640, 480);
stage.setScene(scene);
stage.setTitle("scale transition");
stage.show();
}
public static void main(String[] args) {
launch();
}
}

Click to draw a shape (circle)

this code Draw a circle and I want to put a button that when I click on the button I can do draw a circle. How Can I put Button for code below?
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class HelloApplication extends Application {
private Circle circle;
private boolean firstClick = true;
private double centerX, centerY;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Group root = new Group();
circle = new Circle();
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.BLACK);
root.getChildren().add(circle);
Scene scene = new Scene(root, 600, 600);
setHandlers(scene);
primaryStage.setTitle("blank");
primaryStage.setScene(scene);
primaryStage.show();
}
public void setHandlers(Scene scene) {
scene.setOnMouseClicked(e -> {
if (firstClick) {
centerX = e.getX();
centerY = e.getY();
// sets the center
circle.setCenterX(centerX);
circle.setCenterY(centerY);
// sets next "stage" of drawing your circle
firstClick = false;
// on second click will reset the process by setting firstClick to true
}
else {
firstClick = true;
}
});
scene.setOnMouseMoved(e -> {
// will only evaluate on the first instance of a click
if (!firstClick) {
// Distance formula between center of circle and mouse pointer
double c = Math
.sqrt(Math.pow(centerX - e.getX(), 2) + Math.pow(centerY - e.getY(), 2));
circle.setRadius(c);
}
});
}
}
The following mre demonstrates the basic functionality you required :
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
public class DrawCircles extends Application {
private Pane drawPane; //container for shapes
private Circle clickedPoint;
private static Color POINT_COLOR = Color.BLUEVIOLET, CIRCLE_COLOR = Color.RED;
private static int POINT_RADIUS = 2;
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
drawPane = new Pane(); //container for shapes
root.setCenter(drawPane);
Label help = new Label();
ToggleButton draw = new ToggleButton(" Draw ");
draw.selectedProperty().addListener((ChangeListener<Boolean>) (obs, oldV, newV) -> {
draw.setText(newV ? "Drawing" : " Draw ");
help.setText(newV ? "Double click on one point. Double click on second point" : "");
});
HBox buttonBar = new HBox(10, draw, help);
buttonBar.setAlignment(Pos.BOTTOM_LEFT);
root.setBottom(buttonBar);
drawPane.setOnMouseClicked(event -> {
if(!draw.isSelected()) return;
if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {
addPoint( event.getX() , event.getY());
}
});
Scene scene = new Scene(root, 500, 500);
primaryStage.setScene(scene);
primaryStage.setTitle("Draw Circles");
primaryStage.show();
}
private void addPoint(double x, double y) {
if(clickedPoint == null){ //no previously clicked point
clickedPoint = new Circle(x, y, POINT_RADIUS, POINT_COLOR);
drawPane.getChildren().add(clickedPoint); //mark clicked point
}else{
Shape shape = makeCircle(clickedPoint.getCenterX(), clickedPoint.getCenterY(), x, y);
drawPane.getChildren().add(shape); //add line
drawPane.getChildren().remove(clickedPoint);// remove first clicked point
clickedPoint = null;
}
}
private Shape makeCircle(double xCenter, double yCenter, double xEdge, double yEdge){
double radius =Math.sqrt( Math.pow(xEdge - xCenter, 2) + Math.pow(yEdge - yCenter, 2));
return new Circle(xCenter, yCenter, radius, CIRCLE_COLOR);
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX Pan and Zoom with Draggable Nodes Inside

I have a simple JavaFX pan and zoom application as show below. The pan and zoom functionality work great, but I would also like to be able to drag and drop the circle node too. The problem I have is that the scrollpane gets all of the mouse events first, so I'm unable to assign a drag and drop to just the circle. Is it possible to have a draggable/zoomable scrollpane and also be able to drag a node inside the pane?
Screenshot
Here us the code that I'm using:
package sample;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
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.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private ScrollPane scrollPane = new ScrollPane();
private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1.0d);
private final DoubleProperty deltaY = new SimpleDoubleProperty(0.0d);
private final Group group = new Group();
ImageView bigImageView = null;
PanAndZoomPane panAndZoomPane = null;
Pane featuresPane = new Pane();
#Override
public void start(Stage primaryStage) throws Exception{
bigImageView = new ImageView();
StackPane bigStackpane = new StackPane();
bigStackpane.getChildren().add(bigImageView);
bigStackpane.getChildren().add(featuresPane);
featuresPane.toFront();
featuresPane.setOpacity(.8);
Circle circle = new Circle();
circle.setCenterX(200);
circle.setCenterY(200);
circle.setRadius(100);
circle.setFill(Color.RED);
circle.setOnMouseClicked(e -> {
System.out.println("circle clicked");
});
featuresPane.getChildren().add(circle);
scrollPane.setPannable(true);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
group.getChildren().add(bigImageView);
group.getChildren().add(bigStackpane);
panAndZoomPane = new PanAndZoomPane();
zoomProperty.bind(panAndZoomPane.myScale);
deltaY.bind(panAndZoomPane.deltaY);
panAndZoomPane.getChildren().add(group);
SceneGestures sceneGestures = new SceneGestures(panAndZoomPane);
scrollPane.setContent(panAndZoomPane);
panAndZoomPane.toBack();
scrollPane.addEventFilter( MouseEvent.MOUSE_CLICKED, sceneGestures.getOnMouseClickedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
scrollPane.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
AnchorPane bigImageAnchorPane = new AnchorPane();
bigImageAnchorPane.getChildren().add(scrollPane);
Image image = new Image("https://i.imgur.com/8p1XBag.jpg");
bigImageView.setImage(image);
bigImageAnchorPane.setTopAnchor(scrollPane, 1.0d);
bigImageAnchorPane.setRightAnchor(scrollPane, 1.0d);
bigImageAnchorPane.setBottomAnchor(scrollPane, 1.0d);
bigImageAnchorPane.setLeftAnchor(scrollPane, 1.0d);
BorderPane root = new BorderPane(bigImageAnchorPane);
Label label = new Label("Pan and Zoom Test");
root.setTop(label);
Scene scene = new Scene(root, 1000, 1000);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
class PanAndZoomPane extends Pane {
public static final double DEFAULT_DELTA = 1.5d; //1.3d
DoubleProperty myScale = new SimpleDoubleProperty(1.0);
public DoubleProperty deltaY = new SimpleDoubleProperty(0.0);
private Timeline timeline;
public PanAndZoomPane() {
this.timeline = new Timeline(30);//60
// add scale transform
scaleXProperty().bind(myScale);
scaleYProperty().bind(myScale);
}
public double getScale() {
return myScale.get();
}
public void setScale( double scale) {
myScale.set(scale);
}
public void setPivot( double x, double y, double scale) {
// note: pivot value must be untransformed, i. e. without scaling
// timeline that scales and moves the node
timeline.getKeyFrames().clear();
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.millis(100), new KeyValue(translateXProperty(), getTranslateX() - x)), //200
new KeyFrame(Duration.millis(100), new KeyValue(translateYProperty(), getTranslateY() - y)), //200
new KeyFrame(Duration.millis(100), new KeyValue(myScale, scale)) //200
);
timeline.play();
}
public double getDeltaY() {
return deltaY.get();
}
public void setDeltaY( double dY) {
deltaY.set(dY);
}
}
/**
* Mouse drag context used for scene and nodes.
*/
class DragContext {
double mouseAnchorX;
double mouseAnchorY;
double translateAnchorX;
double translateAnchorY;
}
/**
* Listeners for making the scene's canvas draggable and zoomable
*/
public class SceneGestures {
private DragContext sceneDragContext = new DragContext();
PanAndZoomPane panAndZoomPane;
public SceneGestures( PanAndZoomPane canvas) {
this.panAndZoomPane = canvas;
}
public EventHandler<MouseEvent> getOnMouseClickedEventHandler() {
return onMouseClickedEventHandler;
}
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) {
sceneDragContext.mouseAnchorX = event.getX();
sceneDragContext.mouseAnchorY = event.getY();
sceneDragContext.translateAnchorX = panAndZoomPane.getTranslateX();
sceneDragContext.translateAnchorY = panAndZoomPane.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
panAndZoomPane.setTranslateX(sceneDragContext.translateAnchorX + event.getX() - sceneDragContext.mouseAnchorX);
panAndZoomPane.setTranslateY(sceneDragContext.translateAnchorY + event.getY() - 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 = PanAndZoomPane.DEFAULT_DELTA;
double scale = panAndZoomPane.getScale(); // currently we only use Y, same value is used for X
double oldScale = scale;
panAndZoomPane.setDeltaY(event.getDeltaY());
if (panAndZoomPane.deltaY.get() < 0) {
scale /= delta;
} else {
scale *= delta;
}
double f = (scale / oldScale)-1;
double dx = (event.getX() - (panAndZoomPane.getBoundsInParent().getWidth()/2 + panAndZoomPane.getBoundsInParent().getMinX()));
double dy = (event.getY() - (panAndZoomPane.getBoundsInParent().getHeight()/2 + panAndZoomPane.getBoundsInParent().getMinY()));
panAndZoomPane.setPivot(f*dx, f*dy, scale);
event.consume();
}
};
/**
* Mouse click handler
*/
private EventHandler<MouseEvent> onMouseClickedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.PRIMARY)) {
if (event.getClickCount() == 2) {
System.out.println("Image Layer Double Clicked...");
}else{
System.out.println("Image Layer Clicked...");
}
}
}
};
}
}
Your code is adding behavior via event filters. These filters are invoked during the event capturing phase which means they are invoked before the events reach your circle. You should strive to implement your behavior via event handlers, which are invoked during the event bubbling phase. Then you can consume events to prevent them from reaching ancestors, allowing you to drag your circle without scrolling/panning the scroll-pane content. For more information about event handling and propagation, check out this tutorial.
Here's a proof-of-concept which adds the zoom-handling to the scroll-pane's content and still let's you drag around a circle:
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
// using your example image
ImageView imageView = new ImageView("https://i.imgur.com/8p1XBag.jpg");
Circle circle = new Circle(100, 100, 25, Color.FIREBRICK);
circle.setOnMousePressed(
e -> {
// prevent pannable ScrollPane from changing cursor on drag-detected (implementation
// detail)
e.setDragDetect(false);
Point2D offset =
new Point2D(e.getX() - circle.getCenterX(), e.getY() - circle.getCenterY());
circle.setUserData(offset);
e.consume(); // prevents MouseEvent from reaching ScrollPane
});
circle.setOnMouseDragged(
e -> {
// prevent pannable ScrollPane from changing cursor on drag-detected (implementation
// detail)
e.setDragDetect(false);
Point2D offset = (Point2D) circle.getUserData();
circle.setCenterX(e.getX() - offset.getX());
circle.setCenterY(e.getY() - offset.getY());
e.consume(); // prevents MouseEvent from reaching ScrollPane
});
// the zoom-able content of the ScrollPane
Group group = new Group(imageView, circle);
// wrap Group in another Group since it's the former that's scaled and
// Groups only take transformations of their **children** into account (not themselves)
StackPane content = new StackPane(new Group(group));
content.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
// due to later configuration, the StackPane will always cover the entire viewport
content.setOnScroll(
e -> {
if (e.isShortcutDown() && e.getDeltaY() != 0) {
if (e.getDeltaY() < 0) {
group.setScaleX(Math.max(group.getScaleX() - 0.1, 0.5));
} else {
group.setScaleX(Math.min(group.getScaleX() + 0.1, 5.0));
}
group.setScaleY(group.getScaleX());
e.consume(); // prevents ScrollEvent from reaching ScrollPane
}
});
// use StackPane (or some other resizable node) as content since Group is not
// resizable. Note StackPane will center content if smaller than viewport.
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setPannable(true);
// ensure StackPane content always has at least the same dimensions as the viewport
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
primaryStage.setScene(new Scene(scrollPane, 1000, 650));
primaryStage.show();
}
}
Note this does not exactly replicate the behavior of your example. It does not use animations nor does it zoom on a pivot point. But hopefully it can help you move forward in your application.

Multi Pane Mouse Events

We have a requirement to have a "main Node" that when the mouse hovers inside of it another Node ("hover popup Node") appears in the Scene. When the mouse hovers out of the "main Node" then the "hover popup Node" dissapears. Also, the "main Node" has an "inner Node" within that when clicked, another Node ("click popup Node") appears in the Scene and if clicked again the "click popup Node" dissapears.
The problem we have is that, when the mouse hovers inside the "inner Node", the "hover popup Node" dissapears which we do not want. We only want the "hover popup Node" to dissapear when the mouse hovers outside the "main Node". i.e. when the mouse is inside the "inner Node", we still want the "hover popup Node" to be visible.
See sample code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class MultiPaneMouseTest extends Application {
private Rectangle hoverPopupNode;
private Rectangle clickPopupNode;
#Override
public void init() throws Exception {
super.init();
}
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
Rectangle mainNode = new Rectangle(200, 100);
mainNode.setArcWidth(6);
mainNode.setArcHeight(6);
mainNode.setFill(Color.web("0x00ff00"));
mainNode.setOnMouseEntered(e -> {
System.out.println("mouse entered " + e);
hoverPopupNode.setVisible(true);
});
mainNode.setOnMouseExited(e -> {
System.out.println("mouse exited " + e);
hoverPopupNode.setVisible(false);
});
Rectangle innerNode = new Rectangle(50, 25);
innerNode.setArcWidth(6);
innerNode.setArcHeight(6);
innerNode.setFill(Color.web("0xffffff"));
innerNode.setOnMouseClicked(e -> {
System.out.println("mouse clicked " + e);
clickPopupNode.setVisible(!clickPopupNode.isVisible());
});
hoverPopupNode = new Rectangle(100, 100);
hoverPopupNode.setArcWidth(6);
hoverPopupNode.setArcHeight(6);
hoverPopupNode.setFill(Color.web("0x0000ff"));
hoverPopupNode.setVisible(false);
borderPane.setRight(hoverPopupNode);
clickPopupNode = new Rectangle(100, 100);
clickPopupNode.setArcWidth(6);
clickPopupNode.setArcHeight(6);
clickPopupNode.setFill(Color.web("0xff0000"));
clickPopupNode.setVisible(false);
borderPane.setLeft(clickPopupNode);
StackPane stackPane = new StackPane();
stackPane.getChildren().add(mainNode);
stackPane.getChildren().add(innerNode);
borderPane.setCenter(stackPane);
Scene scene = new Scene(borderPane);
primaryStage.setTitle("Sample Test");
primaryStage.setScene(scene);
primaryStage.setX(500.0);
primaryStage.setY(500.0);
primaryStage.setWidth(500.0);
primaryStage.setHeight(500.0);
primaryStage.show();
}
#Override
public void stop() throws Exception {
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}
Make the mainNode a Pane (or suitable subclass) and add the innerNode to it as a child node. If there is a parent-child relationship between mainNode and childNode, then the mouse will still be considered to be hovering over mainNode when it is over innerNode. You can set the background of a Pane (or any region) to achieve the same fill and corner radius effects that you have on the Rectangle.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class MultiPaneMouseTest extends Application {
private Rectangle hoverPopupNode;
private Rectangle clickPopupNode;
#Override
public void init() throws Exception {
super.init();
}
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
StackPane mainNode = new StackPane();
mainNode.setMinSize(200, 100);
mainNode.setMaxSize(200, 100);
mainNode.setBackground(new Background(new BackgroundFill(Color.web("0x00ff00"), new CornerRadii(6), Insets.EMPTY)));
// note you can replace these listeners with
// hoverPopupNode.visibleProperty().bind(mainNode.hoverProperty());
// (but obviously after the hoverPopupNode has been instantiated)
mainNode.setOnMouseEntered(e -> {
System.out.println("mouse entered " + e);
hoverPopupNode.setVisible(true);
});
mainNode.setOnMouseExited(e -> {
System.out.println("mouse exited " + e);
hoverPopupNode.setVisible(false);
});
Rectangle innerNode = new Rectangle(50, 25);
innerNode.setArcWidth(6);
innerNode.setArcHeight(6);
innerNode.setFill(Color.web("0xffffff"));
innerNode.setOnMouseClicked(e -> {
System.out.println("mouse clicked " + e);
clickPopupNode.setVisible(!clickPopupNode.isVisible());
});
mainNode.getChildren().add(innerNode);
hoverPopupNode = new Rectangle(100, 100);
hoverPopupNode.setArcWidth(6);
hoverPopupNode.setArcHeight(6);
hoverPopupNode.setFill(Color.web("0x0000ff"));
hoverPopupNode.setVisible(false);
borderPane.setRight(hoverPopupNode);
clickPopupNode = new Rectangle(100, 100);
clickPopupNode.setArcWidth(6);
clickPopupNode.setArcHeight(6);
clickPopupNode.setFill(Color.web("0xff0000"));
clickPopupNode.setVisible(false);
borderPane.setLeft(clickPopupNode);
StackPane stackPane = new StackPane();
stackPane.getChildren().add(mainNode);
borderPane.setCenter(stackPane);
Scene scene = new Scene(borderPane);
primaryStage.setTitle("Sample Test");
primaryStage.setScene(scene);
primaryStage.setX(500.0);
primaryStage.setY(500.0);
primaryStage.setWidth(500.0);
primaryStage.setHeight(500.0);
primaryStage.show();
}
#Override
public void stop() throws Exception {
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}

How to set Menubutton always on top of other component like HBox (which is draggable)

I have a parent VBox which holds a menu button and a draggable HBox. When I drag the HBox, the menu button is not responding (because the HBox is set over the menu button). How do I always set the menu button on top of the HBox if it is draggable?
HBoxandVBoxExampleupdated.java:
import DraggableNode;
import javafx.scene.chart.NumberAxis;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class HBoxandVBoxExampleupdated extends Application
{
static Pane pane = new Pane();
static DraggableNode node = new DraggableNode();
static NumberAxis noaxis = new NumberAxis();
static String ref = "HHHHHelllelelelellelellelelellelelelaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
static HBox nobox = new HBox();
static NumberAxis lineXAxis;
static String Style = "-fx-border-color: blue;\n"
+ "-fx-border-insets: 5;\n"
+ "-fx-border-width: 3;\n"
+ "-fx-border-style: dashed;\n";
static String Style1 = "-fx-border-color: red;\n"
+ "-fx-border-insets: 5;\n"
+ "-fx-border-width: 3;\n"
+ "-fx-border-style: dashed;\n";
#Override
public void start(Stage primaryStage) throws Exception
{
pane.setStyle(Style);
node.setStyle(Style1);
VBox mainbox = new VBox(80);
mainbox.setAlignment(Pos.CENTER);
mainbox.setPadding(new Insets(50, 30, 100, 50));
VBox hbox = new VBox(60);
hbox.setAlignment(Pos.CENTER); // default TOP_LEFT
HBox vbox1 = new HBox();
HBox vbox2 = new HBox(10);
HBox vbox3 = new HBox(20);
Button close = new Button("X");
Button close1 = new Button("X");
MenuButton vcfmenu = new MenuButton("Vcf");
vcfmenu.getItems().add(new CheckMenuItem("About This Track"));
vcfmenu.getItems().add(new CheckMenuItem("Ping To Tap"));
vcfmenu.getItems().add(new CheckMenuItem("Edit Config"));
vcfmenu.getItems().add(new CheckMenuItem("Delete Track"));
vcfmenu.getItems().add(new CheckMenuItem("Save Track Data"));
vcfmenu.getItems().add(new CheckMenuItem("Show Labels"));
vcfmenu.getItems().add(new CheckMenuItem("Hides Sites Passing All Filters"));
vcfmenu.getItems().add(new CheckMenuItem("Hides Sites not Passing All Filters"));
vbox2.getChildren().add(vcfmenu);
for (String s : ref.split("")) {
Label l = new Label(s);
l.setBorder(new Border(
new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
l.setBackground(
new Background(
new BackgroundFill(
(s.equals("N") ? Color.web("#DDDDDD")
: (s.equals("A") ? Color.web("#00BF00")
: (s.equals("C") ? Color.web("#0099FF")
: (s.equals("T") ? Color.web("#F00")
: Color.web("#D5BB04"))))),
CornerRadii.EMPTY, Insets.EMPTY)));
l.setAlignment(Pos.CENTER);
l.setPadding(new Insets(1, 4, 1, 4));
vbox1.getChildren().add(l);
}
lineXAxis = new NumberAxis(1, ref.length(), 4);
nobox.getChildren().add(lineXAxis);
nobox.setHgrow(lineXAxis, Priority.ALWAYS);
mainbox.getChildren().addAll(vbox2);
hbox.getChildren().addAll(nobox, vbox1);
node.getChildren().add(hbox);
pane.getChildren().addAll(node, mainbox);
Scene scene = new Scene(pane, 1150, 250);
primaryStage.setTitle("HBox and VBox Example");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
DraggableNode.java:
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
public class DraggableNode extends StackPane {
private double x = 0;
private double y = 0;
private double mousex = 0;
private double mousey = 0;
private Node view;
private boolean dragging = false;
private boolean moveToFront = true;
private double size = 0;
private double newSize = 0;
public DraggableNode() {
init();
}
public DraggableNode(Node view) {
this.view = view;
getChildren().add(view);
setMouseTransparent(true);
init();
}
private void init() {
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
getScene().setCursor(Cursor.HAND);
// record the current mouse X and Y position on Node
mousex = event.getSceneX();
mousey = event.getSceneY();
x = getLayoutX();
y = getLayoutY();
if (isMoveToFront()) {
toFront();
}
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - mousex;
x += offsetX;
double scaledX = x;
System.out.println(" : " + scaledX);
if (scaledX > 0)
{
return;
}
setLayoutX(scaledX);
dragging = false;
mousex = event.getSceneX();
event.consume();
}
});
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragging = false;
}
});
}
/**
* #return the dragging
*/
protected boolean isDragging() {
return dragging;
}
/**
* #return the view
*/
public Node getView() {
return view;
}
/**
* #param moveToFront
* the moveToFront to set
*/
public void setMoveToFront(boolean moveToFront) {
this.moveToFront = moveToFront;
}
/**
* #return the moveToFront
*/
public boolean isMoveToFront() {
return moveToFront;
}
public void removeNode(Node n) {
getChildren().remove(n);
}
}
You set the DraggableNode to the front in the EventHandler for onMousePressedProperty. This puts DraggableNode on top of its sibling nodes and prevents the menu button from receiving mouse inputs.
To prevent this, I see two options:
don't set the DraggableNode to the front by setting moveToFront = false or, if that isn't possible,
set the DraggableNode to the back again after dragging by adding
onMouseReleasedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
toBack();
}
});
to the init method of DraggableNode.
For a more general solution, you could add a property to DraggableNode
private BooleanProperty dragInProcessProperty = new SimpleBooleanProperty(false);
public BooleanProperty dragInProcessProperty() {
return this.dragInProcessProperty;
}
Set the property to true while dragging in onMouseDraggedProperty and to false when the mouse is released
onMouseReleasedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragInProcessProperty.set(false);
}
});
and add a ChangeListener to the dragInProgressProperty in HBoxandVBoxExampleupdated
node.dragInProcessProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue.booleanValue()) {
mainbox.toFront();
}
}
});
to set the node to the front whenever dragging is finished.

Resources