JavaFX translateZ for a non-root node causes it to disappear - javafx

I'm trying to use the translateZ property on a VBox to move the panel "into the screen".
If I use setTranslateZ() on the root node this works fine. However if I change root.setTranslateZ(200); to panel.setTranslateZ(200); the window is blank.
public class Demo01HelloWorld3D extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button button = new Button("Press me");
VBox panel = new VBox(button);
panel.setAlignment(Pos.CENTER);
panel.setDepthTest(DepthTest.ENABLE);
VBox root = new VBox(panel);
root.setAlignment(Pos.CENTER);
root.setDepthTest(DepthTest.ENABLE);
root.setTranslateZ(200);
// panel.setTranslateZ(200); <== I want this to work
Scene scene = new Scene(root, 320, 240, true);
primaryStage.setScene(scene);
scene.setCamera(new PerspectiveCamera(false));
primaryStage.show();
}
}
Setting translateZ on the root node
Setting translateZ on the panel node
Things I've tried
Setting depthTest attribute to enable - although I don't think this is necessary as it defaults to DepthTest.INHERIT
Lots of searching for similar questions!
Checking SCENE3D is enabled - yes it is
Checking the javadoc for translateZProperty
checked Z value is less than camera clippingFar property
Looked at Oracle JavaFX 3D tutorial - this does not specifically address 3D with standard controls and containers etc.

With panel.setTranslateZ(200); you're pushing the panel behind the root, so the root obscures it.
Add root.setStyle("-fx-background-color: transparent;"); and it works:
#Override
public void start(Stage primaryStage) throws Exception {
Button button = new Button("Press me");
VBox panel = new VBox(button);
panel.setAlignment(Pos.CENTER);
panel.setDepthTest(DepthTest.ENABLE);
VBox root = new VBox(panel);
root.setAlignment(Pos.CENTER);
root.setDepthTest(DepthTest.ENABLE);
root.setStyle("-fx-background-color: transparent;");
panel.setTranslateZ(200);
Scene scene = new Scene(root, 320, 240, true);
primaryStage.setScene(scene);
scene.setCamera(new PerspectiveCamera(false));
primaryStage.show();
}

Related

How to make a GridPane right of BorderPane closeable?

