Why does this mess up my program? - java

I've looked at all my notes and comments from old code but I don't understand why it doesn't work.
import javafx.application.Application;
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.StackPane;
import javafx.stage.Stage;
import java.util.Stack;
import javafx.scene.layout.VBox;
public class Main extends Application{
Stage window;//makes window a stage
Scene scene1, scene2;//makes scene1 and scene2 into Scene's
public static void main(String []args) {
//launches the main
launch(args);
}
#Override//Overrides Application
public void start(Stage primaryStage) throws Exception {
window = primaryStage;//Makes window primary stage
Label label1 = new Label ("This is the first scene!");//Makes a label
Button button1 = new Button();//Declares button as a Button
button1.setText("Click me for Scene 2");//Sets the text of the button
button1.setOnAction(e -> window.setScene(scene2));
//This say, on the action, change the scene
VBox layout1 = new VBox(20);//Makes layout1 into a VBox
layout1.getChildren().addAll(label1, button1);//Adds the 'Children'
scene1 = new Scene(layout1, 500, 400);//Sets up layout1
Button button2 = new Button();//makes a second button
button2.setText("Click me for scene 1");//sets the text for button2
button2.setOnAction(e -> window.setScene(scene1));//When button 2 is click, it changes scene
StackPane layout2 = new StackPane();//Makes a new StackPane layout
layout2.getChildren().add(button2);//Adds button2 to the layout
scene2 = new Scene (layout2, 500, 400);//Gives arguemnts for Scene2
window.setScene(scene1);//Sets scene of the window stage
window.setTitle("This is a title");//Sets title
window.show();//Shows the window
}
}
Vs.
import javafx.application.Application;
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.StackPane;
import javafx.stage.Stage;
import java.util.Stack;
import javafx.scene.layout.VBox;
public class Main extends Application{
Stage window;//makes window a stage
Scene scene1, scene2;//makes scene1 and scene2 into Scene's
public static void main(String []args) {
//launches the main
launch(args);
}
#Override//Overrides Application
public void start(Stage primaryStage) throws Exception {
window = primaryStage;//Makes window primary stage
Label label1 = new Label ("This is the first scene!");//Makes a label
Button button1 = new Button();//Declares button as a Button
button1.setText("Click me for Scene 2");//Sets the text of the button
button1.setOnAction(e -> window.setScene(scene2));
//This say, on the action, change the scene
VBox layout1 = new VBox(20);//Makes layout1 into a VBox
layout1.getChildren().addAll(label1, button1);//Adds the 'Children'
Scene scene1 = new Scene(layout1, 500, 400);//Sets up layout1
Button button2 = new Button();//makes a second button
button2.setText("Click me for scene 1");//sets the text for button2
button2.setOnAction(e -> window.setScene(scene1));//When button 2 is click, it changes scene
StackPane layout2 = new StackPane();//Makes a new StackPane layout
layout2.getChildren().add(button2);//Adds button2 to the layout
Scene scene2 = new Scene (layout2, 500, 400);//Gives arguemnts for Scene2
window.setScene(scene1);//Sets scene of the window stage
window.setTitle("This is a title");//Sets title
window.show();//Shows the window
}
}
The only thing I changed between the two was adding the word 'Scene':
scene1 = new Scene.... Works
vs
Scene scene2 = new Scene.... Doesn't work. Why is this?

The reason putting Scene before them doesn't work is because you've already declared them, but not initialised them, as Scenes in the line
Scene scene1, scene2
So you don't need to specify that again.
It'd be like making an integer int a then initialising it int a = 1. That doesn't make sense, because a has already been defined as an int.
And really, it's less of "it doesn't work" and more of "it doesn't make sense".

You are shadowing your member variables and leaving them as null in the second code.
In other words, there's only 2 instances of a Scene in the first code, and 4 in the other
When you do this, it uses Main.this.scene2, not the local instance .
button1.setOnAction(e -> window.setScene(scene2));
If you did need to use the local instance, you need to declare it final and initialize before the action listener was set.

Related

JavaFX - BorderPane is not centered in scene/stage

