You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2022/09/01 21:54:26 UTC

[plc4x] branch develop updated: chore(plc4j/ui): Implemented a first step towards a JavaFX based GUI application for PLC4J

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new 737c332c1 chore(plc4j/ui): Implemented a first step towards a JavaFX based GUI application for PLC4J
737c332c1 is described below

commit 737c332c1b02224b2908513f68c8248cbd69d4b9
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Sep 1 23:54:14 2022 +0200

    chore(plc4j/ui): Implemented a first step towards a JavaFX based GUI application for PLC4J
---
 plc4j/tools/pom.xml                                |   1 +
 plc4j/tools/ui/pom.xml                             | 207 +++++++++++++++++++++
 .../org/apache/plc4x/java/ui/FxmlController.java   | 174 +++++++++++++++++
 .../java/org/apache/plc4x/java/ui/Plc4xUi.java     |  46 +++++
 .../main/java/org/apache/plc4x/java/ui/UiIcon.java |  54 ++++++
 .../main/resources/org/apache/plc4x/java/ui/ui.css |  18 ++
 .../resources/org/apache/plc4x/java/ui/ui.fxml     |  53 ++++++
 pom.xml                                            |   7 +
 8 files changed, 560 insertions(+)

diff --git a/plc4j/tools/pom.xml b/plc4j/tools/pom.xml
index 57df6c736..b0cfa03f5 100644
--- a/plc4j/tools/pom.xml
+++ b/plc4j/tools/pom.xml
@@ -40,6 +40,7 @@
     <module>opm</module>
     <module>plc4x-server</module>
     <module>scraper</module>
+    <module>ui</module>
   </modules>
 
 </project>
\ No newline at end of file
diff --git a/plc4j/tools/ui/pom.xml b/plc4j/tools/ui/pom.xml
new file mode 100644
index 000000000..81ab0d531
--- /dev/null
+++ b/plc4j/tools/ui/pom.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      https://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+  -->
+<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4j-tools</artifactId>
+    <version>0.10.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>plc4j-ui</artifactId>
+
+  <name>PLC4J: Tools: UI</name>
+  <description>Utility used to execute PLC4X API calls in a GUI.</description>
+
+  <properties>
+    <javafx.version>18.0.2</javafx.version>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.openjfx</groupId>
+        <artifactId>javafx-maven-plugin</artifactId>
+        <version>0.0.8</version>
+        <configuration>
+          <mainClass>org.apache.plc4x.java.ui.Plc4xUi</mainClass>
+        </configuration>
+        <executions>
+          <execution>
+            <id>run</id>
+          </execution>
+          <execution>
+            <id>debug</id>
+            <configuration>
+              <options>
+                <option>-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005</option>
+              </options>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <configuration>
+          <usedDependencies combine.children="append">
+            <usedDependency>org.apache.plc4x:plc4j-driver-ab-eth</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-ads</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-bacnet</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-can</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-c-bus</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-eip</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-firmata</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-knxnetip</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-modbus</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-opcua</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-plc4x</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-profinet</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4j-driver-s7</usedDependency>
+          </usedDependencies>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-api</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.openjfx</groupId>
+      <artifactId>javafx-base</artifactId>
+      <version>${javafx.version}</version>
+      <classifier>${javafx.classifier}</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.openjfx</groupId>
+      <artifactId>javafx-graphics</artifactId>
+      <version>${javafx.version}</version>
+      <classifier>${javafx.classifier}</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.openjfx</groupId>
+      <artifactId>javafx-controls</artifactId>
+      <version>${javafx.version}</version>
+      <classifier>${javafx.classifier}</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.openjfx</groupId>
+      <artifactId>javafx-fxml</artifactId>
+      <version>${javafx.version}</version>
+      <classifier>${javafx.classifier}</classifier>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.kordamp.ikonli</groupId>
+      <artifactId>ikonli-javafx</artifactId>
+      <version>12.3.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.kordamp.ikonli</groupId>
+      <artifactId>ikonli-core</artifactId>
+      <version>12.3.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.kordamp.ikonli</groupId>
+      <artifactId>ikonli-materialdesign-pack</artifactId>
+      <version>12.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-ab-eth</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-ads</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-bacnet</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-c-bus</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-can</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-eip</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-firmata</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-knxnetip</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-modbus</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-opcua</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-plc4x</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-profinet</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-driver-s7</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/FxmlController.java b/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/FxmlController.java
new file mode 100644
index 000000000..ede44fd7e
--- /dev/null
+++ b/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/FxmlController.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.plc4x.java.ui;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.*;
+import javafx.scene.control.cell.TextFieldTreeCell;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Paint;
+
+import javafx.util.Callback;
+import javafx.util.StringConverter;
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcDriver;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
+import org.kordamp.ikonli.javafx.FontIcon;
+import org.kordamp.ikonli.materialdesign.MaterialDesign;
+
+import java.util.concurrent.CompletableFuture;
+
+public class FxmlController {
+
+    private PlcDriverManager driverManager;
+
+    @FXML
+    public TreeView<TreeEntry> plcTreeView;
+
+    @FXML
+    public Button browseButton;
+
+    public FxmlController() {
+        driverManager = new PlcDriverManager();
+    }
+
+    @FXML
+    public void initialize() throws Exception {
+        plcTreeView.setCellFactory(new Callback<TreeView<TreeEntry>, TreeCell<TreeEntry>>() {
+            @Override
+            public TreeCell<TreeEntry> call(TreeView<TreeEntry> treeEntryTreeView) {
+                return new TextFieldTreeCell<>(new StringConverter<TreeEntry>(){
+                    @Override
+                    public String toString(TreeEntry treeEntry) {
+                        return treeEntry.getName();
+                    }
+
+                    @Override
+                    public TreeEntry fromString(String string) {
+                        return null;
+                    }
+                });
+            }
+        });
+        TreeItem<TreeEntry> rootItem = new TreeItem<>(new TreeEntry(
+            TreeEntryType.ROOT, "", "Available Drivers"));
+        rootItem.setGraphic(new FontIcon(MaterialDesign.MDI_FOLDER));
+        for (String protocolCode : driverManager.listDrivers()) {
+            PlcDriver driver = driverManager.getDriver(protocolCode);
+            TreeItem<TreeEntry> driverItem = new TreeItem<>(new TreeEntry(
+                TreeEntryType.DRIVER, driver.getProtocolCode(), driver.getProtocolName()));
+            FontIcon icon;
+            if(driver.getMetadata().canDiscover()) {
+                icon = new FontIcon(MaterialDesign.MDI_CHECK_CIRCLE);
+                icon.setIconColor(Paint.valueOf("green"));
+            } else {
+                icon = new FontIcon(MaterialDesign.MDI_MINUS_CIRCLE);
+                icon.setIconColor(Paint.valueOf("red"));
+            }
+            driverItem.setGraphic(icon);
+            rootItem.getChildren().add(driverItem);
+        }
+        rootItem.setExpanded(true);
+        plcTreeView.setRoot(rootItem);
+
+        browseButton.setDisable(true);
+    }
+
+    @FXML
+    public void handleTreeSelectionChanged(MouseEvent mouseEvent) {
+        MultipleSelectionModel<TreeItem<TreeEntry>> selectionModel = plcTreeView.getSelectionModel();
+        boolean buttonEnabled = false;
+        if(!selectionModel.getSelectedItems().isEmpty()) {
+            TreeEntry selectedItem = selectionModel.getSelectedItems().get(0).getValue();
+            switch (selectedItem.getType()) {
+                case DRIVER:
+                    try {
+                        buttonEnabled = driverManager.getDriver(selectedItem.getCode()).getMetadata().canDiscover();
+                    } catch (Exception e) {
+                        buttonEnabled = false;
+                    }
+                    break;
+                case ADDRESS:
+                    buttonEnabled = true;
+                    break;
+            }
+        }
+        browseButton.setDisable(!buttonEnabled);
+    }
+
+    @FXML
+    public void handleBrowseButtonClicked(ActionEvent actionEvent) throws Exception {
+        MultipleSelectionModel<TreeItem<TreeEntry>> selectionModel = plcTreeView.getSelectionModel();
+        if(!selectionModel.getSelectedItems().isEmpty()) {
+            TreeItem<TreeEntry> selectedTreeItem = selectionModel.getSelectedItems().get(0);
+            TreeEntry selectedItem = selectedTreeItem.getValue();
+            selectedTreeItem.setExpanded(true);
+            PlcDriver driver = driverManager.getDriver(selectedItem.getCode());
+            CompletableFuture<? extends PlcDiscoveryResponse> browseFuture = driver.discoveryRequestBuilder().build().execute();
+            browseFuture.whenComplete((response, throwable) -> {
+                if(throwable == null) {
+                    for (PlcDiscoveryItem discoveredPlc : response.getValues()) {
+                        TreeItem<TreeEntry> plcItem = new TreeItem<>(new TreeEntry(
+                            TreeEntryType.PLC, discoveredPlc.getConnectionUrl(), discoveredPlc.getName()));
+                        plcItem.setGraphic(new FontIcon(MaterialDesign.MDI_LAN_CONNECT));
+                        selectedTreeItem.getChildren().add(plcItem);
+                    }
+                }
+            });
+        }
+    }
+
+    public static enum TreeEntryType {
+        ROOT,
+        DRIVER,
+        PLC,
+        ADDRESS
+    };
+
+    public static class TreeEntry {
+
+        private final TreeEntryType type;
+
+        private final String code;
+
+        private final String name;
+
+        public TreeEntry(TreeEntryType type, String code, String name) {
+            this.type = type;
+            this.code = code;
+            this.name = name;
+        }
+
+        public TreeEntryType getType() {
+            return type;
+        }
+
+        public String getCode() {
+            return code;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+    }
+
+}
diff --git a/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/Plc4xUi.java b/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/Plc4xUi.java
new file mode 100644
index 000000000..af4e618fd
--- /dev/null
+++ b/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/Plc4xUi.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.plc4x.java.ui;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+import java.util.Objects;
+
+public class Plc4xUi extends Application {
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(Stage primaryStage) throws Exception {
+        Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("ui.fxml")));
+        Scene scene = new Scene(root, 800, 600);
+        scene.getStylesheets().add("ui.css");
+
+        primaryStage.setTitle("PLC4X UI");
+        primaryStage.setScene(scene);
+        primaryStage.show();
+    }
+
+}
diff --git a/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/UiIcon.java b/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/UiIcon.java
new file mode 100644
index 000000000..30c1de2eb
--- /dev/null
+++ b/plc4j/tools/ui/src/main/java/org/apache/plc4x/java/ui/UiIcon.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.plc4x.java.ui;
+
+import org.kordamp.ikonli.Ikon;
+
+public enum UiIcon implements Ikon {
+
+    FOLDER("ui-icon-folder", '\ue815');
+
+    private String description;
+    private char icon;
+
+    UiIcon(String description, char icon) {
+        this.description = description;
+        this.icon = icon;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public int getCode() {
+        return icon;
+    }
+
+    public static UiIcon findByDescription(String description) {
+        for (UiIcon icon : values()) {
+            if (icon.description.equals(description)) {
+                return icon;
+            }
+        }
+        throw new IllegalArgumentException("Icon not supported: " + description);
+    }
+
+}
diff --git a/plc4j/tools/ui/src/main/resources/org/apache/plc4x/java/ui/ui.css b/plc4j/tools/ui/src/main/resources/org/apache/plc4x/java/ui/ui.css
new file mode 100644
index 000000000..ba830d1f7
--- /dev/null
+++ b/plc4j/tools/ui/src/main/resources/org/apache/plc4x/java/ui/ui.css
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
diff --git a/plc4j/tools/ui/src/main/resources/org/apache/plc4x/java/ui/ui.fxml b/plc4j/tools/ui/src/main/resources/org/apache/plc4x/java/ui/ui.fxml
new file mode 100644
index 000000000..f64441743
--- /dev/null
+++ b/plc4j/tools/ui/src/main/resources/org/apache/plc4x/java/ui/ui.fxml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      https://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+  -->
+
+<?import javafx.scene.control.*?>
+<?import java.lang.*?>
+<?import javafx.scene.layout.*?>
+
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
+            fx:controller="org.apache.plc4x.java.ui.FxmlController"
+            xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
+   <left>
+      <VBox prefHeight="600.0" prefWidth="300.0" BorderPane.alignment="CENTER">
+         <children>
+            <TreeView fx:id="plcTreeView" prefHeight="573.0" onMouseClicked="#handleTreeSelectionChanged"/>
+            <Button fx:id="browseButton" mnemonicParsing="false" onAction="#handleBrowseButtonClicked" prefHeight="27.0" prefWidth="300.0" text="Browse..." />
+         </children>
+      </VBox>
+   </left>
+   <center>
+      <TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
+        <tabs>
+          <Tab text="Untitled Tab 1">
+            <content>
+              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
+            </content>
+          </Tab>
+          <Tab text="Untitled Tab 2">
+            <content>
+              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
+            </content>
+          </Tab>
+        </tabs>
+      </TabPane>
+   </center>
+</BorderPane>
diff --git a/pom.xml b/pom.xml
index b25fab9b4..05557da50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1584,6 +1584,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-linux-x86_64/bin/</cmake.root>
         <cmake.generator>Unix Makefiles</cmake.generator>
         <python.venv.bin>venv/bin/</python.venv.bin>
+        <javafx.classifier>linux</javafx.classifier>
       </properties>
     </profile>
     <!-- Profile for linux x86_64 (Self-Enabling) -->
@@ -1602,6 +1603,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-linux-x86_64/bin/</cmake.root>
         <cmake.generator>Unix Makefiles</cmake.generator>
         <python.venv.bin>venv/bin/</python.venv.bin>
+        <javafx.classifier>linux</javafx.classifier>
       </properties>
     </profile>
     <!-- Profile for linux aarch64 (Self-Enabling) -->
@@ -1620,6 +1622,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-linux-${os.arch}/bin/</cmake.root>
         <cmake.generator>Unix Makefiles</cmake.generator>
         <python.venv.bin>venv/bin/</python.venv.bin>
+        <javafx.classifier>linux-aarch64</javafx.classifier>
       </properties>
     </profile>
     <!-- Profile for mac amd64 (Self-Enabling) -->
@@ -1638,6 +1641,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-macos-universal/CMake.app/Contents/bin</cmake.root>
         <cmake.generator>Unix Makefiles</cmake.generator>
         <python.venv.bin>venv/bin/</python.venv.bin>
+        <javafx.classifier>mac</javafx.classifier>
       </properties>
     </profile>
     <!-- Profile for mac x86_64 (Self-Enabling) -->
@@ -1656,6 +1660,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-macos-universal/CMake.app/Contents/bin</cmake.root>
         <cmake.generator>Unix Makefiles</cmake.generator>
         <python.venv.bin>venv/bin/</python.venv.bin>
+        <javafx.classifier>mac</javafx.classifier>
       </properties>
     </profile>
     <!-- Profile for mac (Self-Enabling) -->
@@ -1674,6 +1679,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-macos-universal/CMake.app/Contents/bin</cmake.root>
         <cmake.generator>Unix Makefiles</cmake.generator>
         <python.venv.bin>venv/bin/</python.venv.bin>
+        <javafx.classifier>mac-aarch64</javafx.classifier>
       </properties>
     </profile>
     <!-- profile for windows (Self-Enabling) -->
@@ -1691,6 +1697,7 @@
         <cmake.root>${project.build.directory}/cmake-${cmake-version}-windows-x86_64/bin</cmake.root>
         <cmake.generator>MinGW Makefiles</cmake.generator>
         <python.venv.bin>venv/Scripts/</python.venv.bin>
+        <javafx.classifier>win-x86</javafx.classifier>
       </properties>
     </profile>