diff --git a/README.md b/README.md
index 3d368171..ee279aa8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
diff --git a/doc/owlplug-logo.png b/doc/owlplug-logo.png
deleted file mode 100644
index dfcf7d4b..00000000
Binary files a/doc/owlplug-logo.png and /dev/null differ
diff --git a/doc/owlplug-title-bg.png b/doc/owlplug-title-bg.png
new file mode 100644
index 00000000..cfa0e23d
Binary files /dev/null and b/doc/owlplug-title-bg.png differ
diff --git a/owlplug-client/pom.xml b/owlplug-client/pom.xml
index 9b40f62d..0f560b4a 100644
--- a/owlplug-client/pom.xml
+++ b/owlplug-client/pom.xml
@@ -38,6 +38,11 @@
owlplug-host
1.33.1
+
+ com.owlplug
+ owlplug-theme
+ 1.33.1
+
com.owlplug
owlplug-controls
@@ -124,7 +129,7 @@
io.github.mkpaz
atlantafx-base
- 2.1.0
+ ${atlantafx.version}
org.kordamp.ikonli
diff --git a/owlplug-client/src/main/java/com/owlplug/OwlPlug.java b/owlplug-client/src/main/java/com/owlplug/OwlPlug.java
index 0dbe55a7..fd3a8016 100644
--- a/owlplug-client/src/main/java/com/owlplug/OwlPlug.java
+++ b/owlplug-client/src/main/java/com/owlplug/OwlPlug.java
@@ -18,7 +18,7 @@
package com.owlplug;
-import atlantafx.base.theme.PrimerDark;
+import com.owlplug.theme.OwlPlugDarkTheme;
import com.owlplug.controls.OwlPlugControlsResources;
import com.owlplug.core.components.ApplicationDefaults;
import com.owlplug.core.controllers.MainController;
@@ -120,7 +120,7 @@ public void start(Stage primaryStage) throws Exception {
double width = 1050;
double height = 800;
- Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet());
+ Application.setUserAgentStylesheet(new OwlPlugDarkTheme().getUserAgentStylesheet());
Scene scene = new Scene(rootNode, width, height);
diff --git a/owlplug-client/src/main/java/com/owlplug/OwlPlugPreloader.java b/owlplug-client/src/main/java/com/owlplug/OwlPlugPreloader.java
index 5111db2c..7db2ff12 100644
--- a/owlplug-client/src/main/java/com/owlplug/OwlPlugPreloader.java
+++ b/owlplug-client/src/main/java/com/owlplug/OwlPlugPreloader.java
@@ -18,7 +18,7 @@
package com.owlplug;
-import atlantafx.base.theme.PrimerDark;
+import com.owlplug.theme.OwlPlugDarkTheme;
import com.owlplug.core.components.ApplicationDefaults;
import javafx.application.Application;
import javafx.application.Preloader;
@@ -42,7 +42,7 @@ public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Preloader.fxml"));
Parent root = loader.load();
- Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet());
+ Application.setUserAgentStylesheet(new OwlPlugDarkTheme().getUserAgentStylesheet());
Scene scene = new Scene(root);
String owlplugCss = OwlPlugPreloader.class.getResource("/owlplug.css").toExternalForm();
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/MainController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/MainController.java
index 4a2de393..07d4dd3a 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/MainController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/MainController.java
@@ -111,6 +111,9 @@ public class MainController extends BaseController {
private Button downloadUpdateButton;
public static int PLUGINS_TAB_INDEX = 1;
+ public static int EXPLORE_TAB_INDEX = 2;
+ public static int PROJECTS_TAB_INDEX = 3;
+ public static int OPTIONS_TAB_INDEX = 4;
/**
* FXML initialize method.
@@ -204,7 +207,7 @@ public void dispatchPostInitialize() {
}
- public void selectMainTab(int index) {
+ public void navigateToMainTab(int index) {
this.tabPaneHeader.getSelectionModel().select(index);
}
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java
index f25a963b..27578e7a 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/OptionsController.java
@@ -18,321 +18,135 @@
package com.owlplug.core.controllers;
-import com.owlplug.controls.Dialog;
-import com.owlplug.controls.DialogLayout;
import com.owlplug.core.components.ApplicationDefaults;
-import com.owlplug.core.controllers.dialogs.DonateDialogController;
-import com.owlplug.core.controllers.fragments.PluginPathFragmentController;
+import com.owlplug.core.controllers.options.ApplicationOptionsController;
+import com.owlplug.core.controllers.options.InstallationOptionsController;
+import com.owlplug.core.controllers.options.PluginScanOptionsController;
+import com.owlplug.core.controllers.options.ProjectsOptionsController;
import com.owlplug.core.events.PreferencesChangedEvent;
-import com.owlplug.core.model.OperatingSystem;
-import com.owlplug.core.services.OptionsService;
import com.owlplug.core.ui.SlidingLabel;
import com.owlplug.core.utils.FX;
-import com.owlplug.core.utils.PlatformUtils;
-import com.owlplug.host.loaders.NativePluginLoader;
-import com.owlplug.core.controllers.dialogs.ListDirectoryDialogController;
-import com.owlplug.plugin.services.NativeHostService;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
import javafx.fxml.FXML;
-import javafx.scene.control.Button;
-import javafx.scene.control.CheckBox;
-import javafx.scene.control.ComboBox;
-import javafx.scene.control.Hyperlink;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
import javafx.scene.control.Label;
-import javafx.scene.control.TextField;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.TextFlow;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.kordamp.ikonli.javafx.FontIcon;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Controller;
@Controller
public class OptionsController extends BaseController {
- @Autowired
- private OptionsService optionsService;
- @Autowired
- private NativeHostService nativeHostService;
- @Autowired
- private ListDirectoryDialogController listDirectoryDialogController;
- @Autowired
- private DonateDialogController donateDialogController;
@FXML
- private CheckBox pluginNativeCheckbox;
+ private VBox navContainer;
@FXML
- private ComboBox pluginNativeComboBox;
- @FXML
- private TextField loaderTimeoutTextField;
- @FXML
- private CheckBox syncPluginsCheckBox;
- @FXML
- private CheckBox syncFileStatCheckbox;
- @FXML
- private Button removeDataButton;
+ private StackPane contentArea;
@FXML
private Label versionLabel;
-
- @FXML
- private Button clearCacheButton;
- @FXML
- private CheckBox storeSubDirectoryCheckBox;
- @FXML
- private CheckBox storeByCreatorCheckBox;
- @FXML
- private Label storeByCreatorLabel;
@FXML
- private Label storeSubDirectoryLabel;
- @FXML
- private Label warningSubDirectory;
- @FXML
- private CheckBox storeDirectoryCheckBox;
+ private TextFlow contributorsFlow;
+
+ // Section root nodes injected via fx:include fx:id convention
@FXML
- private TextField storeDirectoryTextField;
+ private ScrollPane pluginScanOptions;
@FXML
- private Label storeDirectorySeparator;
+ private ScrollPane installationOptions;
@FXML
- private Hyperlink owlplugWebsiteLink;
+ private ScrollPane projectsOptions;
@FXML
- private VBox pluginPathContainer;
+ private ScrollPane applicationOptions;
+ // Section controllers injected via {fx:id}Controller convention
@FXML
- private CheckBox telemetryCheckBox;
- @FXML
- private Hyperlink telemetryHyperlink;
+ private PluginScanOptionsController pluginScanOptionsController;
@FXML
- private Button moreFeaturesButton;
+ private InstallationOptionsController installationOptionsController;
@FXML
- private Button openLogsButton;
+ private ProjectsOptionsController projectsOptionsController;
@FXML
- private TextFlow versionTextFlow;
+ private ApplicationOptionsController applicationOptionsController;
- private PluginPathFragmentController vst2PluginPathFragment;
- private PluginPathFragmentController vst3PluginPathFragment;
- private PluginPathFragmentController auPluginPathFragment;
- private PluginPathFragmentController lv2PluginPathFragment;
+ private final ToggleGroup navToggleGroup = new ToggleGroup();
+
+ public static int SCAN_SECTION_INDEX = 0;
+ public static int INSTALLATION_SECTION_INDEX = 1;
+ public static int PROJECTS_SECTION_INDEX = 2;
+ public static int APPLICATION_SECTION_INDEX = 3;
- /**
- * FXML initialize method.
- */
@FXML
public void initialize() {
-
- vst2PluginPathFragment = new PluginPathFragmentController("VST2",
- ApplicationDefaults.VST2_DISCOVERY_ENABLED_KEY,
- ApplicationDefaults.VST_DIRECTORY_KEY,
- ApplicationDefaults.VST2_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
- this.listDirectoryDialogController);
-
- vst3PluginPathFragment = new PluginPathFragmentController("VST3",
- ApplicationDefaults.VST3_DISCOVERY_ENABLED_KEY,
- ApplicationDefaults.VST3_DIRECTORY_KEY,
- ApplicationDefaults.VST3_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
- this.listDirectoryDialogController);
-
- auPluginPathFragment = new PluginPathFragmentController("AU",
- ApplicationDefaults.AU_DISCOVERY_ENABLED_KEY,
- ApplicationDefaults.AU_DIRECTORY_KEY,
- ApplicationDefaults.AU_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
- this.listDirectoryDialogController);
-
- lv2PluginPathFragment = new PluginPathFragmentController("LV2",
- ApplicationDefaults.LV2_DISCOVERY_ENABLED_KEY,
- ApplicationDefaults.LV2_DIRECTORY_KEY,
- ApplicationDefaults.LV2_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
- this.listDirectoryDialogController);
-
- pluginPathContainer.getChildren().add(vst2PluginPathFragment.getNode());
- pluginPathContainer.getChildren().add(vst3PluginPathFragment.getNode());
- pluginPathContainer.getChildren().add(auPluginPathFragment.getNode());
- pluginPathContainer.getChildren().add(lv2PluginPathFragment.getNode());
-
- storeByCreatorLabel.setVisible(false);
- storeSubDirectoryLabel.setVisible(false);
-
- pluginNativeCheckbox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.NATIVE_HOST_ENABLED_KEY, newValue);
- this.pluginNativeComboBox.setDisable(!newValue);
- updateScannerTimeoutFieldState();
- });
-
- ObservableList pluginLoaders = FXCollections.observableArrayList(
- nativeHostService.getAvailablePluginLoaders());
- pluginNativeComboBox.setItems(pluginLoaders);
-
- pluginNativeComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
- if (newValue != null) {
- this.getPreferences().put(ApplicationDefaults.PREFERRED_NATIVE_LOADER, newValue.getId());
- nativeHostService.setCurrentPluginLoader(newValue);
- updateScannerTimeoutFieldState();
+ hideSection(pluginScanOptions);
+ hideSection(installationOptions);
+ hideSection(projectsOptions);
+ hideSection(applicationOptions);
+
+ addNavButton("Plugin Scan", "mdi2m-magnify", pluginScanOptions);
+ addNavButton("Installation", "mdi2d-download-outline", installationOptions);
+ addNavButton("Projects", "mdi2f-folder-music-outline", projectsOptions);
+ addNavButton("Application", "mdi2c-cog-outline", applicationOptions);
+
+ // Prevent deselecting all buttons
+ navToggleGroup.selectedToggleProperty().addListener((obs, prev, next) -> {
+ if (next == null && prev != null) {
+ prev.setSelected(true);
}
});
- loaderTimeoutTextField.textProperty().addListener((observable, oldValue, newValue) -> {
- if (!newValue.matches("\\d*")) {
- loaderTimeoutTextField.setText(newValue.replaceAll("[^\\d]", ""));
- return;
- }
- try {
- long timeout = Long.parseLong(newValue);
- if (timeout >= 0 && timeout <= 3600) {
- this.getPreferences().putLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, timeout);
- nativeHostService.setScannerTimeout(timeout);
- } else {
- loaderTimeoutTextField.setText(oldValue);
- }
- } catch (NumberFormatException ignored) {
- // Ignore in case of invalid values (empty string)
- }
- });
-
- syncPluginsCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.SYNC_PLUGINS_STARTUP_KEY, newValue);
- });
-
- syncFileStatCheckbox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.SYNC_FILE_STAT_KEY, newValue);
- });
-
- storeSubDirectoryCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, newValue);
- warningSubDirectory.setVisible(!newValue);
- storeSubDirectoryLabel.setVisible(newValue);
- });
-
- warningSubDirectory.managedProperty().bind(warningSubDirectory.visibleProperty());
- storeSubDirectoryLabel.managedProperty().bind(storeSubDirectoryLabel.visibleProperty());
- storeDirectorySeparator.managedProperty().bind(storeDirectorySeparator.visibleProperty());
- storeDirectoryTextField.managedProperty().bind(storeDirectoryTextField.visibleProperty());
+ navToggleGroup.getToggles().get(0).setSelected(true);
- storeDirectoryCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.STORE_DIRECTORY_ENABLED_KEY, newValue);
- storeDirectorySeparator.setVisible(newValue);
- storeDirectoryTextField.setVisible(newValue);
- });
-
- storeByCreatorCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.STORE_BY_CREATOR_ENABLED_KEY, newValue);
- storeByCreatorLabel.setVisible(newValue);
- });
-
- storeByCreatorLabel.managedProperty().bind(storeByCreatorLabel.visibleProperty());
-
- storeDirectoryTextField.textProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().put(ApplicationDefaults.STORE_DIRECTORY_KEY, newValue);
- });
-
- telemetryCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
- this.getPreferences().putBoolean(ApplicationDefaults.TELEMETRY_ENABLED_KEY, newValue);
- });
-
- telemetryHyperlink.setOnAction(e -> {
- PlatformUtils.openDefaultBrowser(this.getApplicationDefaults().getEnvProperty("owlplug.github.wiki.url")
- + "/Telemetry");
- });
-
- clearCacheButton.setOnAction(e -> {
- optionsService.clearCache();
- });
-
- removeDataButton.setOnAction(e -> {
- Dialog dialog = this.getDialogManager().newDialog();
- DialogLayout layout = new DialogLayout();
- layout.setHeading(new Label("Remove user data"));
- layout.setBody(new Label("Do you really want to remove all user data including accounts, "
- + "stores and custom settings ? \n\nYou must restart OwlPlug for a complete reset."));
-
- Button cancelButton = new Button("Cancel");
- cancelButton.setOnAction(cancelEvent -> {
- dialog.close();
- });
-
- Button removeButton = new Button("Remove data");
- removeButton.setOnAction(removeEvent -> {
- dialog.close();
- optionsService.clearAllUserData();
- this.refreshView();
-
- // User data cleared twice because the refreshView() triggers UI changes that may be replicated in data
- optionsService.clearAllUserData();
-
- });
- removeButton.getStyleClass().add("button-danger");
+ versionLabel.setText("OwlPlug " + getApplicationDefaults().getVersion());
+ contributorsFlow.getChildren().add(new SlidingLabel(ApplicationDefaults.getContributors()));
- layout.setActions(removeButton, cancelButton);
- dialog.setContent(layout);
- dialog.show();
- });
-
- versionLabel.setText(this.getApplicationDefaults().getVersion());
-
- owlplugWebsiteLink.setOnAction(e -> {
- PlatformUtils.openDefaultBrowser(owlplugWebsiteLink.getText());
- });
+ refreshView();
+ }
- moreFeaturesButton.setOnAction(e -> {
- donateDialogController.show();
+ private void addNavButton(String label, String iconCode, Node section) {
+ FontIcon icon = new FontIcon(iconCode);
+ icon.setIconSize(15);
+
+ ToggleButton btn = new ToggleButton(label, icon);
+ btn.setToggleGroup(navToggleGroup);
+ btn.setMaxWidth(Double.MAX_VALUE);
+ btn.setAlignment(Pos.CENTER_LEFT);
+ btn.getStyleClass().add("options-nav-button");
+ btn.setGraphicTextGap(10);
+
+ btn.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
+ if (isSelected) {
+ showSection(section);
+ } else {
+ hideSection(section);
+ }
});
- openLogsButton.setOnAction(e -> {
- PlatformUtils.openFromDesktop(ApplicationDefaults.getLogDirectory());
- });
+ navContainer.getChildren().add(btn);
+ }
- versionTextFlow.getChildren().add(new SlidingLabel(ApplicationDefaults.getContributors()));
+ private void showSection(Node section) {
+ section.setVisible(true);
+ section.setManaged(true);
+ }
- refreshView();
+ private void hideSection(Node section) {
+ section.setVisible(false);
+ section.setManaged(false);
}
- private void updateScannerTimeoutFieldState() {
- NativePluginLoader selected = pluginNativeComboBox.getSelectionModel().getSelectedItem();
- boolean isOwlPlugScanner = selected != null && "owlplug-scanner".equals(selected.getId());
- boolean nativeEnabled = pluginNativeCheckbox.isSelected() && !pluginNativeCheckbox.isDisable();
- loaderTimeoutTextField.setDisable(!nativeEnabled || !isOwlPlugScanner);
+ public void navigateToSection(int index) {
+ navToggleGroup.getToggles().get(index).setSelected(true);
}
public void refreshView() {
-
- vst2PluginPathFragment.refresh();
- vst3PluginPathFragment.refresh();
- auPluginPathFragment.refresh();
- lv2PluginPathFragment.refresh();
-
- pluginNativeCheckbox.setDisable(!nativeHostService.isNativeHostAvailable());
- pluginNativeComboBox.setDisable(!nativeHostService.isNativeHostAvailable());
- pluginNativeCheckbox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.NATIVE_HOST_ENABLED_KEY, false));
- syncPluginsCheckBox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.SYNC_PLUGINS_STARTUP_KEY, false));
- syncFileStatCheckbox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.SYNC_FILE_STAT_KEY, true));
- storeSubDirectoryCheckBox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, true));
- warningSubDirectory.setVisible(!this.getPreferences().getBoolean(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, true));
- storeDirectoryCheckBox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.STORE_DIRECTORY_ENABLED_KEY, false));
- storeByCreatorCheckBox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.STORE_BY_CREATOR_ENABLED_KEY, false));
- storeDirectoryTextField.setText(this.getPreferences().get(ApplicationDefaults.STORE_DIRECTORY_KEY, ""));
- telemetryCheckBox.setSelected(this.getPreferences().getBoolean(ApplicationDefaults.TELEMETRY_ENABLED_KEY, true));
-
- NativePluginLoader pluginLoader = nativeHostService.getCurrentPluginLoader();
- pluginNativeComboBox.getSelectionModel().select(pluginLoader);
-
- long timeout = this.getPreferences().getLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, 10L);
- loaderTimeoutTextField.setText(String.valueOf(timeout));
- updateScannerTimeoutFieldState();
-
- if (!storeDirectoryCheckBox.isSelected()) {
- storeDirectoryTextField.setVisible(false);
- }
- if (!storeByCreatorCheckBox.isSelected()) {
- storeByCreatorLabel.setVisible(false);
- }
-
- // Disable AU options for non MAC users
- if (!this.getApplicationDefaults().getRuntimePlatform()
- .getOperatingSystem().equals(OperatingSystem.MAC)) {
- auPluginPathFragment.disable();
- }
-
+ pluginScanOptionsController.refresh();
+ installationOptionsController.refresh();
+ projectsOptionsController.refresh();
+ applicationOptionsController.refresh();
}
@EventListener
@@ -340,4 +154,4 @@ private void handle(PreferencesChangedEvent event) {
FX.run(this::refreshView);
}
-}
+}
\ No newline at end of file
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/DonateDialogController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/DonateDialogController.java
index f6d3af91..01b95a94 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/DonateDialogController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/DonateDialogController.java
@@ -45,7 +45,7 @@ public class DonateDialogController extends AbstractDialogController {
private Button cancelButton;
DonateDialogController() {
- super(550, 480);
+ super(600, 480);
this.setOverlayClose(false);
}
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/ListDirectoryDialogController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/ListDirectoryDialogController.java
index 0cc56c3e..f3a93e4b 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/ListDirectoryDialogController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/ListDirectoryDialogController.java
@@ -20,87 +20,101 @@
import com.owlplug.controls.DialogLayout;
import com.owlplug.core.components.LazyViewRegistry;
-import com.owlplug.core.controllers.dialogs.AbstractDialogController;
import com.owlplug.core.events.PreferencesChangedEvent;
+import java.io.File;
import java.util.ArrayList;
-import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
+import javafx.scene.control.Label;
import javafx.scene.control.ListView;
-import javafx.scene.control.cell.TextFieldListCell;
+import javafx.stage.DirectoryChooser;
+import org.kordamp.ikonli.javafx.FontIcon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Controller;
@Controller
-public class ListDirectoryDialogController extends AbstractDialogController implements ListChangeListener {
+public class ListDirectoryDialogController extends AbstractDialogController {
@Autowired
private ApplicationEventPublisher publisher;
@Autowired
private LazyViewRegistry lazyViewRegistry;
+ @FXML
+ private ListView directoryListView;
@FXML
private Button addDirectoryButton;
@FXML
- private ListView directoryListView;
+ private Button removeDirectoryButton;
@FXML
private Button closeButton;
- private final String newDirectoryItem = "[New directory] (double-click to update)";
private String currentPreferenceKey;
private ObservableList observableItems;
+ public ListDirectoryDialogController() {
+ super(700,500);
+ }
+
public void initialize() {
+ Label placeholder = new Label("No additional directories configured.");
+ placeholder.getStyleClass().add("label-disabled");
+ directoryListView.setPlaceholder(placeholder);
+
+ FontIcon addIcon = new FontIcon("mdi2p-plus");
+ addIcon.setIconSize(14);
+ addDirectoryButton.setGraphic(addIcon);
+
+ FontIcon removeIcon = new FontIcon("mdi2m-minus");
+ removeIcon.setIconSize(14);
+ removeDirectoryButton.setGraphic(removeIcon);
addDirectoryButton.setOnAction(e -> {
- observableItems.add(newDirectoryItem);
+ DirectoryChooser chooser = new DirectoryChooser();
+ File dir = chooser.showDialog(addDirectoryButton.getScene().getWindow());
+ if (dir != null) {
+ String path = dir.getAbsolutePath();
+ if (!observableItems.contains(path)) {
+ observableItems.add(path);
+ }
+ }
});
- directoryListView.setCellFactory(TextFieldListCell.forListView());
- closeButton.setOnAction(e -> {
- this.close();
+
+ removeDirectoryButton.setOnAction(e -> {
+ String selected = directoryListView.getSelectionModel().getSelectedItem();
+ if (selected != null) {
+ observableItems.remove(selected);
+ }
});
+
+ closeButton.setOnAction(e -> this.close());
}
public void configure(String preferenceKey) {
this.currentPreferenceKey = preferenceKey;
- if (observableItems != null) {
- observableItems.removeListener(this);
- }
+ observableItems = FXCollections.observableArrayList(
+ this.getPreferences().getList(preferenceKey, new ArrayList<>()));
- List items = this.getPreferences().getList(preferenceKey, new ArrayList<>());
- observableItems = FXCollections.observableArrayList(items);
+ observableItems.addListener((ListChangeListener) change -> {
+ this.getPreferences().putList(currentPreferenceKey, new ArrayList<>(observableItems));
+ publisher.publishEvent(new PreferencesChangedEvent());
+ });
- observableItems.addListener(this);
directoryListView.setItems(observableItems);
- directoryListView.refresh();
- }
-
-
- @Override
- public void onChanged(Change extends String> change) {
-
- ObservableList extends String> eventList = change.getList();
- // Remove blank entries in the backed list
- eventList.removeIf(x -> x.isBlank());
-
- // Create a new list and filter it before saving it to preferences
- List prefList = new ArrayList<>(change.getList());
- prefList.removeIf(x -> x.isBlank() || x.equals(newDirectoryItem));
- this.getPreferences().putList(currentPreferenceKey, prefList);
-
- publisher.publishEvent(new PreferencesChangedEvent());
-
}
protected DialogLayout getLayout() {
DialogLayout layout = new DialogLayout();
+ Label title = new Label("Additional Directories");
+ title.setGraphic(new FontIcon("mdi2f-folder-multiple"));
+ layout.setHeading(title);
layout.setBody(lazyViewRegistry.get(LazyViewRegistry.LIST_DIRECTORY_VIEW));
return layout;
}
-}
+}
\ No newline at end of file
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/WelcomeDialogController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/WelcomeDialogController.java
index 48fde37f..77746b3a 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/WelcomeDialogController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/dialogs/WelcomeDialogController.java
@@ -27,6 +27,7 @@
import com.owlplug.core.model.OperatingSystem;
import com.owlplug.core.utils.FX;
import com.owlplug.plugin.components.PluginTaskFactory;
+import com.owlplug.plugin.model.PluginFormat;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@@ -70,28 +71,28 @@ public class WelcomeDialogController extends AbstractDialogController {
*/
public void initialize() {
- vst2PluginPathFragment = new PluginPathFragmentController("VST2",
+ vst2PluginPathFragment = new PluginPathFragmentController(PluginFormat.VST2,
ApplicationDefaults.VST2_DISCOVERY_ENABLED_KEY, ApplicationDefaults.VST_DIRECTORY_KEY,
ApplicationDefaults.VST2_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
+ this.getPreferences(), this.getApplicationDefaults(),
this.listDirectoryDialogController);
- vst3PluginPathFragment = new PluginPathFragmentController("VST3",
+ vst3PluginPathFragment = new PluginPathFragmentController(PluginFormat.VST3,
ApplicationDefaults.VST3_DISCOVERY_ENABLED_KEY,
ApplicationDefaults.VST3_DIRECTORY_KEY,
ApplicationDefaults.VST3_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
+ this.getPreferences(), this.getApplicationDefaults(),
this.listDirectoryDialogController);
- auPluginPathFragment = new PluginPathFragmentController("AU",
+ auPluginPathFragment = new PluginPathFragmentController(PluginFormat.AU,
ApplicationDefaults.AU_DISCOVERY_ENABLED_KEY,
ApplicationDefaults.AU_DIRECTORY_KEY,
ApplicationDefaults.AU_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
+ this.getPreferences(), this.getApplicationDefaults(),
this.listDirectoryDialogController);
- lv2PluginPathFragment = new PluginPathFragmentController("LV2",
+ lv2PluginPathFragment = new PluginPathFragmentController(PluginFormat.LV2,
ApplicationDefaults.LV2_DISCOVERY_ENABLED_KEY,
ApplicationDefaults.LV2_DIRECTORY_KEY,
ApplicationDefaults.LV2_EXTRA_DIRECTORY_KEY,
- this.getPreferences(),
+ this.getPreferences(), this.getApplicationDefaults(),
this.listDirectoryDialogController);
pluginPathContainer.getChildren().add(vst2PluginPathFragment.getNode());
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/fragments/PluginPathFragmentController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/fragments/PluginPathFragmentController.java
index db3ca0d2..8f8f5ba7 100644
--- a/owlplug-client/src/main/java/com/owlplug/core/controllers/fragments/PluginPathFragmentController.java
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/fragments/PluginPathFragmentController.java
@@ -19,10 +19,13 @@
package com.owlplug.core.controllers.fragments;
import atlantafx.base.controls.ToggleSwitch;
+import com.owlplug.core.components.ApplicationDefaults;
import com.owlplug.core.components.ApplicationPreferences;
+import com.owlplug.core.controllers.dialogs.ListDirectoryDialogController;
import com.owlplug.core.ui.SVGPaths;
import com.owlplug.core.utils.FileUtils;
-import com.owlplug.core.controllers.dialogs.ListDirectoryDialogController;
+import com.owlplug.plugin.model.PluginFormat;
+import com.owlplug.plugin.ui.PluginFormatBadgeView;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
@@ -34,6 +37,7 @@
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
+import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.shape.SVGPath;
import javafx.stage.DirectoryChooser;
@@ -48,6 +52,8 @@ public class PluginPathFragmentController {
@FXML
private Node mainNode;
@FXML
+ private HBox headerBox;
+ @FXML
private Label headerLabel;
@FXML
private ToggleSwitch activationToggleButton;
@@ -69,21 +75,24 @@ public class PluginPathFragmentController {
private SVGPath crossPath = new SVGPath();
- private String name;
+ private PluginFormat pluginFormat;
private String enableOptionKey;
private String directoryOptionKey;
private String extraDirectoryOptionKey;
private ApplicationPreferences prefs;
+ private ApplicationDefaults applicationDefaults;
private ListDirectoryDialogController listDirectoryDialogController;
- public PluginPathFragmentController(String name, String enableOptionKey, String directoryOptionKey,
+ public PluginPathFragmentController(PluginFormat pluginFormat, String enableOptionKey, String directoryOptionKey,
String extraDirectoryOptionKey, ApplicationPreferences prefs,
+ ApplicationDefaults applicationDefaults,
ListDirectoryDialogController listDirectoryDialogController) {
- this.name = name;
+ this.pluginFormat = pluginFormat;
this.enableOptionKey = enableOptionKey;
this.directoryOptionKey = directoryOptionKey;
this.extraDirectoryOptionKey = extraDirectoryOptionKey;
this.prefs = prefs;
+ this.applicationDefaults = applicationDefaults;
this.listDirectoryDialogController = listDirectoryDialogController;
init();
@@ -104,10 +113,14 @@ public void init() {
checkPath.setContent(SVGPaths.check);
crossPath.setContent(SVGPaths.cross);
- headerLabel.setText(name);
- directoryTextField.setPromptText(name + " plugin directory");
+ PluginFormatBadgeView badge = new PluginFormatBadgeView(
+ pluginFormat, applicationDefaults, PluginFormatBadgeView.DisplayMode.DEFAULT);
+ headerBox.getChildren().add(0, badge);
+
+ headerLabel.setText(pluginFormat.getFullName());
+ directoryTextField.setPromptText(pluginFormat.getName() + " plugin directory");
- activationToggleButton.setText("Explore " + name + " plugins");
+ activationToggleButton.setText("Scan " + pluginFormat.getName() + " plugins");
activationToggleButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
prefs.putBoolean(enableOptionKey, newValue);
refresh();
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/options/ApplicationOptionsController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/ApplicationOptionsController.java
new file mode 100644
index 00000000..082ec448
--- /dev/null
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/ApplicationOptionsController.java
@@ -0,0 +1,99 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+package com.owlplug.core.controllers.options;
+
+import com.owlplug.controls.Dialog;
+import com.owlplug.controls.DialogLayout;
+import com.owlplug.core.components.ApplicationDefaults;
+import com.owlplug.core.controllers.BaseController;
+import com.owlplug.core.controllers.dialogs.DonateDialogController;
+import com.owlplug.core.services.OptionsService;
+import com.owlplug.core.utils.PlatformUtils;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Hyperlink;
+import javafx.scene.control.Label;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class ApplicationOptionsController extends BaseController {
+
+ @Autowired
+ private OptionsService optionsService;
+ @Autowired
+ private DonateDialogController donateDialogController;
+
+ @FXML
+ private CheckBox telemetryCheckBox;
+ @FXML
+ private Hyperlink telemetryHyperlink;
+ @FXML
+ private Button clearCacheButton;
+ @FXML
+ private Button removeDataButton;
+ @FXML
+ private Hyperlink websiteLink;
+ @FXML
+ private Button contributeButton;
+ @FXML
+ private Button openLogsButton;
+
+ @FXML
+ public void initialize() {
+ telemetryCheckBox.selectedProperty().addListener((obs, o, n) ->
+ getPreferences().putBoolean(ApplicationDefaults.TELEMETRY_ENABLED_KEY, n));
+
+ telemetryHyperlink.setOnAction(e -> PlatformUtils.openDefaultBrowser(
+ getApplicationDefaults().getEnvProperty("owlplug.github.wiki.url") + "/Telemetry"));
+
+ clearCacheButton.setOnAction(e -> optionsService.clearCache());
+
+ removeDataButton.setOnAction(e -> {
+ Dialog dialog = getDialogManager().newDialog();
+ DialogLayout layout = new DialogLayout();
+ layout.setHeading(new Label("Remove User Data"));
+ layout.setBody(new Label(
+ "Do you really want to remove all user data including accounts, "
+ + "stores and custom settings?\n\nYou must restart OwlPlug for a complete reset."));
+ Button cancelButton = new Button("Cancel");
+ cancelButton.setOnAction(ce -> dialog.close());
+ Button confirmButton = new Button("Remove Data");
+ confirmButton.getStyleClass().add("button-danger");
+ confirmButton.setOnAction(ce -> {
+ dialog.close();
+ optionsService.clearAllUserData();
+ });
+ layout.setActions(confirmButton, cancelButton);
+ dialog.setContent(layout);
+ dialog.show();
+ });
+
+ websiteLink.setOnAction(e -> PlatformUtils.openDefaultBrowser(websiteLink.getText()));
+ contributeButton.setOnAction(e -> donateDialogController.show());
+ openLogsButton.setOnAction(e -> PlatformUtils.openFromDesktop(ApplicationDefaults.getLogDirectory()));
+ }
+
+ public void refresh() {
+ telemetryCheckBox.setSelected(
+ getPreferences().getBoolean(ApplicationDefaults.TELEMETRY_ENABLED_KEY, true));
+ }
+
+}
\ No newline at end of file
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/options/InstallationOptionsController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/InstallationOptionsController.java
new file mode 100644
index 00000000..db43b15d
--- /dev/null
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/InstallationOptionsController.java
@@ -0,0 +1,145 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+package com.owlplug.core.controllers.options;
+
+import com.owlplug.core.components.ApplicationDefaults;
+import com.owlplug.core.controllers.BaseController;
+import com.owlplug.plugin.ui.PluginFormatBadgeView;
+import java.io.File;
+import javafx.fxml.FXML;
+import javafx.geometry.Pos;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.VBox;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class InstallationOptionsController extends BaseController {
+
+ @FXML
+ private CheckBox storeDirectoryCheckBox;
+ @FXML
+ private HBox storeDirectoryRow;
+ @FXML
+ private TextField storeDirectoryTextField;
+ @FXML
+ private CheckBox storeByCreatorCheckBox;
+ @FXML
+ private CheckBox storeSubDirectoryCheckBox;
+ @FXML
+ private Label warningSubDirectory;
+ @FXML
+ private VBox pathPreviewContainer;
+
+ private Label vst2PathLabel;
+ private Label vst3PathLabel;
+ private Label auPathLabel;
+ private Label lv2PathLabel;
+
+ @FXML
+ public void initialize() {
+ storeDirectoryRow.visibleProperty().bind(storeDirectoryCheckBox.selectedProperty());
+ storeDirectoryRow.managedProperty().bind(storeDirectoryCheckBox.selectedProperty());
+ warningSubDirectory.managedProperty().bind(warningSubDirectory.visibleProperty());
+
+ storeDirectoryCheckBox.selectedProperty().addListener((obs, o, n) -> {
+ getPreferences().putBoolean(ApplicationDefaults.STORE_DIRECTORY_ENABLED_KEY, n);
+ refreshPathPreview();
+ });
+ storeByCreatorCheckBox.selectedProperty().addListener((obs, o, n) -> {
+ getPreferences().putBoolean(ApplicationDefaults.STORE_BY_CREATOR_ENABLED_KEY, n);
+ refreshPathPreview();
+ });
+ storeSubDirectoryCheckBox.selectedProperty().addListener((obs, o, n) -> {
+ getPreferences().putBoolean(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, n);
+ warningSubDirectory.setVisible(!n);
+ refreshPathPreview();
+ });
+ storeDirectoryTextField.textProperty().addListener((obs, o, n) -> {
+ getPreferences().put(ApplicationDefaults.STORE_DIRECTORY_KEY, n);
+ refreshPathPreview();
+ });
+
+ vst2PathLabel = previewLabel();
+ vst3PathLabel = previewLabel();
+ auPathLabel = previewLabel();
+ lv2PathLabel = previewLabel();
+
+ pathPreviewContainer.getChildren().addAll(
+ previewRow("vst2", vst2PathLabel),
+ previewRow("vst3", vst3PathLabel),
+ previewRow("au", auPathLabel),
+ previewRow("lv2", lv2PathLabel));
+ }
+
+ public void refresh() {
+ boolean storeSubDir = getPreferences().getBoolean(ApplicationDefaults.STORE_SUBDIRECTORY_ENABLED, true);
+ storeSubDirectoryCheckBox.setSelected(storeSubDir);
+ warningSubDirectory.setVisible(!storeSubDir);
+ storeDirectoryCheckBox.setSelected(
+ getPreferences().getBoolean(ApplicationDefaults.STORE_DIRECTORY_ENABLED_KEY, false));
+ storeByCreatorCheckBox.setSelected(
+ getPreferences().getBoolean(ApplicationDefaults.STORE_BY_CREATOR_ENABLED_KEY, false));
+ storeDirectoryTextField.setText(
+ getPreferences().get(ApplicationDefaults.STORE_DIRECTORY_KEY, ""));
+ refreshPathPreview();
+ }
+
+ private void refreshPathPreview() {
+ vst2PathLabel.setText(simulatePath(ApplicationDefaults.VST_DIRECTORY_KEY));
+ vst3PathLabel.setText(simulatePath(ApplicationDefaults.VST3_DIRECTORY_KEY));
+ auPathLabel.setText(simulatePath(ApplicationDefaults.AU_DIRECTORY_KEY));
+ lv2PathLabel.setText(simulatePath(ApplicationDefaults.LV2_DIRECTORY_KEY));
+ }
+
+ private String simulatePath(String formatDirKey) {
+ String baseDir = getPreferences().get(formatDirKey, "");
+ if (baseDir == null || baseDir.isBlank()) return "Format directory not configured";
+
+ File path = new File(baseDir);
+ if (storeDirectoryCheckBox.isSelected()) {
+ String sub = storeDirectoryTextField.getText();
+ if (sub != null && !sub.isBlank()) path = new File(path, sub);
+ }
+ if (storeByCreatorCheckBox.isSelected()) path = new File(path, "Acme Audio");
+ if (storeSubDirectoryCheckBox.isSelected()) path = new File(path, "MyPlugin");
+ return path.getAbsolutePath();
+ }
+
+ private Label previewLabel() {
+ Label l = new Label();
+ l.getStyleClass().add("label-disabled");
+ l.setWrapText(true);
+ HBox.setHgrow(l, Priority.ALWAYS);
+ return l;
+ }
+
+ private HBox previewRow(String formatValue, Label pathLabel) {
+ PluginFormatBadgeView badge = new PluginFormatBadgeView(
+ formatValue, getApplicationDefaults(), PluginFormatBadgeView.DisplayMode.TEXT_ONLY);
+ badge.setMinWidth(42);
+ HBox row = new HBox(12, badge, pathLabel);
+ row.setAlignment(Pos.CENTER_LEFT);
+ return row;
+ }
+
+}
\ No newline at end of file
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/options/PluginScanOptionsController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/PluginScanOptionsController.java
new file mode 100644
index 00000000..b70b8cbe
--- /dev/null
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/PluginScanOptionsController.java
@@ -0,0 +1,156 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+package com.owlplug.core.controllers.options;
+
+import com.owlplug.core.components.ApplicationDefaults;
+import com.owlplug.core.controllers.BaseController;
+import com.owlplug.core.controllers.dialogs.ListDirectoryDialogController;
+import com.owlplug.core.controllers.fragments.PluginPathFragmentController;
+import com.owlplug.core.model.OperatingSystem;
+import com.owlplug.host.loaders.NativePluginLoader;
+import com.owlplug.plugin.model.PluginFormat;
+import com.owlplug.plugin.services.NativeHostService;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.scene.layout.VBox;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class PluginScanOptionsController extends BaseController {
+
+ @Autowired
+ private NativeHostService nativeHostService;
+ @Autowired
+ private ListDirectoryDialogController listDirectoryDialogController;
+
+ @FXML
+ private VBox formatsContainer;
+ @FXML
+ private CheckBox pluginNativeCheckbox;
+ @FXML
+ private ComboBox pluginNativeComboBox;
+ @FXML
+ private Spinner loaderTimeoutSpinner;
+ @FXML
+ private CheckBox syncPluginsCheckBox;
+ @FXML
+ private CheckBox syncFileStatCheckbox;
+
+ private PluginPathFragmentController vst2Fragment;
+ private PluginPathFragmentController vst3Fragment;
+ private PluginPathFragmentController auFragment;
+ private PluginPathFragmentController lv2Fragment;
+
+ @FXML
+ public void initialize() {
+ vst2Fragment = new PluginPathFragmentController(PluginFormat.VST2,
+ ApplicationDefaults.VST2_DISCOVERY_ENABLED_KEY, ApplicationDefaults.VST_DIRECTORY_KEY,
+ ApplicationDefaults.VST2_EXTRA_DIRECTORY_KEY, getPreferences(),
+ getApplicationDefaults(), listDirectoryDialogController);
+ vst3Fragment = new PluginPathFragmentController(PluginFormat.VST3,
+ ApplicationDefaults.VST3_DISCOVERY_ENABLED_KEY, ApplicationDefaults.VST3_DIRECTORY_KEY,
+ ApplicationDefaults.VST3_EXTRA_DIRECTORY_KEY, getPreferences(),
+ getApplicationDefaults(), listDirectoryDialogController);
+ auFragment = new PluginPathFragmentController(PluginFormat.AU,
+ ApplicationDefaults.AU_DISCOVERY_ENABLED_KEY, ApplicationDefaults.AU_DIRECTORY_KEY,
+ ApplicationDefaults.AU_EXTRA_DIRECTORY_KEY, getPreferences(),
+ getApplicationDefaults(), listDirectoryDialogController);
+ lv2Fragment = new PluginPathFragmentController(PluginFormat.LV2,
+ ApplicationDefaults.LV2_DISCOVERY_ENABLED_KEY, ApplicationDefaults.LV2_DIRECTORY_KEY,
+ ApplicationDefaults.LV2_EXTRA_DIRECTORY_KEY, getPreferences(),
+ getApplicationDefaults(), listDirectoryDialogController);
+
+ formatsContainer.getChildren().addAll(
+ vst2Fragment.getNode(),
+ vst3Fragment.getNode(),
+ auFragment.getNode(),
+ lv2Fragment.getNode());
+
+ pluginNativeCheckbox.selectedProperty().addListener((obs, o, n) -> {
+ getPreferences().putBoolean(ApplicationDefaults.NATIVE_HOST_ENABLED_KEY, n);
+ pluginNativeComboBox.setDisable(!n);
+ updateScannerTimeoutFieldState();
+ });
+
+ pluginNativeComboBox.setItems(
+ FXCollections.observableArrayList(nativeHostService.getAvailablePluginLoaders()));
+ pluginNativeComboBox.getSelectionModel().selectedItemProperty().addListener((obs, o, n) -> {
+ if (n != null) {
+ getPreferences().put(ApplicationDefaults.PREFERRED_NATIVE_LOADER, n.getId());
+ nativeHostService.setCurrentPluginLoader(n);
+ updateScannerTimeoutFieldState();
+ }
+ });
+
+ loaderTimeoutSpinner.setValueFactory(
+ new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 3600, 10));
+ loaderTimeoutSpinner.valueProperty().addListener((obs, o, n) -> {
+ getPreferences().putLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, n.longValue());
+ nativeHostService.setScannerTimeout(n.longValue());
+ });
+
+ syncPluginsCheckBox.selectedProperty().addListener((obs, o, n) ->
+ getPreferences().putBoolean(ApplicationDefaults.SYNC_PLUGINS_STARTUP_KEY, n));
+ syncFileStatCheckbox.selectedProperty().addListener((obs, o, n) ->
+ getPreferences().putBoolean(ApplicationDefaults.SYNC_FILE_STAT_KEY, n));
+ }
+
+ public void refresh() {
+ vst2Fragment.refresh();
+ vst3Fragment.refresh();
+ auFragment.refresh();
+ lv2Fragment.refresh();
+
+ boolean nativeAvailable = nativeHostService.isNativeHostAvailable();
+ pluginNativeCheckbox.setDisable(!nativeAvailable);
+ pluginNativeComboBox.setDisable(!nativeAvailable);
+ pluginNativeCheckbox.setSelected(
+ getPreferences().getBoolean(ApplicationDefaults.NATIVE_HOST_ENABLED_KEY, false));
+
+ NativePluginLoader loader = nativeHostService.getCurrentPluginLoader();
+ pluginNativeComboBox.getSelectionModel().select(loader);
+
+ int timeout = (int) getPreferences().getLong(ApplicationDefaults.NATIVE_LOADER_TIMEOUT_KEY, 10L);
+ loaderTimeoutSpinner.getValueFactory().setValue(timeout);
+ updateScannerTimeoutFieldState();
+
+ syncPluginsCheckBox.setSelected(
+ getPreferences().getBoolean(ApplicationDefaults.SYNC_PLUGINS_STARTUP_KEY, false));
+ syncFileStatCheckbox.setSelected(
+ getPreferences().getBoolean(ApplicationDefaults.SYNC_FILE_STAT_KEY, true));
+
+ if (!getApplicationDefaults().getRuntimePlatform()
+ .getOperatingSystem().equals(OperatingSystem.MAC)) {
+ auFragment.disable();
+ }
+ }
+
+ private void updateScannerTimeoutFieldState() {
+ NativePluginLoader selected = pluginNativeComboBox.getSelectionModel().getSelectedItem();
+ boolean isOwlPlugScanner = selected != null && "owlplug-scanner".equals(selected.getId());
+ boolean nativeEnabled = pluginNativeCheckbox.isSelected() && !pluginNativeCheckbox.isDisable();
+ loaderTimeoutSpinner.setDisable(!nativeEnabled || !isOwlPlugScanner);
+ }
+
+}
\ No newline at end of file
diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/options/ProjectsOptionsController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/ProjectsOptionsController.java
new file mode 100644
index 00000000..f70c9fe1
--- /dev/null
+++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/options/ProjectsOptionsController.java
@@ -0,0 +1,92 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+package com.owlplug.core.controllers.options;
+
+import com.owlplug.core.components.ApplicationDefaults;
+import com.owlplug.core.controllers.BaseController;
+import java.io.File;
+import java.util.ArrayList;
+import javafx.collections.FXCollections;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.stage.DirectoryChooser;
+import javafx.stage.Window;
+import org.kordamp.ikonli.javafx.FontIcon;
+import org.springframework.stereotype.Controller;
+
+@Controller
+public class ProjectsOptionsController extends BaseController {
+
+ @FXML
+ private ListView projectListView;
+ @FXML
+ private Button addDirButton;
+ @FXML
+ private Button removeDirButton;
+
+ private ObservableList projectDirectories;
+
+ @FXML
+ public void initialize() {
+ Label placeholder = new Label("No project directories configured.");
+ placeholder.getStyleClass().add("label-disabled");
+ projectListView.setPlaceholder(placeholder);
+
+ FontIcon addIcon = new FontIcon("mdi2p-plus");
+ addIcon.setIconSize(14);
+ addDirButton.setGraphic(addIcon);
+
+ FontIcon removeIcon = new FontIcon("mdi2m-minus");
+ removeIcon.setIconSize(14);
+ removeDirButton.setGraphic(removeIcon);
+
+ projectDirectories = FXCollections.observableArrayList(
+ getPreferences().getList(ApplicationDefaults.PROJECT_DIRECTORY_KEY, new ArrayList<>()));
+ projectListView.setItems(projectDirectories);
+
+ projectDirectories.addListener((ListChangeListener) change ->
+ getPreferences().putList(
+ ApplicationDefaults.PROJECT_DIRECTORY_KEY, new ArrayList<>(projectDirectories)));
+
+ addDirButton.setOnAction(e -> {
+ DirectoryChooser chooser = new DirectoryChooser();
+ Window window = addDirButton.getScene().getWindow();
+ File dir = chooser.showDialog(window);
+ if (dir != null) {
+ String path = dir.getAbsolutePath();
+ if (!projectDirectories.contains(path)) projectDirectories.add(path);
+ }
+ });
+
+ removeDirButton.setOnAction(e -> {
+ String selected = projectListView.getSelectionModel().getSelectedItem();
+ if (selected != null) projectDirectories.remove(selected);
+ });
+ }
+
+ public void refresh() {
+ projectDirectories.setAll(
+ getPreferences().getList(ApplicationDefaults.PROJECT_DIRECTORY_KEY, new ArrayList<>()));
+ }
+
+}
\ No newline at end of file
diff --git a/owlplug-client/src/main/java/com/owlplug/explore/ui/ExploreChipView.java b/owlplug-client/src/main/java/com/owlplug/explore/ui/ExploreChipView.java
index 2ec150ed..3d27822b 100644
--- a/owlplug-client/src/main/java/com/owlplug/explore/ui/ExploreChipView.java
+++ b/owlplug-client/src/main/java/com/owlplug/explore/ui/ExploreChipView.java
@@ -139,10 +139,10 @@ public ExploreFilterCriteria fromString(String string) {
root.getStyleClass().add("chip-brown");
}
if (getItem().getFilterType() == ExploreFilterCriteriaType.TAG) {
- root.getStyleClass().add("chip-blue");
+ root.getStyleClass().add("chip-red");
}
if (getItem().getFilterType() == ExploreFilterCriteriaType.CREATOR) {
- root.getStyleClass().add("chip-red");
+ root.getStyleClass().add("chip-blue");
}
}
});
diff --git a/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java
index 4903bcc7..0c7caeb7 100644
--- a/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java
+++ b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java
@@ -102,7 +102,7 @@ private StackPane buildThumbnail(Image image, RemotePackage remotePackage) {
} else if (remotePackage.getStage() == PluginStage.DEMO) {
stageBadge.getStyleClass().add("package-stage-demo");
}
- StackPane.setAlignment(stageBadge, Pos.BOTTOM_LEFT);
+ StackPane.setAlignment(stageBadge, Pos.TOP_LEFT);
thumb.getChildren().add(stageBadge);
}
diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFormat.java b/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFormat.java
index 5958d131..60963614 100644
--- a/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFormat.java
+++ b/owlplug-client/src/main/java/com/owlplug/plugin/model/PluginFormat.java
@@ -19,16 +19,30 @@
package com.owlplug.plugin.model;
public enum PluginFormat {
- VST2("VST2"), VST3("VST3"), AU("AU"), LV2("LV2");
+ VST2("VST2", "Virtual Instrument (v2)"),
+ VST3("VST3", "Virtual Instrument (v3)"),
+ AU("AU", "Audio Unit"),
+ LV2("LV2", "LADSPA (v2)");
- private final String text;
+ private final String name;
+ private final String fullName;
- PluginFormat(String text) {
- this.text = text;
+ PluginFormat(String name, String fullName) {
+ this.name = name;
+ this.fullName = fullName;
}
+ @Deprecated
public String getText() {
- return text;
+ return name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getFullName() {
+ return fullName;
}
/**
@@ -43,7 +57,7 @@ public static PluginFormat fromBundleString(String text) {
return PluginFormat.VST2;
}
for (PluginFormat f : PluginFormat.values()) {
- if (f.text.equalsIgnoreCase(text)) {
+ if (f.name.equalsIgnoreCase(text)) {
return f;
}
}
diff --git a/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectInfoController.java b/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectInfoController.java
index 2396522f..afc02099 100644
--- a/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectInfoController.java
+++ b/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectInfoController.java
@@ -170,7 +170,7 @@ public void updateItem(Plugin item, boolean empty) {
link.setGraphic(new ImageView(getApplicationDefaults().linkIconImage));
link.setOnAction(e -> {
pluginsController.selectPluginById(item.getId());
- mainController.selectMainTab(MainController.PLUGINS_TAB_INDEX);
+ mainController.navigateToMainTab(MainController.PLUGINS_TAB_INDEX);
});
setGraphic(link);
}
diff --git a/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectsController.java b/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectsController.java
index c27ea2b9..d8802ca5 100644
--- a/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectsController.java
+++ b/owlplug-client/src/main/java/com/owlplug/project/controllers/ProjectsController.java
@@ -18,9 +18,9 @@
package com.owlplug.project.controllers;
-import com.owlplug.core.components.ApplicationDefaults;
import com.owlplug.core.controllers.BaseController;
-import com.owlplug.core.controllers.dialogs.ListDirectoryDialogController;
+import com.owlplug.core.controllers.MainController;
+import com.owlplug.core.controllers.OptionsController;
import com.owlplug.core.ui.FilterableTreeItem;
import com.owlplug.core.utils.FX;
import com.owlplug.project.events.ProjectSyncEvent;
@@ -46,7 +46,9 @@ public class ProjectsController extends BaseController {
@Autowired
private ProjectService projectService;
@Autowired
- private ListDirectoryDialogController listDirectoryDialogController;
+ private MainController mainController;
+ @Autowired
+ private OptionsController optionsController;
@Autowired
private ProjectInfoController projectInfoController;
@@ -81,8 +83,8 @@ public void initialize() {
});
projectDirectoryButton.setOnAction(e -> {
- listDirectoryDialogController.configure(ApplicationDefaults.PROJECT_DIRECTORY_KEY);
- listDirectoryDialogController.show();
+ optionsController.navigateToSection(OptionsController.PROJECTS_SECTION_INDEX);
+ mainController.navigateToMainTab(MainController.OPTIONS_TAB_INDEX);
});
projectTreeNode = new FilterableTreeItem<>("(all)");
diff --git a/owlplug-client/src/main/resources/fxml/MainView.fxml b/owlplug-client/src/main/resources/fxml/MainView.fxml
index 3b027854..49db49c0 100644
--- a/owlplug-client/src/main/resources/fxml/MainView.fxml
+++ b/owlplug-client/src/main/resources/fxml/MainView.fxml
@@ -5,8 +5,6 @@
-
-
diff --git a/owlplug-client/src/main/resources/fxml/OptionsView.fxml b/owlplug-client/src/main/resources/fxml/OptionsView.fxml
index cccce37e..e0bd9a5d 100644
--- a/owlplug-client/src/main/resources/fxml/OptionsView.fxml
+++ b/owlplug-client/src/main/resources/fxml/OptionsView.fxml
@@ -1,141 +1,45 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/owlplug-client/src/main/resources/fxml/Preloader.fxml b/owlplug-client/src/main/resources/fxml/Preloader.fxml
index 2970ee6f..f234b868 100644
--- a/owlplug-client/src/main/resources/fxml/Preloader.fxml
+++ b/owlplug-client/src/main/resources/fxml/Preloader.fxml
@@ -5,7 +5,7 @@
diff --git a/owlplug-client/src/main/resources/fxml/dialogs/ListDirectoryView.fxml b/owlplug-client/src/main/resources/fxml/dialogs/ListDirectoryView.fxml
index 749897b2..a4224593 100644
--- a/owlplug-client/src/main/resources/fxml/dialogs/ListDirectoryView.fxml
+++ b/owlplug-client/src/main/resources/fxml/dialogs/ListDirectoryView.fxml
@@ -3,13 +3,12 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml b/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml
index 5ce1ae30..05480c49 100644
--- a/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml
+++ b/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml
@@ -26,7 +26,7 @@
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
-
+
@@ -64,7 +64,7 @@
-
-
+
diff --git a/owlplug-client/src/main/resources/fxml/projects/ProjectsView.fxml b/owlplug-client/src/main/resources/fxml/projects/ProjectsView.fxml
index e48f5e8e..3dbaf744 100644
--- a/owlplug-client/src/main/resources/fxml/projects/ProjectsView.fxml
+++ b/owlplug-client/src/main/resources/fxml/projects/ProjectsView.fxml
@@ -6,8 +6,6 @@
-
-
@@ -36,9 +34,7 @@
-
-
-
+
-
+
@@ -67,7 +63,7 @@
-
+
diff --git a/owlplug-client/src/main/resources/icons/heart-white-32.png b/owlplug-client/src/main/resources/icons/heart-white-32.png
deleted file mode 100644
index 7ef205f9..00000000
Binary files a/owlplug-client/src/main/resources/icons/heart-white-32.png and /dev/null differ
diff --git a/owlplug-client/src/main/resources/media/owlplug-logo.png b/owlplug-client/src/main/resources/media/owlplug-logo.png
index 8102fada..4a5e26ab 100644
Binary files a/owlplug-client/src/main/resources/media/owlplug-logo.png and b/owlplug-client/src/main/resources/media/owlplug-logo.png differ
diff --git a/owlplug-client/src/main/resources/owlplug.css b/owlplug-client/src/main/resources/owlplug.css
index 8e970e3e..7d6e13bd 100644
--- a/owlplug-client/src/main/resources/owlplug.css
+++ b/owlplug-client/src/main/resources/owlplug.css
@@ -50,10 +50,10 @@
*/
-fx-font-family: "Segoe UI";
- disabled-color: -color-neutral-muted;
- danger-color: -color-danger-subtle;
- warning-color: -color-warning-fg;
- success-color: -color-success-fg;
+ disabled-color: -color-fg-muted;
+ danger-color: -color-danger-emphasis;
+ warning-color: -color-warning-emphasis;
+ success-color: -color-success-emphasis;
theme-primary-color: -color-accent-fg;
@@ -100,12 +100,23 @@ Label {
}
.heading-1 {
- -fx-font-size: 22px;
+ -fx-font-size: 25px;
+ -fx-font-weight: bold;
-fx-margin: 0, 0, 8, 0;
}
.heading-2 {
- -fx-font-size: 20px;
+ -fx-font-size: 21px;
+ -fx-font-weight: bold;
+}
+
+.heading-3 {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+}
+
+.heading-4 {
+ -fx-font-size: 14px;
}
Text {
@@ -295,8 +306,10 @@ Text {
-fx-tab-max-height: 0;
}
-.no-header-tab-pane>.tab-header-area {
- visibility: hidden;
+.no-header-tab-pane > .tab-header-area {
+ -fx-min-height: 0;
+ -fx-pref-height: 0;
+ -fx-max-height: 0;
}
.drawer-side-pane {
@@ -349,9 +362,14 @@ ProgressIndicator {
-fx-text-fill: text-color;
}
-/* Change the content area background */
+/* Collapse the unused content area of the navigation TabPane */
+/* JavaFX always renders a content area below the tab strip even when tabs have no content */
#tabPaneHeader .tab-content-area {
-fx-background-color: -color-bg-subtle;
+ -fx-padding: 0;
+ -fx-min-height: 0;
+ -fx-pref-height: 0;
+ -fx-max-height: 0;
}
/* Change the tab header area background */
@@ -591,6 +609,7 @@ ProgressIndicator {
}
+
/****************************************************************
* Install step dialog
****************************************************************/
@@ -599,6 +618,45 @@ ProgressIndicator {
-fx-background-radius: 0;
}
+/****************************************************************
+ * Options view
+ ****************************************************************/
+
+.options-sidebar {
+ -fx-background-color: -color-bg-subtle;
+ -fx-border-color: -color-border-default;
+ -fx-border-width: 0 1 0 0;
+}
+
+.options-sidebar-footer {
+ -fx-border-color: -color-border-default;
+ -fx-border-width: 1 0 0 0;
+}
+
+.options-nav-button {
+ -fx-background-color: transparent;
+ -fx-background-radius: 6px;
+ -fx-padding: 8 12 8 12;
+ -fx-alignment: CENTER_LEFT;
+ -fx-cursor: hand;
+ -fx-font-size: 13px;
+ -fx-max-width: Infinity;
+}
+
+.options-nav-button:hover {
+ -fx-background-color: -color-neutral-subtle;
+}
+
+.options-nav-button:selected {
+ -fx-background-color: -color-accent-subtle;
+ -fx-text-fill: -color-accent-fg;
+ -fx-font-weight: bold;
+}
+
+.options-nav-button:selected .ikonli-font-icon {
+ -fx-icon-color: -color-accent-fg;
+}
+
/****************************************************************
*
* Utilities
diff --git a/owlplug-theme/.gitignore b/owlplug-theme/.gitignore
new file mode 100644
index 00000000..8e5e9fea
--- /dev/null
+++ b/owlplug-theme/.gitignore
@@ -0,0 +1,5 @@
+# Maven extraction target
+target/
+
+# Generated css files
+*.css
diff --git a/owlplug-theme/pom.xml b/owlplug-theme/pom.xml
new file mode 100644
index 00000000..b2594ff7
--- /dev/null
+++ b/owlplug-theme/pom.xml
@@ -0,0 +1,134 @@
+
+
+ 4.0.0
+
+
+ com.owlplug
+ owlplug
+ 1.33.1
+
+
+ owlplug-theme
+ jar
+ owlplug-theme
+ OwlPlug custom AtlantaFX theme
+
+
+ 1.87.0
+ ${project.build.directory}
+
+
+
+
+ io.github.mkpaz
+ atlantafx-base
+ ${atlantafx.version}
+
+
+ org.openjfx
+ javafx-graphics
+ ${javafx.version}
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.6.1
+
+
+ unpack-atlantafx-styles
+ generate-resources
+
+ unpack
+
+
+
+
+ io.github.mkpaz
+ atlantafx-styles
+ ${atlantafx.version}
+ jar
+ false
+ ${atlantafx.styles.unpack.dir}
+
+
+
+
+
+
+
+
+
+ us.hebi.sass
+ sass-cli-maven-plugin
+ 1.0.4
+
+
+ compile-scss
+ generate-resources
+
+ run
+
+
+ ${sass.version}
+
+ --no-source-map
+ --load-path=${atlantafx.styles.unpack.dir}/atlantafx/styles/src
+ ${project.basedir}/src/main/scss/owlplug-dark.scss:${project.basedir}/src/main/resources/com/owlplug/theme/owlplug-dark.css
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+
+
+
+
+
+
+
+ watch
+
+
+
+ us.hebi.sass
+ sass-cli-maven-plugin
+ 1.0.4
+
+
+ watch-scss
+ generate-resources
+
+ run
+
+
+ ${sass.version}
+
+ --no-source-map
+ --load-path=${atlantafx.styles.unpack.dir}/atlantafx/styles/src
+ --watch
+ ${project.basedir}/src/main/scss/owlplug-dark.scss:${project.basedir}/src/main/resources/com/owlplug/theme/owlplug-dark.css
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/owlplug-theme/src/main/java/com/owlplug/theme/OwlPlugDarkTheme.java b/owlplug-theme/src/main/java/com/owlplug/theme/OwlPlugDarkTheme.java
new file mode 100644
index 00000000..b343af3a
--- /dev/null
+++ b/owlplug-theme/src/main/java/com/owlplug/theme/OwlPlugDarkTheme.java
@@ -0,0 +1,48 @@
+/* OwlPlug
+ * Copyright (C) 2021 Arthur
+ *
+ * This file is part of OwlPlug.
+ *
+ * OwlPlug is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3
+ * as published by the Free Software Foundation.
+ *
+ * OwlPlug is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OwlPlug. If not, see .
+ */
+
+package com.owlplug.theme;
+
+import atlantafx.base.theme.Theme;
+import java.util.Objects;
+
+public class OwlPlugDarkTheme implements Theme {
+
+ @Override
+ public String getName() {
+ return "OwlPlug Dark";
+ }
+
+ @Override
+ public String getUserAgentStylesheet() {
+ return Objects.requireNonNull(
+ OwlPlugDarkTheme.class.getResource("owlplug-dark.css")
+ ).toExternalForm();
+ }
+
+ @Override
+ public String getUserAgentStylesheetBSS() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isDarkMode() {
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/owlplug-theme/src/main/scss/owlplug-dark.scss b/owlplug-theme/src/main/scss/owlplug-dark.scss
new file mode 100644
index 00000000..20e84828
--- /dev/null
+++ b/owlplug-theme/src/main/scss/owlplug-dark.scss
@@ -0,0 +1,144 @@
+// OwlPlug Dark — custom AtlantaFX theme for OwlPlug
+// Primary: #A8492E (warm terracotta/burnt sienna)
+// Base: Primer Dark with a faint warm tint to harmonise with the primary
+// Theme variants : #b86a54 #220f09 #b15b43 #793521
+
+@use "sass:color";
+
+@use "../../../target/atlantafx/styles/settings/utils" as utils;
+
+// ── Color scale ──────────────────────────────────────────────────────────────
+
+@forward "../../../target/atlantafx/styles/settings/color-scale" with (
+
+ $dark: #010409,
+ $light: #ffffff,
+
+ // Neutral — Primer Dark warmed very slightly to harmonise with terracotta
+ $base-0: #f0f6fc,
+ $base-1: #cdd1d6,
+ $base-2: #b0b7be,
+ $base-3: #8b9199,
+ $base-4: #6c7280,
+ $base-5: #484d56,
+ $base-6: #31333a,
+ $base-7: #22252c,
+ $base-8: #181a20,
+ $base-9: #0e1018,
+
+ // Accent — OwlPlug terracotta (#A8492E is the primary at accent-5)
+ $accent-0: #fef2ed,
+ $accent-1: #fdd8c8,
+ $accent-2: #f7b099,
+ $accent-3: #d97a58,
+ $accent-4: #be5f3e,
+ $accent-5: #a8492e,
+ $accent-6: #8a3a22,
+ $accent-7: #6b2c18,
+ $accent-8: #4c1e10,
+ $accent-9: #301209,
+
+ // Success (identical to Primer Dark)
+ $success-0: #aff5b4,
+ $success-1: #7ee787,
+ $success-2: #56d364,
+ $success-3: #3fb950,
+ $success-4: #2ea043,
+ $success-5: #238636,
+ $success-6: #196c2e,
+ $success-7: #0f5323,
+ $success-8: #033a16,
+ $success-9: #04260f,
+
+ // Warning (identical to Primer Dark)
+ $warning-0: #f8e3a1,
+ $warning-1: #f2cc60,
+ $warning-2: #e3b341,
+ $warning-3: #d29922,
+ $warning-4: #bb8009,
+ $warning-5: #9e6a03,
+ $warning-6: #845306,
+ $warning-7: #693e00,
+ $warning-8: #4b2900,
+ $warning-9: #341a00,
+
+ // Danger (identical to Primer Dark)
+ $danger-0: #ffdcd7,
+ $danger-1: #ffc1ba,
+ $danger-2: #ffa198,
+ $danger-3: #ff7b72,
+ $danger-4: #f85149,
+ $danger-5: #da3633,
+ $danger-6: #b62324,
+ $danger-7: #8e1519,
+ $danger-8: #67060c,
+ $danger-9: #490202
+);
+
+@use "../../../target/atlantafx/styles/settings/color-scale" as scale;
+
+// ── Functional color tokens ───────────────────────────────────────────────────
+
+@forward "../../../target/atlantafx/styles/settings/color-vars" with (
+
+ $fg-default: scale.$base-1,
+ $fg-muted: scale.$base-3,
+ $fg-subtle: scale.$base-4,
+ $fg-onEmphasis: scale.$light,
+
+ $canvas-default: scale.$base-9,
+ $canvas-overlay: scale.$base-8,
+ $canvas-inset: scale.$dark,
+ $canvas-subtle: scale.$base-8,
+
+ $border-default: scale.$base-6,
+ $border-muted: scale.$base-7,
+ $border-subtle: utils.flattenColor(scale.$base-9, color.change(scale.$base-0, $alpha: 0.1)),
+
+ $shadow-default: scale.$dark,
+
+ $neutral-emphasisPlus: scale.$base-4,
+ $neutral-emphasis: scale.$base-4,
+ $neutral-muted: color.change(scale.$base-4, $alpha: 0.4),
+ $neutral-subtle: color.change(scale.$base-4, $alpha: 0.1),
+
+ // accent-fg: lighter terracotta for text/icons on dark canvas (~5.5:1 on base-9)
+ // accent-emphasis: #A8492E primary for filled elements
+ $accent-fg: scale.$accent-3,
+ $accent-emphasis: scale.$accent-5,
+ $accent-muted: color.change(scale.$accent-5, $alpha: 0.4),
+ $accent-subtle: color.change(scale.$accent-5, $alpha: 0.15),
+
+ $success-fg: scale.$success-3,
+ $success-emphasis: scale.$success-5,
+ $success-muted: color.change(scale.$success-4, $alpha: 0.4),
+ $success-subtle: color.change(scale.$success-4, $alpha: 0.15),
+
+ $warning-fg: scale.$warning-3,
+ $warning-emphasis: scale.$warning-5,
+ $warning-muted: color.change(scale.$warning-4, $alpha: 0.4),
+ $warning-subtle: color.change(scale.$warning-4, $alpha: 0.15),
+
+ $danger-fg: scale.$danger-4,
+ $danger-emphasis: scale.$danger-5,
+ $danger-muted: color.change(scale.$danger-4, $alpha: 0.4),
+ $danger-subtle: color.change(scale.$danger-4, $alpha: 0.15)
+);
+
+@forward "../../../target/atlantafx/styles/settings/config" with (
+ $darkMode: true
+);
+
+@use "../../../target/atlantafx/styles/general";
+@use "../../../target/atlantafx/styles/components";
+
+// Override floating TabPane: use default canvas instead of inset background
+.tab-pane.floating {
+ > .tab-header-area {
+ -fx-background-color: -color-border-default, -color-bg-default;
+ }
+
+ > .tab-header-area > .headers-region > .tab > .tab-container {
+ -fx-background-color: -color-border-subtle, -color-bg-default;
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 6cfb1a93..7929e89d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
UTF-8
25
25.0.1
+ 2.1.0
25
25
@@ -25,6 +26,7 @@
owlplug-host
owlplug-controls
owlplug-parsers
+ owlplug-theme