|
@@ -27,7 +27,6 @@ import javafx.beans.property.*;
|
|
|
import javafx.fxml.FXMLLoader;
|
|
|
import javafx.geometry.Pos;
|
|
|
import javafx.scene.Node;
|
|
|
-import javafx.scene.control.ToggleButton;
|
|
|
import javafx.scene.control.ToggleGroup;
|
|
|
import javafx.scene.layout.HBox;
|
|
|
import javafx.scene.layout.Pane;
|
|
@@ -39,25 +38,27 @@ import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.concurrent.Callable;
|
|
|
-import java.util.concurrent.ExecutionException;
|
|
|
import java.util.function.Supplier;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* Convenience class for creating dashboards, no more hassle on managing multiple views.
|
|
|
* <p>
|
|
|
- * This control extends {@code HBox} and has a {@code ThreadExecutorService} for loading fxml files in background
|
|
|
- * leaving the UI responsive
|
|
|
+ * This control extends {@code HBox} and relies on {@link LoaderUtils} for loading fxml files in the background
|
|
|
+ * leaving the UI responsive.
|
|
|
* <p></p>
|
|
|
- * Every time an fxml file is submitted with '{@code addItem}' a wrapper class (MFXItem) is created,
|
|
|
- * then it's sent to the '{@code load}' method which creates a {@code Task} and submits it to the executor.
|
|
|
- * <p>
|
|
|
- * Everytime an MFXItem is loaded, a new ToggleButton is added to the {@code HBox}, the button already
|
|
|
- * has a listener on the selectedProperty for switching the view and since nodes are cached the transition is faster
|
|
|
- * than loading the fxml again.
|
|
|
+ * Once everything is set up and the fxml files have been added with the various {@code addItem} methods
|
|
|
+ * to start loading the views invoke the {@link #start()} method. That method then will get all the
|
|
|
+ * {@link MFXLoaderBean}s in the views map and for each of them checks if the root has not been loaded yet,
|
|
|
+ * creates the load callable by calling {@link #buildLoadCallable(MFXLoaderBean)} and submits it to the executor in
|
|
|
+ * {@link LoaderUtils}.
|
|
|
* <p></p>
|
|
|
- * Every toggle button is part of a ToggleGroup which is modified with
|
|
|
- * the {@code ToggleButtonsUtil} class to add 'always one selected' support
|
|
|
+ * Once everything is loaded: {@link ToggleButtonsUtil#addAlwaysOneSelectedSupport(ToggleGroup)} is called, the loaded
|
|
|
+ * toggles are added to the children list and {@link #setDefault()} is called.
|
|
|
+ *
|
|
|
+ * @see LoaderUtils
|
|
|
+ * @see MFXLoaderBean
|
|
|
+ * @see ToggleButtonsUtil
|
|
|
*/
|
|
|
public class MFXHLoader extends HBox {
|
|
|
//================================================================================
|
|
@@ -109,6 +110,11 @@ public class MFXHLoader extends HBox {
|
|
|
addListeners();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Adds a listener to loadedItems to check when all the views have been loaded.
|
|
|
+ * Then: calls {@link ToggleButtonsUtil#addAlwaysOneSelectedSupport(ToggleGroup)},
|
|
|
+ * adds all the toggles to the children list, calls {@link #setDefault()}
|
|
|
+ */
|
|
|
private void addListeners() {
|
|
|
loadedItems.addListener((observable, oldValue, newValue) -> {
|
|
|
if (newValue.intValue() == idViewMap.size()) {
|
|
@@ -122,41 +128,68 @@ public class MFXHLoader extends HBox {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public void addItem(ToggleButton button, URL fxmlFile) {
|
|
|
- addItem(LoaderUtils.generateKey(fxmlFile), button, fxmlFile, false);
|
|
|
- }
|
|
|
-
|
|
|
- public void addItem(ToggleButton button, URL fxmlFile, boolean defaultRoot) {
|
|
|
- addItem(LoaderUtils.generateKey(fxmlFile), button, fxmlFile, defaultRoot);
|
|
|
+ /**
|
|
|
+ * Gets the built {@code MFXLoaderBean} from the builder,
|
|
|
+ * adds the toggle to the loader's toggle group and puts
|
|
|
+ * the bean in the {@code idViewMap} with a key generated by
|
|
|
+ * {@link LoaderUtils#generateKey(URL)}.
|
|
|
+ */
|
|
|
+ public void addItem(MFXLoaderBean.Builder builder) {
|
|
|
+ MFXLoaderBean loaderBean = builder.get();
|
|
|
+ LoaderUtils.checkFxmlFile(loaderBean.getFxmlURL());
|
|
|
+ loaderBean.getButton().setToggleGroup(toggleGroup);
|
|
|
+ idViewMap.put(LoaderUtils.generateKey(loaderBean.getFxmlURL()), loaderBean);
|
|
|
}
|
|
|
|
|
|
- public void addItem(String key, ToggleButton button, URL fxmlFile, boolean defaultRoot) {
|
|
|
- LoaderUtils.checkFxmlFile(fxmlFile);
|
|
|
- MFXLoaderBean loaderBean = new MFXLoaderBean(button, fxmlFile, defaultRoot);
|
|
|
+ /**
|
|
|
+ * Gets the built {@code MFXLoaderBean} from the builder,
|
|
|
+ * adds the toggle to the loader's toggle group and puts
|
|
|
+ * the bean in the {@code idViewMap} with the specified key.
|
|
|
+ */
|
|
|
+ public void addItem(String key, MFXLoaderBean.Builder builder) {
|
|
|
+ MFXLoaderBean loaderBean = builder.get();
|
|
|
+ LoaderUtils.checkFxmlFile(loaderBean.getFxmlURL());
|
|
|
loaderBean.getButton().setToggleGroup(toggleGroup);
|
|
|
idViewMap.put(key, loaderBean);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Checks if there is a {@code MFXLoaderBean} which has the defaultRoot flag set to true
|
|
|
+ * and sets the bean's toggle state to selected so that the view is shown, this is handled on
|
|
|
+ * the JavaFX's thread.
|
|
|
+ */
|
|
|
private void setDefault() {
|
|
|
idViewMap.values().stream()
|
|
|
.filter(MFXLoaderBean::isDefault)
|
|
|
.findFirst().ifPresent(loaderBean -> Platform.runLater(() -> loaderBean.getButton().setSelected(true)));
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Starts the loading process.
|
|
|
+ * <p></p>
|
|
|
+ * Retrieves the {@link MFXLoaderBean}s in the idViewMa, for each of them
|
|
|
+ * checks if the node has been already loaded, if not then calls {@link #buildLoadCallable(MFXLoaderBean)},
|
|
|
+ * submit the callable to {@link LoaderUtils#submit(Callable)}, then increments the loadedItems counter.
|
|
|
+ */
|
|
|
public void start() {
|
|
|
+ if (contentPane == null) {
|
|
|
+ throw new NullPointerException("Content pane has not been set!");
|
|
|
+ }
|
|
|
+
|
|
|
List<MFXLoaderBean> loaderBeans = new ArrayList<>(idViewMap.values());
|
|
|
for (MFXLoaderBean loaderBean : loaderBeans) {
|
|
|
- if (loaderBean.getRoot() == null){
|
|
|
- try {
|
|
|
- LoaderUtils.submit(buildLoadCallable(loaderBean)).get();
|
|
|
- } catch (InterruptedException | ExecutionException ex) {
|
|
|
- ex.printStackTrace();
|
|
|
- }
|
|
|
+ if (loaderBean.getRoot() == null) {
|
|
|
+ LoaderUtils.submit(buildLoadCallable(loaderBean));
|
|
|
loadedItems.set(loadedItems.get() + 1);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Builds the callable which loads the fxmlFile with {@link LoaderUtils#fxmlLoad(FXMLLoader, MFXLoaderBean)}
|
|
|
+ * or {@link LoaderUtils#fxmlLoad(MFXLoaderBean)} if the FXMLLoader supplier is null.
|
|
|
+ * When the file is loaded adds a listener to the toggle's selected property to handle the view switching.
|
|
|
+ */
|
|
|
private Callable<Node> buildLoadCallable(MFXLoaderBean loaderBean) {
|
|
|
return () -> {
|
|
|
Node root;
|
|
@@ -168,8 +201,8 @@ public class MFXHLoader extends HBox {
|
|
|
loaderBean.setRoot(root);
|
|
|
|
|
|
loaderBean.getButton().selectedProperty().addListener((observable, oldValue, newValue) -> {
|
|
|
- if (isAnimated.get()) {
|
|
|
- animationType.build(loaderBean.getRoot(), animationMillis.doubleValue()).play();
|
|
|
+ if (isIsAnimated()) {
|
|
|
+ getAnimationType().build(loaderBean.getRoot(), getAnimationMillis()).play();
|
|
|
}
|
|
|
if (newValue) {
|
|
|
try {
|
|
@@ -183,14 +216,24 @@ public class MFXHLoader extends HBox {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @return the {@code MFXLoaderBean} to which the specified key is mapped,
|
|
|
+ * or null if this map contains no mapping for the key.
|
|
|
+ */
|
|
|
public MFXLoaderBean getLoadItem(String key) {
|
|
|
return this.idViewMap.get(key);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @return the pane on which the views are switched.
|
|
|
+ */
|
|
|
public Pane getContentPane() {
|
|
|
return contentPane;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the pane on which the views are switched.
|
|
|
+ */
|
|
|
public void setContentPane(Pane contentPane) {
|
|
|
this.contentPane = contentPane;
|
|
|
}
|
|
@@ -211,6 +254,9 @@ public class MFXHLoader extends HBox {
|
|
|
return isAnimated.get();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Specifies if the view switching is animated.
|
|
|
+ */
|
|
|
public BooleanProperty isAnimatedProperty() {
|
|
|
return isAnimated;
|
|
|
}
|
|
@@ -223,6 +269,9 @@ public class MFXHLoader extends HBox {
|
|
|
return animationMillis.get();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Specified the switch animation duration.
|
|
|
+ */
|
|
|
public DoubleProperty animationMillisProperty() {
|
|
|
return animationMillis;
|
|
|
}
|
|
@@ -235,6 +284,9 @@ public class MFXHLoader extends HBox {
|
|
|
return animationType;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the switch animation type.
|
|
|
+ */
|
|
|
public void setAnimationType(MFXAnimationFactory animationType) {
|
|
|
this.animationType = animationType;
|
|
|
}
|