I have code in the form:
<BorderPane>
...
<right>
<GridPane>
...
</GridPane>
</right>
...
</BorderPane>
Obviously, now the GridPane takes a big space right of my BorderPane. What I'd like to do is add a button (or another element) that minimizes and maximizes the GridPane, so it's only fully in the view of the user when it is really needed. How can I easily achieve this?
You can do what you want by setting the Visible and Managed properties of your GridPane off and on. The centre of the BorderPane will automatically expand to take over the entire width of the BorderPane. "Managed" controls whether or not the layout manager will leave space for the node, so if you just turn Visible off, then you'll have an unused area the size of your GridPane on the right. The following code demonstrates it, I put the buttons in a VBox with a border around it so that you can see how it expands:
public class ResizeRight extends BorderPane {
public ResizeRight() {
Button openButton = new Button("Open");
Button closeButton = new Button("Close");
GridPane gridPane = new GridPane();
gridPane.addRow(0, new Text("This is just some text"));
gridPane.addRow(1, new Text("This is just some more text"));
VBox vbox = new VBox(10, openButton, closeButton);
vbox.setBorder(new Border(new BorderStroke(Color.BLACK,
BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
setCenter(vbox);
setRight(gridPane);
setPadding(new Insets(10));
openButton.setOnAction(evt -> {
gridPane.setVisible(true);
gridPane.setManaged(true);
});
closeButton.setOnAction(evt -> {
gridPane.setVisible(false);
gridPane.setManaged(false);
});
}
}
Run it from something like this:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new ResizeRight(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}

JavaFX listener to a visible part of VBox

I have a JavaFX VBox inside a BorderPane (central). The content of the VBox is calculated using some business logic and it depends on the height of the visible part of the vbox.
So basically I need a listener watching changes of the visible height of the vbox = height of the central part of the border pane.
The following code demonstrates what I have tried:
public class HelloFX extends Application {
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
vbox.boundsInParentProperty()
.addListener((obs, oldValue, newValue) ->
System.out.println(newValue.getHeight()));
Button button = new Button("ADD LINE");
button.setPrefHeight(25);
button.setOnAction(event ->
vbox.getChildren().add(new Label("line")));
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
borderPane.setTop(button);
Scene scene = new Scene(borderPane, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
BorderPane with simple button on the top position and VBox on the central. The button click adds one line to vBox. Total scene height is 100, 25 is the button height and the rest (75) is the vBox.
I'm looking for some listener to report changes of the height of the central part of border pane. So in my example it should always print "75" no matter how many lines I have added to the vBox. The only event changing the value should be resizing the whole window. In reality once the vBox is filled my listener reports increasing height values. Apparently the height property includes the invisible part of the vbox.
EDIT
Finally I've found some solution - placing the vBox in the ScrollPane with disabled scrollbars. Then I can simply listen on the height property of the scrollpane and everything works as expected.
public class HelloFX extends Application {
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
ScrollPane scrollPane = new ScrollPane();
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setContent(vbox);
scrollPane.heightProperty()
.addListener((obs, oldValue, newValue) ->
System.out.println(newValue));
Button button = new Button("ADD LINE");
button.setPrefHeight(25);
button.setOnAction(event ->
vbox.getChildren().add(new Label("line")));
BorderPane borderPane = new BorderPane();
borderPane.setCenter(scrollPane);
borderPane.setTop(button);
Scene scene = new Scene(borderPane, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}

How to disable right click on a menu in javafx

In javaFX code, a menu can popup by left click or right click. How to disable right click?
public void start(Stage primaryStage)
{
BorderPane root = new BorderPane();
MenuBar menuBar = new MenuBar();
Menu hello = new Menu("hello");
menuBar.getMenus().addAll(hello);
Menu world = new Menu("world");
menuBar.getMenus().addAll(world);
root.setCenter(menuBar);
MenuItem item = new MenuItem("laugh");
hello.getItems().add(item);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
When I right click the "hello" menu, it will popup menuitem "laugh".
The basic approach is to register a eventFilter on the MenuBar that consumes the events that should not be delivered to the children.
Doing so manually in your application code:
public class DisableRightClickOpenMenu extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
MenuBar menuBar = new MenuBar();
menuBar.addEventFilter(MouseEvent.MOUSE_PRESSED, ev -> {
if (ev.getButton() == MouseButton.SECONDARY) {
ev.consume();
}
});
Menu hello = new Menu("hello");
menuBar.getMenus().addAll(hello);
Menu world = new Menu("world");
menuBar.getMenus().addAll(world);
root.setCenter(menuBar);
MenuItem item = new MenuItem("laugh");
hello.getItems().add(item);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you want this behaviour across all your applications, you can implement a custom menuBarSkin that registers the filter and install the custom skin via a stylesheet.
The skin:
public class ExMenuBarSkin extends MenuBarSkin {
/**
* Instantiates a skin for the given MenuBar. Registers an
* event filter that consumes right mouse press.
*
* #param menuBar
*/
public ExMenuBarSkin(MenuBar menuBar) {
super(menuBar);
menuBar.addEventFilter(MouseEvent.MOUSE_PRESSED, ev -> {
if (ev.getButton() == MouseButton.SECONDARY) {
ev.consume();
}
});
}
}
In your stylesheet (replace with your fully qualified class name):
.menu-bar {
-fx-skin: "de.swingempire.fx.event.ExMenuBarSkin";
}
Its usage (replace the name with your stylesheet file name):
URL uri = getClass().getResource("contextskin.css");
primaryStage.getScene().getStylesheets().add(uri.toExternalForm());
This is usual behavior of menu in many programs. I don't think you can change it. However, you can use some other controls and simulate menu. (Like HBox and Labels).
I agree as far as I know there's no a standard way to do this, but you may want to consider the following workaround.
It is replacing the Menu node with a Menu object composed by an HBox and a Label: an EventHandler is added to the HBox and by checking the mouse button pressed we add/remove on the fly the MenuItem to its parent.
#Override
public void start(final Stage primaryStage) {
final BorderPane root = new BorderPane();
final MenuBar menuBar = new MenuBar();
final Menu menuHello = new Menu();
final Menu menuWorld = new Menu("world");
final MenuItem menuitem = new MenuItem("laugh");
final HBox hbox = new HBox();
menuBar.getMenus().addAll(menuHello, menuWorld);
root.setCenter(menuBar);
hbox.setPrefWidth(30);
hbox.getChildren().add(new Label("hello"));
menuHello.setGraphic(hbox);
menuHello.getItems().add(menuitem);
hbox.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
System.out.println("Right click");
menuHello.getItems().remove(menuitem);
} else {
System.out.println("Left click");
if (!menuHello.getItems().contains(menuitem)) {
menuHello.getItems().add(menuitem);
menuHello.show(); // The .show method prevent 'losing' the current click }
}
}
});
final Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
This will produce the following result - preview
Note that I've used an HBox just for habit, there's no a particular reason.
While using a workaround like this, my suggestion would be to fill all the Menus with the same 'pattern', such as the HBox + Label combo in my example, and stylize them via css/code (width/height, background/fill/hover... colors etc.) in order to have them as uniform as possible and avoid creating graphic inconsistencies due to have different nodes types in the same menubar.

How to make pane and elements in it resizable when stage size is changing

Can you please tell me how can I realize that the whole content of a pane will be resized while the stage is resized with mousedragg. Here is my code:
public class fab extends Application {
private Stage stage;
private Pane pane;
private Scene scene;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
Button button = new Button("Button");
pane = new Pane();
pane.getChildren().add(button);
stage.setTitle("Test");
scene = new Scene(pane, 640, 640);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
I think there is the idea of binding. But I don't know how to use that, in order to make all nodes of a pane resizable, when the stage size is changing.
I'm searching a solution without Fxml or sceneBuilder.
Thank you in advance.
If you insists to use the Pane container then after the line scene = new Scene(pane, 640, 640); add this:
scene.widthProperty().addListener((c,o,n)->button.setPrefWidth((Double)n));
scene.heightProperty().addListener((c,o,n)->button.setPrefHeight((Double)n));
and after the line stage.setScene(scene); add this:
button.setPrefSize(scene.getWidth(), scene.getHeight());
This works fine with Pane and do your required thing.
But I prefer using an AnchorPane container and set the Top, Right,Bottom and Left anchors to 0 .
Here is the solution if you wish to bind the width of the button to you scene width
button.minWidthProperty().bind(scene.widthProperty());
You can also modify this +/- whatever you want for ex
button.minWidthProperty().bind(scene.widthProperty().subtract(20));
and you can do the same for the height
button.minHeightProperty().bind(scene.heightProperty().subtract(200));

How to create own window rather then use default window

I've been working on javafx and i want to remove default windows and create a window with my style
It's quite easy to create a window in javafx. To create your own window you need to modify the style of your stage which can be done using initStyle() method.
public class Test extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createParent(), Color.TRANSPARENT);
primaryStage.initStyle(StageStyle.TRANSPARENT);
// primaryStage.initStyle(StageStyle.UNDECORATED);
// primaryStage.initStyle(StageStyle.DECORATED);
primaryStage.setTitle("My Own Window");
primaryStage.setScene(scene);
primaryStage.show();
}
private Parent createParent() {
Pane rootPane = new Pane();
rootPane.setPrefSize(1000,400);
Button btn = new Button("RandomButton");
btn.setOnAction(e -> Platform.exit());
rootPane.getChildren().add(btn);
return rootPane;
}
}

Resources