I am currently coding a board game and I am using java and for GUI purposes javaFX. The game requires for a map (the game board) to be in the middle of the screen and various options and additional information to be around it. My idea was to have a BorderPane where the center node is the game board and top, bottom etc are the additional options.
My problem is that upon starting the App the BorderPane is not centered in the stage but is slightly extended to the right and botton, where it is not visible. Thus my bottom Node can't be seen. Strangely if I minimize the window and maximize it again everything is where it should be and perfectly inside the bounds of the stage.
The application before minimizing and maximizing again
And afterwards (The way it should look like from the beginning)
My center Node is a normal Pane. Also I do stage.setMaximize(true). So the window is already maximized upon starting the application and it should not make a difference to minimze and maximize again.
The code for this scene essentially boils down to this:
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
Pane pane = createGameBoard();
pane.setId("mapPane");
Button button = new Button("Save");
VBox box = new VBox(0, button);
box.setAlignment(Pos.CENTER);
Label label = new Label("Bottom");
label.setPrefHeight(20);
BorderPane borderPane = new BorderPane();
borderPane.setTop(top);
borderPane.setBottom(bottom);
borderPane.setCenter(center);
borderPane.setPadding(new Insets(10));
Scene scene = new Scene(borderPane);
scene.getStylesheets().add(Objects.requireNonNull(MainGUI.class.getResource("/game.css")).toString());
stage.setScene(scene);
}
The game.css stylesheet only sets a background colour at the moment.
I am using: Java 17, JavaFX 17.
If you need any further information I am happy to provide it :)
Thanks!
Edit:
To reproduce my problem run this code. The issue should appear once you click on the button "Next Screen".
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.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class App extends Application {
private Stage stage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setTitle("App");
stage.setMinHeight(600);
stage.setMinWidth(800);
stage.setWidth(java.awt.Toolkit.getDefaultToolkit().getScreenSize().getWidth());
stage.setHeight(java.awt.Toolkit.getDefaultToolkit().getScreenSize().getHeight());
stage.setMaximized(true);
stage.setOnCloseRequest(e -> System.exit(0));
Button button = new Button("Next Screen");
button.setOnAction(e -> {gameScreen();});
BorderPane borderPane = new BorderPane();
borderPane.setCenter(button);
Scene scene = new Scene(borderPane);
stage.setScene(scene);
stage.show();
}
private void gameScreen() {
Circle circle = new Circle(0, 0, 4);
Pane pane = new Pane(circle);
pane.setStyle("-fx-background-color: #00c3ff");
Button button = new Button("Top");
VBox vBox = new VBox(0, button);
vBox.setAlignment(Pos.CENTER);
Label label = new Label("Bottom");
BorderPane borderPane = new BorderPane();
borderPane.setTop(vBox);
borderPane.setCenter(pane);
borderPane.setBottom(label);
borderPane.setPadding(new Insets(10));
Scene scene = new Scene(borderPane);
stage.setScene(scene);
}
}
My problem was resolved by changing the root of the scene I was using and not create a new scene everytime I switch the layout (e.g. from main menu to the game screen).

How to get buttons to overlap the image in JavaFX?

