Skip to content

Commit

Permalink
Change FxViewData::rootNode type from Parent to Object (#77)
Browse files Browse the repository at this point in the history
* Change FxViewData::rootNode type from Parent to Object

* Polish code and add some tests

* throw exception instead of returning null as it's would be a programming issue

---------

Co-authored-by: Clément de Tastes <isomorphisme@gmail.com>
  • Loading branch information
Eng-Fouad and CodeSimcoe authored Oct 14, 2024
1 parent e3a96a2 commit 8ca3136
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@
import io.quarkus.runtime.Quarkus;
import io.quarkus.test.QuarkusUnitTest;
import javafx.collections.ObservableList;
import javafx.scene.Parent;

class FxViewTest {

@RegisterExtension
static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
.withApplicationRoot((jar) -> {
jar.addClasses(SampleTestController.class, SubSampleTestController.class);
jar.addClasses(SampleStageController.class, SampleDialogController.class, SampleSceneController.class);
jar.addAsResource("SampleTest.fxml");
jar.addAsResource("SampleTest.properties");
jar.addAsResource("SampleTest.css");
jar.addAsResource("SubSampleTest.fxml");
jar.addAsResource("SampleStage.css");
jar.addAsResource("SampleStage.fxml");
jar.addAsResource("SampleDialog.css");
jar.addAsResource("SampleDialog.fxml");
jar.addAsResource("SampleScene.css");
jar.addAsResource("SampleScene.fxml");
});

@Inject
Expand All @@ -46,6 +54,15 @@ class FxViewTest {
@Inject
SubSampleTestController subSampleTestController;

@Inject
SampleStageController sampleStageController;

@Inject
SampleDialogController sampleDialogController;

@Inject
SampleSceneController sampleSceneController;

private static final AtomicBoolean eventObserved = new AtomicBoolean(false);

@Test
Expand All @@ -71,13 +88,17 @@ void testFxView() throws InterruptedException {
String text = controller.label.getText();
Assertions.assertEquals("Bonjour", text);

ObservableList<String> stylesheets = viewData.getRootNode().getStylesheets();
Parent rootNode = viewData.getRootNode();
ObservableList<String> stylesheets = rootNode.getStylesheets();
Assertions.assertEquals(1, stylesheets.size());
URI uri = URI.create(stylesheets.get(0));
Path path = Path.of(uri);
Assertions.assertEquals("SampleTest.css", path.getFileName().toString());

Assertions.assertNotNull(this.subSampleTestController.button);
Assertions.assertNotNull(this.sampleStageController.stage);
Assertions.assertNotNull(this.sampleDialogController.dialog);
Assertions.assertNotNull(this.sampleSceneController.scene);
}

void observeEvent(@Observes final FxPostStartupEvent event) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkiverse.fx.deployment.fxviews;

import jakarta.inject.Singleton;

import io.quarkiverse.fx.views.FxView;
import javafx.fxml.FXML;
import javafx.scene.control.Dialog;

@FxView
@Singleton
public class SampleDialogController {

@FXML
Dialog<?> dialog;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkiverse.fx.deployment.fxviews;

import jakarta.inject.Singleton;

import io.quarkiverse.fx.views.FxView;
import javafx.fxml.FXML;
import javafx.scene.Scene;

@FxView
@Singleton
public class SampleSceneController {

@FXML
Scene scene;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkiverse.fx.deployment.fxviews;

import jakarta.inject.Singleton;

import io.quarkiverse.fx.views.FxView;
import javafx.fxml.FXML;
import javafx.stage.Stage;

@FxView
@Singleton
public class SampleStageController {

@FXML
Stage stage;
}
Empty file.
20 changes: 20 additions & 0 deletions deployment/src/test/resources/SampleDialog.fxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Dialog?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<Dialog xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" width="720.0" height="480.0"
fx:id="dialog" fx:controller="io.quarkiverse.fx.deployment.fxviews.SampleDialogController">
<dialogPane>
<DialogPane>
<content>
<BorderPane>
<center>
<Label text="Hello World"/>
</center>
</BorderPane>
</content>
</DialogPane>
</dialogPane>
</Dialog>
Empty file.
13 changes: 13 additions & 0 deletions deployment/src/test/resources/SampleScene.fxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.Scene?>
<Scene xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1"
fx:id="scene" fx:controller="io.quarkiverse.fx.deployment.fxviews.SampleSceneController">
<BorderPane>
<center>
<Label text="Hello World"/>
</center>
</BorderPane>
</Scene>
Empty file.
9 changes: 9 additions & 0 deletions deployment/src/test/resources/SampleStage.fxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.stage.Stage?>
<Stage xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" width="1024.0" height="768.0"
fx:id="stage" fx:controller="io.quarkiverse.fx.deployment.fxviews.SampleStageController">
<scene>
<fx:include source="SampleScene.fxml" />
</scene>
</Stage>
10 changes: 4 additions & 6 deletions runtime/src/main/java/io/quarkiverse/fx/views/FxViewData.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.quarkiverse.fx.views;

import javafx.scene.Parent;

/**
* Combination of loaded FXML elements.
* Provides convenient accessors with automatic casts.
Expand All @@ -11,14 +9,14 @@ public interface FxViewData {
/**
* Root UI element accessor with automatic cast
*/
<T extends Parent> T getRootNode();
<T> T getRootNode();

/**
* Controller accessor with automatic cast
*/
<T> T getController();

static FxViewData of(final Parent rootNode, final Object controller) {
static FxViewData of(final Object rootNode, final Object controller) {
return new FxViewDataImpl(rootNode, controller);
}

Expand All @@ -28,10 +26,10 @@ 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(Object rootNode, Object controller) implements FxViewData {
@Override
@SuppressWarnings("unchecked")
public <T extends Parent> T getRootNode() {
public <T> T getRootNode() {
return (T) this.rootNode;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@
import io.quarkiverse.fx.FxViewLoadEvent;
import io.quarkiverse.fx.style.StylesheetWatchService;
import io.quarkus.runtime.LaunchMode;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Dialog;
import javafx.stage.Window;

@ApplicationScoped
public class FxViewRepository {
Expand Down Expand Up @@ -114,14 +118,15 @@ void setupViews(@Observes final FxViewLoadEvent event) {
}
loader.setLocation(url);

Parent rootNode = loader.load(stream);
Object rootNode = loader.load(stream);
if (style != null) {
ObservableList<String> styleSheets = getFxmlObjectStyleSheets(rootNode);
if (stylesheetReload) {
// Stylesheet live reload in dev mode
StylesheetWatchService.setStyleAndStartWatchingTask(rootNode::getStylesheets, style);
StylesheetWatchService.setStyleAndStartWatchingTask(() -> styleSheets, style);
} else {
// Regular setting (no live reload)
rootNode.getStylesheets().add(style);
styleSheets.add(style);
}
}

Expand Down Expand Up @@ -155,6 +160,23 @@ private static InputStream lookupResourceAsStream(final ClassLoader classLoader,
return stream;
}

private static ObservableList<String> getFxmlObjectStyleSheets(final Object rootNode) {
ObservableList<String> stylesheets;
if (rootNode instanceof Parent p) {
stylesheets = p.getStylesheets();
} else if (rootNode instanceof Window w) {
stylesheets = w.getScene().getStylesheets();
} else if (rootNode instanceof Scene s) {
stylesheets = s.getStylesheets();
} else if (rootNode instanceof Dialog<?> d) {
stylesheets = d.getDialogPane().getStylesheets();
} else {
String message = "rootNode shall be a valid UI root component (Parent, Window, Scene or Dialog)";
throw new IllegalArgumentException(message);
}
return stylesheets;
}

/**
* Retrieve view data associated to a given view name
*
Expand Down

0 comments on commit 8ca3136

Please sign in to comment.