package eu.ggnet.saft.core.fx; import java.net.URL; import java.util.Objects; import java.util.concurrent.*; import java.util.function.Consumer; import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.layout.Pane; import javafx.stage.Window; import org.slf4j.LoggerFactory; import eu.ggnet.saft.api.ui.FxController; import eu.ggnet.saft.api.ui.Initialiser; /** * * @author oliver.guenther */ public class FxSaft { public static <R extends FxController> URL loadView(Class<R> controllerClazz) { if ( !controllerClazz.getSimpleName().endsWith("Controller") ) throw new IllegalArgumentException(controllerClazz + " does not end with Controller"); String head = controllerClazz.getSimpleName().substring(0, controllerClazz.getSimpleName().length() - "Controller".length()); return controllerClazz.getResource(head + "View.fxml"); } public static <T, R extends FxController> FXMLLoader constructFxml(Class<R> controllerClazz, T parameter) throws Exception { FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(loadView(controllerClazz), "No View for " + controllerClazz)); loader.load(); R controller = Objects.requireNonNull(loader.getController(), "No controller based on " + controllerClazz + ". Controller set in Fxml ?"); if ( parameter != null && controller instanceof Consumer ) { try { ((Consumer<T>)controller).accept(parameter); } catch (ClassCastException e) { LoggerFactory.getLogger(FxSaft.class).warn(controller.getClass() + " implements Consumer, but not of type " + parameter.getClass()); } } return loader; } public static <T, R extends Pane> R construct(Class<R> paneClass, T parameter) throws Exception { R pane = paneClass.getConstructor().newInstance(); if ( pane instanceof Initialiser ) { ((Initialiser)pane).initialise(); } if ( parameter != null && pane instanceof Consumer ) { try { ((Consumer<T>)pane).accept(parameter); } catch (ClassCastException e) { LoggerFactory.getLogger(FxSaft.class).warn(pane.getClass() + " implements Consumer, but not of type " + parameter.getClass()); } } return pane; } /** * Dispatches the Callable to the Platform Ui Thread. * * @param <T> Return type of callable * @param callable the callable to dispatch * @return the result of the callable * @throws RuntimeException wraps InterruptedException of {@link CountDownLatch#await() } and ExecutionException of {@link FutureTask#get() } */ public static <T> T dispatch(Callable<T> callable) throws RuntimeException { try { FutureTask<T> futureTask = new FutureTask<>(callable); final CountDownLatch cdl = new CountDownLatch(1); if ( Platform.isFxApplicationThread() ) { futureTask.run(); cdl.countDown(); } else { Platform.runLater(() -> { futureTask.run(); cdl.countDown(); }); } cdl.await(); return futureTask.get(); } catch (InterruptedException | ExecutionException ex) { throw new RuntimeException(ex); } } public static Window windowAncestor(Node c) { if ( c == null ) return null; return c.getScene().getWindow(); } }