JavaFX setting root of scene causes root to change size - java

I have found that when I set the root of a scene (as advised - instead of simply setting the scene), the root pane is resized and does not fit in the stage properly, even though I supply the same dimensions as the stage. Screenshots can be seen in the links below.
If I use the setScene function, it works as expected and the root pane is the correct size, but there is a flash of a white screen between scene changes.
Here are examples from my project:
// resizes root pane - too small for stage
public void handle(MouseEvent event) {
if (event.getSource() instanceof Button) {
Button sourceButton = (Button)event.getSource();
Stage primaryStage = (Stage ((Node)event.getSource()).getScene().getWindow();
if (sourceButton.getId().equals("back-button")) {
primaryStage.getScene().setRoot(view.getWelcomePane());
}
}
}
public Pane getWelcomePane() {
return new BorderPane(bpWelcomeMain); // bpWelcomeMain is the border pane that contains the page components
}
Or:
// produces 'flashing' between scene transition (but root pane is correct size)
public void handle(MouseEvent event) {
if (event.getSource() instanceof Button) {
Button sourceButton = (Button)event.getSource();
Stage primaryStage = (Stage ((Node)event.getSource()).getScene().getWindow();
if (sourceButton.getId().equals("back-button")) {
primaryStage.setScene(view.getWelcomeScene());
}
}
}
public Pane getWelcomeSceme() {
return new Scene(bpWelcomeMain, 1500, 750); // bpWelcomeMain is the border pane that contains the page components
}
I have tried sizeToScene() as well as other things mentioned on other posts, but I am yet to discover a solution. The function setRoot() seems to work on one of my pages but not the others, even though they are all the same dimensions and the code for switching between the root panes is the same. I don't understand why setRoot() works for one but doesn't work for the other pages. I also don't understand why setScene() produces the correctly sized root pane but setRoot() does not.
So, I would like to use the setRoot() function without the root pane resizing, or setScene() without the flash between scene transitions (it ruins the fluidity of the UI). Is there a way?
This is the result of setScene() (although it looks good because of the flashing between scene changes I feel like an epilepsy warning may be needed haha)
This is the result of setRoot()
Any help would be greatly appreciated.

Related

Parent Stage hidden on switching to the 3rd party window and again switching to Application Stage

My parent stage "stage1" is opening child stage "stage2" and i have set child stage's modality as below.
stage2.initModality(Modality.APPLICATION_MODAL);
Now when i open stage2 from stage1, stage1 is appears behind stage2 that is expected, but when i press "Ctrl+Tab" key, control switches to 3rd paty applicaton for example "Outlook", then I again press "Ctrl+Tab" key, it returns to our main applicaton and it shows stage2 but stage1 appears hidden. My expectation is that stage1 should be hidden behind stage2.
Any help is greatly appriciated.
It is because Stage2.getOwner() == null is true. Your expectation is the way it works, when it is false. so to solve your problem do this
Stage2.initOwner(Stage1);
ediit
here is some demo
#Override
public void start(Stage stage) {
Pane p = new Pane();
p.setStyle("-fx-background-color: red");
stage.setTitle("I AM THE PARENT");
Scene scene = new Scene(p);
stage.setWidth(600);
stage.setHeight(600);
stage.setScene(scene);
stage.show();
Stage s = new Stage(StageStyle.DECORATED);
s.initModality(Modality.APPLICATION_MODAL);
p = new Pane();
p.setStyle("-fx-background-color: yellow");
s.setScene(new Scene(p,150,150));
//s.initOwner(stage); //with this commented it wont work
s.show();
}
Also you will notice that when your press CTRL + TAB the windows popup only shows your second window STAGE2, that is who he knows of, because its has not parent, but when it has owner is shows only the owner

WebView size in JavaFX Stage

I want to change the size of the Stage and WebView of a JavaFx application.
I can change the windows size but the browser size does not increases from 800px so the display of the html page does not fit all the windows.
It looks like this (note the light grey zone on the right):
This is some code of the page:
public class EfadosApp extends Application {
private Scene scene;
#Override public void start(Stage stage) {
scene = new Scene(new Browser(), 1000,500);
stage.setScene(scene);
stage.show();
}
}
class Browser extends Region {
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
public Browser() {
webEngine.load("www.oracle.com");
getChildren().add(browser);
}
}
You can simply fix this with extending for example StackPane rather than Region:
class Browser extends StackPane {
...
}
The difference is that Region resizes the children to their preferred size :
By default a Region inherits the layout behavior of its superclass,
Parent, which means that it will resize any resizable child nodes to
their preferred size, but will not reposition them. If an application
needs more specific layout behavior, then it should use one of the
Region subclasses: StackPane, HBox, VBox, TilePane, FlowPane,
BorderPane, GridPane, or AnchorPane.
While StackPane tries to resize its children to fit the content area:
The stackpane will attempt to resize each child to fill its content
area. If the child could not be sized to fill the stackpane (either
because it was not resizable or its max size prevented it) then it
will be aligned within the area using the alignment property, which
defaults to Pos.CENTER.
This of course also means, if you use Region but either you set the preferred size of the WebView to "something big" like
browser.setPrefSize(5000, 5000);
or you bind the heightProperty and widthProperty of the WebView to the corresponding properties of the Stage,
Browser browser = new Browser();
browser.browser.prefHeightProperty().bind(stage.heightProperty());
browser.browser.prefWidthProperty().bind(stage.widthProperty());
scene = new Scene(browser, 1000, 500);
it will also work as you expect.

JavaFX - Adding a ScrollPane popup which closes when user clicks out of it

My title is badly worded because my problem is very hard to describe, so I drew an image for it:
I have an ImageView object which represents a pile of cards (not poker cards but just used them as an example). When this image view is clicked, I need a window to popup that features a ScrollPane and shows them all the card objects that are in the linked list. When the user clicks anywhere outside of the window (and later on, any right mouse button click) the scrollpane window needs to close.
Ways that I have already tried:
Scene with APPLICATION_MODAL. Then did Scene.showAndWait(). I didn't like this method because it made another application on the user's taskbar. It also felt clunky and just bad.
Changed my root pane to a StackPane, then added this Scrollpane to the stackpane when the user clicked on the deck. This for some reason was really slow and seemed really obtrusive. It was also annoying because my alternate class needed to have access to the root pane (since when it closes, it needs to go to the root StackPane and call .remove() on itself).
Are there any other better ways to accomplish this? My application is going to have many of these piles and so this framework needs to work very well.
I would still propose to open a new Stage with some restrictions to solve your issues with this approach.
You can use the initOwner property of the Stage to have another Stage as owner, therefore no other icon will appear on the taskbar.
You can use the initStyle property with TRANSPARENT or UNDECORATED StageStlye, this will ensure that only the content is visible.
And in the end you can use the focusedProperty to check whether the Stage lost focus to close it.
Example
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
Scene sc = new Scene(new ScrollPane(), 300, 300);
popupStage.setScene(sc);
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
Boolean newValue) {
if(!newValue)
popupStage.close();
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
It is also possible of course to not open it in a new Stage, but draw a ScrollPane inside the current Stage which overlaps the content of the Stage using for example an AnchorPane or Group as root, but the first solution has the advantage that you are not bound to the dimensions of main Stage (the popup can have any size that you want).
You can achieve this with a low level system hook that catches the mouse events.
http://kra.lc/blog/2016/02/java-global-system-hook/ or https://github.com/kwhat/jnativehook/releases
I hope that is what you needed, otherwise i got your question wrong.

Updating JavaFX Label doesn't work with DataBinding

I tried to write my 'own' log in a tab and I experienced problems with updating the label with a naive solution that I had.
So after I googled I checked out this solution here:
Displaying changing values in JavaFx Label
I don't know if I did everything right but unfortunately this solution doesn't work for me.
final static Label logLabel = new Label();
final static SimpleStringProperty logString = new SimpleStringProperty("test");
...
...
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.getTabs().add(createSettingsTab());
tabPane.getTabs().add(createParticipantTab());
tabPane.getTabs().add(createSpectatorTab());
tabPane.getTabs().add(createOverviewTab());
tabPane.getTabs().add(createTournamentTab());
tabPane.getTabs().add(createLogTab());
tabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
Scene scene = new Scene(tabPane, 1200, 800);
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
private Tab createLogTab() {
// TODO Auto-generated method stub
logLabel.textProperty().bind(logString);
Tab tab = new Tab("Log");
tab.setContent(logLabel);
return tab;
}
I got this lines for initializing the Label and for setting the new text I do this:
logString.set(logString.get() + "foo");
The log tab keeps being blank...
I'd appreciate any help! Thanks!
edit// That's the only useful mcve I can think of. As I said the other create methods to create the other tabs are not making use of the label or the SimpleStringProperty
This is the button that doesn't work as supposed concerning the label.
buttonLoadConfig.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("test");
logString.set(logString.get() + "\ttest");
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose Config");
File config = fileChooser.showOpenDialog(new Stage());
}
});
Use of static members is often not a good idea and is probably contributing to your issue.
Your code as it is currently written can have many tabs but only one label. You are trying to add the same label to multiple tabs. But, when you do that, the label will be removed from the previous tabs because of rules on how the JavaFX Scenegraph works.
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.
So the binding is working, but, due to the above rule and the way you have written the rest of your code, the program as a whole is likely not working as you expected or wished.
If you are still having issues, update your question with an mcve.