package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.awt.*;
public class Main extends Application {
Stage window;
Scene scene1, scene2;
Button button1, button2;
ImageView iv = new ImageView();
#Override
public void start(Stage primaryStage) throws Exception{
window = primaryStage;
Label label1 = new Label("First stage");
Button button1 = new Button("PLAY");
//Layout 1
VBox layout1 = new VBox(20);
scene1 = new Scene(layout1, 1280, 720);
layout1.getChildren().add(changeBackground("file:menuBackground.png"));
layout1.getChildren().addAll(label1, button1);
//defines dimensions of vbox so button and label can be centered
layout1.prefWidthProperty().bind(window.widthProperty());
layout1.prefHeightProperty().bind(window.heightProperty());
layout1.setAlignment(Pos.CENTER);
button1.setOnAction(event -> window.setScene(scene2));
//Button 2
Button button2 = new Button("This scene sucks, go back to scene 1");
button2.setOnAction(event -> window.setScene(scene1));
//Layout 2
StackPane layout2 = new StackPane();
layout2.getChildren().add(button2);
scene2 = new Scene(layout2, 1280, 720);
layout2.getChildren().add(changeBackground("file:gameBackground.png"));
window.setScene(scene1);
window.setTitle("Scramblo");
window.show();
}
public static void main(String[] args) {
launch(args);
}
public Node changeBackground(String imageFile){
ImageView iv = new ImageView();
Image image = new Image(imageFile);
iv.setImage(image);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getWidth();
iv.setFitHeight(height);
iv.setFitHeight(width);
iv.setPreserveRatio(true);
return iv;
}
}
So what I want is for the buttons to overlap the background images. I want to see the buttons and be able to interact with them with just having the background chillin' there. Right now when I add the background it completely removes the buttons/labels and anything else I put on there. I'm confused on how to go about this! Thanks!
Make layout1 a StackPane and not a VBox. Use a VBox as the container for label1 and button1 and add the VBox to layout1.
A StackPane places nodes added to it one on top of the other. The first node added is the lowest. Hence you want to first add the background image to the StackPane and then the button. That way the button appears on top of the image.
Similarly for layout2, because you first added button2 and after that added the image, the image sits on top of the button and therefore hides the button. You just need to swap them, i.e. add the image first and then add the button.
Note that the URL for the image, in your code, looks incorrect to me. I simply put the image in the same folder as the compiled Main.class file and used method getResource() to get the image file.
Also, I don't think it is a good idea to mix AWT and JavaFX. JavaFX has class Screen that will provide you with the screen dimensions.
Here is your code with my fixes. Compare it with your code to see the differences.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.net.URL;
public class Main extends Application {
Stage window;
Scene scene1, scene2;
Button button1, button2;
ImageView iv = new ImageView();
#Override
public void start(Stage primaryStage) throws Exception {
Screen screen = Screen.getPrimary();
Rectangle2D screenBounds = screen.getBounds();
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
window = primaryStage;
Label label1 = new Label("First stage");
Button button1 = new Button("PLAY");
VBox vBox = new VBox(20);
vBox.getChildren().addAll(label1, button1);
vBox.setAlignment(Pos.CENTER);
// Layout 1
StackPane layout1 = new StackPane();
scene1 = new Scene(layout1, 1280, 720);
layout1.getChildren().addAll(changeBackground("snowymountains.png"), vBox);
// defines dimensions of vbox so button and label can be centered
layout1.prefWidthProperty().bind(window.widthProperty());
layout1.prefHeightProperty().bind(window.heightProperty());
layout1.setAlignment(Pos.CENTER);
button1.setOnAction(event -> window.setScene(scene2));
// Button 2
Button button2 = new Button("This scene sucks, go back to scene 1");
button2.setOnAction(event -> window.setScene(scene1));
// Layout 2
StackPane layout2 = new StackPane();
scene2 = new Scene(layout2, 1280, 720);
layout2.getChildren().addAll(changeBackground("gameBackground.jpg"), button2);
window.setScene(scene1);
window.setTitle("Scramblo");
window.show();
}
public static void main(String[] args) {
launch(args);
}
public Node changeBackground(String imageFile) {
URL url = getClass().getResource(imageFile);
ImageView iv = new ImageView();
Image image = new Image(url.toExternalForm());
iv.setImage(image);
iv.setFitHeight(window.getHeight());
iv.setFitHeight(window.getWidth());
iv.setPreserveRatio(true);
return iv;
}
}

JavaFX create popover without external libraries

