I have a Pane and a VBox inside of a StackPane. I added the Pane first and the VBox on top of it. The VBox includes several kes that in turn have Buttons as children. I use the normal Pane as a "canvas" to position Lines on it. The Lines as well as the Buttons need to be interactive. So by clicking on them they shall for example change their color.
But at the moment the Pane and its Line objects are shown but they are covered by the VBox so I can not interact with them but only with the Buttons.
How can I provide that I can interact with the Line as well, though they are in a lower layer of the StackPane?
You can set the pickOnBoundsProperty of your container Nodes (VBox and HBox) to false.
Defines how the picking computation is done for this node when
triggered by a MouseEvent or a contains function call. If pickOnBounds
is true, then picking is computed by intersecting with the bounds of
this node, else picking is computed by intersecting with the geometric
shape of this node.
As result, "transparent" areas of the HBox and the VBox will not register the click event.
Example:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Pane pane = new Pane();
pane.setStyle("-fx-background-color:red;");
StackPane sp = new StackPane();
VBox vbox = new VBox();
HBox hbox = new HBox();
hbox.setSpacing(30);
for (int i = 0; i< 5; i++) {
Button b = new Button("Button");
b.setOnAction(e -> System.out.println("Button clicked"));
hbox.getChildren().add(b);
}
vbox.getChildren().add(hbox);
sp.getChildren().addAll(pane, vbox);
Line line = new Line(10, 10, 500, 10);
line.setStrokeWidth(3);
pane.getChildren().add(line);
line.setOnMouseClicked(e -> {
System.out.println("Line Clicked!");
});
// Set pickOnBounds to vbox and hbox
vbox.setPickOnBounds(false);
hbox.setPickOnBounds(false);
root.setCenter(sp);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
They are covered cause the VBOX is in front of the Pane.
First Way:
You can setVisible(false) the VBox so the Pane can be accessible and then setVisible(true) the VBox again.
Second Way:
You can use methods called toBack(); and toFront(); and bring a Node back or front to the hierarchy:
vBox.toBack(); //the the vBox goes back to the hierarchy,it is like zOrder in html
and then use:
vBox.toFront(); //to bring the vBox again in front.
Finally:
You can somewhere provide a ToggleButton that when is pressed the VBox is appearing and when is not pressed the VBox is disappeared.
Related
So I want to do a title Menu for a video game project for college. I want to display a message press any key to continue... then when the user pressed any key, including the mouse, a method would run to set the stage to the next menu.
I posted all the relevant code bellow but short version is:
BackgroundImangeDisplayPane extends display pane and adds a background image.
TitleCard extends BackgroundImangeDisplayPane adds a VBox and 2 labels to the VBox
I'm using public void start(Stage primaryStage) throws Exception as the main, I set the setOnActionxxx methods here
I have tried using the set on action method on root and Vbox and non of them work... when I click nothing happens... But when I resize the window The root.setOnActionXXX "activates".
If I write the setOnAction methods on the TitleCard class It kind of works but then I cant switch the stage.
I will post the code bellow as well an explanation of the Scene structure its not to complicated:
// this will be the borderpane for every scene it recives a backgund
//images that will be present in every menu
public BackgroundImangeDisplayPane() {
try {
stream = new FileInputStream(imagePath.toString());
Image image = new Image(stream);
ImageView imageView = new ImageView();
imageView.setImage(image);
imageView.setFitWidth(1920);
imageView.setPreserveRatio(true);
this.getChildren().add(imageView);
BackgroundSize backgoundSize = new BackgroundSize(AUTO, AUTO, true, true, true, true);
BackgroundImage backgroundImage = new BackgroundImage(image, NO_REPEAT, NO_REPEAT, CENTER, backgoundSize);
Background background = new Background(backgroundImage);
this.setBackground(background);
} catch (Exception e) {
}
}
//This extends `BackgroundImangeDisplayPane` and places on top of it a A Vbox with two lables: the title and "press any key to continue..."
// it then adds styles to the labels
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
}
...
//Works as the "main" in javaFX
private Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
/*BasicMenu menu = new BasicMenu(5);
menu.ButtonSetOnAction(0, e -> changeScene() );
BackgroundImangeWithCustomMenu background = new
BackgroundImangeWithCustomMenu(menu,50,50);
root.setCenter(background);*/
Button b = new Button();
b.setOnAction(e -> changeSceneToLoginMenu());
System.out.println(root.getChildren().get(1).getClass());
root.getChildren().get(1).setFocusTraversable(true);
root.getChildren().get(1).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(1).setOnKeyPressed(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnKeyPressed(e -> changeSceneToLoginMenu());
/*
root.setOnMouseClicked(e -> changeSceneToLoginMenu());
root.setOnKeyReleased(e -> changeSceneToLoginMenu());
root.setOnKeyPressed(e -> changeSceneToLoginMenu());
*/
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
As proposed by James in comments, when a key is pressed on the scene, navigate to the next scene (or replace the root in the current scene, and remove the key press handler).
scene.setOnKeyPressed(e -> navigateToNextScene());
I managed to find a very simple working solution but I don't fully undestand why it does work. I Noticed that if I set the handler in the same class the node was instanciated the handler would work fine But if I tried to get the node with a method to the main fuction via root.getChildren().get(1) and then cast it to the VBox element the handler would not work.
As a solution I made the VBox a field and wrote a setter method for the VBox event Handler in the TitleCard Class. This fixed the problem.
I marked the code added as solution code with comments
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
VBox vbox = new VBox; // solution code
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
// Solution Code
public void setVBoxHandler(EventHandler<? super MouseEvent> value){
vbox.setOnMouseClicked(value);
}
}
Then I set the handler in the start method:
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
VBox vBox =(VBox) root.getChildren().get(1);
root.setVBoxHandler(e->changeSceneToLoginMenu() ); // solution Code
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
public void changeSceneToLoginMenu() {
System.out.println("It finally worked");
Scene currentScene = new Scene(new Group(),100,100); // just a demo
primaryStage.setScene(currentScene);
}
notes: The type of value on the method setVBoxHandler(EventHandler<? super MouseEvent> value) will depend on the setOnXXX method used. For example I tested and this soultion also works for buttons just need to change the type to EventHandler<ActionEvent> value.
Some coments on the question posted links on "how to use handlers", this posts used anomimous classes. I belived This way is outdated. I used lambdas in the code the end result is the same but more redable code
Just for reference if future readers are using anomimous classes the solution would be the same just change the way you set up the handler:
// lambdas
setVBoxHandler( e -> System.out.println("Code run if mouse is clicked "));
// anomimous classes
setVBoxHandler(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event){
System.out.println("Code run if mouse is clicked ");
}
});
So I have to make a Zodiac Sign GUI, and we are tasked with having the following:
a Label in the top left, and a TextField in the top right (both with padding)
an exit Button in the center of the GUI, along with a clear and find my sign on either side
and finally, a Label in the bottom center prompting the sign
I am utterly confused on how to have this come out, as I am a novice in JavaFX. I believe I would need a branch node along with the root node in order to get this kind of layout. I do not need assistance in instantiating the button, labels etc., mainly confused with how this layout can even work. The code I have now is the following:
public class ZodiacGUI extends Application {
public static void main(String args[]) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane mainPane = new BorderPane();
mainPane.setStyle("-fx-background-color: PINK");
setupControls(mainPane);
Scene scene = new Scene(mainPane);
setStage(primaryStage, scene);
}
public void setStage(Stage primaryStage, Scene scene) {
primaryStage.setWidth(500);
primaryStage.setHeight(200);
primaryStage.setTitle("What is my Zodiac Sign?");
primaryStage.setScene(scene);
primaryStage.show();
}
public void setupControls(BorderPane mainPane) {
Label label = new Label("Enter you birthday formatted as -> mm/dd");
Button exitButton = new Button();
Button findSign = new Button();
Button clear = new Button();
TextField userInput = new TextField();
userInput.setPromptText("Enter birthday");
exitButton.setText("Exit.");
findSign.setText("Find my sign.");
clear.setText("Clear.");
exitButton.setOnAction(e -> System.exit(0));
mainPane.setLeft(label);
mainPane.setRight(userInput);
mainPane.setCenter(exitButton);
mainPane.setCenter(findSign);
mainPane.setCenter(clear);
BorderPane.setAlignment(label, Pos.TOP_LEFT);
BorderPane.setAlignment(userInput, Pos.TOP_RIGHT);
BorderPane.setAlignment(exitButton, Pos.CENTER);
BorderPane.setAlignment(findSign, Pos.CENTER_LEFT);
BorderPane.setAlignment(clear, Pos.CENTER_RIGHT);
}
}
This only outputs one of the buttons out of the three, as I assume it is because the necessary addition of another BorderPane? Here is a drawn out picture of what I would like to come out with:
Just to clarify, I do not need assistance with the handling of finding the zodiac sign, etc. Mainly need assistance on the layout, as it has stumped me for days. Thank you in advance for helping out a novice to JavaFX :).
You have three rows with diffrent number of children. You can use HBox if row have more than one child.
BorderPane mainPane = new BorderPane();
mainPane.setTop(new HBox(topLabel, topField));
mainPane.setCenter(new HBox(centerLabel, centerField, centerButtom));
mainPane.setBottom(bottomCenterButton);
If you need more than 3 rows (top, center, bottom section of BorderPane) you can use VBox, where every child is row like:
HBox row1 new HBox(child1, child2)
HBox row2 new HBox(child1, child2, child3)
HBox row3 new HBox(child1)
HBox row4 new HBox(child1, child2)
VBox pane = new VBox(row1, row2, row3, row4);
You might want to use a GridPane
GridPane grid = new GridPane();
grid.add(label,0,0);
grid.add(userInput,2,0);
grid.add(findSign,0,1);
grid.add(exitButton,1,1);
grid.add(clear,2,1);
or use a VBox with BorderPane to help with the layout/alignment
BorderPane mainPane, centerPane;
mainPane.setLeft(label);
mainPane.setRight(userInput);
centerPane.setLeft(findSign);
centerPane.setRight(clear);
centerPane.setCenter(exitButton);
BorderPane.setAlignment(label, Pos.LEFT);
BorderPane.setAlignment(userInput, Pos.RIGHT);
BorderPane.setAlignment(exitButton, Pos.CENTER);
BorderPane.setAlignment(findSign, Pos.LEFT);
BorderPane.setAlignment(clear, Pos.RIGHT);
VBox items = new VBox();
items.getChildren().addAll(mainPane, centerPane);
I have a GUI that I've created, and I would like to add a ProgressIndicatorwhen the application is doing something in the background. I've created a Tab in the constructor similar to the following:
public class myGUI {
Tab myTab;
myGUI() {
myTab = new Tab("My Tab");
HBox view = new HBox();
VBox left = new VBox();
BorderPane right = new BorderPane();
/*A lot of other things are declared that go in left and right*/
view.getChildren().addAll(left, right);
myTab.setContent(view);
}
...
Later on, I have a button press that starts the application doing a background task, and I would like to add a ProgressIndicator to the center of the BorderPane. I tried something like the following:
private void handleMyAction(MouseEvent e) {
myTab.getContent().getChildren().get(1).setCenter(new ProgressIndicator(-1.0f));
}
I would think that this works, however, getContent returns a Node, and I cannot call getChildren on that Node. How can I access the BorderPane to add another Node without making the BorderPane a field in my class?
Just make the border pane an instance variable:
public class MyGUI {
private Tab myTab;
private BorderPane right ;
MyGUI() {
myTab = new Tab("My Tab");
HBox view = new HBox();
VBox left = new VBox();
right = new BorderPane();
/*A lot of other things are declared that go in left and right*/
view.getChildren().addAll(left, right);
myTab.setContent(view);
}
private void handleMyAction(MouseEvent e) {
right.setCenter(new ProgressIndicator(-1.0f));
}
}
I'm trying to add a ScrollBar to a HBox. The ScrollBar gets added, but I get no scrolling. How can I make it work?
public class ScrollableItems {
public void scrollableItems(HBox content) {
double height = 180;
ScrollBar sc = new ScrollBar();
content.getChildren().add(sc);
sc.setLayoutX(content.getWidth() - sc.getWidth());
sc.setMin(0);
sc.setOrientation(Orientation.VERTICAL);
sc.setPrefHeight(height);
sc.setMax(height * 2);
sc.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
content.setLayoutY(-new_val.doubleValue());
}
});
}
}
Add children to a HBox then pass it to scrollableItems(HBox content) above to add a SCrollBar
public HBox mainItemsWrapper() {
HBox scrollabelWrapper = new HBox();
scrollabelWrapper.setMaxHeight(180);
HBox entityDetailViewWrapper = new HBox();
entityDetailViewWrapper.getChildren().addAll(.....);
scrollabelWrapper.getChildren().add(entityDetailViewWrapper);
new ScrollableItems().scrollableItems(scrollabelWrapper);
return scrollabelWrapper;
}
Thank you all.....
I do not really get why you are trying to reinvent the wheel.. you should probably use the ScrollPane instead.
This little example shows how to create a horizontally scrollable HBox with the ScrollPane class:
#Override
public void start(Stage primaryStage) {
HBox hbox = new HBox();
Button b = new Button("add");
b.setOnAction(ev -> hbox.getChildren().add(new Label("Test")));
ScrollPane scrollPane = new ScrollPane(hbox);
scrollPane.setFitToHeight(true);
BorderPane root = new BorderPane(scrollPane);
root.setPadding(new Insets(15));
root.setTop(b);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
As eckig said, you can wrap you HBox into a ScrollPane.
Additionnaly, you can customize the visual part of the scrollbar in CSS. I found this link useful for the comprehension of the differents parts of a scrollbar: Customize ScrollBar via CSS
Here is my summary code
public class KlArgon extends Application {
BorderPane border ;
Scene scene ;
Stage stage;
#Override
public void start(Stage stage) {
:
border = new BorderPane();
:
HBox infoBox = addInfoHBox();
border.setTop(infoBox);
:
VBox menuBox = addMenuVBox();
border.setLeft(menuBox);
:
border.setCenter(addAnchorPane(addGridPane()));
// setRight and setBottom is not used
:
scene = new Scene (border);
stage.setScene(scene);
stage.show();
}
private Node addAnchorPane(GridPane grid) {
AnchorPane anchorpane = new AnchorPane();
anchorpane.getChildren().add(grid);
AnchorPane.setTopAnchor(grid, 10.0);
return anchorpane;
}
private GridPane addGridPane() {
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(0, 10, 0, 10));
grid.add(addWhiteboard(), 1, 0);
return grid;
}
private Node addWhiteboard() {
Canvas canvas = new Canvas (wboardWd, wdboardHt);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
drawfromClipboard(gc);
return canvas;
}
}
I refer to the Center pane as the "Whiteboard". Among other things, I have two buttons in menuBox - btnCopyFromClipboard and btnClearWhiteboard.
When user presses btnCopyFromClipboard - the user should be able to draw an rectangle in the "Whiteboard" only (i.e. Center pane only) and then the clipboard image will be copied (scaled) into that rectangle.
So I made border,scene, stage as global and I am trying to get this to work - not only it is buggy/ugly- to me it looks like a hack. Is there a cleaner way to do this i.e. manage Center Pane when button in left pane is pressed?
Basically I want the Center Pane to be the Canvas and the GraphicsContext operations are performed whe the Buttons in Left Pane is pressed.
What I have working is pressing the btnCopyFromClipboard lets me draw the rectangle anywhere/everywhere (instead of limiting it to the Center Pane / the whiteboard). I want to restrict the rectangle to be drawn inside the Center Pane / the whiteboard only.
Some inputs/pointers from someone who has been through this will be very much appreciated.
http://docs.oracle.com/javafx/2/layout/builtin_layouts.htm was helpful to get me started.
I was in a fix as well and here is a question which I asked in Oracle Forums, to which, James gave me a vivid reply. Please go through this, it has your answer
https://community.oracle.com/thread/2598756