I'm trying to create a custom component in JavaFX and import it to SceneBuilder. I created a project that contains only that custom component, and I want to be able to import that component into SceneBuilder. I expected to see CustomComponent
in the SceneBuilder selection. However, the custom component selection in SceneBuilder turned out to be blank! How can I solve that? Note that if I use the example described in JavaFX custom component usage in SceneBuilder, it works perfectly.
I don't have a specific FXML file that I want SceneBuilder to show correctly, I just want to import this custom component (that is named CustomComponent
) into SceneBuilder.
Here are all my project files. (Note: The Artifact ID of this project is custom-component
)
src/main/java/com/remmymilkyway/customcomponent/CustomComponent.java
package com.remmymilkyway.customcomponent;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import java.net.URL;
public class CustomComponent extends Region {
private final WebView webView;
private final WebEngine webEngine;
public CustomComponent() {
this.webView = new WebView();
this.webEngine = webView.getEngine();
URL url = getClass().getResource("/monaco_editor.html");
if (url != null) {
webEngine.load(url.toExternalForm());
}
this.getChildren().add(webView);
}
public String getEditorContent() {
return (String) webEngine.executeScript("getEditorValue()");
}
public void setEditorContent(String newValue) {
String escapedContent = newValue.replace("'", "\\'").replace("\n", "\\n");
webEngine.executeScript("setEditorValue('" + escapedContent + "');");
}
public void setFontFamily(String fontFamily) {
webEngine.executeScript("setFontFamily('" + fontFamily + "');");
}
public void setFontSize(int fontSize) {
webEngine.executeScript("setFontSize(" + fontSize + ");");
}
public void setLanguage(String languageIdentifier) {
webEngine.executeScript("setLanguage('" + languageIdentifier + "');");
}
@Override
protected void layoutChildren() {
webView.setPrefSize(getWidth(), getHeight());
webView.resize(getWidth(), getHeight());
}
}
src/main/java/resources/monaco_editor.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Monaco Editor in JavaFX</title>
<script src="monaco-editor/min/vs/loader.js"></script>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
#container {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
require.config({ paths: { 'vs': 'monaco-editor/min/vs' }});
require(['vs/editor/editor.main'], function () {
var editor = monaco.editor.create(document.getElementById('container'), {
language: 'cpp',
automaticLayout: true
});
window.getEditorValue = function () {
return editor.getValue();
}
window.setEditorValue = function (newValue) {
editor.setValue(newValue);
}
window.setFontFamily = function(fontFamily) {
editor.updateOptions({
fontFamily: fontFamily
});
};
window.setFontSize = function(fontSize) {
editor.updateOptions({
fontSize: fontSize
});
};
window.setLanguage = function(language) {
monaco.editor.setModelLanguage(editor.getModel(), language);
};
});
</script>
</body>
</html>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.remmymilkyway</groupId>
<artifactId>custom-component</artifactId>
<version>1.0-SNAPSHOT</version>
<name>custom-component</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.10.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>22.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>22.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>22.0.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>22</source>
<target>22</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>
com.remmymilkyway.customcomponent/com.remmymilkyway.customcomponent.HelloApplication
</mainClass>
<launcher>app</launcher>
<jlinkZipName>app</jlinkZipName>
<jlinkImageName>app</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<noHeaderFiles>true</noHeaderFiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
The Monaco Editor distribution files are downloaded in the folder src/main/resources/monaco-editor
.
I ran the command mvn install
and tried to import the project into SceneBuilder by clicking on the Manually add Library from repository button and imported version 1.0-SNAPSHOT
. As shown in this picture:
And I an empty selection when I clicked on the ADD JAR button.
I tried this and was also unable to get imports of components that rely on WebView to work with SceneBuilder 22.
Even though SceneBuilder will work with the in-built WebView control (it has a component that works with that), it won't work with a custom component that includes a WebView, at least from my testing. You could file a feature request with Gluon to add the support if you wish.
You can use a much simpler case to get it to fail.
This test project, roughly follows the example at:
A project with these two components will only see for import the ButtonComponent
, not the WebViewComponent
.
src/main/java/com/example/customcomponent/ButtonComponent.java
package com.example.customcomponent;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
public class ButtonComponent extends Pane {
public ButtonComponent() {
super(new Button("Button"));
}
}
src/main/java/com/example/customcomponent/WebViewComponent.java
package com.example.customcomponent;
import javafx.scene.layout.Pane;
import javafx.scene.web.WebView;
public class WebViewComponent extends Pane {
public WebViewComponent() {
super(new WebView());
}
}
src/main/java/module-info.java
module com.example.customcomponent {
requires javafx.web;
exports com.example.customcomponent;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>editor-component</artifactId>
<version>1.0-SNAPSHOT</version>
<name>editor-component</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>22.0.2</version>
</dependency>
</dependencies>
</project>
Importing into SceneBuilder from the local maven repository (after running a mvn install
on the project):
<groupId>com.example</groupId>
<artifactId>editor-component</artifactId>
<version>1.0-SNAPSHOT</version>
Available components: