TabPane shrink to biggest child - javafx

I am breaking my head to make this work. Basically I have 2 Tabs. Initially one of them has content which let's suppose is 100px big (I don't know the sizes in advance. This is just an example) and the other has no content.
After some event, I add to the previously empty tab something with, let's say,a 200px height.
Up to hear all good. The TabPane resizes (after me calling requestLayout() ) correctly and fits the new content.
Now if I remove the 200px content, I would like the tab pane to go back to being 100px high, i.e. the biggest item still left on the tabs, however it remains at 200px leaving me with a lot of empty space. Could someone provide me a clue of how to solve this?
Here is my Demo code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TabPaneTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Test 1");
Tab tab2 = new Tab("Test 2");
final Pane tab2Content = new Pane();
tab2Content.getStyleClass().add("tab2Content");
tab2.setContent(tab2Content);
tabPane.getTabs().addAll(tab1, tab2);
Button addSquare = new Button("Add");
addSquare.setOnAction(event -> {
final Pane tab1Content = new Pane();
tab1Content.getStyleClass().add("tab1Content");
tab1.setContent(tab1Content);
tabPane.requestLayout();
});
Button removeSquare = new Button("Remove");
removeSquare.setOnAction(event -> {
tab1.setContent(null);
tabPane.requestLayout();
});
HBox buttons = new HBox(addSquare, removeSquare);
VBox vBox = new VBox(tabPane, buttons);
Scene scene = new Scene(vBox, 500, 400);
scene.getStylesheets().add(LargeTooltipSample.class.getResource("tabPaneTest.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
}
and here is my CSS (just to better visualize the content):
.tab2Content{
-fx-background-color: red;
-fx-max-height: 100;
-fx-pref-height: 100;
-fx-max-width: 100;
-fx-pref-width: 100;
}
.tab1Content{
-fx-background-color: green;
-fx-max-height: 200;
-fx-pref-height: 200;
-fx-max-width: 200;
-fx-pref-width: 200;
}
Thanks in advance

Try this:
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Test 1");
Tab tab2 = new Tab("Test 2");
final Pane tab2Content = new Pane();
final Pane tab1Content = new Pane();
tab2Content.getStyleClass().add("tab2Content");
tab2.setContent(tab2Content);
tabPane.getTabs().addAll(tab1, tab2);
Button addSquare = new Button("Add");
Button removeSquare = new Button("Remove");
HBox buttons = new HBox(addSquare, removeSquare);
VBox vBox = new VBox(tabPane, buttons);
double[] tabPaneOriginalHeight = new double[1];//Used to hold tabPane's original height.
addSquare.setOnAction(event -> {
System.out.println(tabPane.getHeight());
tab1Content.getStyleClass().add("tab1Content");
tab1.setContent(tab1Content);
System.out.println(tabPane.getHeight());
tabPane.setPrefHeight(200);//sets the height to 200 after adding tabl1 content
tabPane.requestLayout();
});
removeSquare.setOnAction(event -> {
System.out.println(tabPane.getHeight());
tab1Content.getStyleClass().remove("tab1Content");
tab1.setContent(tab1Content);
System.out.println(tabPane.getHeight());
tabPane.setPrefHeight(100);//sets the height to 100 after removing tab1 content
tabPane.requestLayout();
});
Scene scene = new Scene(vBox, 500, 400);
scene.getStylesheets().add(JavaFXApplication16.class.getResource("tabPaneTest.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}

Related

What Layout to use for JavaFx

I've been experimenting with different layouts in order to recreate Brandi's Bagel House
However I just can't figure out what layout is being used here.
So far I've tried BorderPane, FlowPane, GridPane, HBox and VBox but I still don't get the correct layout. I'm not allowed to implement any Swing or AWT. It has to be pure JavaFX and I'm not allowed to use any GUI builder like WindowBuilder. Any tip, hints or advice to recreate this [Swing] GUI layout in JavaFX?
You have [an image of] a Swing GUI and you want to convert it to JavaFX.
You need a combination of layouts. For the root of the node graph, use a BorderPane.
The top node is a Label but since you want that Label centered, you need to place it in a HBox.
The left node is the Bagel pane. The RadioButtons are placed in a VBox.
The center node is the Toppings pane. The CheckBoxes are placed in a VBox.
The right node is the Coffee pane. The RadioButtons are placed in a VBox.
The bottom node contains the Buttons. Since you want the Buttons centered, you need to place them in a HBox.
The below code demonstrates. Note that the code for class BorderedTitledPane comes from the accepted answer to this question: GroupBox / TitledBorder in JavaFX 2?
(Since JavaFX does not have a TitledBorder as Swing does.)
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class OrdaCalc extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
Label label = new Label("Welcome to Brandi's Bagel House");
HBox labelHBox = new HBox(label);
labelHBox.setAlignment(Pos.CENTER);
root.setTop(labelHBox);
root.setLeft(createLeftPane());
root.setCenter(createCenterPane());
root.setRight(createRightPane());
root.setBottom(createBottomPane());
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox createBottomPane() {
Button calculate = new Button("Calculate");
Button exit = new Button("Exit");
HBox buttonsHBox = new HBox(10.0d, calculate, exit);
buttonsHBox.setAlignment(Pos.CENTER);
return buttonsHBox;
}
private BorderedTitledPane createCenterPane() {
CheckBox creamCheese = new CheckBox("Cream Cheese");
CheckBox butter = new CheckBox("Butter");
CheckBox peachJelly = new CheckBox("Peach Jelly");
CheckBox blueberryJam = new CheckBox("Blueberry Jam");
VBox vBox = new VBox(5.0d, creamCheese, butter, peachJelly, blueberryJam);
BorderedTitledPane center = new BorderedTitledPane("Toppings", vBox);
return center;
}
private BorderedTitledPane createLeftPane() {
RadioButton white = new RadioButton("White");
RadioButton wheat = new RadioButton("Wheat");
ToggleGroup group = new ToggleGroup();
group.getToggles().addAll(white, wheat);
VBox vBox = new VBox(30.0d, white, wheat);
BorderedTitledPane left = new BorderedTitledPane("Bagel", vBox);
return left;
}
private BorderedTitledPane createRightPane() {
RadioButton none = new RadioButton("None");
RadioButton regular = new RadioButton("Regular");
RadioButton decaf = new RadioButton("Decaf");
RadioButton cappuccino = new RadioButton("Cappuccino");
ToggleGroup group = new ToggleGroup();
group.getToggles().addAll(none, regular, decaf, cappuccino);
VBox vBox = new VBox(5.0d, none, regular, decaf, cappuccino);
BorderedTitledPane right = new BorderedTitledPane("Coffee", vBox);
return right;
}
public static void main(String[] args) {
launch(args);
}
}
class BorderedTitledPane extends StackPane {
BorderedTitledPane(String titleString, Node content) {
Label title = new Label(" " + titleString + " ");
title.getStyleClass().add("bordered-titled-title");
StackPane.setAlignment(title, Pos.TOP_CENTER);
StackPane contentPane = new StackPane();
content.getStyleClass().add("bordered-titled-content");
contentPane.getChildren().add(content);
getStyleClass().add("bordered-titled-border");
getChildren().addAll(title, contentPane);
}
}
Here is file application.css which is located in the same package as class OrdaCalc.
.bordered-titled-title {
-fx-background-color: white;
-fx-translate-y: -16;
}
.bordered-titled-border {
-fx-content-display: top;
-fx-border-insets: 20 15 15 15;
-fx-background-color: white;
-fx-border-color: black;
-fx-border-width: 2;
}
.bordered-titled-content {
-fx-padding: 26 10 10 10;
}
Here is a screen capture.

Automatically resizing the window based on dynamic content

I am checking for a feature to automatically resize the window based on the content. I am already aware of Window.sizeToScene() method. But this is so cumbersome that I need to take care in every place to call the sizeToScene(). For example, when I add/remove nodes, when I expanded the titlePanes, etc...
Can someone let me know if it is possible to automatically adjust the window based on the content. I am looking this feature for both normal and transparent windows.
Any hints/suggestion is highly appreciated.
Please find below a quick demo of what I am looking for. Everything works as expected if I consider calling sizeToScene(). I am targeting for a way to get the same behavior without calling sizeToScene for every scenario.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentWindowResizeDemo extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setSpacing(15);
root.setAlignment(Pos.CENTER);
Scene sc = new Scene(root, 300, 300);
stage.setScene(sc);
stage.show();
Button showNormal = new Button("Show Normal Window");
showNormal.setOnAction(e -> showWindow(false));
Button showTransparent = new Button("Show Transparent Window");
showTransparent.setOnAction(e -> showWindow(true));
root.getChildren().addAll(showNormal, showTransparent);
}
private void showWindow(boolean isTransparent) {
Stage window = new Stage();
VBox root = new VBox();
root.setStyle("-fx-background-color: grey; -fx-border-width:2px;-fx-border-color:black;");
Scene scene = new Scene(root, Color.TRANSPARENT);
window.setScene(scene);
VBox layout = new VBox();
layout.setSpacing(5);
layout.setPadding(new Insets(5));
CheckBox sizeToScene = new CheckBox("sizeToScene");
sizeToScene.setSelected(true);
StackPane box = new StackPane();
box.setStyle("-fx-background-color:yellow; -fx-border-width:1px;-fx-border-color:black;");
box.setMinSize(200, 100);
box.setMaxSize(200, 100);
Button add = new Button("Add Pane");
Button remove = new Button("Remove Pane");
remove.setDisable(true);
add.setOnAction(e -> {
layout.getChildren().add(box);
add.setDisable(true);
remove.setDisable(false);
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
remove.setOnAction(e -> {
layout.getChildren().remove(box);
add.setDisable(false);
remove.setDisable(true);
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
HBox btnLayout = new HBox();
btnLayout.setSpacing(5);
btnLayout.getChildren().addAll(add, remove);
StackPane tpContent = new StackPane();
tpContent.setStyle("-fx-background-color:pink; -fx-border-width:1px;-fx-border-color:black;");
tpContent.setMinSize(200, 100);
tpContent.setMaxSize(200, 100);
TitledPane tp = new TitledPane("Titled Pane", tpContent);
tp.setAnimated(false);
tp.expandedProperty().addListener((obs, oldVal, newVal) -> {
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
if (isTransparent) {
window.initStyle(StageStyle.TRANSPARENT);
StackPane close = new StackPane();
close.setMaxWidth(30);
close.setStyle("-fx-background-color:red;");
close.getChildren().add(new Label("X"));
close.setOnMouseClicked(e -> window.hide());
DoubleProperty x = new SimpleDoubleProperty();
DoubleProperty y = new SimpleDoubleProperty();
StackPane header = new StackPane();
header.setOnMousePressed(e -> {
x.set(e.getSceneX());
y.set(e.getSceneY());
});
header.setOnMouseDragged(e -> {
window.setX(e.getScreenX() - x.get());
window.setY(e.getScreenY() - y.get());
});
header.setStyle("-fx-border-width:0px 0px 2px 0px;-fx-border-color:black;");
header.setMinHeight(30);
header.setAlignment(Pos.CENTER_RIGHT);
Label lbl = new Label("I am draggable !!");
lbl.setStyle("-fx-text-fill:white;-fx-font-weight:bold;");
StackPane.setAlignment(lbl,Pos.CENTER_LEFT);
header.getChildren().addAll(lbl,close);
root.getChildren().add(header);
}
layout.getChildren().addAll(sizeToScene, btnLayout, tp);
root.getChildren().add(layout);
window.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Edit :: This is different from what I already found in other questions or with the link in the comment. The solution that is specified is what I am aware of and I already mentioned that in the question. I have a heavy screen where it contain many nodes in differnt hirerchy. I am checking for any generic solution to include in one place rather that calling from every node add/remove scenario.
I would say this is not a graceful solution (it's more like a hack), but at least it has worked using your example:
VBox root = new VBox() {
private boolean needsResize = false;
#Override
protected void layoutChildren()
{
super.layoutChildren();
if (!needsResize) {
needsResize = true;
Platform.runLater(() -> {
if (needsResize) {
window.sizeToScene();
needsResize = false;
}
});
}
}
};
Of course, you should add in the sizeToScene.isSelected() part, and you could also make this an actual subclass.

JavaFX ScrollPane and Scaling of the Content

I would like to show a photo as an ImageView in a ScrollPane with an ZoomIn and ZoomOut Function. But if I reduce by means of scale the imageview, an undesirable empty edge is created in the ScrollPane. How can you make sure that the ScrollPane is always the size of the scaled ImageView?
See the following example. For simplicity, I replaced the ImageView with a rectangle.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ScrollPaneDemo extends Application {
double scale;
Pane contPane = new Pane();
#Override
public void start(Stage primaryStage) {
BorderPane pane = new BorderPane();
ScrollPane sp = new ScrollPane();
sp.setContent(contPane);
sp.setVvalue(0.5);
sp.setHvalue(0.5);
Rectangle rec = new Rectangle(2820, 1240,Color.RED);
scale = 0.2;
contPane.setScaleX(scale);
contPane.setScaleY(scale);
contPane.getChildren().add(rec);
Button but1 = new Button("+");
but1.setOnAction((ActionEvent event) -> {
scale*=2;
contPane.setScaleX(scale);
contPane.setScaleY(scale);
});
Button but2 = new Button("-");
but2.setOnAction((ActionEvent event) -> {
scale/=2;
contPane.setScaleX(scale);
contPane.setScaleY(scale);
});
HBox buttons = new HBox(but1, but2);
pane.setTop(buttons);
pane.setCenter(sp);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
contPane scaled by using transform don't change its layoutBounds automatically. If you want not to make empty space in contPane, you'd better wrap the node in Group.
See this post. Layout using the transformed bounds
sp.setContent(new Group(contPane));
In addition, if you don't want to make empty space in ScrollPane, limit minimum scale to rate which width or height of the content fits viewport's one.
Button but1 = new Button("+");
but1.setOnAction((ActionEvent event) -> {
updateScale(scale * 2.0d);
});
Button but2 = new Button("-");
but2.setOnAction((ActionEvent event) -> {
updateScale(scale / 2.0d);
});
HBox buttons = new HBox(but1, but2);
pane.setTop(buttons);
pane.setCenter(sp);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
updateScale(0.2d);
private void updateScale(double newScale) {
scale = Math.max(newScale, Math.max(sp.getViewportBounds().getWidth() / rec.getWidth(), sp.getViewportBounds().getHeight() / rec.getHeight()));
contPane.setScaleX(scale);
contPane.setScaleY(scale);
}
Consider a case of the image is smaller than ScrollPane's viewport. Because for showing no empty space, this code will stretch contents when it doesn't have enough size.
In a case of huge images, TravisF's comment helps you.

JavaFX Node will not grow in height despite constraints when buried within several panes

I've been struggling with this issue for a couple hours and managed to reproduce the problem with the sample below. The actual program changes what is set in main.add(node, 0, 1) as the content based on what button is pressed in menu. I tried several different things such as AnchorPanes, changing setMaxHeight, and setVgrow for many different nodes in the each pane but have not been very successful. How would I make the ListView in the example fill the rest of the window height where it is located?
public class Main extends Application {
public void start(Stage stage) throws Exception {
// I've tried with this as a VBox, no effect.
GridPane main = new GridPane();
main.setGridLinesVisible(true);
main.setHgap(5);
main.setVgap(10);
// Meant to change what content is displayed in the actual program.
HBox menu = new HBox();
menu.setMaxWidth(Double.MAX_VALUE);
menu.setAlignment(Pos.CENTER);
main.add(menu, 0, 0);
GridPane.setHgrow(menu, Priority.ALWAYS);
menu.getChildren().addAll(new Button("Button 1"), new Button("Button 2"), new Button("Button 3"), new Label("Hello world!"));
// Changes often.
GridPane content = new GridPane();
main.add(content, 0, 1);
// Options for the displayed content, changes the StackPane displayed below in my actual program.
HBox submenu = new HBox();
submenu.setMaxWidth(Double.MAX_VALUE);
submenu.setAlignment(Pos.CENTER);
content.add(submenu, 0, 0);
GridPane.setHgrow(submenu, Priority.ALWAYS);
submenu.getChildren().addAll(new Button("Button A"), new Button("Button B"), new Button("Button C"), new Label("Hello world!"));
// This is a custom class extended by StackPane in my actual program. Is often over overlayed with another transparent StackPane (not relevant to the problem).
StackPane subcontent = new StackPane();
subcontent.setMaxHeight(Double.MAX_VALUE);
subcontent.setMaxWidth(Double.MAX_VALUE);
content.add(subcontent, 0, 1);
// This was meant to be a TabPane in my actual program but this has the same outcome that won't fill the rest of the window height.
ListView<String> list = new ListView<>();
list.setMaxHeight(Double.MAX_VALUE);
list.setMaxWidth(Double.MAX_VALUE);
ObservableList<String> items = FXCollections.observableArrayList();
list.setItems(items);
subcontent.getChildren().add(list);
for(int i=0; i<100; i++) {
items.add("# "+i);
}
Scene scene = new Scene(main, 900, 550);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You need both
GridPane.setVgrow(content, Priority.ALWAYS);
and
GridPane.setVgrow(subcontent, Priority.ALWAYS);
The grid panes are going to size things to their preferred heights with the default vgrow: the preferred height of content is determined by the preferred height(s) of its child nodes, and the preferred height of a ListView is a fixed (arbitrary) size (I believe 400 pixels). So if you don't instruct subcontent to grow, it will by the preferred size of the list view, and forcing content to grow will just add extra (blank) space to content: if you don't force content to grow, it will take its preferred size, which is the preferred size of subcontent plus the preferred size of submenu.
Setting the max height doesn't do anything in this example, as you just need to allow the various nodes to grow beyond their preferred size:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public void start(Stage stage) throws Exception {
// I've tried with this as a VBox, no effect.
GridPane main = new GridPane();
main.setGridLinesVisible(true);
main.setHgap(5);
main.setVgap(10);
// Meant to change what content is displayed in the actual program.
HBox menu = new HBox();
// menu.setMaxWidth(Double.MAX_VALUE);
menu.setAlignment(Pos.CENTER);
main.add(menu, 0, 0);
GridPane.setHgrow(menu, Priority.ALWAYS);
menu.getChildren().addAll(new Button("Button 1"), new Button("Button 2"), new Button("Button 3"), new Label("Hello world!"));
// Changes often.
GridPane content = new GridPane();
main.add(content, 0, 1);
// Options for the displayed content, changes the StackPane displayed below in my actual program.
HBox submenu = new HBox();
// submenu.setMaxWidth(Double.MAX_VALUE);
submenu.setAlignment(Pos.CENTER);
content.add(submenu, 0, 0);
GridPane.setHgrow(submenu, Priority.ALWAYS);
submenu.getChildren().addAll(new Button("Button A"), new Button("Button B"), new Button("Button C"), new Label("Hello world!"));
// This is a custom class extended by StackPane in my actual program. Is often over overlayed with another transparent StackPane (not relevant to the problem).
StackPane subcontent = new StackPane();
// subcontent.setMaxHeight(Double.MAX_VALUE);
// subcontent.setMaxWidth(Double.MAX_VALUE);
content.add(subcontent, 0, 1);
// This was meant to be a TabPane in my actual program but this has the same outcome that won't fill the rest of the window height.
ListView<String> list = new ListView<>();
// list.setMaxHeight(Double.MAX_VALUE);
// list.setMaxWidth(Double.MAX_VALUE);
ObservableList<String> items = FXCollections.observableArrayList();
list.setItems(items);
subcontent.getChildren().add(list);
for(int i=0; i<100; i++) {
items.add("# "+i);
}
GridPane.setVgrow(content, Priority.ALWAYS);
GridPane.setVgrow(subcontent, Priority.ALWAYS);
Scene scene = new Scene(main, 900, 550);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Popping TitledPane content out to window

I'm trying to get a UI behavior of an accordion in which the user can pop out any TitledPane to a window, and pop the window back into a TitledPane inside the accordion.
However, when popping out a collapsed TitledPane the content isn't aligned properly in the Stage, and if the no pane is expanded it won't even show at all.
Attached is a minimal example showing the problem - note that I'm keeping two placeholder panes to avoid having the content node (A VBox in my case) be in the Scene Graph more than once. I have tried setting preferredSize and visible properties on the VBox and also calling layout before and after showing, and even programmatically expanding the titled pane, but nothing seems to work.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TitledPane t1 = new TitledPane();
TitledPane t2 = new TitledPane();
Accordion accordion = new Accordion(t1, t2);
t1.setContent(buildComponent("Pane 1", t1, accordion));
t2.setContent(buildComponent("Pane 2", t2, accordion));
primaryStage.setScene(new Scene(accordion, 300, 300));
primaryStage.show();
}
private VBox buildComponent(String name, TitledPane titledPane, Accordion holder) {
final Button popout = new Button("Pop out");
titledPane.setGraphic(popout);
titledPane.setText(name);
final VBox component = new VBox(new Label(name), new TableView<>());
final Pane placeholder1 = new Pane();
final Pane placeholder2 = new Pane();
Stage st = new Stage();
st.setScene(new Scene(placeholder1, 300, 300));
popout.setOnAction(event -> {
if (!st.equals(component.getScene().getWindow())) {
holder.getPanes().remove(titledPane);
titledPane.setContent(placeholder2);
st.getScene().setRoot(component);
st.show();
}
});
st.setOnHidden(windowEvent -> {
st.getScene().setRoot(placeholder1);
titledPane.setContent(component);
holder.getPanes().add(titledPane);
});
return component;
}
public static void main(String[] args) {
launch(args);
}
}
Illustrations of results:
Result when no pane is expanded:
Result when other pane is expanded. Note how the label is not visible:
Result when pane is expanded - this is the result I want to have in all cases:
After some more playing around I've found a way to circumvent this problem, by using AnchorPanes instead of directly setting the Stage's root and the TitledPane's content:
private VBox buildComponent(String name, TitledPane titledPane, Accordion holder) {
final Button popout = new Button("Pop out");
titledPane.setGraphic(popout);
titledPane.setText(name);
final VBox component = new VBox(new Label(name), new TableView<>());
final AnchorPane placeholder1 = new AnchorPane();
final AnchorPane placeholder2 = new AnchorPane();
AnchorPane.setTopAnchor(component, 0D);
AnchorPane.setBottomAnchor(component, 0D);
AnchorPane.setLeftAnchor(component, 0D);
AnchorPane.setRightAnchor(component, 0D);
Stage st = new Stage();
st.setScene(new Scene(placeholder1, 300, 300));
titledPane.setContent(placeholder2);
placeholder2.getChildren().add(component);
popout.setOnAction(event -> {
if (!st.equals(component.getScene().getWindow())) {
holder.getPanes().remove(titledPane);
placeholder2.getChildren().clear();
placeholder1.getChildren().add(component);
st.show();
}
});
st.setOnHidden(windowEvent -> {
placeholder1.getChildren().clear();
placeholder2.getChildren().add(component);
holder.getPanes().add(titledPane);
});
return component;
}
This new buildComponent also adds the VBox to the TitledPane, so the call to TitledPane#setContent in the question should be removed.
I'm still interested to know why this problem occurs, or if there is a way to solve it without wrapping my pane with another pane (AnchorPane in my case).

Resources