I am trying to make a group which contains Rectangle and Text objects to scale up and down using the ScrollEvent. I am having trouble getting the correct scale to use from the delta of the scroll. The following code is what I have at the moment. Any help will be appreciated.
Thanks
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
// demonstrates scaling a test pane with content in it.
public class ScaleTest extends Application {
Group page;
double textScale =1.0;
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("scale test");
Group root = new Group();
page= new Group();
Rectangle r = new Rectangle(300,300);
Text t = new Text("asodjoijdjiasjdoijaisjdijasijdiajsidjoiasjiodjiajosijdiojaisjdoijaoisjdiojaoisjdjaoisjdiojsoidj\njdoasijdoajsoidjoaisjoidjaoisjdiojs");
t.setFill(Color.FLORALWHITE);
t.setWrappingWidth(280);
t.setX(10);
t.setY(20);
page.getChildren().addAll(r,t);
root.getChildren().add(page);
Scene scene = new Scene(root,800,600);
this.setSceneEvents(scene);
stage.setScene(scene);
stage.show();
}
private void setSceneEvents(final Scene scene) {
//handles mouse scrolling
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double xscale = page.getScaleX() * event.getDeltaY()/35;
double yscale= page.getScaleY() * event.getDeltaY()/35;
System.out.println("xscale: "+ xscale);
System.out.println("yscale: "+ yscale);
//page.setScaleX(page.getScaleX() * event.getDeltaY()/35);
// page.setScaleY(page.getScaleY() * event.getDeltaY()/35);
event.consume();
}
});
}
}
|deltaY| is for all scroll events the same. Also note that deltaY is negative for scrolling down, and the scaling always is normalized at 1.0. So i suggest to use a "zoom factor" which determines the scaling. Try something like:
private void setSceneEvents(final Scene scene) {
//handles mouse scrolling
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0){
zoomFactor = 2.0 - zoomFactor;
}
System.out.println(zoomFactor);
page.setScaleX(page.getScaleX() * zoomFactor);
page.setScaleY(page.getScaleY() * zoomFactor);
event.consume();
}
});
}
Related
Im starting to learn JavaFX. I have made a simple program that creates shapes.
I want to do basic events driven execution method, such as:
print the coordinate of the cursor if its inside a shape
changing the colour everytime the cursor enters a shape, then changing it back to the original once it is out of the shape
the following is what I have done
import javafx.application.Application;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Polygon;
public class ColouredShapes extends Application {
#Override
public void start(Stage stage) {
int radius = 40;
int strokeWidth = 3;
Color strokeColor = Color.BROWN;
Circle circle1 = new Circle(250, 125, radius, Color.RED);
circle1.setStroke(strokeColor);
circle1.setStrokeWidth(strokeWidth);
Rectangle square = new Rectangle(375, 85, 80, 80);
square.setFill(Color.BLUE);
square.setStroke(strokeColor);
square.setStrokeWidth(strokeWidth);
Polygon polygon = new Polygon();
polygon.getPoints().addAll(new Double[]{
50.0, 85.0,
110.0, 60.0, 125.0, 175.0});
polygon.setFill(Color.YELLOWGREEN);
Group root = new Group();
root.getChildren().addAll(circle1, square, polygon);
Scene scene = new Scene(root, 500, 250, Color.LIGHTYELLOW);
stage.setScene(scene);
stage.show();
}
public static void maint(String[] args){
launch(args);
}
}
Add event mouse entered, exited, and moved handlers to the shape
square.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent event) {
System.out.println(event.getScreenX());
System.out.println(event.getScreenY());
}
});
square.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent event) {
square.setFill(Color.GREEN);
}
});
square.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent event) {
square.setFill(null);
}
});
I want a cursor that displays the coordinates of the place the mouse is pointing to, besides the pointer itself (it should look like a crosshair with little numbers is bottom right corner, that change when the mouse is moving). How to achive such effect efficiently? I tried to use the tooltip mechanism, but then the text lags behind the cursor a lot...
You could attach a label, and change the text inside the mouse moved event everytime that the mouse coordinates changes.
You can create a Text object with the current coordinates, snapshot it to an Image, and then create a WritableImage from that and any other cursor decoration you need. Then wrap the WritableImage in an ImageCursor:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MouseCoordinateCursor extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
root.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
root.setCursor(Cursor.DEFAULT);
}
});
root.setOnMouseMoved(new EventHandler<MouseEvent>() {
final int padding = 9 ;
final int offset = 6 ;
#Override
public void handle(MouseEvent e) {
Text text = new Text(String.format("[%.1f,%.1f]", e.getX(), e.getY()));
Image textImage = text.snapshot(null, null);
int width = (int)textImage.getWidth();
int height = (int)textImage.getHeight();
WritableImage cursorImage = new WritableImage(width + offset, height + offset);
cursorImage.getPixelWriter().setPixels(offset, offset, width, height, textImage.getPixelReader(), 0, 0);
for (int i = 0; i < padding; i++) {
cursorImage.getPixelWriter().setColor(i, padding/2, Color.BLACK);
cursorImage.getPixelWriter().setColor(padding/2, i, Color.BLACK);
}
root.setCursor(new ImageCursor(cursorImage));
}
});
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a JavaFX 3D scene with a bunch of boxes and spheres added at random locations. It seems like the depth order is all wrong and I'm not sure why. I have tried to use
myNode.setDepthTest(DepthTest.ENABLE) but that doesn't seem to help. I've attached an application which should demonstrate the problem.
Any idea what I might be doing wrong here? Any help much appreciated.
import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class Array3DTest extends Application {
double mousePosX;
double mousePosY;
double mouseOldX;
double mouseOldY;
double mouseDeltaX;
double mouseDeltaY;
/**
* This is the group which rotates
*/
Group root3D;
/**
* The camnera to
*/
private Rotate rotateY;
private Rotate rotateX;
private Translate translate;
public Array3DTest( ){
}
public Group createScene(){
// Create and position camera
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(15000);
camera.setNearClip(0.1);
camera.setDepthTest(DepthTest.ENABLE);
camera.getTransforms().addAll(
rotateY=new Rotate(0, Rotate.Y_AXIS),
rotateX=new Rotate(0, Rotate.X_AXIS),
translate=new Translate(250, 250, -1000));
root3D=new Group();
root3D.getChildren().add(camera);
root3D.setDepthTest(DepthTest.ENABLE);
final PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setSpecularColor(Color.ORANGE);
redMaterial.setDiffuseColor(Color.RED);
for (int i=0; i<50; i++){
Shape3D mySphere;
if (i%2==0) mySphere = new Box(100,100, 100);
else mySphere= new Sphere(30);
mySphere.setTranslateX(Math.random()*500);
mySphere.setTranslateY(Math.random()*500);
mySphere.setTranslateZ(Math.random()*200);
mySphere.setMaterial(redMaterial);
mySphere.setDepthTest(DepthTest.ENABLE);
root3D.getChildren().add(mySphere);
}
// Use a SubScene to mix 3D and 3D stuff.
SubScene subScene = new SubScene(root3D, 500,500);
subScene.setFill(Color.WHITE);
subScene.setCamera(camera);
subScene.setDepthTest(DepthTest.ENABLE);
Group group = new Group();
group.getChildren().add(subScene);
handleMouse(subScene);
return group;
}
private void handleMouse(SubScene scene) {
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent me) {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
}
});
scene.setOnScroll(new EventHandler<ScrollEvent>() {
#Override public void handle(ScrollEvent event) {
System.out.println("Scroll Event: "+event.getDeltaX() + " "+event.getDeltaY());
translate.setZ(translate.getZ()+ event.getDeltaY() *0.001*translate.getZ()); // +
}
});
scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
double modifier = 1.0;
double modifierFactor = 0.1;
if (me.isControlDown()) {
modifier = 0.1;
}
if (me.isShiftDown()) {
modifier = 10.0;
}
if (me.isPrimaryButtonDown()) {
rotateY.setAngle(rotateY.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0); // +
rotateX.setAngle(rotateX.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0); // -
}
if (me.isSecondaryButtonDown()) {
translate.setX(translate.getX() -mouseDeltaX * modifierFactor * modifier * 5);
translate.setY(translate.getY() - mouseDeltaY * modifierFactor * modifier * 5); // +
}
}
});
}
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println(
"3D supported? " +
Platform.isSupported(ConditionalFeature.SCENE3D)
);
Group group=createScene();
primaryStage.setResizable(false);
Scene scene = new Scene(group, 500,500, true);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Since you are using a SubScene object, it's convenient that you enable deep buffer like you already do for the Scene. Also, enabling anti-aliasing will help too.
According to JavaDoc:
The depthBuffer and antiAliasing flags are conditional features. With the respective default values of: false and SceneAntialiasing.DISABLED.
So just use the other constructor for SubScene:
SubScene subScene = new SubScene(root3D, 500, 500, true, SceneAntialiasing.BALANCED);
and you'll notice the difference.
I have this JavaFX application that lets you plot locations on a map and connect them.
I do this by drawing a map as a background image on a canvas and then drawing circles and lines on it. I have made the circles clickable by using the contains() method in the Circle class, but how can I make the lines clickable?
edit: Look at this example where I just draw a line and set an event handler:
Canvas canvas = new Canvas();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setLineWidth(5);
gc.strokeLine(100, 100, 200, 200);
canvas.setOnMouseClicked(event -> {
double x = event.getX(), y = event.getY();
});
My question is simply this: how do I finish the event handler so that it detects if the click is inside the line I just drew?
You should create a canvas and add the nodes (Circle, Line, etc) to it. Then you add mouse listeners to the nodes.
Example:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
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.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class DragNodes extends Application {
public static List<Circle> circles = new ArrayList<Circle>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Canvas canvas = new Canvas(300, 300);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
Circle circle1 = new Circle(50);
circle1.setStroke(Color.GREEN);
circle1.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.7));
circle1.relocate(100, 100);
Circle circle2 = new Circle(50);
circle2.setStroke(Color.BLUE);
circle2.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.7));
circle2.relocate(200, 200);
Line line = new Line(circle1.getLayoutX(), circle1.getLayoutY(), circle2.getLayoutX(), circle2.getLayoutY());
line.setStrokeWidth(20);
Pane overlay = new Pane();
overlay.getChildren().addAll(circle1, circle2, line);
MouseGestures mg = new MouseGestures();
mg.makeDraggable(circle1);
mg.makeDraggable(circle2);
mg.makeDraggable(line);
root.getChildren().addAll(canvas, overlay);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
private void drawShapes(GraphicsContext gc) {
gc.setStroke(Color.RED);
gc.strokeRoundRect(10, 10, 230, 230, 10, 10);
}
public static class MouseGestures {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public void makeDraggable(Node node) {
node.setOnMousePressed(circleOnMousePressedEventHandler);
node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
orgTranslateX = p.getCenterX();
orgTranslateY = p.getCenterY();
} else {
Node p = ((Node) (t.getSource()));
orgTranslateX = p.getTranslateX();
orgTranslateY = p.getTranslateY();
}
}
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
p.setCenterX(newTranslateX);
p.setCenterY(newTranslateY);
} else {
Node p = ((Node) (t.getSource()));
p.setTranslateX(newTranslateX);
p.setTranslateY(newTranslateY);
}
}
};
}
}
I create a new Stage in the Code and it's needed, that the Stage is undecorated! In this case the new created Stage loses the Drag-Function.
I created the following code, now i can drag the Stage, but the handling is incorrect and the Stage follows not in a correct way the Mouse.
I hope you can tell me my mistake and fix my error.
Thanks for your help.
EXAMPLE - CODE:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuBar;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class DragStuff extends Application{
VBox mainContainer;
Stage mainSt;
#Override
public void start(Stage mainStage) throws Exception {
// TODO Auto-generated method stub
mainStage.initStyle(StageStyle.UNDECORATED);
mainSt = mainStage;
mainContainer = new VBox();
mainContainer.setStyle("-fx-background-color:red");
Label headlineInformation = new Label("Testing");
headlineInformation.getStyleClass().addAll("popup-label-name");
headlineInformation.setMaxWidth(Double.MAX_VALUE);
Button closeButton = new Button("X");
closeButton.setVisible(true);
closeButton.getStyleClass().addAll("popup-button",
"popup-button-color");
HBox headContainer = new HBox();
HBox.setHgrow(headlineInformation, Priority.ALWAYS);
headContainer.setPadding(new Insets(5, 5, 5, 5));
headContainer.getChildren().addAll(headlineInformation, closeButton);
Pane pane = new Pane();
pane.getChildren().addAll(new Label("Text Stuff"));
mainContainer.getChildren().addAll(headContainer,pane);
Scene sc = new Scene(mainContainer);
mainStage.setScene(sc);
mainStage.show();
dragHandling();
}
public void dragHandling()
{
final ObjectProperty<Point2D> mouseKoordinates = new SimpleObjectProperty<>();
mainContainer.setOnMousePressed(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
mouseKoordinates.set(new Point2D(event.getSceneX(), event.getSceneY()));
mainContainer.getScene().setCursor(Cursor.HAND);
};
});
mainContainer.setOnMouseReleased(new EventHandler<MouseEvent>()
{
#Override
public void handle(final MouseEvent arg0)
{
mouseKoordinates.set(null);
mainContainer.getScene().setCursor(Cursor.DEFAULT);
}
});
mainContainer.setOnMouseDragged(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
if (mouseKoordinates.get() != null)
{
double x = event.getScreenX();
double deltaX = x - mouseKoordinates.get().getX();
double y = event.getScreenY();
double deltaY = y - mouseKoordinates.get().getY();
mainSt.setX(mainSt.getX() + deltaX);
mainSt.setY(mainSt.getY() + deltaY);
mouseKoordinates.set(new Point2D(x, y));
}
}
});
}
public static void main(String[] args) {
launch(args);
}
}
You must not use getScreenX() and getScreenY() in your code as they return the absolute positions. In place of them try using getX() and getY().
Moreover, I don't understand why are you using mouseKoordinates.set(new Point2D(x, y)); as well.
Try using the following code
mainContainer.setOnMouseDragged(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
if (mouseKoordinates.get() != null)
{
double x = event.getX();
double deltaX = x - mouseKoordinates.get().getX();
double y = event.getY();
double deltaY = y - mouseKoordinates.get().getY();
mainSt.setX(mainSt.getX() + deltaX);
mainSt.setY(mainSt.getY() + deltaY);
}
}
});