diff --git a/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java b/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java index 57855792..4047aa7f 100644 --- a/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java +++ b/owlplug-client/src/main/java/com/owlplug/core/components/ApplicationDefaults.java @@ -22,6 +22,7 @@ import com.owlplug.core.model.RuntimePlatform; import com.owlplug.explore.model.RemotePackage; import com.owlplug.plugin.model.PluginFormat; +import com.owlplug.plugin.model.PluginType; import com.owlplug.project.model.DawApplication; import java.io.BufferedReader; import java.io.InputStream; @@ -31,6 +32,7 @@ import java.util.List; import java.util.stream.Collectors; import javafx.scene.image.Image; +import org.kordamp.ikonli.javafx.FontIcon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -68,13 +70,7 @@ public class ApplicationDefaults { public final Image taskSuccessImage = new Image(getClass().getResourceAsStream("/icons/check-green-16.png")); public final Image taskFailImage = new Image(getClass().getResourceAsStream("/icons/cross-red-16.png")); public final Image taskRunningImage = new Image(getClass().getResourceAsStream("/icons/play-green-16.png")); - public final Image rocketImage = new Image(getClass().getResourceAsStream("/icons/rocket-white-64.png")); - public final Image serverImage = new Image(getClass().getResourceAsStream("/icons/server-white-32.png")); - public final Image instrumentImage = new Image(getClass().getResourceAsStream("/icons/synth-white-16.png")); - public final Image effectImage = new Image(getClass().getResourceAsStream("/icons/effect-white-16.png")); - public final Image tagImage = new Image(getClass().getResourceAsStream("/icons/tag-white-16.png")); public final Image symlinkImage = new Image(getClass().getResourceAsStream("/icons/folderlink-grey-16.png")); - public final Image userImage = new Image(getClass().getResourceAsStream("/icons/user-white-32.png")); public final Image scanDirectoryImage = new Image(getClass().getResourceAsStream("/icons/foldersearch-grey-16.png")); public final Image verifiedSourceImage = new Image(getClass().getResourceAsStream("/icons/doublecheck-grey-16.png")); public final Image suggestedSourceImage = new Image( @@ -142,29 +138,23 @@ public RuntimePlatform getRuntimePlatform() { * @return Associated icon */ public Image getPluginFormatIcon(PluginFormat format) { - + if (format == null) { + return pluginComponentImage; + } return switch (format) { case VST2 -> vst2Image; case VST3 -> vst3Image; case AU -> auImage; case LV2 -> lv2Image; - default -> vst2Image; + default -> pluginComponentImage; }; } - /** - * Returns plugin icon based on plugin format. - * - * @param remotePackage - package - * @return Associated icon - */ - public Image getPackageTypeIcon(RemotePackage remotePackage) { - - return switch (remotePackage.getType()) { - case INSTRUMENT -> instrumentImage; - case EFFECT -> effectImage; - default -> null; + public String getPackageTypeIconLiteral(PluginType type) { + return switch (type) { + case INSTRUMENT -> "mdi2p-piano"; + case EFFECT -> "mdi2w-waveform"; }; } 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 37b00bdf..48fde37f 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 @@ -30,8 +30,8 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; +import org.kordamp.ikonli.javafx.FontIcon; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Controller; @@ -139,10 +139,9 @@ protected void onDialogShow() { protected DialogLayout getLayout() { Label title = new Label("Owlplug is almost ready !"); title.getStyleClass().add("heading-3"); - ImageView iv = new ImageView(this.getApplicationDefaults().rocketImage); - iv.setFitHeight(20); - iv.setFitWidth(20); - title.setGraphic(iv); + FontIcon icon = new FontIcon("mdi2r-rocket-launch-outline"); + icon.setIconSize(20); + title.setGraphic(icon); DialogLayout layout = new DialogLayout(); layout.setHeading(title); layout.setBody(lazyViewRegistry.get(LazyViewRegistry.WELCOME_VIEW)); diff --git a/owlplug-client/src/main/java/com/owlplug/explore/controllers/ExploreController.java b/owlplug-client/src/main/java/com/owlplug/explore/controllers/ExploreController.java index a33d1c05..90ef346a 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/controllers/ExploreController.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/controllers/ExploreController.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with OwlPlug. If not, see . */ - + package com.owlplug.explore.controllers; import com.google.common.collect.Iterables; @@ -38,6 +38,7 @@ import com.owlplug.explore.services.ExploreService; import com.owlplug.explore.ui.ExploreChipView; import com.owlplug.explore.ui.PackageBlocViewBuilder; +import com.owlplug.explore.ui.PackageListRowView; import com.owlplug.plugin.model.PluginFormat; import java.util.ArrayList; import java.util.HashMap; @@ -53,6 +54,9 @@ import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.image.Image; import javafx.scene.input.MouseButton; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; @@ -98,6 +102,12 @@ public class ExploreController extends BaseController { @FXML private Button syncSourcesButton; @FXML + private TabPane displaySwitchTabPane; + @FXML + private Tab displayGridTab; + @FXML + private Tab displayListTab; + @FXML private Text resultCounter; @FXML private MasonryPane masonryPane; @@ -108,29 +118,35 @@ public class ExploreController extends BaseController { @FXML private Hyperlink lazyLoadLink; @FXML + private ScrollPane listScrollPane; + @FXML + private VBox listPane; + @FXML + private HBox listLazyLoadBar; + @FXML + private Hyperlink listLazyLoadLink; + @FXML private Pane exploreChipViewContainer; - - private final HashMap targetFilterCheckBoxes = new HashMap<>(); + private final HashMap targetFilterCheckBoxes = new HashMap<>(); private final HashMap formatsFilterCheckBoxes = new HashMap<>(); - private ExploreChipView exploreChipView; private PackageBlocViewBuilder packageBlocViewBuilder = null; /** * Loaded packages from remote sources are displayed by partitions (like pagination). - * When the user scrolls the entire partition, the next one is appended in the - * UI. + * When the user scrolls the entire partition, the next one is appended in the UI. */ private Iterable> loadedPackagePartitions; private Iterable loadedRemotePackages = new ArrayList<>(); - /** - * Counter of loaded partitions on UI. - */ + /** Counter of loaded partitions on UI — masonry view. */ private int displayedPartitions = 0; + /** Counter of loaded partitions on UI — list view. */ + private int listDisplayedPartitions = 0; + /** * FXML initialize. */ @@ -141,16 +157,13 @@ public void initialize() { sourcesButton.setOnAction(e -> { mainController.setLeftDrawer(viewRegistry.get(LazyViewRegistry.SOURCE_MENU_VIEW)); mainController.getLeftDrawer().open(); - }); for (PluginFormat format : PluginFormat.values()) { CheckBox checkbox = new CheckBox(format.getText()); formatsFilterCheckBoxes.put(format.getText().toLowerCase(), checkbox); checkbox.setSelected(false); - checkbox.setOnAction(e -> { - performPackageSearch(); - }); + checkbox.setOnAction(e -> performPackageSearch()); } VBox formatFilterVbox = new VBox(); @@ -168,7 +181,6 @@ public void initialize() { popup.show(formatFilterButton, Popup.PopupVPosition.TOP, Popup.PopupHPosition.RIGHT); }); - targetFilterCheckBoxes.put("win-x32", new CheckBox("Windows x32")); targetFilterCheckBoxes.put("win-x64", new CheckBox("Windows x64")); targetFilterCheckBoxes.put("win-arm64", new CheckBox("Windows arm64")); @@ -182,11 +194,9 @@ public void initialize() { for (Entry entry : targetFilterCheckBoxes.entrySet()) { Set preselected = this.getApplicationDefaults().getRuntimePlatform().getCompatiblePlatformsTags(); entry.getValue().setSelected(preselected.contains(entry.getKey())); - entry.getValue().setOnAction(e -> { - performPackageSearch(); - }); + entry.getValue().setOnAction(e -> performPackageSearch()); } - + VBox platformFilterVbox = new VBox(); platformFilterVbox.setSpacing(5); platformFilterVbox.setPadding(new Insets(5,10,5,10)); @@ -219,27 +229,47 @@ public void initialize() { performPackageSearch(); }); + // Grid view scroll → load next partition scrollPane.vvalueProperty().addListener((observable, oldValue, newValue) -> { if (newValue.doubleValue() == 1) { displayNewPackagePartition(); } }); + lazyLoadLink.setOnAction(e -> displayNewPackagePartition()); + lazyLoadBar.setVisible(false); - lazyLoadLink.setOnAction(e -> { - displayNewPackagePartition(); + // List view scroll → load next partition + listScrollPane.vvalueProperty().addListener((observable, oldValue, newValue) -> { + if (newValue.doubleValue() == 1) { + displayNewListPartition(); + } }); - lazyLoadBar.setVisible(false); + listLazyLoadLink.setOnAction(e -> displayNewListPartition()); + listLazyLoadBar.setVisible(false); + + displaySwitchTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldTab, newTab) -> { + boolean listActive = newTab.equals(displayListTab); + scrollPane.setVisible(!listActive); + scrollPane.setManaged(!listActive); + listScrollPane.setVisible(listActive); + listScrollPane.setManaged(listActive); + if (!listActive) { + FX.run(() -> { + masonryPane.requestLayout(); + scrollPane.requestLayout(); + }); + } + }); + displaySwitchTabPane.getSelectionModel().select(displayListTab); performPackageSearch(); masonryPane.setHSpacing(5); masonryPane.setVSpacing(5); - masonryPane.setCellHeight(130); masonryPane.setCellWidth(130); - } - + private synchronized void performPackageSearch() { final List criteriaChipList = exploreChipView.getChips(); List criteriaList = new ArrayList<>(criteriaChipList); @@ -270,33 +300,33 @@ protected Iterable call() { return exploreService.getRemotePackages(criteriaList); } }; - task.setOnSucceeded(e -> { - displayPackages(task.getValue()); - }); + task.setOnSucceeded(e -> displayPackages(task.getValue())); new Thread(task).start(); - } /** * Display remote source package list. - * + * * @param remotePackages - Remote package list */ public synchronized void displayPackages(Iterable remotePackages) { - if (shouldRefreshPackages(remotePackages)) { this.masonryPane.getChildren().clear(); this.masonryPane.requestLayout(); + // Remove all list rows (keep the lazyLoadBar at the end) + listPane.getChildren().removeIf(node -> node instanceof PackageListRowView); + loadedRemotePackages = remotePackages; loadedPackagePartitions = Iterables.partition(loadedRemotePackages, PARTITION_SIZE); displayedPartitions = 0; + listDisplayedPartitions = 0; displayNewPackagePartition(); + displayNewListPartition(); } } private void displayNewPackagePartition() { - if (Iterables.size(loadedPackagePartitions) > displayedPartitions) { for (RemotePackage remotePackage : Iterables.get(loadedPackagePartitions, displayedPartitions)) { Rippler rippler = new Rippler(packageBlocViewBuilder.build(remotePackage)); @@ -309,48 +339,65 @@ private void displayNewPackagePartition() { } displayedPartitions += 1; - if (Iterables.size(loadedPackagePartitions) == displayedPartitions) { - lazyLoadBar.setVisible(false); - } else { - lazyLoadBar.setVisible(true); - - } + boolean allLoaded = Iterables.size(loadedPackagePartitions) == displayedPartitions; + lazyLoadBar.setVisible(!allLoaded); FX.run(() -> { masonryPane.requestLayout(); scrollPane.requestLayout(); }); } - - resultCounter.setText(this.masonryPane.getChildren().size() + " / " + Iterables.size(this.loadedRemotePackages)); + refreshResultCounter(); + } + + private void displayNewListPartition() { + if (Iterables.size(loadedPackagePartitions) > listDisplayedPartitions) { + // Insert rows before the lazyLoadBar (always the last child) + int lazyBarIndex = listPane.getChildren().size() - 1; + int insertIndex = lazyBarIndex; + for (RemotePackage remotePackage : Iterables.get(loadedPackagePartitions, listDisplayedPartitions)) { + Image image = imageCache.get(remotePackage.getScreenshotUrl()); + PackageListRowView row = new PackageListRowView( + this.getApplicationDefaults(), remotePackage, image, this); + row.setOnMouseClicked(e -> { + if (e.getButton().equals(MouseButton.PRIMARY)) { + selectPackage(remotePackage); + } + }); + listPane.getChildren().add(insertIndex++, row); + } + listDisplayedPartitions += 1; + + boolean allLoaded = Iterables.size(loadedPackagePartitions) == listDisplayedPartitions; + listLazyLoadBar.setVisible(!allLoaded); + + refreshResultCounter(); + } + } + + private void refreshResultCounter() { + resultCounter.setText(this.masonryPane.getChildren().size() + " / " + Iterables.size(this.loadedRemotePackages)); } /** - * Returns true if the given package list is different from the previously - * loaded package list. - * - * @param newPackages - the new package list - * @return + * Returns true if the given package list is different from the previously loaded package list. */ private boolean shouldRefreshPackages(Iterable newPackages) { - if (Iterables.size(newPackages) != Iterables.size(loadedRemotePackages)) { return true; } - for (int i = 0; i < Iterables.size(newPackages); i++) { if (!Iterables.get(newPackages, i).getId().equals(Iterables.get(loadedRemotePackages, i).getId())) { return true; } } - return false; } /** * Displays full package information. - * + * * @param remotePackage - package */ public void selectPackage(RemotePackage remotePackage) { @@ -365,9 +412,8 @@ public void addSearchChip(String chip) { } /** - * Trigger package installation. The best bundle will be selected based on the - * current user platform. - * + * Trigger package installation. The best bundle will be selected based on the current user platform. + * * @param remotePackage Package to install */ public boolean installPackage(RemotePackage remotePackage) { @@ -411,4 +457,4 @@ private void handle(RemoteSourceUpdatedEvent event) { FX.run(this::performPackageSearch); } -} +} \ No newline at end of file diff --git a/owlplug-client/src/main/java/com/owlplug/explore/controllers/NewSourceDialogController.java b/owlplug-client/src/main/java/com/owlplug/explore/controllers/NewSourceDialogController.java index 5cf86888..683f732e 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/controllers/NewSourceDialogController.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/controllers/NewSourceDialogController.java @@ -31,7 +31,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.TextField; -import javafx.scene.image.ImageView; +import org.kordamp.ikonli.javafx.FontIcon; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -159,10 +159,9 @@ protected DialogLayout getLayout() { Label title = new Label("Add a new source"); title.getStyleClass().add("heading-3"); - ImageView iv = new ImageView(this.getApplicationDefaults().serverImage); - iv.setFitHeight(20); - iv.setFitWidth(20); - title.setGraphic(iv); + FontIcon icon = new FontIcon("mdi2w-warehouse"); + icon.setIconSize(20); + title.setGraphic(icon); DialogLayout layout = new DialogLayout(); layout.setHeading(title); diff --git a/owlplug-client/src/main/java/com/owlplug/explore/controllers/PackageInfoController.java b/owlplug-client/src/main/java/com/owlplug/explore/controllers/PackageInfoController.java index 87b0be2d..e3b8940c 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/controllers/PackageInfoController.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/controllers/PackageInfoController.java @@ -27,9 +27,9 @@ import com.owlplug.explore.model.RemotePackage; import com.owlplug.explore.ui.PackageBundlesView; import com.owlplug.explore.ui.PackageSourceBadgeView; +import com.owlplug.plugin.model.Plugin; import com.owlplug.plugin.model.PluginType; import javafx.fxml.FXML; -import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; @@ -41,9 +41,9 @@ import javafx.scene.layout.BackgroundRepeat; import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; +import org.kordamp.ikonli.javafx.FontIcon; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Controller; @@ -147,7 +147,7 @@ private void configureHeader(RemotePackage remotePackage) { this.nameLabel.setText(remotePackage.getName()); this.remoteSourceLabel.setText(remotePackage.getRemoteSource().getName()); - // Redirect button configuratio + // Redirect button configuration browsePageButton.setOnAction(e -> { PlatformUtils.openDefaultBrowser(remotePackage.getPageUrl()); }); @@ -197,13 +197,13 @@ private void configureBody(RemotePackage remotePackage) { } else if (remotePackage.getType() == PluginType.EFFECT) { this.typeLabel.setText("Effect (VST)"); } + this.typeLabel.setGraphic(new FontIcon( + this.getApplicationDefaults().getPackageTypeIconLiteral(remotePackage.getType()))); // Tag display tagContainer.getChildren().clear(); for (PackageTag tag : remotePackage.getTags()) { - Node chip = new FakeChip(tag.getName()); - chip.getStyleClass().add("chip"); - chip.getStyleClass().add("fake-chip"); + FakeChip chip = new FakeChip(tag.getName()); chip.setOnMouseClicked(e -> { exploreController.addSearchChip(tag.getName()); }); @@ -222,6 +222,7 @@ public void show() { if (sidebar.isCollapsed()) { sidebar.expand(); } + } public void hide() { @@ -232,15 +233,11 @@ public void toggleVisibility() { sidebar.toggle(); } - private static class FakeChip extends HBox { + private static class FakeChip extends Label { public FakeChip(String text) { - - Label label = new Label(text); - label.setWrapText(true); - label.setMaxWidth(100); - getChildren().add(label); - this.getStyleClass().add("chip-label"); + super(text); + this.getStyleClass().add("package-tag-badge"); } } diff --git a/owlplug-client/src/main/java/com/owlplug/explore/controllers/dialogs/InstallStepDialogController.java b/owlplug-client/src/main/java/com/owlplug/explore/controllers/dialogs/InstallStepDialogController.java index 375a8e64..6caf9b80 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/controllers/dialogs/InstallStepDialogController.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/controllers/dialogs/InstallStepDialogController.java @@ -47,9 +47,9 @@ import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; -import javafx.scene.text.Text; import javafx.stage.DirectoryChooser; import javafx.stage.Window; +import org.kordamp.ikonli.javafx.FontIcon; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -78,7 +78,7 @@ public class InstallStepDialogController extends AbstractDialogController { @FXML private ProgressBar progressBar; @FXML - private Text formatText; + private Label formatText; @FXML private TextField installationDirectoryTextField; @FXML @@ -90,11 +90,11 @@ public class InstallStepDialogController extends AbstractDialogController { @FXML private ToggleButton lv2ToggleButton; @FXML - private Text installationDirectoryText; + private Label installationDirectoryText; @FXML - private Text directoryValidText; + private Label directoryValidText; @FXML - private Text directoryOverrideText; + private Label directoryOverrideText; @FXML private Button directoryChooserButton; @FXML @@ -109,7 +109,7 @@ public class InstallStepDialogController extends AbstractDialogController { private InstallationParameters installParams; public InstallStepDialogController() { - super(500,600); + super(580,630); } public void initialize() { @@ -197,7 +197,7 @@ public void install(PackageBundle bundle) { private void resumeInstall() { stepAccordion.setExpandedPane(prepareStep); if (prepareInstallation()) { - prepareStep.setText("✅ Prepare installation"); + prepareStep.setGraphic(createCheckIcon()); prepareStep.setDisable(true); progressBar.setProgress((double) 1 / 3); } else { @@ -209,7 +209,7 @@ private void resumeInstall() { stepAccordion.setExpandedPane(selectDirectoryStep); if (selectInstallationDirectory()) { - selectDirectoryStep.setText("✅ Select installation directory"); + selectDirectoryStep.setGraphic(createCheckIcon()); selectDirectoryStep.setDisable(true); progressBar.setProgress((double) 2 / 3); } else { @@ -221,7 +221,7 @@ private void resumeInstall() { stepAccordion.setExpandedPane(verifyStep); if (verifyInstallationDirectory()) { - verifyStep.setText("✅ Verify plugin installation"); + verifyStep.setGraphic(createCheckIcon()); verifyStep.setDisable(true); progressBar.setProgress(1); } else { @@ -273,8 +273,7 @@ private boolean selectInstallationDirectory() { } if (installParams != null && installParams.getBundle() != null) { - formatText.setText(String.join(", ", installParams.getBundle().getFormats()) - .toUpperCase()); + formatText.setText("Bundle formats: " + String.join(", ", installParams.getBundle().getFormats()).toUpperCase()); } return false; @@ -284,7 +283,7 @@ private boolean selectInstallationDirectory() { * Verify final predicates for bundle installation: * - Installation directory must be a valid target file, otherwise * the user can choose another directory. - * - Verify that folder does not already exits, otherwise ask the user + * - Verify that folder does not already exist, otherwise ask the user * for permission to overwrite existing file in this folder. * @return true if installation predicates are met. */ @@ -295,36 +294,42 @@ private boolean verifyInstallationDirectory() { boolean result = true; - installationDirectoryText.setText(installParams.getInstallationDirectory().getAbsolutePath()); + File installationDirectory = installParams.getInstallationDirectory(); + installationDirectoryText.setText(installationDirectory.getAbsolutePath()); + + // Reset validation styles + directoryValidText.getStyleClass().removeAll("label-success", "label-danger"); + directoryOverrideText.getStyleClass().remove("label-warning"); + directoryChooserButton.setVisible(false); + directoryChooserButton.setManaged(false); if (!installParams.isInstallationConfirmed()) { installParams.setInstallationConfirmed(true); result = false; } - File installationDirectory = installParams.getInstallationDirectory(); - - // If any install target directory can be found, and it's not a valid directory if (installationDirectory.exists() && !installationDirectory.isDirectory()) { - directoryValidText.setText("Installation directory " + installationDirectory.getName() + " is not a directory."); + directoryValidText.setText(installationDirectory.getName() + " is not a valid installation directory."); + directoryValidText.getStyleClass().add("label-danger"); directoryChooserButton.setVisible(true); + directoryChooserButton.setManaged(true); result = false; } else { - directoryValidText.setText("Installation directory " + installationDirectory.getName() + " is valid."); + directoryValidText.setText(installationDirectory.getName() + " directory is a valid installation path."); + directoryValidText.getStyleClass().add("label-success"); } - if (installationDirectory.exists()) { directoryOverrideText.setText("Directory " + installationDirectory.getName() - + " already exists, existing files might get overwritten." - + "\nPlease, allow file overwrite to continue." - ); + + " already exists. Existing files may be overwritten. Please confirm below to continue."); + directoryOverrideText.getStyleClass().add("label-warning"); directoryOverrideCheckBox.setDisable(false); if (!installParams.isDirectoryOverrideAllowed()) { result = false; } } else { - directoryOverrideText.setText("Directory " + installationDirectory.getName() + " will be created."); + directoryOverrideText.setText("A new directory will be created: " + installationDirectory.getName()); + directoryOverrideCheckBox.setDisable(true); } return result; @@ -369,11 +374,11 @@ private File generateInstallationDirectoryFromBasePath(String baseDirectoryPath) public void reset() { prepareStep.setDisable(true); - prepareStep.setText("Prepare installation"); + prepareStep.setGraphic(null); selectDirectoryStep.setDisable(true); - selectDirectoryStep.setText("Select installation directory"); + selectDirectoryStep.setGraphic(null); verifyStep.setDisable(true); - verifyStep.setText("Verify plugin installation"); + verifyStep.setGraphic(null); vst2ToggleButton.setDisable(!this.getPreferences().getBoolean( ApplicationDefaults.VST2_DISCOVERY_ENABLED_KEY, false)); @@ -387,16 +392,31 @@ public void reset() { toggleGroup.selectToggle(null); directoryChooserButton.setVisible(false); + directoryChooserButton.setManaged(false); directoryOverrideCheckBox.setDisable(true); directoryOverrideCheckBox.setSelected(false); + installationDirectoryTextField.setText(""); + installationDirectoryText.setText(""); + directoryValidText.setText(""); + directoryValidText.getStyleClass().removeAll("label-success", "label-danger"); + directoryOverrideText.setText(""); + directoryOverrideText.getStyleClass().remove("label-warning"); + formatText.setText(""); } + private FontIcon createCheckIcon() { + FontIcon icon = new FontIcon("mdi2c-check"); + icon.setIconSize(16); + return icon; + } + @Override protected DialogLayout getLayout() { DialogLayout layout = new DialogLayout(); - Label title = new Label("Prepare installation"); + Label title = new Label("Install Plugin"); + title.setGraphic(new FontIcon("mdi2d-download")); title.getStyleClass().add("heading-3"); layout.setHeading(title); layout.setBody(lazyViewRegistry.get(LazyViewRegistry.INSTALL_STEP_VIEW)); diff --git a/owlplug-client/src/main/java/com/owlplug/explore/model/search/ExploreFilterCriteria.java b/owlplug-client/src/main/java/com/owlplug/explore/model/search/ExploreFilterCriteria.java index ab631afa..b7cc94d2 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/model/search/ExploreFilterCriteria.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/model/search/ExploreFilterCriteria.java @@ -15,22 +15,21 @@ * You should have received a copy of the GNU General Public License * along with OwlPlug. If not, see . */ - + package com.owlplug.explore.model.search; import java.util.Objects; -import javafx.scene.image.Image; public class ExploreFilterCriteria { private Object value; private String textValue; private ExploreFilterCriteriaType filterType; - private Image icon; + private String iconLiteral; /** * Creates a ExploreFilterCriteria. - * + * * @param value - criteria value * @param filterType - criteria type */ @@ -42,31 +41,32 @@ public ExploreFilterCriteria(Object value, ExploreFilterCriteriaType filterType) /** * Creates a ExploreFilterCriteria. - * - * @param value - criteria value - * @param filterType - criteria type - * @param icon - criteria icon displayed + * + * @param value - criteria value + * @param filterType - criteria type + * @param iconLiteral - Ikonli icon literal identifier */ - public ExploreFilterCriteria(Object value, ExploreFilterCriteriaType filterType, Image icon) { + public ExploreFilterCriteria(Object value, ExploreFilterCriteriaType filterType, String iconLiteral) { super(); this.value = value; this.filterType = filterType; - this.icon = icon; + this.iconLiteral = iconLiteral; } /** * Creates a ExploreFilterCriteria. - * - * @param value - criteria value - * @param filterType - criteria type - * @param icon - criteria icon to display - * @param textValue - custom text value overwriting original value toString() - * conversion. + * + * @param value - criteria value + * @param filterType - criteria type + * @param iconLiteral - Ikonli icon literal identifier + * @param textValue - custom text value overwriting original value toString() + * conversion. */ - public ExploreFilterCriteria(Object value, ExploreFilterCriteriaType filterType, Image icon, String textValue) { + public ExploreFilterCriteria(Object value, ExploreFilterCriteriaType filterType, String iconLiteral, + String textValue) { super(); this.value = value; - this.icon = icon; + this.iconLiteral = iconLiteral; this.filterType = filterType; this.textValue = textValue; } @@ -87,12 +87,12 @@ public void setFilterType(ExploreFilterCriteriaType filterType) { this.filterType = filterType; } - public Image getIcon() { - return icon; + public String getIconLiteral() { + return iconLiteral; } - public void setIcon(Image icon) { - this.icon = icon; + public void setIconLiteral(String iconLiteral) { + this.iconLiteral = iconLiteral; } @Override @@ -119,4 +119,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(value); } -} +} \ 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 cf67020f..845b444c 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with OwlPlug. If not, see . */ - + package com.owlplug.explore.ui; import com.owlplug.controls.ChipView; @@ -29,19 +29,19 @@ import java.util.List; import javafx.collections.ListChangeListener; import javafx.scene.control.ListCell; -import javafx.scene.image.ImageView; import javafx.util.StringConverter; +import org.kordamp.ikonli.javafx.FontIcon; public class ExploreChipView extends ChipView { - private ApplicationDefaults applicationDefaults; - private List pluginCreators; + private final ApplicationDefaults applicationDefaults; + private final List pluginCreators; private static final String PROMPT_TEXT = "Enter your search query by Name, Authors, Category..."; /** * Creates an ExploreChipView. - * + * * @param applicationDefaults - OwlPlug application defaults */ public ExploreChipView(ApplicationDefaults applicationDefaults, List pluginCreators) { @@ -55,66 +55,67 @@ public ExploreChipView(ApplicationDefaults applicationDefaults, List plu private void init() { HashMap suggestions = new LinkedHashMap<>(); - + suggestions.put("Effect", new ExploreFilterCriteria(PluginType.EFFECT, ExploreFilterCriteriaType.TYPE, - applicationDefaults.effectImage, "Effect")); + applicationDefaults.getPackageTypeIconLiteral(PluginType.EFFECT), "Effect")); suggestions.put("Instrument", new ExploreFilterCriteria(PluginType.INSTRUMENT, ExploreFilterCriteriaType.TYPE, - applicationDefaults.instrumentImage, "Instrument")); - - suggestions.put("Amp", new ExploreFilterCriteria("Amp", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + applicationDefaults.getPackageTypeIconLiteral(PluginType.INSTRUMENT), "Instrument")); + + suggestions.put("Amp", + new ExploreFilterCriteria("Amp", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Analog", - new ExploreFilterCriteria("Analog", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Analog", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Ambient", - new ExploreFilterCriteria("Ambient", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); - suggestions.put("Bass", new ExploreFilterCriteria("Bass", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Ambient", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); + suggestions.put("Bass", new ExploreFilterCriteria("Bass", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Brass", - new ExploreFilterCriteria("Brass", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Brass", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Compressor", - new ExploreFilterCriteria("Compressor", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Compressor", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Delay", - new ExploreFilterCriteria("Delay", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Delay", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Distortion", - new ExploreFilterCriteria("Distortion", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); - suggestions.put("Drum", new ExploreFilterCriteria("Drum", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Distortion", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); + suggestions.put("Drum", new ExploreFilterCriteria("Drum", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Equalizer", - new ExploreFilterCriteria("Equalizer", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Equalizer", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Filter", - new ExploreFilterCriteria("Filter", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Filter", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Flanger", - new ExploreFilterCriteria("Flanger", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); - suggestions.put("Gate", new ExploreFilterCriteria("Gate", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Flanger", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); + suggestions.put("Gate", new ExploreFilterCriteria("Gate", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Guitar", - new ExploreFilterCriteria("Guitar", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); - suggestions.put("LFO", new ExploreFilterCriteria("LFO", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Guitar", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); + suggestions.put("LFO", new ExploreFilterCriteria("LFO", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Limiter", - new ExploreFilterCriteria("Limiter", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Limiter", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Maximizer", - new ExploreFilterCriteria("Maximizer", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Maximizer", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Monophonic", - new ExploreFilterCriteria("Monophonic", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Monophonic", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Orchestral", - new ExploreFilterCriteria("Orchestral", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Orchestral", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Organ", - new ExploreFilterCriteria("Organ", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Organ", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Panner", - new ExploreFilterCriteria("Panner", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Panner", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Phaser", - new ExploreFilterCriteria("Phaser", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Phaser", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Piano", - new ExploreFilterCriteria("Piano", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Piano", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Reverb", - new ExploreFilterCriteria("Reverb", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Reverb", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Tremolo", - new ExploreFilterCriteria("Tremolo", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); - suggestions.put("Tube", new ExploreFilterCriteria("Tube", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Tremolo", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); + suggestions.put("Tube", new ExploreFilterCriteria("Tube", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Synth", - new ExploreFilterCriteria("Synth", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Synth", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); suggestions.put("Vintage", - new ExploreFilterCriteria("Vintage", ExploreFilterCriteriaType.TAG, applicationDefaults.tagImage)); + new ExploreFilterCriteria("Vintage", ExploreFilterCriteriaType.TAG, "mdi2t-tag-outline")); for (String creator : pluginCreators) { suggestions.put(creator, new ExploreFilterCriteria(creator, ExploreFilterCriteriaType.CREATOR, - applicationDefaults.userImage)); + "mdi2a-account-group-outline")); } this.getSuggestions().addAll(suggestions.values()); @@ -138,10 +139,10 @@ public ExploreFilterCriteria fromString(String string) { root.getStyleClass().add("chip-brown"); } if (getItem().getFilterType() == ExploreFilterCriteriaType.TAG) { - root.getStyleClass().add("chip-red"); + root.getStyleClass().add("chip-blue"); } if (getItem().getFilterType() == ExploreFilterCriteriaType.CREATOR) { - root.getStyleClass().add("chip-blue"); + root.getStyleClass().add("chip-red"); } } }); @@ -160,10 +161,8 @@ protected void updateItem(ExploreFilterCriteria item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { setText(item.toString()); - ImageView imageView = new ImageView(item.getIcon()); - imageView.setFitWidth(10); - imageView.setFitHeight(10); - setGraphic(imageView); + String literal = item.getIconLiteral(); + setGraphic(literal != null ? new FontIcon(literal) : null); } else { setGraphic(null); setText(null); @@ -173,4 +172,4 @@ protected void updateItem(ExploreFilterCriteria item, boolean empty) { } -} +} \ No newline at end of file diff --git a/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBlocView.java b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBlocView.java index ddbed9d2..f7f49b7a 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBlocView.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBlocView.java @@ -54,6 +54,7 @@ import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; +import org.kordamp.ikonli.javafx.FontIcon; public class PackageBlocView extends AnchorPane { @@ -94,13 +95,11 @@ public PackageBlocView(ApplicationDefaults applicationDefaults, RemotePackage re footer.setSpacing(5); footer.getStyleClass().add("package-bloc-title"); if (remotePackage.getType() != null) { - Image typeIcon = applicationDefaults.getPackageTypeIcon(remotePackage); - if (typeIcon != null) { - ImageView typeImageView = new ImageView(typeIcon); - typeImageView.setFitHeight(16); - typeImageView.setFitWidth(16); - footer.getChildren().add(typeImageView); - } + FontIcon typeIcon = new FontIcon( + applicationDefaults.getPackageTypeIconLiteral(remotePackage.getType())); + typeIcon.setIconSize(16); + footer.getChildren().add(typeIcon); + } footer.getChildren().add(new Label(remotePackage.getName())); footer.setPrefSize(USE_COMPUTED_SIZE, USE_COMPUTED_SIZE); diff --git a/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBundlesView.java b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBundlesView.java index 993a313b..6e7518dd 100644 --- a/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBundlesView.java +++ b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageBundlesView.java @@ -19,17 +19,15 @@ package com.owlplug.explore.ui; import com.owlplug.core.components.ApplicationDefaults; +import com.owlplug.plugin.ui.PluginFormatBadgeView; import com.owlplug.core.utils.FileUtils; import com.owlplug.explore.model.PackageBundle; -import com.owlplug.plugin.model.PluginFormat; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import javafx.scene.image.ImageView; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; @@ -71,15 +69,11 @@ public void addPackageBundle(PackageBundle bundle, EventHandler ins hbox.setAlignment(Pos.CENTER_LEFT); hbox.setFillHeight(false); - HBox formatsContainer = new HBox(2); - for (String formatValue : bundle.getFormats()) { - PluginFormat format = PluginFormat.fromBundleString(formatValue); - if (format != null) { - ImageView iv = new ImageView(this.applicationDefaults.getPluginFormatIcon(format)); - Label labelIv = new Label(); - labelIv.setGraphic(iv); - labelIv.setTooltip(new Tooltip(format.toString())); - formatsContainer.getChildren().add(labelIv); + HBox formatsContainer = new HBox(4); + if (bundle.getFormats() != null) { + for (String formatValue : bundle.getFormats()) { + formatsContainer.getChildren().add(new PluginFormatBadgeView(formatValue, + applicationDefaults, PluginFormatBadgeView.DisplayMode.ICON_ONLY)); } } hbox.getChildren().add(formatsContainer); @@ -94,7 +88,7 @@ public void addPackageBundle(PackageBundle bundle, EventHandler ins HBox.setHgrow(filler, Priority.SOMETIMES); hbox.getChildren().add(filler); Button installButton = new Button("Install"); - installButton.getStyleClass().add("button-info"); + installButton.getStyleClass().add("small"); installButton.setOnAction(installHandler); installButton.setMinWidth(USE_PREF_SIZE); hbox.getChildren().add(installButton); 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 new file mode 100644 index 00000000..f82e552b --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java @@ -0,0 +1,249 @@ +/* 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.explore.ui; + +import com.owlplug.core.components.ApplicationDefaults; +import com.owlplug.plugin.ui.PluginFormatBadgeView; +import com.owlplug.core.utils.PlatformUtils; +import com.owlplug.core.utils.StringUtils; +import com.owlplug.explore.controllers.ExploreController; +import com.owlplug.explore.model.PackageBundle; +import com.owlplug.explore.model.PackageTag; +import com.owlplug.explore.model.RemotePackage; +import com.owlplug.plugin.model.PluginStage; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.CustomMenuItem; +import javafx.scene.control.Label; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SeparatorMenuItem; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import org.kordamp.ikonli.javafx.FontIcon; +import atlantafx.base.layout.InputGroup; + +public class PackageListRowView extends HBox { + + private static final int THUMB_WIDTH = 80; + private static final int THUMB_HEIGHT = 56; + + public PackageListRowView(ApplicationDefaults applicationDefaults, RemotePackage remotePackage, + Image image, ExploreController parentController) { + super(); + this.getStyleClass().add("package-list-row"); + this.setAlignment(Pos.CENTER_LEFT); + this.setSpacing(12); + this.setPadding(new Insets(8, 12, 8, 12)); + + // Thumbnail + StackPane thumb = buildThumbnail(image, remotePackage); + this.getChildren().add(thumb); + + // Name + creator + tags + VBox meta = buildMeta(applicationDefaults, remotePackage, parentController); + HBox.setHgrow(meta, Priority.ALWAYS); + this.getChildren().add(meta); + + // Right side: type + formats + source badge + install + HBox rightSection = buildRightSection(applicationDefaults, remotePackage, parentController); + this.getChildren().add(rightSection); + + // Context menu + buildContextMenu(remotePackage, parentController); + } + + private StackPane buildThumbnail(Image image, RemotePackage remotePackage) { + StackPane thumb = new StackPane(); + thumb.setPrefSize(THUMB_WIDTH, THUMB_HEIGHT); + thumb.setMinSize(THUMB_WIDTH, THUMB_HEIGHT); + thumb.setMaxSize(THUMB_WIDTH, THUMB_HEIGHT); + thumb.getStyleClass().add("package-list-thumb"); + + ImageView imageView = new ImageView(); + imageView.setFitWidth(THUMB_WIDTH); + imageView.setFitHeight(THUMB_HEIGHT); + imageView.setPreserveRatio(true); + imageView.setSmooth(true); + if (image != null) { + imageView.setImage(image); + } + thumb.getChildren().add(imageView); + + if (remotePackage.getStage() != null && remotePackage.getStage() != PluginStage.RELEASE) { + Label stageBadge = new Label(remotePackage.getStage().name()); + stageBadge.getStyleClass().add("package-stage-badge"); + if (remotePackage.getStage() == PluginStage.BETA) { + stageBadge.getStyleClass().add("package-stage-beta"); + } else if (remotePackage.getStage() == PluginStage.DEMO) { + stageBadge.getStyleClass().add("package-stage-demo"); + } + StackPane.setAlignment(stageBadge, Pos.BOTTOM_LEFT); + thumb.getChildren().add(stageBadge); + } + + return thumb; + } + + private VBox buildMeta(ApplicationDefaults applicationDefaults, RemotePackage remotePackage, + ExploreController parentController) { + VBox meta = new VBox(3); + meta.setAlignment(Pos.CENTER_LEFT); + + // Name + source badge + HBox nameRow = new HBox(6); + nameRow.setAlignment(Pos.CENTER_LEFT); + Label nameLabel = new Label(remotePackage.getName()); + nameLabel.getStyleClass().add("package-list-name"); + nameRow.getChildren().add(nameLabel); + nameRow.getChildren().add(new PackageSourceBadgeView(remotePackage.getRemoteSource(), applicationDefaults, true)); + + // Creator + HBox subRow = new HBox(6); + subRow.setAlignment(Pos.CENTER_LEFT); + if (remotePackage.getCreator() != null && !remotePackage.getCreator().isBlank()) { + Label creatorLabel = new Label(remotePackage.getCreator()); + creatorLabel.getStyleClass().add("package-list-creator"); + subRow.getChildren().add(creatorLabel); + } + + meta.getChildren().addAll(nameRow, subRow); + + // Tags row + if (remotePackage.getTags() != null && !remotePackage.getTags().isEmpty()) { + HBox tagsRow = new HBox(4); + tagsRow.setAlignment(Pos.CENTER_LEFT); + int shown = 0; + for (PackageTag tag : remotePackage.getTags()) { + if (shown >= 5) break; + Label tagChip = new Label(tag.getName()); + tagChip.getStyleClass().add("package-tag-badge"); + tagChip.setOnMouseClicked(e -> parentController.addSearchChip(tag.getName())); + tagsRow.getChildren().add(tagChip); + shown++; + } + meta.getChildren().add(tagsRow); + } + + return meta; + } + + private HBox buildRightSection(ApplicationDefaults applicationDefaults, RemotePackage remotePackage, + ExploreController parentController) { + HBox right = new HBox(12); + right.setAlignment(Pos.CENTER_RIGHT); + + // Stacked info: type, formats, source + VBox infoStack = new VBox(4); + infoStack.setAlignment(Pos.CENTER_RIGHT); + + // Line 1: type icon + name + format chips + HBox typeAndFormats = new HBox(6); + typeAndFormats.setAlignment(Pos.CENTER_RIGHT); + + if (remotePackage.getType() != null) { + FontIcon typeIcon = new FontIcon( + applicationDefaults.getPackageTypeIconLiteral(remotePackage.getType())); + typeIcon.setIconSize(16); + typeAndFormats.getChildren().add(typeIcon); + + Label typeLabel = new Label(remotePackage.getType().name()); + typeLabel.getStyleClass().add("label-disabled"); + typeAndFormats.getChildren().add(typeLabel); + } + + if (remotePackage.getBundles() != null && !remotePackage.getBundles().isEmpty()) { + java.util.Set seenFormats = new java.util.LinkedHashSet<>(); + for (PackageBundle bundle : remotePackage.getBundles()) { + if (bundle.getFormats() != null) { + seenFormats.addAll(bundle.getFormats()); + } + } + for (String fmt : seenFormats) { + typeAndFormats.getChildren().add(new PluginFormatBadgeView(fmt, applicationDefaults)); + } + } + + if (!typeAndFormats.getChildren().isEmpty()) { + infoStack.getChildren().add(typeAndFormats); + } + + // Install button group stacked under type+formats + InputGroup inputGroup = new InputGroup(); + Label versionLabel = new Label("v" + remotePackage.getVersion()); + versionLabel.setDisable(true); + versionLabel.setMaxWidth(USE_COMPUTED_SIZE); + inputGroup.getChildren().add(versionLabel); + Button installButton = new Button("Install"); + installButton.getStyleClass().add("button-primary"); + installButton.setOnAction(e -> parentController.installPackage(remotePackage)); + inputGroup.getChildren().add(installButton); + HBox installRow = new HBox(inputGroup); + installRow.setAlignment(Pos.CENTER_RIGHT); + infoStack.getChildren().add(installRow); + + right.getChildren().add(infoStack); + + return right; + } + + private void buildContextMenu(RemotePackage remotePackage, ExploreController parentController) { + TextFlow textFlow = new TextFlow(); + textFlow.getChildren().add(new Label("Install")); + Text autoText = new Text(" (Auto)"); + autoText.getStyleClass().add("text-disabled"); + textFlow.getChildren().add(autoText); + CustomMenuItem installMenuItem = new CustomMenuItem(textFlow); + installMenuItem.setOnAction(e -> parentController.installPackage(remotePackage)); + + ContextMenu contextMenu = new ContextMenu(); + contextMenu.getItems().add(installMenuItem); + + if (remotePackage.getBundles() != null) { + Menu bundlesMenu = new Menu("Other Packages"); + for (PackageBundle bundle : remotePackage.getBundles()) { + TextFlow bundleTextFlow = new TextFlow(); + bundleTextFlow.getChildren().add(new Label("Install")); + Text bundleSource = new Text(" (" + StringUtils.truncate(bundle.getName(), 50, "...") + ")"); + bundleSource.getStyleClass().add("text-disabled"); + bundleTextFlow.getChildren().add(bundleSource); + CustomMenuItem bundleMenuItem = new CustomMenuItem(bundleTextFlow); + bundleMenuItem.setOnAction(e -> parentController.installBundle(bundle)); + bundlesMenu.getItems().add(bundleMenuItem); + } + contextMenu.getItems().add(bundlesMenu); + } + + MenuItem pageMenuItem = new MenuItem("Browse plugin page..."); + pageMenuItem.setOnAction(e -> PlatformUtils.openDefaultBrowser(remotePackage.getPageUrl())); + contextMenu.getItems().add(new SeparatorMenuItem()); + contextMenu.getItems().add(pageMenuItem); + + this.setOnContextMenuRequested(e -> contextMenu.show(this, e.getScreenX(), e.getScreenY())); + } + +} \ No newline at end of file diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java index ab29052e..5189ae10 100644 --- a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java +++ b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginInfoController.java @@ -35,6 +35,7 @@ import com.owlplug.plugin.model.PluginComponent; import com.owlplug.plugin.model.PluginState; import com.owlplug.plugin.services.PluginService; +import com.owlplug.plugin.ui.PluginFormatBadgeView; import com.owlplug.plugin.ui.PluginComponentCellFactory; import com.owlplug.plugin.ui.PluginStateView; import java.io.File; @@ -52,6 +53,7 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.Background; +import javafx.scene.layout.HBox; import javafx.scene.layout.BackgroundImage; import javafx.scene.layout.BackgroundPosition; import javafx.scene.layout.BackgroundRepeat; @@ -79,9 +81,7 @@ public class PluginInfoController extends BaseController { @FXML private Pane pluginScreenshotPane; @FXML - private ImageView pluginFormatIcon; - @FXML - private Label pluginFormatLabel; + private HBox pluginFormatBadgeContainer; @FXML private Label pluginTitleLabel; @FXML @@ -178,8 +178,9 @@ public void refresh() { return; } - pluginFormatIcon.setImage(this.getApplicationDefaults().getPluginFormatIcon(plugin.getFormat())); - pluginFormatLabel.setText(plugin.getFormat().getText() + " Plugin"); + pluginFormatBadgeContainer.getChildren().setAll( + new PluginFormatBadgeView(plugin.getFormat(), this.getApplicationDefaults())); + pluginTitleLabel.setText(plugin.getName()); pluginNameLabel.setText(Optional.ofNullable(plugin.getDescriptiveName()).orElse(plugin.getName())); pluginVersionLabel.setText(Optional.ofNullable(plugin.getVersion()).orElse("Unknown")); diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java index 86a71eae..0b80ac22 100644 --- a/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java +++ b/owlplug-client/src/main/java/com/owlplug/plugin/controllers/PluginTableController.java @@ -28,6 +28,7 @@ import com.owlplug.plugin.model.PluginFormat; import com.owlplug.plugin.model.PluginState; import com.owlplug.plugin.services.PluginService; +import com.owlplug.plugin.ui.PluginFormatBadgeView; import com.owlplug.plugin.ui.PluginStateView; import java.io.File; import com.owlplug.core.utils.Async; @@ -46,6 +47,7 @@ import javafx.scene.control.TableRow; import javafx.scene.control.TableView; import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import org.springframework.beans.factory.annotation.Autowired; @@ -119,8 +121,8 @@ public void updateItem(PluginFormat item, boolean empty) { setText(null); setGraphic(null); } else { - setText(item.getText()); - setGraphic(new ImageView(getApplicationDefaults().getPluginFormatIcon(item))); + setText(null); + setGraphic(new HBox(new PluginFormatBadgeView(item, getApplicationDefaults(), PluginFormatBadgeView.DisplayMode.ICON_ONLY))); } } }); diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginFormatBadgeView.java b/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginFormatBadgeView.java new file mode 100644 index 00000000..92f03bc8 --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginFormatBadgeView.java @@ -0,0 +1,77 @@ +/* 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.plugin.ui; + +import com.owlplug.core.components.ApplicationDefaults; +import com.owlplug.plugin.model.PluginFormat; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +public class PluginFormatBadgeView extends HBox { + + public enum DisplayMode { + DEFAULT, ICON_ONLY, TEXT_ONLY + } + + public PluginFormatBadgeView(String formatValue, ApplicationDefaults applicationDefaults) { + this(PluginFormat.fromBundleString(formatValue), formatValue, applicationDefaults, DisplayMode.DEFAULT); + } + + public PluginFormatBadgeView(String formatValue, ApplicationDefaults applicationDefaults, DisplayMode mode) { + this(PluginFormat.fromBundleString(formatValue), formatValue, applicationDefaults, mode); + } + + public PluginFormatBadgeView(PluginFormat format, ApplicationDefaults applicationDefaults) { + this(format, format != null ? format.getText() : "?", applicationDefaults, DisplayMode.DEFAULT); + } + + public PluginFormatBadgeView(PluginFormat format, ApplicationDefaults applicationDefaults, DisplayMode mode) { + this(format, format != null ? format.getText() : "?", applicationDefaults, mode); + } + + private PluginFormatBadgeView(PluginFormat format, String displayText, ApplicationDefaults applicationDefaults, + DisplayMode mode) { + super(4); + setAlignment(Pos.CENTER); + getStyleClass().add("plugin-format-badge"); + + String formatClass = format != null + ? "plugin-format-badge-" + format.name().toLowerCase() + : "plugin-format-badge-default"; + getStyleClass().add(formatClass); + + if (mode != DisplayMode.TEXT_ONLY) { + ImageView iconView = new ImageView(applicationDefaults.getPluginFormatIcon(format)); + iconView.setFitHeight(16); + iconView.setFitWidth(16); + iconView.setPreserveRatio(true); + getChildren().add(iconView); + } + + if (mode != DisplayMode.ICON_ONLY) { + Label label = new Label(displayText.toUpperCase()); + getChildren().add(label); + } + + Tooltip.install(this, new Tooltip(displayText)); + } +} \ No newline at end of file diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginListCellFactory.java b/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginListCellFactory.java index 3bc49eb4..506c5142 100644 --- a/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginListCellFactory.java +++ b/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginListCellFactory.java @@ -22,7 +22,6 @@ import com.owlplug.plugin.model.Plugin; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; -import javafx.scene.image.ImageView; import javafx.util.Callback; public class PluginListCellFactory implements Callback, ListCell> { @@ -37,8 +36,6 @@ public PluginListCellFactory(ApplicationDefaults applicationDefaults) { @Override public ListCell call(ListView arg0) { return new ListCell<>() { - private ImageView imageView = new ImageView(); - @Override public void updateItem(Plugin plugin, boolean empty) { super.updateItem(plugin, empty); @@ -46,9 +43,9 @@ public void updateItem(Plugin plugin, boolean empty) { setText(null); setGraphic(null); } else { - imageView.setImage(applicationDefaults.getPluginFormatIcon(plugin.getFormat())); setText(plugin.getName()); - setGraphic(imageView); + setGraphic(new PluginFormatBadgeView(plugin.getFormat(), applicationDefaults, + PluginFormatBadgeView.DisplayMode.ICON_ONLY)); } } }; diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginTreeCell.java b/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginTreeCell.java index 01fcac67..c577740d 100644 --- a/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginTreeCell.java +++ b/owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginTreeCell.java @@ -75,7 +75,7 @@ public void updateItem(Object item, boolean empty) { private void renderPlugin(Plugin plugin) { HBox hbox = new HBox(4); hbox.setAlignment(Pos.CENTER_LEFT); - hbox.getChildren().add(new ImageView(applicationDefaults.getPluginFormatIcon(plugin.getFormat()))); + hbox.getChildren().add(new PluginFormatBadgeView(plugin.getFormat(), applicationDefaults, PluginFormatBadgeView.DisplayMode.ICON_ONLY)); hbox.getChildren().add(new Label(plugin.getName())); Circle circle = new Circle(0, 0, 2); hbox.getChildren().add(circle); diff --git a/owlplug-client/src/main/java/com/owlplug/plugin/ui/RecoveredPluginView.java b/owlplug-client/src/main/java/com/owlplug/plugin/ui/RecoveredPluginView.java index b48f6c80..4b561740 100644 --- a/owlplug-client/src/main/java/com/owlplug/plugin/ui/RecoveredPluginView.java +++ b/owlplug-client/src/main/java/com/owlplug/plugin/ui/RecoveredPluginView.java @@ -25,7 +25,6 @@ import com.owlplug.core.utils.Async; import javafx.geometry.Pos; import javafx.scene.control.Label; -import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; @@ -44,12 +43,10 @@ public RecoveredPluginView(Plugin plugin, PluginService pluginService, Applicati this.setAlignment(Pos.BASELINE_LEFT); + PluginFormatBadgeView badge = new PluginFormatBadgeView(plugin.getFormat(), applicationDefaults, + PluginFormatBadgeView.DisplayMode.ICON_ONLY); Label label = new Label(plugin.getName()); - ImageView imageView = new ImageView(); - imageView.setImage(applicationDefaults.getPluginFormatIcon(plugin.getFormat())); - - label.setGraphic(imageView); - this.getChildren().add(label); + this.getChildren().addAll(badge, label); Pane transparentPane = new Pane(); HBox.setHgrow(transparentPane, Priority.ALWAYS); 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 5282f5b3..2396522f 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 @@ -19,6 +19,7 @@ package com.owlplug.project.controllers; import com.owlplug.core.controllers.BaseController; +import com.owlplug.plugin.ui.PluginFormatBadgeView; import com.owlplug.core.controllers.MainController; import com.owlplug.core.utils.PlatformUtils; import com.owlplug.core.utils.TimeUtils; @@ -41,6 +42,7 @@ import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -183,8 +185,9 @@ public void updateItem(PluginFormat item, boolean empty) { setText(null); setGraphic(null); } else { - setText(item.getText()); - setGraphic(new ImageView(getApplicationDefaults().getPluginFormatIcon(item))); + setText(null); + setGraphic(new HBox(new PluginFormatBadgeView( + item, getApplicationDefaults(), PluginFormatBadgeView.DisplayMode.ICON_ONLY))); } } }); diff --git a/owlplug-client/src/main/resources/fxml/MainView.fxml b/owlplug-client/src/main/resources/fxml/MainView.fxml index 23c0fc38..3b027854 100644 --- a/owlplug-client/src/main/resources/fxml/MainView.fxml +++ b/owlplug-client/src/main/resources/fxml/MainView.fxml @@ -57,9 +57,7 @@