JavaFX Fill empty space when component is not visible? - javafx

i use Linux Suse 12.3, JDK 1.7.0-45, JavaFX 2.2.
my Question is: why the following Code not working and how to implement a toggleShow/hide functionality?
here is my Test Code:
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.ToolBar;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.HTMLEditor;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) {
AnchorPane root = new AnchorPane();
BorderPane inner = new BorderPane();
AnchorPane.setTopAnchor(inner, 0.0);
AnchorPane.setRightAnchor(inner, 0.0);
AnchorPane.setBottomAnchor(inner, 0.0);
AnchorPane.setLeftAnchor(inner, 0.0);
final HTMLEditor center = new HTMLEditor();
final ToolBar top = new ToolBar();
final Button button = new Button("hide");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
top.setVisible(false);
//center.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
}
});
center.setManaged(false);
top.getItems().add(button);
//top.managedProperty().bind(top.visibleProperty());
top.setManaged(false);
inner.setTop(top);
inner.setCenter(center);
root.getChildren().add(inner);
Scene scene = new Scene(root,600,400);
stage.setScene(scene);
stage.show();
}
}
what i want is the same effect as the Solution of Sergey to this Question but without changing width/height!:
How to solve the overlapping of the controls each other belonging to two different panes
as i said its just a Test Code. i tried using another Layouts as BorderPane but still not working. i don't want to recalculate the size's manually ...etc. Removing the node and adding it again is not an option for me.
whats wrong in my Code? any idea is welcomed!
thanks

Filling the empty space with the usage of BorderPane seems to be not an option, due to the prompt in its javadoc:
BorderPane lays out each child set in the five positions regardless of
the child's visible property value; unmanaged children are ignored.
Additionally, using AnchorPane just for resizable content as:
AnchorPane.setTopAnchor(inner, 0.0);
AnchorPane.setRightAnchor(inner, 0.0);
AnchorPane.setBottomAnchor(inner, 0.0);
AnchorPane.setLeftAnchor(inner, 0.0);
seems to be an overusing. Just using the VBox will be more suitable for your layout case.
Rewritten test code:
#Override
public void start(final Stage stage) {
final HTMLEditor center = new HTMLEditor();
final ToolBar top = new ToolBar();
final Button button = new Button("hide");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
top.setVisible(false);
top.setManaged(false);
}
});
top.getItems().add(button);
VBox inner = new VBox();
inner.getChildren().addAll(top, center);
Scene scene = new Scene(inner, 600, 400);
stage.setScene(scene);
stage.show();
}
Now the question is;
How are you going to implement the "show" part of your "show/hide" toggle bar? Since there is no clue about it in your question.

Try to remove the top.setManaged(false); line and then put the toolbar into a Group object like this:
Group g = new Group();
Region spacer = new Region();
spacer.setPrefWidth(10000);
spacer.setMinWidth(100);
top.getItems().add(spacer);
g.getChildren().add(top); //Toolbar here...
inner.setTop(g); //set Group into Borderpane instead of the toolbar
After clicking the button the free space is consumed entirely by the HTML-Editor.

Related

JavaFX HBox layout binding as Circle

