From 8776dcccced6db5c015f096d1f4f93d0be9dd1ca Mon Sep 17 00:00:00 2001 From: Arthur Poiret Date: Fri, 12 Jun 2026 01:00:25 +0200 Subject: [PATCH 1/5] feat: add remote package flat list view --- .../controllers/ExploreController.java | 142 ++++++---- .../explore/ui/PackageListRowView.java | 252 ++++++++++++++++++ .../resources/fxml/explore/ExploreView.fxml | 162 ++++++----- owlplug-client/src/main/resources/owlplug.css | 76 ++++++ 4 files changed, 518 insertions(+), 114 deletions(-) create mode 100644 owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java 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..420ab104 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,59 @@ 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)); + } + 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); + } } /** - * 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 +406,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 +451,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/ui/PackageListRowView.java b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java new file mode 100644 index 00000000..38d8e9fb --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/explore/ui/PackageListRowView.java @@ -0,0 +1,252 @@ +/* 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.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.control.TextField; +import javafx.scene.control.Tooltip; +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 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-list-tag"); + 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) { + Image typeIcon = applicationDefaults.getPackageTypeIcon(remotePackage); + if (typeIcon != null) { + ImageView typeImg = new ImageView(typeIcon); + typeImg.setFitHeight(16); + typeImg.setFitWidth(16); + typeAndFormats.getChildren().add(typeImg); + } + 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) { + Label fmtLabel = new Label(fmt.toUpperCase()); + fmtLabel.getStyleClass().add("package-list-format"); + Tooltip.install(fmtLabel, new Tooltip(fmt)); + typeAndFormats.getChildren().add(fmtLabel); + } + } + + 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); + + 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/resources/fxml/explore/ExploreView.fxml b/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml index 26b79828..85d63c72 100644 --- a/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml +++ b/owlplug-client/src/main/resources/fxml/explore/ExploreView.fxml @@ -6,88 +6,124 @@ + + + + + - - + - - + + + + - + + + + - - - - - - - - - - - - - + + + + + + + + + - - - - - - - + + + - - - - - - - - - - - - - - + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+
+
+ + -
-
-
+ + + \ No newline at end of file diff --git a/owlplug-client/src/main/resources/owlplug.css b/owlplug-client/src/main/resources/owlplug.css index 79790ee2..1de02e68 100644 --- a/owlplug-client/src/main/resources/owlplug.css +++ b/owlplug-client/src/main/resources/owlplug.css @@ -479,6 +479,82 @@ ProgressIndicator { dot_color: disabled-color; } +/**************************************************************** + * Package list view + ****************************************************************/ + +.package-list-row { + -fx-background-color: -color-bg-subtle; + -fx-background-radius: 6px; + -fx-border-radius: 6px; + -fx-border-width: 1px; + -fx-border-color: -color-border-muted; + -fx-cursor: hand; +} + +.package-list-row:hover { + -fx-background-color: -color-bg-overlay; + -fx-border-color: -color-accent-muted; +} + +.package-list-thumb { + -fx-background-color: -color-bg-inset; + -fx-background-radius: 4px; + -fx-border-radius: 4px; +} + +.package-list-name { + -fx-font-weight: bold; + -fx-font-size: 13px; + -fx-text-fill: -color-fg-default; +} + +.package-list-creator { + -fx-text-fill: -color-fg-muted; + -fx-font-size: 11px; +} + +.package-list-tag { + -fx-background-color: -color-neutral-subtle; + -fx-background-radius: 3px; + -fx-border-radius: 3px; + -fx-padding: 1 5 1 5; + -fx-font-size: 10px; + -fx-text-fill: -color-fg-muted; + -fx-cursor: hand; +} + +.package-list-tag:hover { + -fx-background-color: -color-accent-subtle; + -fx-text-fill: -color-accent-fg; +} + +.package-list-format { + -fx-background-color: -color-accent-subtle; + -fx-background-radius: 3px; + -fx-border-radius: 3px; + -fx-padding: 1 5 1 5; + -fx-font-size: 10px; + -fx-text-fill: -color-accent-fg; +} + +.package-stage-badge { + -fx-font-size: 9px; + -fx-font-weight: bold; + -fx-padding: 1 4 1 4; + -fx-background-radius: 0 3px 0 0; + -fx-text-fill: white; +} + +.package-stage-beta { + -fx-background-color: rgba(231, 76, 60, 0.85); +} + +.package-stage-demo { + -fx-background-color: rgba(155, 89, 182, 0.85); +} + + /**************************************************************** * * Utilities From 75215dfa7bdf6feaf37e4bd4b5d71e3ded2f161e Mon Sep 17 00:00:00 2001 From: Arthur Poiret Date: Fri, 12 Jun 2026 22:17:57 +0200 Subject: [PATCH 2/5] feat: add plugin format badge view display --- .../core/components/ApplicationDefaults.java | 6 +- .../explore/ui/PackageBundlesView.java | 16 +--- .../explore/ui/PackageListRowView.java | 8 +- .../controllers/PluginInfoController.java | 11 +-- .../controllers/PluginTableController.java | 6 +- .../plugin/ui/PluginFormatBadgeView.java | 77 +++++++++++++++++++ .../plugin/ui/PluginListCellFactory.java | 7 +- .../com/owlplug/plugin/ui/PluginTreeCell.java | 2 +- .../plugin/ui/RecoveredPluginView.java | 9 +-- .../controllers/ProjectInfoController.java | 7 +- .../fxml/plugins/PluginInfoView.fxml | 8 +- owlplug-client/src/main/resources/owlplug.css | 46 ++++++++++- 12 files changed, 151 insertions(+), 52 deletions(-) create mode 100644 owlplug-client/src/main/java/com/owlplug/plugin/ui/PluginFormatBadgeView.java 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..7900766d 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 @@ -142,13 +142,15 @@ 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; }; } 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..6c0e8d15 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,16 +69,10 @@ public void addPackageBundle(PackageBundle bundle, EventHandler ins hbox.setAlignment(Pos.CENTER_LEFT); hbox.setFillHeight(false); - HBox formatsContainer = new HBox(2); + HBox formatsContainer = new HBox(4); 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); - } + formatsContainer.getChildren().add(new PluginFormatBadgeView(formatValue, + applicationDefaults, PluginFormatBadgeView.DisplayMode.ICON_ONLY)); } hbox.getChildren().add(formatsContainer); 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 38d8e9fb..babc15da 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 @@ -19,6 +19,7 @@ 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; @@ -35,8 +36,6 @@ import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; @@ -186,10 +185,7 @@ private HBox buildRightSection(ApplicationDefaults applicationDefaults, RemotePa } } for (String fmt : seenFormats) { - Label fmtLabel = new Label(fmt.toUpperCase()); - fmtLabel.getStyleClass().add("package-list-format"); - Tooltip.install(fmtLabel, new Tooltip(fmt)); - typeAndFormats.getChildren().add(fmtLabel); + typeAndFormats.getChildren().add(new PluginFormatBadgeView(fmt, applicationDefaults)); } } 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/plugins/PluginInfoView.fxml b/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml index 4ac8b00b..7d23aae8 100644 --- a/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml +++ b/owlplug-client/src/main/resources/fxml/plugins/PluginInfoView.fxml @@ -5,7 +5,6 @@ - @@ -37,12 +36,7 @@