It's good practice to use singleton to create new window?
I have main window and i want create another one. This window will be used only to change properties in main window.
My code:
Main Window
public class MainWindow {
private StackPane root = new StackPane();
private Stage primaryStage = new Stage();
public void run(){
primaryStage.setTitle("v0.2-alpha");
Scene scene = new Scene(root, 600, 400);
scene.getStylesheets().addAll("css/style.css");
MainMenu mmb = new MainMenu();
VBox vBox = new VBox();
vBox.getChildren().add(mmb.createMenuBar());
ISplitPane lsp = new SplitPaneLeftImpl();
ISplitPane csp = new SplitPaneCenterImpl();
ISplitPane rsp = new SplitPaneRightImpl();
HBox hboxpane = new HBox();
hboxpane.getChildren().addAll(spl.createSplitPane(), spc.createSplitPane(), spr.createSplitPane());
root.getChildren().addAll(vBox,hboxpane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Create new window class
public interface IStage {
public void createStage();
}
class StageOptionsImpl implements IStage{
private OptionsStage(){}
private Stage stageOptions = new Stage();
private static StageOptionsImpl os = null;
public static StageOptionsImpl getInstance(){
if(os == null){
synchronized(StageOptionsImpl.class){
if(os == null){
os = new StageOptionsImpl();
}
}
}
return os;
}
#Override
public void createStage(){
GridPane gp = new GridPane();
TabPane tabPane = new TabPane();
tabPane.setSide(Side.LEFT);
Tab tabSecurity = new Tab("Security");
tabSecurity.setContent(new SecurityTab().tabCreate());
Tab tab2 = new Tab("System Data");
tab2.setContent(new DataTab().tabCreate());
Tab tab3 = new Tab("tab 3");
tab3.setContent(new SecurityTab().tabCreate());
tabPane.getTabs().addAll(tabSecurity, tab2, tab3);
Scene sceneOptions = new Scene(tabPane, 400, 300, Color.AQUA);
stageOptions.setScene(sceneOptions);
stageOptions.show();
}
}
If it's only used from there, why would you want a singleton. Whole point of a Singleton is so you can use the same intance of it from anywhere.
It's good practice to use singleton to create new window?
Well, that comes down to a design question.
If you only ever want to have a single, shared instance of a Component in you're application, then I would say a singlton is not a bad approach. It will force this choice.
If, however, you want to display multiple instances of the same Component but want, say, a simpler way to construct it, or want to expose a non-concrete interface through the API, then a Factory pattern would be of a better choice.
The best approach to manage these windows is usually by creating a class that manages all of the windows/panels in the program.
If you wanted to open the panel called 'ClientForm' it should have a method like this:
public void OpenClientForm(){
// Set the other forms to their default form(when != null) and set their visibility as false (when != null).
RestorePanels();
// In case it hasn't been created yet
if (ClientForm == null){
// calls a factory that creates the form.
}
else{
// Set the form as visible
}
}
When 'disposing' of panels/forms inside of the program you should have a method that clears the panels, forcing them back to their original state and setting their visible as false.
Swing is pretty buggy, disposing of the forms and recreating them could mess up a lot of things in your interface. So, back to your question... implemeting a singleton or a factory to manage ur forms should be the best approach.
Hope this helps.
Related
I was working on JavaFX, and I realised something which kept me thinking. So, there's a scene in my constructor which adds the variable root of the type BorderPane. In the main class, this class' constructor is called first, thereby initialising the BorderPane in the Scene. However, the BorderPane is being modified and edited in a method called build. I am confused how the BorderPane is still being able to display its updated version from the build() since in the constructor it has already been commanded to display.
Here's my code:
public class SnacksOrDrinks
{
private BorderPane root;
private Stage primaryStage;
private Scene scene;
private Cart crt;
public SnacksOrDrinks(Stage primaryStage, Cart crt)
{
// BorderPane is being initialised and is being put inside the scene:
BorderPane root1 = new BorderPane();
this.scene = new Scene(root1, 800, 475);
this.crt = crt;
ImageView imgview = new ImageView("./application/MountainPainting.jpg");
imgview.fitWidthProperty().bind(primaryStage.widthProperty());
imgview.fitHeightProperty().bind(primaryStage.heightProperty());
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
this.primaryStage = primaryStage;
this.root = root1;
root.setStyle("-fx-background-color: blue");
root.getChildren().add(imgview);
}
public void build(Dispenser dis)
{
// ItemsPaneProp extends GridPane and stores the common properties shared in other classes.
ItemsPaneProp pane = new ItemsPaneProp();
// Header Created:
CommHeaderProp header = new CommHeaderProp("Lopes Vending Machine");
// However, BorderPane is being modified here, and the constructor has already been called.
BorderPane.setAlignment(header, Pos.CENTER);
root.setTop(header);
// Create a Line:
Line line = new Line(100, 10, 300, 10);
line.setFill(Color.GOLDENROD);
line.setStrokeWidth(2);
line.setSmooth(true);
pane.add(line, 0, 0);
// Create all the Scene's Components and setup the Event Handlers
ImageView img = new ImageView("./application/softdrink.jpg");
img.setFitWidth(75);
img.setFitHeight(75);
Button btn1 = new Button("Select to get Drinks", img);
btn1.setStyle("-fx-background-color: white");
btn1.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 12));
btn1.setContentDisplay(ContentDisplay.TOP);
pane.add(btn1, 0, 1);
ImageView img1 = new ImageView("./application/potato-chips.png");
img1.setFitWidth(75);
img1.setFitHeight(75);
Button btn2 = new Button("Select to get Snacks", img1);
btn2.setStyle("-fx-background-color: white");
btn2.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 12));
btn2.setContentDisplay(ContentDisplay.TOP);`enter code here`
pane.add(btn2, 1, 1);
root.setCenter(pane); // The BorderPane is being modified here too, but the constructor isn't updating the scene. Then, how does it still work?
btn1.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
// Create, build, and show Scene 2
DrinksList scene = new DrinksList(primaryStage, crt);
scene.build(dis);
scene.show();
}
});
btn2.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
// Create, build, and show Scene 2
SnacksList scene = new SnacksList(primaryStage, crt);
scene.build(dis);
scene.show();
}
});
}
public void show()
{// setting scene:
primaryStage.setScene(scene);
}
}
Take a look at https://docs.oracle.com/javase/8/javafx/scene-graph-tutorial/scenegraph.htm#JFXSG107
The JavaFX scene graph is a retained mode API, meaning that it maintains an internal model of all graphical objects in your application. At any given time, it knows what objects to display, what areas of the screen need repainting, and how to render it all in the most efficient manner.
Your borderPane - part of SceneGraph, so JavaFX knows, when you change anything in your borderPane, and automatically invoking redraw method for it.
The title may be a bit vague, so allow me to define it a little better. I have a working piece of code (down below): a simple main menu for a game I am working on. Everything works well, except for the Start button.
What I want to be able to do is click the Start button, and have a new scene appear on the same stage (window). I do not want to see a new window open. I have talked with someone more experienced in Java, and they told me to create separate classes for the MenuFX and the GameFX. If that is the case, I would need to call some start or launch method on the GameFX class from the MenuFX class, correct? Is this the best approach, or would I want to keep all FX-related code in one class? Also, I should keep the same stage for all FX work, no?
This post shed some light on things, but I am not well-versed in some of the terms discussed- for instance, I still do not understand the concept of Root.
Also, this post talks about a similar application, but I am not using FXML or SceneBuilder... I do not know if any of it is relatable.
MenuFX.java - I have removed some of the working code, simply for brevity. You can see that all I need help with is tying the Start button to some functionality that makes a new empty scene.
/*
* This is simply working on the title screen.
*/
// Asssume all imports are correct
import java.everythingNeeded
public class MenuFX extends Application {
#Override
public void start (Stage primaryStage) {
// Make the window a set size...
primaryStage.setResizable(false);
// Create menu vbox and set the background image
VBox menuVBox = new VBox(30);
menuVBox.setBackground(new Background(new BackgroundImage(new
Image("image/bambooBG.jpg"), null, null, null, new BackgroundSize(45,
45, true, true, true, true))));
// Create start button
Button startButton = new Button("Start Game");
// TODO Some things...
// Need assistance here
// Create help button
Button helpButton = new Button("Help");
helpButton.setOnAction(e -> THINGS);
// Create music toggle button
ToggleButton musicButton = new ToggleButton("Music On/Off");
musicButton.setOnAction(e -> THINGS);
// Create credits button
Button creditsButton = new Button("Credits");
creditsButton.setOnAction(THINGS);
// Create exit button and set it to close the program when clicked
Button endButton = new Button("Exit Game");
endButton.setOnAction(e -> Platform.exit());
// Add all nodes to the vbox pane and center it all
// Must be in order from top to bottom
menuVBox.getChildren().addAll(startButton, helpButton, musicButton, creditsButton, endButton);
menuVBox.setAlignment(Pos.CENTER);
// New scene, place pane in it
Scene scene = new Scene(menuVBox, 630, 730);
// Place scene in stage
primaryStage.setTitle("-tiles-");
primaryStage.setScene(scene);
primaryStage.show();
}
// Needed to run JavaFX w/o the use of the command line
public static void main(String[] args) {
launch(args);
}
}
Restating: I want to click the Start button and have the currently open window change to an empty scene.
Here is a pastebin of the MenuFX class in its entirety:
http://pastebin.com/n6XbQfhc
Thank you for any help,
Bagger
The basic idea here is you would do something like:
public class GameFX {
private final BorderPane rootPane ; // or any other kind of pane, or Group...
public GameFX() {
rootPane = new BorderPane();
// build UI, register event handlers, etc etc
}
public Pane getRootPane() {
return rootPane ;
}
// other methods you may need to access, etc...
}
Now back in the MenuFX class you would do
Button startButton = new Button("Start Game");
startButton.setOnAction(e -> {
GameFX game = new GameFX();
primaryStage.getScene().setRoot(game.getRootPane());
});
I am new in Javafx (and Java).
I am trying to display a label "title" to different scenes. Therefore this label in a instance variable. I create it using the method title().
The problem is that the title does not appear in my scenes. I tried to initialize it at class level, or inside the start(), or inside scene1(). Nothing works but when I create a local label inside the scene1() method.
Thanks in advance for your help.
ps: full repo here if needed
private Label title;
public void start(Stage primaryStage) {
title();
scene1();
scene2();
[some more code...]
}
public Node title() {
title = new Label();
title.setText("SNAKE");
title.setTextFill(Color.YELLOW);
title.setFont(Font.font(STYLESHEET_MODENA, FontWeight.BOLD, 80));
return title;
}
public Scene scene1() {
Label lbl1scene1 = new Label("Welcome to Game !");
Label lbl2scene1 = new Label("Click to start the game");
Button btnStart = new Button("Start the game now!");
btnStart.setOnAction(e -> {
runGame();
});
VBox vbox1 = new VBox(title, lbl1scene1, lbl2scene1, btnStart);
vbox1.setAlignment(Pos.CENTER);
vbox1.setSpacing(10);
scene1 = new Scene(vbox1, width, height);
return scene1;
}
I am trying to display a label "title" to different scenes.
Please note that a node can have only a single parent.
Create multiple instances using local variables instead of a title member field.
EDIT: The problems seem to emerge from the use of a SplitPane. I've removed that, but now I'm encountering MouseTransparent issues, cf comments
I'm encountering a problem on a StackPane, where a Controller class is supposed to manage node translations via mouse dragging:
For some reason, the .setOnMouseEntered event correctly fires whenever the mouse enters the StackPane. However, setOnMousePressed (when clicking on a node in the StackPane) doesn't fire at all, except on a label in the center of the StackPane. When I remove the label, nothing fires. The StackPane itself is comprised of a SplitPane, which is comprised of several other nodes.
Some of those nodes (the ones supposed to be dragged around) also have some setOnMousePressed, etc event management, which gets fired correctly !
To understand the context a bit more: I am working on a Naval Battle game implementation. I am currently trying to manage the boat placement via a controller class (so that I can verify that a boat doesn't get placed out of bounds or on another boat). The controller class (BoatMoveManager), when instancied, creates all these mouseEvent handlers on the highest StackPane (GameBoard extends StackPane).
public class BoatMoveManager {
GameBoard gB;
public BoatMoveManager(GameBoard g){
gB = g;
//the following mouseEventHandler fires correctly whenever the mouse enters the GameBoard (StackPane)
gB.setOnMouseEntered(event -> {
System.out.println("GameBoard OnMouseEntered");
//the following mouseEventHandler doesn't fire, except on a label in the middle of the GameBoard
gB.setOnMousePressed(event -> {
System.out.println("GameBoard OnMousePressed");
});
I don't understand why the event fires with the label, and not with the rest of the nodes in the GameBoard... (It might be the fact that the label is directly "inside" the GameBoard, while other nodes are in the top part of a horizontal splitter ?)
For example, here is the constructor for the GameBoard:
public class GameBoard extends StackPane {
private SplitPane splitter;
private TilePane gameZone;
private TabZone tabs;
private Grid playerGrid, opponentGrid, playerHarbor, opponentHarbor;
private FlowPane playerGrids, opponentGrids, playerZone, opponentZone;
private Score playerScore, opponentScore;
private Label popup;
private static HBox playerHBox, opponentHBox;
private static VBox playerVBox, opponentVBox;
private static BoatMoveManager bMM;
private List<Boat> playerBoatsList = new ArrayList<Boat>(), opponentBoatsList = new ArrayList<Boat>() ;
public GameBoard(){
super();
playerGrid = new Grid('G','H');
opponentGrid = new Grid('G','H');
playerHarbor = new Grid('H','H');
opponentHarbor = new Grid('H','H');
bMM = new BoatMoveManager(this);
for(int i = 1 ; i < 5 ; ++i){
playerBoatsList.add(new Boat(i, playerGrid, playerHarbor));
opponentBoatsList.add(new Boat(i, opponentHarbor, opponentGrid));
}
playerGrid.drawBoat(playerBoatsList.get(0), 3, 4);
playerGrid.drawBoat(playerBoatsList.get(1), 3, 5);
playerGrid.drawBoat(playerBoatsList.get(2), 3, 6);
playerGrid.drawBoat(playerBoatsList.get(3), 3, 7);
playerScore = new Score();
opponentScore = new Score();
gameZone = new TilePane(Orientation.HORIZONTAL);
gameZone.setStyle("-fx-background-color: lightgray");
playerHBox = new HBox();
playerHBox.getChildren().addAll(playerHarbor, playerGrid);
playerHBox.setSpacing(Grid.getUnit());
playerVBox = new VBox();
playerVBox.getChildren().addAll(playerScore,playerHBox);
playerVBox.maxWidthProperty().bind(gameZone.widthProperty().divide(2));
playerScore.setAlignment(Pos.CENTER);
opponentHBox = new HBox();
opponentHBox.getChildren().addAll(opponentGrid, opponentHarbor);
opponentHBox.setSpacing(Grid.getUnit());
opponentVBox = new VBox();
opponentVBox.getChildren().addAll(opponentScore,opponentHBox);
opponentScore.setAlignment(Pos.CENTER);
gameZone.getChildren().addAll(playerVBox, opponentVBox);
gameZone.setHgap(Grid.getUnit()*3);
gameZone.setAlignment(Pos.CENTER);
gameZone.setSnapToPixel(false);
tabs = new TabZone();
splitter = new SplitPane();
splitter.setOrientation(Orientation.VERTICAL);
splitter.setDividerPositions(0.75f,0.25f);
splitter.getItems().addAll(gameZone,tabs);
splitter.setOnDragEntered(event ->{
BoatMoveManager.setTarget(splitter);
});
popup = new Label("uninitialized");
this.getChildren().addAll(splitter,popup);
this.setOnDragEntered(event -> {
BoatMoveManager.setTarget(this);
});
}
The label I'm talking about is "popup", which is added at the very end of the constructor along with "splitter" in
this.getChildren().addAll(splitter,popup);
but take in account the fact that even when I don't add it, events like MousePressed don't fire through GameBoard...
Here's a layout of the GameBoard to clear things up (grids and boats layout is self explanatory):
(Open this image in a new tab to view it in full size)
I hope this isn't too cluttered to get a sense of what I'm trying to do.. And why it doesn't work.
I'm really not sure at this point. I'd pretty much have to test the code on my computer to know for sure but try
this.setMouseTransparency(true);
This will make it to where the stackpane itself can't be clicked on. Also for future reference you can drop the "this" at the beginning of your calls for GameBoard. It's perfectly legal to call getChildren().add(node) if your class extends StackPane.
After running into the same problem (again) ..
I believe a StackPane is your problem. They do not like to pass event aparently..
In my case, setting :
stackPane.setPickOnBounds(false);
was my fix.
I hope this resolves the Issue for you.. perhaps a bug?
I need to access gui components outside the class that defines them.
My gui class contain the following code for placing object on it:
/**
* Create contents of the window.
*/
protected void createContents() {
shlCertificatesmanager = new Shell(Display.getDefault());
shlCertificatesmanager.setSize(450, 300);
shlCertificatesmanager.setText("CertificatesManager");
shlCertificatesmanager.setLayout(new RowLayout(SWT.HORIZONTAL));
MenuItemListener menuListener = new MenuItemListener(shlCertificatesmanager);
Menu menu = new Menu(shlCertificatesmanager, SWT.BAR);
shlCertificatesmanager.setMenuBar(menu);
MenuItem mntmNewSubmenu = new MenuItem(menu, SWT.CASCADE);
mntmNewSubmenu.setText("File");
Menu menu_1 = new Menu(mntmNewSubmenu);
mntmNewSubmenu.setMenu(menu_1);
MenuItem mntmOpenCertificate = new MenuItem(menu_1, SWT.NONE);
mntmOpenCertificate.setText("Open Certificate");
mntmOpenCertificate.addSelectionListener(menuListener);
MenuItem mntmExit = new MenuItem(menu_1, SWT.NONE);
mntmExit.addSelectionListener(menuListener);
mntmExit.setText("Exit");
MenuItem mntmHelp = new MenuItem(menu, SWT.CASCADE);
mntmHelp.setText("Help");
Menu menu_2 = new Menu(mntmHelp);
mntmHelp.setMenu(menu_2);
MenuItem mntmAbout = new MenuItem(menu_2, SWT.NONE);
mntmAbout.setText("About");
mntmAbout.addSelectionListener(menuListener);
Label lblAliasName = new Label(shlCertificatesmanager, SWT.NONE);
lblAliasName.setText("Alias name: ");
Label label = new Label(shlCertificatesmanager, SWT.NONE);
label.setText("___________");
}
Now my need is to access some of these component from an external class, in that case i need to access the two labels (lblAliasName, label) from MenuItemListener class.
There is a way to access them? (maybe like Android with a findViewById method or similar?)
Or i need for example made them accessible from the other class in some way? (Creating a calss of gui components that will be used by both MenuItemListener class and GuiWindow class)
Two options come to my mind:
If there is only one instance of your class, declare the components as static fields and hand them over via getter methods.
If there is more than one instance, declare the components as fields (this time not static) and create getter methods. The other class would of course have to know the instance of your class to access the methods then.
Keep in mind:
If you try to change components from a thread that is not the gui-thread, you will get an SWTException with value ERROR_THREAD_INVALID_ACCESS.
You can solve this by using:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// change/modify components here
}
});
No, there is no standard way to do this and you need to make them accessible as usual in Java.