I'm having a problem positioning JavaFX's HBox in a similar manner to Circle.
If using a circle shape it is possible to manually position it such that it is bound to a different node. This is what I've done until now, by having a Pane as the point of reference:
Pane node; //can be dragged around/resized
//...
Circle terminal = new Circle(10);
terminal.setStroke(Color.GREEN);
terminal.setFill(Color.GREEN);
terminal.centerXProperty().bind( node.layoutXProperty() );
terminal.centerYProperty().bind( node.layoutYProperty() );
The pane (node) functions as a graph node and can be dragged around and resized. The circle functions as a port/terminal for edge connections in the graph. Seeing that the node should have more than one the idea is to put the circles into an HBox that is attached/bound to the pane like the circle has until now. This makes it so that manual layout calculations are unnecessary when adding or removing ports, resizing the node, etc. So the code then used was:
Pane node; //can be dragged around/resized
//...
HBox terminalContainer = new HBox();
terminalContainer.layoutXProperty().bind( node.layoutXProperty() );
terminalContainer.layoutYProperty().bind( node.layoutYProperty() );
//... adding circles into HBox as scenegraph children
The only difference is swapping out the HBox for the Circle and using the layoutXProperty() as there is no centerXProperty(). But of course this fails, and the ports appear glued on to the top part of the containing frame, acting strangely. Is there a fix for this? I tried changing the parenting Pane to an anchorPane, this allowed to manually anchor down the HBox in the correct place, but caused issues with the resizing/dragging code.
Minimal example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main2 extends Application {
private AnchorPane component;
#Override
public void start(Stage primaryStage) {
component = new AnchorPane();
Scene scene = new Scene(component, 1024, 768);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
//This works, but is hard to maintain
Cell c1 = new Cell();
Cell c2 = new Cell();
Port p1 = new Port(c1);
Port p2 = new Port(c2);
component.getChildren().addAll(c1, c2, p1, p2);
c1.relocate(150, 150);
c2.relocate(550, 550);
//This does not work, even if unbinding circles, but is simpler
HBox pc1 = new HBox();
HBox pc2 = new HBox();
pc1.layoutXProperty().bind( c1.layoutXProperty() );
pc1.layoutYProperty().bind( c1.layoutYProperty() );
pc2.layoutXProperty().bind( c2.layoutXProperty() );
pc2.layoutYProperty().bind( c2.layoutYProperty() );
Port p3 = new Port(c1);
Port p4 = new Port(c2);
pc1.getChildren().add(p3);
pc2.getChildren().add(p4);
component.getChildren().addAll(pc1, pc2);
}
class Cell extends Pane {
public Cell() {
Rectangle view = new Rectangle(50,50);
view.setStroke(Color.DODGERBLUE);
view.setFill(Color.DODGERBLUE);
getChildren().add(view);
}
}
class Port extends Pane {
public Port(Cell owner) {
Circle view = new Circle(10);
view.setStroke(Color.GREEN);
view.setFill(Color.GREEN);
view.centerXProperty().bind( owner.layoutXProperty() );
view.centerYProperty().bind( owner.layoutYProperty() );
getChildren().add(view);
}
}
public static void main(String[] args) {
launch(args);
}
}
Got it to work, was a typo in the code binding the layoutXProperty twice instead of the layoutYProperty facepalm

JavaFX: Determine Bounds of a node while being invisible?

is there any way to determine the bounds (especially height and width) of a node which is already attached to a scene but set to invisible?
I want to show a label on screen only if its width exceeds 100px... but it is always 0:
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root, 500, 500, Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
Label n = new Label();
n.setVisible(false);
n.setStyle("-fx-background-color: red;");
root.getChildren()
.addAll(n);
n.textProperty()
.addListener((v, ov, nv) -> {
System.out.println(n.getBoundsInParent());
n.setVisible(n.getWidth() > 100);
});
n.setText("TEST11111111111111111111111");
}
The result of the sysout: (also n.getWidth() is no better)
BoundingBox [minX:0.0, minY:0.0, minZ:0.0, width:0.0, height:0.0, depth:0.0, maxX:0.0, maxY:0.0, maxZ:0.0]
Is there any trick ?
Thanks all!
Your problem is that you are listening for changes to the text property and expecting the width of the node to be updated at that time - but it's not. The width of nodes are only calculated and set during a render pass which consists of an applyCSS and layout routine (see: Get the height of a node in JavaFX (generate a layout pass)). Your code incorrectly sets the node to invisible before the updated size of the node is calculated.
Instead of using a listener on the text property to determine visibility of the node, I suggest that you use a binding expression to create a direct binding on the visibility property to the desired width property. An example of this approach is provided below. You can see that the label only displays when the text to display is longer than the required width (in this case 100 pixels).
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class BoundSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Scene scene = new Scene(root, 200, 100);
primaryStage.setScene(scene);
primaryStage.show();
Label n = new Label();
n.setVisible(false);
n.visibleProperty().bind(n.widthProperty().greaterThan(100));
TextField textField = new TextField("TEST11111111111111111111111");
n.textProperty().bind(textField.textProperty());
textField.relocate(0, 50);
root.getChildren().addAll(n, textField);
}
}

