From c349406902a6dbdeab75ca4b4ba61bde65fdff26 Mon Sep 17 00:00:00 2001 From: Fouad Almalki Date: Wed, 9 Oct 2024 03:41:07 +0300 Subject: [PATCH] Support injecting @FXMLLoader with @FxView --- .../io/quarkiverse/fx/FXMLLoaderProducer.java | 21 +++++++- .../java/io/quarkiverse/fx/views/FxView.java | 2 +- .../io/quarkiverse/fx/views/FxViewData.java | 31 +++++++++-- .../fx/views/FxViewRepository.java | 54 +++++++++++-------- 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/runtime/src/main/java/io/quarkiverse/fx/FXMLLoaderProducer.java b/runtime/src/main/java/io/quarkiverse/fx/FXMLLoaderProducer.java index c34192e..7ca35fd 100644 --- a/runtime/src/main/java/io/quarkiverse/fx/FXMLLoaderProducer.java +++ b/runtime/src/main/java/io/quarkiverse/fx/FXMLLoaderProducer.java @@ -1,10 +1,15 @@ package io.quarkiverse.fx; +import io.quarkiverse.fx.views.FxView; +import io.quarkiverse.fx.views.FxViewData; +import io.quarkiverse.fx.views.FxViewRepository; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.InjectionPoint; import jakarta.inject.Inject; +import javafx.application.Platform; import javafx.fxml.FXMLLoader; @ApplicationScoped @@ -13,10 +18,24 @@ public class FXMLLoaderProducer { @Inject Instance instance; + @Inject + FxViewRepository fxViewRepository; + @Produces - FXMLLoader produceFXMLLoader() { + FXMLLoader produceFXMLLoader(InjectionPoint ip) { FXMLLoader loader = new FXMLLoader(); loader.setControllerFactory(param -> this.instance.select(param).get()); + + var fxView = ip.getAnnotated().getAnnotation(FxView.class); + if (fxView != null) { + var viewData = fxViewRepository.getViewData(fxView.value()); + if (viewData != null) { + loader.setLocation(viewData.getFxmlLocation()); + loader.setResources(viewData.getBundle()); + Platform.runLater(() -> viewData.getStyleApplier().accept(loader.getRoot())); + } + } + return loader; } } diff --git a/runtime/src/main/java/io/quarkiverse/fx/views/FxView.java b/runtime/src/main/java/io/quarkiverse/fx/views/FxView.java index 012e1cb..1d4469a 100644 --- a/runtime/src/main/java/io/quarkiverse/fx/views/FxView.java +++ b/runtime/src/main/java/io/quarkiverse/fx/views/FxView.java @@ -6,7 +6,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.CLASS) -@Target({ ElementType.TYPE }) +@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER }) public @interface FxView { /** diff --git a/runtime/src/main/java/io/quarkiverse/fx/views/FxViewData.java b/runtime/src/main/java/io/quarkiverse/fx/views/FxViewData.java index 13ed09c..695607e 100644 --- a/runtime/src/main/java/io/quarkiverse/fx/views/FxViewData.java +++ b/runtime/src/main/java/io/quarkiverse/fx/views/FxViewData.java @@ -2,6 +2,10 @@ import javafx.scene.Parent; +import java.net.URL; +import java.util.ResourceBundle; +import java.util.function.Consumer; + /** * Combination of loaded FXML elements. * Provides convenient accessors with automatic casts. @@ -18,8 +22,13 @@ public interface FxViewData { */ T getController(); - static FxViewData of(final Parent rootNode, final Object controller) { - return new FxViewDataImpl(rootNode, controller); + URL getFxmlLocation(); + ResourceBundle getBundle(); + Consumer getStyleApplier(); + + static FxViewData of(final Parent rootNode, final Object controller, final URL fxmlLocation, + final ResourceBundle bundle, final Consumer styleApplier) { + return new FxViewDataImpl(rootNode, controller, fxmlLocation, bundle, styleApplier); } /** @@ -28,7 +37,8 @@ static FxViewData of(final Parent rootNode, final Object controller) { * @param rootNode : the UI root element * @param controller : associated controller */ - record FxViewDataImpl(Parent rootNode, Object controller) implements FxViewData { + record FxViewDataImpl(Parent rootNode, Object controller, URL fxmlLocation, + ResourceBundle bundle, Consumer styleApplier) implements FxViewData { @Override @SuppressWarnings("unchecked") public T getRootNode() { @@ -43,5 +53,20 @@ public T getRootNode() { public T getController() { return (T) this.controller; } + + @Override + public URL getFxmlLocation() { + return this.fxmlLocation; + } + + @Override + public ResourceBundle getBundle() { + return this.bundle; + } + + @Override + public Consumer getStyleApplier() { + return this.styleApplier; + } } } diff --git a/runtime/src/main/java/io/quarkiverse/fx/views/FxViewRepository.java b/runtime/src/main/java/io/quarkiverse/fx/views/FxViewRepository.java index a292af7..f6bf741 100644 --- a/runtime/src/main/java/io/quarkiverse/fx/views/FxViewRepository.java +++ b/runtime/src/main/java/io/quarkiverse/fx/views/FxViewRepository.java @@ -1,5 +1,17 @@ package io.quarkiverse.fx.views; +import io.quarkiverse.fx.FxViewLoadEvent; +import io.quarkiverse.fx.style.StylesheetWatchService; +import io.quarkus.logging.Log; +import io.quarkus.runtime.LaunchMode; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import org.jboss.logging.Logger; + import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -12,19 +24,7 @@ import java.util.MissingResourceException; import java.util.Objects; import java.util.ResourceBundle; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; - -import org.jboss.logging.Logger; - -import io.quarkiverse.fx.FxViewLoadEvent; -import io.quarkiverse.fx.style.StylesheetWatchService; -import io.quarkus.runtime.LaunchMode; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; +import java.util.function.Consumer; @ApplicationScoped public class FxViewRepository { @@ -115,20 +115,30 @@ void setupViews(@Observes final FxViewLoadEvent event) { loader.setLocation(url); Parent rootNode = loader.load(stream); - if (style != null) { - if (stylesheetReload) { - // Stylesheet live reload in dev mode - StylesheetWatchService.setStyleAndStartWatchingTask(rootNode::getStylesheets, style); - } else { - // Regular setting (no live reload) - rootNode.getStylesheets().add(style); + + final String stylesheet = style; + Consumer styleApplier = node -> { + if (stylesheet != null) { + if (stylesheetReload) { + // Stylesheet live reload in dev mode + try { + StylesheetWatchService.setStyleAndStartWatchingTask(node::getStylesheets, stylesheet); + } catch (IOException e) { + Log.errorf(e, "Failed to load stylesheet (%s)", stylesheet); + } + } else { + // Regular setting (no live reload) + node.getStylesheets().add(stylesheet); + } } - } + }; + styleApplier.accept(rootNode); + Object controller = loader.getController(); // Register view - FxViewData viewData = FxViewData.of(rootNode, controller); + FxViewData viewData = FxViewData.of(rootNode, controller, url, bundle, styleApplier); this.viewDataMap.put(name, viewData); } catch (IOException e) {