Explicitly positioning nodes in JavaFX

When I have clicked a button, it changes its position.
But when I move the mouse, the button comes back to the center of the scene, why?
I have the following code:
public class HolaMundo extends Application {
Button btn;
Scene scene;
#Override
public void start(Stage primaryStage) {
btn = new Button();
btn.setText("Hola Mundo");
StackPane root = new StackPane();
root.getChildren().add(btn);
scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnMouseMoved(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent t) {
btn.setText(String.valueOf(t.getX() ));
}
});
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
btn.setLayoutX(Math.random() * (300 - btn.getWidth()));
btn.setLayoutY(Math.random() * (250 - btn.getHeight()));
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Suggested approach
For your particular code snippet, perhaps using a Pane instead of a StackPane is the best approach.
Why your code doesn't do what you want
If you are going to manually set layout values, don't use a layout pane (such as a StackPane) which automatically sets layout values for you. If you use a layout pane, then the layout values you explicitly set will be overridden automatically the next time layout pane performs a layout pass (e.g. it is resized or some its content or location in the scene graph becomes dirty).
Options for explicitly laying out nodes
If you want to explicitly layout items in JavaFX do one of the following:
Subclass Region and override layoutChildren.
Place your content in a Pane if you need the container to be styled with CSS or implement resizing functions.
Place your content in a Group if you don't need the container to be styled with CSS or implement resizing functions.
Related advice on node positioning
If you want to have automatically laid out components using the predefined layout managers but you want to adjust or temporarily modify the locations of some of the components from their default positions, you can adjust the translateX/Y values rather than layoutX/Y values. According to the definition of translateX:
The node's final translation will be computed as layoutX + translateX, where layoutX establishes the node's stable position and translateX optionally makes dynamic adjustments to that position.
This variable can be used to alter the location of a node without disturbing its layoutBounds, which makes it useful for animating a node's location.
This means that the layout manager can compute a child's default position using layoutX, and you can adjust the position from the default using translateX.
Not having deeply investigated the current case, I see a difference when I use an AnchorPane instead of the StackPane to place the Button on.
By changing the label text of the Button by the mouseMoved-Event the Pane is rendered (layout is requested). With a StackPane placing all it's children in the center of itself the Button's position is reset to the center of the Pane. When you have a look into the layoutChildren method of StackPane you'll see a call to resizeRelocate. So layoutX and layoutY are reset and the button moves back to the center position (or whatever you set the alignment of the StackPane to).
So I think this is a correct behavior of the StackPane and I recommend to use another Pane, e.g. AnchorPane.

Categories

Resources