Popping TitledPane content out to window - javafx

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).

Related

JavaFX : Mouse events for a PopOver Window (ControlsFX)

I am having the following code to display a PopOver (Custom PopUp by ControlsFX - mvn repo)
public class JavaFXApplication35 extends Application {
#Override
public void start(Stage primaryStage) {
try {
Label lblName = new Label("Tetsing name");
Label lblStreet = new Label("Some street name");
Label lblCityStateZip = new Label("Some city, 111111");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
PopOver popOver = new PopOver(vBox);
Label label = new Label("Mouse mouse over me");
label.setOnMouseEntered(mouseEvent -> {
popOver.show(label, -3);
});
label.setOnMouseExited(mouseEvent -> {
if (popOver.isShowing()) {
popOver.hide();
}
});
StackPane root = new StackPane();
root.getChildren().add(label);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest((WindowEvent event) -> {
System.exit(0);
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The problem is ,
I want the pop-up to be displayed when mouse entered the Label - works fine.
I want the pop-up to be hidden when user exits mouse from Label but not if he enters mouse in to the pop-up window.
I have added MouseEntered and MouseExited actions on Label but how can i handle the another scenario where i don't want to hide the pop-up if user enters mouse in to pop-up.
I ran into the same problem. Here is my solution. Just pass your label (or other node) and PopOver's content node as arguments to this method.
public static void addAutoHidingPopOver(Node hoverableNode, Node contentNode) {
//Creating PopOver
PopOver popOver = new PopOver(hoverableNode);
popOver.setContentNode(contentNode);
//Here you can set custom parameters of your PopOver
//...
//Mouse Actions handling
final Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(1000)));
timeline.setOnFinished(finishEvent -> {
if (hoverableNode.isHover() || contentNode.isHover()) timeline.play();
else popOver.hide();
});
hoverableNode.setOnMouseEntered(mouseEvent -> {if (!popOver.isShowing()) popOver.show(hoverableNode);});
hoverableNode.setOnMouseExited(mouseEvent -> timeline.play());
}
PopOver will be hidden after 1 sec after mouse leave hoverableNode or contentNode. Use it like this:
addAutoHidingPopOver(someLabel, someContentNode);
Note, that your content node should take all visible space of PopOver for comfort use.
That could be expected behavior. I am not sure, but here is a workaround. You can use a ToggleButton.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.PopOver;
public class App extends Application
{
#Override
public void start(Stage primaryStage)
{
//Build PopOver look and feel
Label lblName = new Label("John Doe");
Label lblStreet = new Label("123 Hello Street");
Button lblCityStateZip = new Button("MadeUpCity, XX 55555");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
//Create PopOver and add look and feel
PopOver popOver = new PopOver(vBox);
ToggleButton toggleButton = new ToggleButton("Click me!");
toggleButton.selectedProperty().addListener((obs, oldValue, newValue) -> {
if (newValue) {
popOver.show(toggleButton);
}
else {
popOver.hide();
}
});
;
StackPane root = new StackPane();
root.getChildren().add(toggleButton);
var scene = new Scene(root, 500, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

Alert box is not showing in the center of the application using JavaFX [duplicate]

This question already has answers here:
Center stage on parent stage
(2 answers)
Closed 4 years ago.
I am trying to pop-up the box in the center of the application when I resize the application or move it.
I tried with css alignment and also using Java. Is it possible if I don't use pane and directly add box in the scene?
Here is my code:
public Boolean call(String question) {
final Stage dialogStage = new Stage(StageStyle.UNDECORATED);
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(owner);
dialogStage.setTitle("ConfirmTitle"); // WIP, waiting for the strings&trans
final Button ok = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.ok")); // WIP,
// waiting
// for
// the
// strings&trans
ok.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Button cancel = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.cancel")); // WIP,
// waiting
// for the
// strings&trans
cancel.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Text text = new Text(question);
text.getStyleClass().add(HTML_POPUP_STYLE);
final Insets ins = new Insets(10);
final VBox box = new VBox();
box.setAlignment(Pos.BOTTOM_CENTER);
box.setSpacing(10);
box.setPadding(ins);
final HBox buttons = new HBox(10);
buttons.getChildren().addAll(ok, cancel);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(ins);
box.getChildren().addAll(text, buttons);
box.getStyleClass().add(HTML_POPUP_STYLE);
StackPane pane = new StackPane();
pane.setAlignment(box, Pos.CENTER);
pane.getChildren().add(box);
Scene scene = new Scene(pane);
try {
URL javafxCss = nmsGuiContainer.getBundleContext().getBundle()
.getResource(NmsGuiContainer.JAVAFX_CSS_URL);
scene.getStylesheets().add(javafxCss.toExternalForm());
} catch (Exception e) {
LOGGER.error("Cannot load the CSS file for JavaFX components ", e);
}
dialogStage.setScene(scene);
ok.setCancelButton(false);
final boolean[] res = new boolean[1];
ok.setOnAction(new CloseDialogHandler(dialogStage, res));
cancel.setCancelButton(true);
cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
dialogStage.centerOnScreen();
nmsGuiContainer.fadeContainer();
dialogStage.showAndWait();
nmsGuiContainer.unfadeContainer();
return res[0];
}
Here is a screenshot of the alertbox:
The Stage.initOwner() method does exactly what you need. While you do call it in your example code, I do not know what owner you are passing to it.
Here is a sample that demonstrates how to do this.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button btnShowAlert = new Button("Show Alert!");
// Set the action to show the alert
btnShowAlert.setOnAction(e -> {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setHeaderText("This is centered over the main window!");
alert.setContentText("Move the main window and show the alert again!");
alert.initOwner(primaryStage);
alert.showAndWait();
});
root.getChildren().add(btnShowAlert);
primaryStage.setTitle("Centered Alerts");
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(500);
primaryStage.setHeight(300);
primaryStage.show();
}
}

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);
}
}

TabPane shrink to biggest child

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();
}

JavaFX Fill empty space when component is not visible?

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.

Resources