I have a list items I want to display in a TabPane, and just re-use the same viewModel and Gui, and just switch between which one is "current" at the moment seeing only 1 tab can be seen at a time.
When the 2nd tab is taller than the 1st, and I switch to it, it does not expand and truncates the panel. But, if I switch back to the 1st panel and then back to 2nd, it finally resizes itself properly.
I believe the issue is with the Dialog, not the TabPane; if I do not use a Dialog to display, it resizes correctly (final boolean showDialog = false in TabApps.java below, note this requires line 61 in TabsMainController.java to be commented out, the one ending in //DialogPane
).
Screen Shots: Dialog:
No dialog:
EDIT: I added in some change listeners that print out the height property:
showDialog = true
(initial load)
Root Tab Container Height Changed: old=0.0, new=83.0
Individual Tab Content Height Changed; old=0.0, new=34.0
-----=======Selected Tab Changed=======-----
(select 2nd tab 1st time)
Individual Tab Content Height Changed; old=34.0, new=85.0
-----=======Selected Tab Changed=======-----
(go back to 1st tab)
Individual Tab Content Height Changed; old=85.0, new=34.0
-----=======Selected Tab Changed=======-----
(select 2nd tab 2nd time)
Root Tab Container Height Changed: old=83.0, new=134.0
Individual Tab Content Height Changed; old=34.0, new=85.0
showDialog = false
(initial load)
Root Tab Container Height Changed: old=0.0, new=63.0
Individual Tab Content Height Changed; old=0.0, new=34.0
-----=======Selected Tab Changed=======-----
(select 2nd tab 1st time)
Root Tab Container Height Changed: old=63.0, new=114.0
Individual Tab Content Height Changed; old=34.0, new=85.0
I have written a sample program to show this:
TabsApp.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.stage.Stage;
public class TabsApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Thing thing1 = new Thing("Thing #1", Arrays.asList("attr1"));
Thing thing2 = new Thing("Thing #2", Arrays.asList("attrA", "attrB", "attrC", "attrD"));
List<Thing> things = new ArrayList<>();
things.add(thing1);
things.add(thing2);
ThingView thingView = new ThingView();
thingView.show(thing1);
FXMLLoader individualLoader = new FXMLLoader(getClass().getResource("individual_tab.fxml"));
individualLoader.setController(new IndividualTabController(thingView));
Node individualTab = individualLoader.load();
FXMLLoader rootLoader = new FXMLLoader(getClass().getResource("tabs_main.fxml"));
rootLoader.setController(new TabsMainController(things, individualTab, thingView));
Node tabsRoot = rootLoader.load();
final boolean showDialog = true;
if (showDialog) {
ButtonType selectButtonType = new ButtonType("Select", ButtonData.OK_DONE);
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("Select A Service");
dialog.getDialogPane().setContent(tabsRoot);
dialog.getDialogPane().getButtonTypes().add(selectButtonType);
dialog.getDialogPane().lookupButton(selectButtonType).setDisable(false);
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent()) {
System.out.println(result.get());
}
} else {
Scene scene = new Scene((Parent) tabsRoot);
primaryStage.setTitle("Tabs Halp");
primaryStage.setScene(scene);
primaryStage.show();
}
}
public static void main(String[] args)
{
launch();
}
}
IndividualTabController.java
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
public class IndividualTabController {
@FXML
protected HBox root;
@FXML
protected Label thingName;
@FXML
protected VBox thingAttributes;
private final ThingView view;
public IndividualTabController(ThingView view) {
this.view = view;
}
@FXML
public void initialize() {
thingName.textProperty().bind(view.getName());
Bindings.bindContentBidirectional(thingAttributes.getChildren(), view.getAttributes());
root.heightProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
System.out.println("Individual Tab Content Height Changed; old=" + oldValue.doubleValue() +", new=" + newValue.doubleValue());
}
});
}
}
TabsMainController.java
package halp;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class TabsMainController {
@FXML
protected TabPane tabsRoot;
private final List<Thing> things;
private final Node individualPanel;
private ThingView view;
public TabsMainController(List<Thing> things, Node individualPanel, ThingView view) {
this.things = things;
this.individualPanel = individualPanel;
this.view = view;
}
@FXML
public void initialize() {
for (Thing thing : things) {
tabsRoot.getTabs().add(new Tab(thing.getName()));
}
tabsRoot.getSelectionModel().getSelectedItem().setContent(individualPanel);
tabsRoot.heightProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
System.out.println("Root Tab Container Height Changed: old=" + oldValue.doubleValue() +", new=" + newValue.doubleValue());
}
});
tabsRoot.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
@Override
public void changed(ObservableValue<? extends Tab> observable,
Tab oldValue, Tab newValue) {
System.out.println("-----=======Selected Tab Changed=======-----");
if (oldValue == newValue) {
return;
}
int selected = tabsRoot.getSelectionModel().getSelectedIndex();
view.show(things.get(selected));
individualPanel.getScene().getWindow().sizeToScene();
tabsRoot.getScene().getWindow().sizeToScene();
tabsRoot.getParent().getScene().getWindow().sizeToScene(); //DialogPane
oldValue.contentProperty().setValue(null);
newValue.setContent(individualPanel);
}
});
}
}
you can see here, in the changed
method that I try the solution here by calling sizeToScene() on the DialogPane's Scene's Window, but it didn't appear to change the behaviour.
Thing.java
package halp;
import java.util.List;
public class Thing {
private final String name;
private final List<String> attributes;
public Thing(String name, List<String> attributes) {
this.name = name;
this.attributes = attributes;
}
public String getName() {
return name;
}
public List<String> getAttributes() {
return attributes;
}
}
ThingView.java
import java.util.stream.Collectors;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Label;
public class ThingView {
private final StringProperty name;
private final ObservableList<Node> attributes;
public ThingView() {
name = new SimpleStringProperty();
attributes = FXCollections.observableArrayList();
}
public void show(Thing thing) {
this.name.set(thing.getName());
this.attributes.setAll(thing.getAttributes().stream().map(attr -> new Label(attr)).collect(Collectors.toList()));
}
public StringProperty getName() {
return name;
}
public ObservableList<Node> getAttributes() {
return attributes;
}
}
individual_tab.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.ToggleGroup?>
<HBox fx:id="root" xmlns:fx="http://javafx.com/fxml" spacing="10">
<!-- Overview -->
<VBox spacing="5">
<VBox>
<HBox spacing="5">
<Label text="Name"/>
<HBox>
<Label fx:id="thingName"/>
</HBox>
</HBox>
<VBox fx:id="thingAttributes">
</VBox>
</VBox>
</VBox>
</HBox>
tabs_main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.ToggleGroup?>
<TabPane fx:id="tabsRoot" xmlns:fx="http://javafx.com/fxml">
</TabPane>
I see there is a bug related to TabPane and resizing in open java fx: link
How do I make it resize correctly when a larger tab is open? Is this approach totally stupid and I should have a controller, view model and model for each tab...?