I have a 2 Nodes (One Text, the other a Circle) in a Pane, and that pane is within an AnchorPane. The reason why I did this is I wanted to use the automatic scaling property that is a result of resizing the AnchorPane to adjust the size of the Nodes (like stretching and shrinking).
I also have a test background to see if I could scale the background aswell.
My issue is that while the background scales when I resize the window, the Nodes do not.
Heres my code:
package javacode;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundRepeat;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
public boolean debug = false;
Pane stack;
#Override
public void start(Stage stage) {
// Check for updates
/*
System.out.println("Checking for update");
boolean newUpdate = Updater.updateAvalible();
System.out.println("New update avalible: " + newUpdate);
if (newUpdate) {
Updater.showUpdatePrompt();
}
*/
// Get the debug background image
Image debugbackgroundimage = new Image(
this.getClass().getClassLoader().getResourceAsStream("resources/debugbackground.jpg"));
// Create a stack pane to add all the objects into
stack = new Pane();
// Setup the background
stack.setBackground(new Background(new BackgroundImage(debugbackgroundimage, BackgroundRepeat.NO_REPEAT,
BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(
debugbackgroundimage.getWidth(), debugbackgroundimage.getHeight(), false, false, true, true))));
// Set the preferred and minimum sizes for the stack
stack.setPrefSize(debugbackgroundimage.getWidth(), debugbackgroundimage.getHeight());
stack.setMinSize(16, 9);
// We need an anchor pane go help automatically constrain the maximum and
// minimum sizes of things
AnchorPane anchor = new AnchorPane();
AnchorPane.setTopAnchor(stack, 0.0);
AnchorPane.setBottomAnchor(stack, 0.0);
AnchorPane.setLeftAnchor(stack, 0.0);
AnchorPane.setRightAnchor(stack, 0.0);
// Add the stack to the anchor pane
anchor.getChildren().addAll(stack);
// Set the anchor background to a light gray, that way we can check for overlap
anchor.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, CornerRadii.EMPTY, Insets.EMPTY)));
// Set the scene for the visualizer, use the anchor pane defined above
Scene mCatScene = new Scene(anchor);
// Set the contents of the window to that of the scene
stage.setScene(mCatScene);
// Show the window (stage)
stage.show();
// Add a test node to check if it scales properly
Text test = new Text(String.format("Test\nFoo bar"));
test.setFont(new Font("Arial", 15));
test.setWrappingWidth(500);
test.setFill(Color.RED);
test.setLayoutX(0);
test.setLayoutY(50);
Circle test2 = new Circle();
test2.setLayoutX(50);
test2.setLayoutY(120);
test2.setFill(Color.GREEN);
test2.setRadius(10);
stack.getChildren().add(0, test);
stack.getChildren().add(1, test2);
}
public static void main(String[] args) {
Main This = new Main();
// Check if debug mode is enabled (Basically check for console spam enabled)
try {
This.debug = Boolean.parseBoolean(args[0]);
} catch (Exception e) {
This.debug = false;
}
launch(args);
}
}
Here is the background image I used:
Edit: Here is a gif of how the background reacts (which is ideal), vs how the Nodes react (not ideal)
Related
Is it possible with Popup opacity mask top and bottom JavaFX? I have TextField autocomplete with Popup. So the idea is to put an opacity mask.
Below is another way you can give a try, for getting the opacity masked effect. Though it is not exactly the same implementation, I took some ideas from the link you provided :).
I created a small utility where you can pass the Popup instance. The utility builds the mask panes and include to the root node of the Popup.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PopupOpacityMaskDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
root.setStyle("-fx-background-color:grey;");
root.setOnMouseClicked(e -> {
ListView<String> content = new ListView<>();
content.getItems().addAll(IntStream.range(100, 200).mapToObj(i -> i + "").collect(Collectors.toList()));
content.setPrefSize(200, 250);
Popup popup = new Popup();
popup.setAutoHide(true);
popup.getContent().add(content);
popup.setX(e.getScreenX());
popup.setY(e.getScreenY());
popup.show(root.getScene().getWindow());
MaskUtil.applyMask(popup);
});
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Demo");
primaryStage.show();
}
static class MaskUtil{
static void applyMask(Popup popup) {
double fadeSize = 70;
Pane pane = (Pane)popup.getScene().getRoot();
// Build the mask panes
Pane topMask = buildMaskPane(pane, fadeSize, false);
Pane bottomMask = buildMaskPane(pane, fadeSize, true);
// Just ensuring to remove any masks (if you are reusing the Popup)
pane.getChildren().removeAll(pane.lookupAll(".mask"));
pane.getChildren().addAll(topMask, bottomMask);
// Update the bottom mask position by listening to height of pane
pane.heightProperty().addListener((obs, old, h) -> bottomMask.setLayoutY(h.doubleValue() - fadeSize));
if (pane.getHeight() > 0) {
bottomMask.setLayoutY(pane.getHeight() - fadeSize);
}
}
private static Pane buildMaskPane(Pane pane, double fadeSize, boolean isBottom) {
Pane mask = new Pane();
mask.setMouseTransparent(true); // Turn this to 'false' if you don't want to interact over mask
mask.setPrefHeight(fadeSize);
mask.prefWidthProperty().bind(pane.widthProperty());
mask.maxHeightProperty().bind(mask.prefHeightProperty());
mask.minHeightProperty().bind(mask.prefHeightProperty());
mask.getStyleClass().add("mask");
mask.setStyle(String.format("-fx-background-color:linear-gradient(to %s, #555555, transparent)", isBottom ? "top" : "bottom"));
return mask;
}
}
}
Hi I am a JavaFX newbie and I am trying to write my first application. I want to start with an empty window and provide a popup menu that allows users to add 3D elements to the window.
I have created a simple Group containing a few trivial geometric shapes and added this group as the parent to a Scene. I define a mouse event handler for the scene and call setScene to make this the scene for my Stage (passed in to my Application's start method).
Unfortunately, I can't seem to find a way of positioning the menu correctly in response to a mouse pressed event. I get it that I need to get the X and Y coordinates from the Event, but when I pass these unchanged to the context menu show method, the menu appears in the top left-hand corner of my laptop display, rather than inside my application window.
Clearly, I need to offset these values by the origin of some other window, but what? I have tried the Scene, the Group and and the Stage, but with no success :-( This ought to be a trivial problem - where am I going wrong??
Code sample shown below:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;
public class PopupTest extends Application {
private static final ContextMenu contextMenu = new ContextMenu();
public static void main(String[] args) {
MenuItem cut = new MenuItem("Cut");
MenuItem copy = new MenuItem("Copy");
MenuItem paste = new MenuItem("Paste");
contextMenu.getItems().addAll(cut, copy, paste);
Application.launch(args);
}
#Override
public void start(Stage stage) {
// Create a Box
Box box = new Box(100, 100, 100);
box.setTranslateX(150);
box.setTranslateY(0);
box.setTranslateZ(400);
// Create a Sphere
Sphere sphere = new Sphere(50);
sphere.setTranslateX(300);
sphere.setTranslateY(-5);
sphere.setTranslateZ(400);
// Create a Cylinder
Cylinder cylinder = new Cylinder(40, 120);
cylinder.setTranslateX(500);
cylinder.setTranslateY(-25);
cylinder.setTranslateZ(600);
// Create a Light
PointLight light = new PointLight(Color.YELLOW);
light.setTranslateX(350);
light.setTranslateY(100);
light.setTranslateZ(300);
// Create a Camera to view the 3D Shapes
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setTranslateX(100);
camera.setTranslateY(-50);
camera.setTranslateZ(300);
// Add the Shapes and the Light to the Group
Group root = new Group(box, sphere, cylinder, light);
// Create a Scene with depth buffer enabled
Scene scene = new Scene(root, 400, 300, true);
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse click detected!");
if (event.isPopupTrigger()) {
// similar results with getX() vs getSceneX() etc.
System.out.println("Display menu at (" + event.getSceneX() + "," + event.getSceneY() + ")");
contextMenu.show(root, event.getSceneX(), event.getSceneY());
}
}
});
// Add the Camera to the Scene
scene.setCamera(camera);
// Add the Scene to the Stage
stage.setScene(scene);
// Set the Title of the Stage
stage.setTitle("Trying to get popup menu working");
// Display the Stage
stage.show();
}
}
What's the best way to show a semi-transparent "copy" of a Node next to the mouse icon during a drag/drop?
Basically I have HBoxes with colored backgrounds and text labels in them, and I'd like to give the appearance that they "stick" to the mouse cursor when they're being dragged.
It's nice if users can visually verify WHAT they're dragging, rather than just seeing the mouse cursor change into the various drag icons. Scene Builder tends to do this when you drag some components, like a RadioButton.
The "semi-transparent "copy" of a Node" is accomplished by calling snapshot(null, null) on a node, which returns a WritableImage. Then you set this WritableImage as the drag view of the DragBoard. Here is a small example on how to do this:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DragAndDrop extends Application {
private static final DataFormat DRAGGABLE_HBOX_TYPE = new DataFormat("draggable-hbox");
#Override
public void start(Stage stage) {
VBox content = new VBox(5);
for (int i = 0; i < 10; i++) {
Label label = new Label("Test drag");
DraggableHBox box = new DraggableHBox();
box.getChildren().add(label);
content.getChildren().add(box);
}
stage.setScene(new Scene(content));
stage.show();
}
class DraggableHBox extends HBox {
public DraggableHBox() {
this.setOnDragDetected(e -> {
Dragboard db = this.startDragAndDrop(TransferMode.MOVE);
// This is where the magic happens, you take a snapshot of the HBox.
db.setDragView(this.snapshot(null, null));
// The DragView wont be displayed unless we set the content of the dragboard as well.
// Here you probably want to do more meaningful stuff than adding an empty String to the content.
ClipboardContent content = new ClipboardContent();
content.put(DRAGGABLE_HBOX_TYPE, "");
db.setContent(content);
e.consume();
});
}
}
public static void main(String[] args) {
launch();
}
}
I want to simulate perspective distortion of boxes and rectangles. My goal is to warp the box and image, as if the camera is being tilted and moved. But I don't really follow how to use the PerspectiveCamera.
When I set the fixedEyeAtCameraZero to false, I can see the box and image on screen. But changing the window size causes weird orthographic perspective changes that aren't realistic.
On the other hand when I set fixedEyeAtCameraZero to true, all I see is a blank window.
False:
True:
Here's the code, with the offending flag at line 51.
package sample;
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class Example_Box extends Application {
#Override
public void start(Stage stage) {
//Drawing a Box
Box box2 = new Box();
//Setting the properties of the Box
box2.setWidth(100.0);
box2.setHeight(100.0);
box2.setDepth(100.0);
//Setting the position of the box
box2.setTranslateX(30); //450
box2.setTranslateY(90);//150
box2.setTranslateZ(300);
//Setting the drawing mode of the box
box2.setDrawMode(DrawMode.LINE);
//Drawing an Image
Image image = new Image("Lenna.png");
ImageView imageView = new ImageView(image);
imageView.setTranslateX(200);
imageView.setTranslateY(150);
imageView.setTranslateZ(200);
//imageView.getTransforms().add(new Rotate(30, 50, 30));
//Creating a Group object
Group root = new Group(box2, imageView);
//Creating a scene object
Scene scene = new Scene(root, 600, 300);
//Setting camera
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateX(30);
camera.setTranslateY(0);
camera.setTranslateZ(-100);
camera.setRotationAxis(new Point3D(1,0,0));
scene.setCamera(camera);
//Setting title to the Stage
stage.setTitle("Drawing a Box");
//Adding scene to the stage
stage.setScene(scene);
//Displaying the contents of the stage
stage.show();
}
public static void main(String args[]){
launch(args);
}
}
Try to change the farClip value. By default farClip value is 100 in FX perspective camera.
camera.setFarClip(2000.0);
With above piece code addition, I could see the Box. If I move the camera further away(in Z direction), I could see Image also,
camera.setTranslateZ(-1000);
Reference: http://www.dummies.com/programming/java/javafx-add-a-perspective-camera/
When I translate a node outside of the bounds of it's parent. The minimum size of the parent of the parent is set to it's current size. You can see it with this demo:
package com.neonorb.test;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.io.IOException;
/**
* Created by chris on 7/20/15.
*/
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
Label label = new Label("translating label");
Label markerLabel = new Label("marker label");
Button button = new Button("button");
VBox leftSpace = new VBox();
Label leftLabel = new Label("left space");
leftSpace.getChildren().add(leftLabel);
Rectangle rectangle = new Rectangle();
rectangle.setFill(Color.RED);
rectangle.heightProperty().bind(leftSpace.heightProperty());
rectangle.widthProperty().bind(leftSpace.widthProperty());
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
new Thread() {
public void run() {
Platform.runLater(() -> label.setTranslateY(1000.0));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> label.setTranslateY(0.0));
}
}.start();
}
});
BorderPane borderPane = new BorderPane();
BorderPane center = new BorderPane();
center.setCenter(label);
center.setBottom(markerLabel);
borderPane.setCenter(center);
borderPane.setTop(button);
borderPane.setLeft(leftSpace);
borderPane.setRight(rectangle);
primaryStage.setScene(new Scene(borderPane));
primaryStage.show();
}
}
The reason for the side bar things (the VBox and Rectangle) is because they exist in my real application. The VBox just holds more content, and the Rectangle is there to keep the center components centered (normally transparent, but here it is colored for visibility). As you can see, the width and height of the rectangle are binded to the VBox's height:
rectangle.heightProperty().bind(leftSpace.heightProperty());
rectangle.widthProperty().bind(leftSpace.widthProperty());
To reproduce the problem, you can increase the height of the window a little (about an inch), then hit the button. The node will be translated down 1000 pixels and back. Now try to shrink the window, the text at the bottom, ("marker label"), will start to be hidden by the bottom of the window.
I fixed it by using a Region instead of a Rectangle and setting it's preferred size.