how to fix javafx - tableview size to current window size

I'm totally new to standalone applications. Please any one help me on this.
I have TableView with 6 columns which is half showing in the window as shown below.
I want to fix its current window size, even when the window is expanded, the tableview should auto resize. Is there any way to do this?
This Is The Code Snippet
GridPane tableGrid= new GridPane();
tableGrid.setVgap(10);
tableGrid.setHgap(10);
Label schoolnameL= new Label(SCHOOL+school_id);
schoolnameL.setId("schoolLabel");
Button exportDataSheetBtn= new Button("Export In File");
tableView.setMaxWidth(Region.USE_PREF_SIZE);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableGrid.getChildren().addAll(schoolnameL,exportDataSheetBtn,tableView);
This can be done by binding the preferred height and width to the height and width of the primary stage. Here's an MCVE:
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class MCVE extends Application {
#Override
public void start(Stage stage) {
TableView<ObservableList<String>> table = new TableView<ObservableList<String>>();
// We bind the prefHeight- and prefWidthProperty to the height and width of the stage.
table.prefHeightProperty().bind(stage.heightProperty());
table.prefWidthProperty().bind(stage.widthProperty());
stage.setScene(new Scene(table, 400, 400));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Create Tabel inside GridPane and Use GridePane inside AnchorPane.Click on GridPane(capture1).
Click on Ancho Pane Constraint inside the GridPane Layout (captur2).
In table view Layout select Hgrow and Vgrow as 'Always'(capture3)
VBox.setVgrow(tableView, Priority.ALWAYS);
OR for your parent layouts:
VBox.setVgrow({{PARENT}}, Priority.ALWAYS);
It fixed for me:
You could use a ScrollPane as the root of the scene and put everything else inside it. Then set the property setFitToWidth and setFitToHeight to true and all the content inside the ScrollPane will be stretched to fit the ScrollPane size and the ScrollPane will fit the Scene since its a Layout Pane. It will also show ScrollBars if the user resizes the window to be smaller than the contents minWidth, so the content doesnt get cut off!
#Override
public void start(Stage stage) {
TableView<ObservableList<String>> table = new TableView<ObservableList<String>>();
table.setMinWidth(400);
ScrollPane sp = new ScrollPane(table);
sp.setFitToHeight(true);
sp.setFitToWidth(true);
stage.setScene(new Scene(table, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
I copied parts of the MCVE From Jonathan's answer, hope you dont mind Jonathan :)
For more general tips for making resizable GUIs check this post!

How to get the size of a label before it is laid out?

I read this old answer on how to accomplish this. But since it involves using impl_processCSS(boolean), a method that is now deprecated, I think we need to update the answer.
I've tried placing the label inside a HBox and then get the size of it, or getting the size of the HBox, without any luck. And I've also tried using .label.getBoundsInLocal().getWidth().
SSCCE:
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.application.Application;
import javafx.scene.Scene;
public class SSCCE extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
HBox root = new HBox();
Label label = new Label("foo");
System.out.println(label.getWidth());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Since JavaFX 8, the method you are looking for is applyCss().
As JavaDoc states:
apply styles to this Node and its children, if any. This method does not normally need to be invoked directly but may be used in conjunction with Parent.layout() to size a Node before the next pulse, or if the Scene is not in a Stage.
So you need to have the node in the container, and this one already on the scene, and also call layout().
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Label label = new Label("foo bla bla");
root.getChildren().add(label);
Scene scene = new Scene(root);
root.applyCss();
root.layout();
System.out.println(label.getWidth());
primaryStage.setScene(scene);
primaryStage.show();
}
Being the output: 17.818359375
Note I've changed HBox for Group, since:
a Group will "auto-size" its managed resizable children to their preferred sizes during the layout pass to ensure that Regions and Controls are sized properly as their state changes
If you use an HBox, you need to set also the dimensions of the scene:
#Override
public void start(Stage primaryStage) {
HBox root = new HBox();
Label label = new Label("foo");
root.getChildren().add(label);
Scene scene = new Scene(root,100,20);
root.applyCss();
root.layout();
System.out.println(label.getWidth());
primaryStage.setScene(scene);
primaryStage.show();
}
Now the output is: 18.0

usrpagestage.initStyle(StageStyle.UTILITY) does not displaying contents fully

Am in attempt to design a pop up window. I designed it and it works, but with a small problem.
This is a part of code of the pop up window :
public class Warning extends BorderPane {
public Warning() {
setCenter(addVBox2());
}
private VBox addVBox2() {
VBox vbox = new VBox();
vbox.setPadding(new Insets(15,10,15,10));
vbox.setSpacing(10);
Label l1 = new Label("WARNING");
l1.setFont(Font.font("Calibri", FontWeight.BOLD, 20));
l1.setTextFill(Color.BLACK);
l1.setUnderline(true);
Label l2 = new Label("Try other User Name..!");
l2.setFont(Font.font("Calibri", FontWeight.BOLD, 18));
l2.setTextFill(Color.RED);
vbox.getChildren().addAll(l1, l2);
return vbox;
}
And this is how I call it :
setEffect(new BoxBlur(5, 10, 10));
Stage usrpagestage = new Stage();
usrpagestage.setMaxHeight(100);
usrpagestage.setMaxWidth(300);
usrpagestage.initStyle(StageStyle.UTILITY);
usrpagestage.setScene(new Scene(new Warning()));
usrpagestage.show();
usrpagestage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
setEffect(new BoxBlur(0, 0, 0));
}
});
The pop up windows works when it is supposed to. But the contents in it is not fully displayed. This is the screen shot :
How to solve this issue ?
Remove the two lines:
usrpagestage.setMaxHeight(100);
usrpagestage.setMaxWidth(300);
After that, the stage will revert to its default behavior of automatically sizing to fit the initial scene content.
No, it didn't worked.
Yep, it didn't worked at all :-)
It should have worked (letting the stage autosize itself should be the correct solution).
It didn't work because of a bug in the JavaFX layout libraries:
RT-31665 Size of stage with style UTILITY is not correctly calculated.
You can workaround the bug by manually calling stage.sizeToScene().
Sample Code
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
import javafx.scene.web.WebView;
import javafx.stage.*;
public class WarningSample extends Application {
#Override public void start(Stage stage) throws Exception {
final WebView view = new WebView();
view.getEngine().load("http://www.google.com");
stage.setScene(new Scene(view));
stage.show();
Stage warningStage = new Stage();
warningStage.initStyle(StageStyle.UTILITY);
warningStage.setScene(new Scene(new Warning()));
// this workaround allows the stage to be sized correctly.
warningStage.sizeToScene();
warningStage.show();
}
public static void main(String[] args) { launch(); }
private class Warning extends VBox {
public Warning() {
setPadding(new Insets(15, 10, 15, 10));
setSpacing(10);
Label heading = new Label("WARNING");
heading.setFont(Font.font("Calibri", FontWeight.BOLD, 20));
heading.setTextFill(Color.BLACK);
heading.setUnderline(true);
Label body = new Label("Try other User Name..!");
body.setFont(Font.font("Calibri", FontWeight.BOLD, 18));
body.setTextFill(Color.RED);
getChildren().addAll(heading, body);
}
}
}

Resources