How can I create a popover like in the DatePicker class from javafx.scene.control.DatePicker as seen here:
The popover should when displayed be on top of all the other components as seen here (the popover is above the TextField):
Found a pretty simple solution to my problem, here is a code snippet in case people encounter the same problem
package main;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane rootPane = new BorderPane();
MenuButton openButton = new MenuButton("Open Context Menu");
BorderPane contentPane = new BorderPane();
CustomMenuItem item = new CustomMenuItem(contentPane);
openButton.setStyle("-fx-selection-bar: transparent;"); //this is optional. it makes the blue background that appears when something is focused transparent
contentPane.setPrefSize(300, 300);
Label text = new Label("The ContextMenu will only close when you click the\nbutton below OR click outside of the ContextMenu.\nHow neat is that?");
text.setStyle(" -fx-text-fill: -fx-text-base-color;"); //needs to bet set if you want the selection-bar to be transparent. if not set the text will become invisible
contentPane.setTop(text);
Button closeButton = new Button("Close this popover");
closeButton.setOnAction(x -> {
openButton.hide();
});
contentPane.setBottom(closeButton);
item.setHideOnClick(false); // this will stop the ContextMenu from being hidden when clicking inside of it.
openButton.getItems().add(item);
rootPane.setCenter(openButton);
Scene scene = new Scene(rootPane, 550, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I just placed a Pane with all of my content inside of a CustomMenuItem and added that CustomMenuItem to my MenuButton.

JavaFX, switching panes in a root window and retaining memory

As stated in the title, I have fxml files, I have a UI that is set up with three labels/buttons up top and the lower half of the window has a pane. Every time a label/button is clicked, the pane must switch to that corresponding fxml file. So in other words, the pane must always be in the same position, kind of like a tabbed layout but without tabs.
I know I can achieve this with just loading a new instance of an fxml file but, I want to avoid that because when a user click on a tab he previously was on, he should be able to see his earlier input.
I have some main.java that starts the program. Some controller.java that controls the UI when it is first loaded, and some fxml file corresponding to that initial view. How can I go about implementing this transition functionality? P.S. I am very novice at JavaFX.
Here is a MCVE of how you can achieve it.
It can of course be implemented using FXML :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StageTest extends Application{
private Pane pane1, pane2, mainPane;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Switch Panes");
Button button1 = new Button("Show Pane 1");
button1.setOnAction(e -> showPane1());
Button button2 = new Button("Show Pane 2");
button2.setOnAction(e -> showPane2());
HBox buttonsPane = new HBox(5.);
buttonsPane.getChildren().addAll(button1, button2);
pane1 = getPane("PANE ONE");
pane2 = getPane("PANE TWO");
mainPane = new StackPane(pane1);
BorderPane root = new BorderPane();
root.setTop(buttonsPane);
root.setCenter(mainPane);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void showPane1() {
mainPane.getChildren().clear();
mainPane.getChildren().add(pane1);
}
private void showPane2() {
mainPane.getChildren().clear();
mainPane.getChildren().add(pane2);
}
private Pane getPane(String txt) {
VBox pane = new VBox();
pane.getChildren().addAll(new TextArea(txt+" add text here: "));
return pane;
}
public static void main(String[] args) {
launch(args);
}
}

Bookmarks and History Buttons not showing up in my Browser using JavaFX

I am making a web browser in JavaFX and I thought everything was good and dandy. I ran the application and now the History and Bookmarks buttons will not appear. I looked through the code and saw no errors. How do I fix this?
package javafxapplication3;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.application.Application;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebHistory;
public class CreateAsIGo2 extends Application{
public static void main(String[] args){
launch(args);
}
public void start(Stage primaryStage){
BorderPane ap = new BorderPane();
BorderPane ap2 = new BorderPane();
BorderPane ap3 = new BorderPane();
Scene scene = new Scene(ap, 700, 700);
Scene scene2 = new Scene(ap2, 700, 700);
Scene scene3 = new Scene(ap3, 700, 700);
VBox sp = new VBox();
VBox sp2 = new VBox();
VBox sp3 = new VBox();
Button HistoryButton = new Button("History");
Button BookmarksButton = new Button("Bookmarks");
Button RefreshButton = new Button("Refresh");
Button BackButton = new Button("Back");
Button BackToBrowser = new Button("Back to surfing the web");
Button ForwardButton = new Button("Forward");
TextField tf = new TextField();
tf.setPromptText("URL");
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.load("http://www.google.com");
webEngine.setJavaScriptEnabled(true);
WebHistory history = webEngine.getHistory();
HistoryButton.setOnAction(e -> primaryStage.setScene(scene2));
BookmarksButton.setOnAction(e -> primaryStage.setScene(scene3));
RefreshButton.setOnAction(e -> webEngine.reload());
BackButton.setOnAction(e -> webEngine.executeScript("history.back()"));
BackToBrowser.setOnAction(e -> primaryStage.setScene(scene));
ForwardButton.setOnAction(e -> webEngine.executeScript("history.forward()"));
tf.setOnKeyPressed((KeyEvent ke) -> {
KeyCode key = ke.getCode();
if(key == KeyCode.ENTER){
webEngine.load("http://" + tf.getText());
}
});
sp.getChildren().addAll(HistoryButton, BookmarksButton, RefreshButton, BackButton, ForwardButton);
sp2.getChildren().addAll(BookmarksButton, BackToBrowser);
sp3.getChildren().addAll(HistoryButton, BackToBrowser);
ap.setRight(sp);
ap2.setRight(sp2);
ap3.setRight(sp3);
ap.setTop(tf);
ap.setCenter(browser);
browser.setPrefSize(700, 700);
primaryStage.setTitle("JTG Browser Alpha");
primaryStage.setScene(scene);
primaryStage.show();
}
}
See the node documentation:
If a program adds a child node to a Parent (including Group, Region, etc) and that node is already a child of a different Parent or the root of a Scene, the node is automatically (and silently) removed from its former parent.
You are adding your HistoryButton (and other buttons) to different scenes and, when you do so, they are automatically removed from the previous scenes. You need to create new button instances if you want them visible in every scene.
Small aside: it is best to follow Java naming conventions, e.g. historyButton instead of HistoryButton.

Categories

Resources