sshahine / JFoenix

JavaFX Material Design Library
MIT License
6.27k stars 1.05k forks source link

Using fxml and adding the JFXSnackbar as a direct child of the root pane causes problems #1188

Open isen-ng opened 3 years ago

isen-ng commented 3 years ago

According to google, when I use JFXSnackbar it must be initialized with the root pane.

snackbar.registerSnackbarContainer(root);

I'm someone who prefers to write all UI based elements in fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import com.jfoenix.controls.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.example.MyController"
      fx:id="root"
      prefWidth="500">
  <JFXSnackbar fx:id="snackbar"/>
</VBox>

this causes the following exception to be thrown

Caused by: java.lang.IllegalArgumentException: Children: duplicate children added: parent = VBox[id=root]
    at javafx.scene.Parent$3.onProposedChange(Parent.java:558)
    at com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
    at com.jfoenix.controls.JFXSnackbar.registerSnackbarContainer(JFXSnackbar.java:193)

presumably due to this line of code

https://github.com/sshahine/JFoenix/blob/2dbb8515da236140c41eabe22eb549b458f1e867/jfoenix/src/main/java/com/jfoenix/controls/JFXSnackbar.java#L193

If I put the snackbar as a child of another node, it works

<?xml version="1.0" encoding="UTF-8"?>

<?import com.jfoenix.controls.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.example.MyController"
      fx:id="root"
      prefWidth="500">
  <HBox>
      <!-- this works -->
      <JFXSnackbar fx:id="snackbar"/>
  </Hbox>
</VBox>

I don't think we can do without calling registerSnackbarContainer as it also does a few other thing, so i guess the correct fix is to check if the snackbar is already a direct descendent of the root container before adding.

        if (!this.snackbarContainer.getChildren().contains(this)) {
            this.snackbarContainer.getChildren().add(this);
        }
isen-ng commented 3 years ago

Perhaps, a similar API like JFXDialog::dialogContainer is nice?