From 1e714e2c4acaa4f8d101eb5755af597d3aeaa75d Mon Sep 17 00:00:00 2001 From: crschnick Date: Sat, 1 Jul 2023 18:05:45 +0000 Subject: [PATCH] More ui rework --- .../comp/storage/store/StoreEntryComp.java | 107 ++++++++---------- .../storage/store/StoreEntryListComp.java | 8 +- .../storage/store/StoreEntrySectionComp.java | 18 +++ .../comp/storage/store/StoreLayoutComp.java | 1 + .../comp/storage/store/StoreSidebarComp.java | 5 +- .../comp/storage/store/StoreViewState.java | 14 ++- .../main/java/io/xpipe/app/fxcomps/Comp.java | 27 +++-- .../xpipe/app/fxcomps/impl/WrapperComp.java | 20 ---- .../io/xpipe/app/storage/DataStorage.java | 54 ++++++--- .../io/xpipe/app/storage/StorageListener.java | 4 +- .../xpipe/app/resources/style/header-bars.css | 2 +- .../app/resources/style/store-entry-comp.css | 40 +++++-- .../io/xpipe/app/resources/style/style.css | 4 + 13 files changed, 179 insertions(+), 125 deletions(-) delete mode 100644 app/src/main/java/io/xpipe/app/fxcomps/impl/WrapperComp.java diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java index 6d03384e1..ada199845 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java @@ -1,6 +1,5 @@ package io.xpipe.app.comp.storage.store; -import atlantafx.base.controls.Spacer; import atlantafx.base.theme.Styles; import com.jfoenix.controls.JFXButton; import io.xpipe.app.comp.base.LoadingOverlayComp; @@ -13,14 +12,12 @@ import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.augment.ContextMenuAugment; import io.xpipe.app.fxcomps.augment.GrowAugment; import io.xpipe.app.fxcomps.impl.*; -import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.SimpleChangeListener; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.util.DesktopHelper; import io.xpipe.app.util.ThreadHelper; -import io.xpipe.core.store.FixedHierarchyStore; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; import javafx.css.PseudoClass; @@ -30,8 +27,6 @@ import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.input.MouseButton; import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import org.kordamp.ikonli.javafx.FontIcon; @@ -50,11 +45,11 @@ public abstract class StoreEntryComp extends SimpleComp { public static final PseudoClass FAILED = PseudoClass.getPseudoClass("failed"); public static final PseudoClass INCOMPLETE = PseudoClass.getPseudoClass("incomplete"); - protected final StoreEntryWrapper entry; + protected final StoreEntryWrapper wrapper; protected final Comp content; - public StoreEntryComp(StoreEntryWrapper entry, Comp content) { - this.entry = entry; + public StoreEntryComp(StoreEntryWrapper wrapper, Comp content) { + this.wrapper = wrapper; this.content = content; } @@ -72,23 +67,19 @@ public abstract class StoreEntryComp extends SimpleComp { button.accessibleTextProperty() .bind(Bindings.createStringBinding( () -> { - return entry.getName(); + return wrapper.getName(); }, - entry.nameProperty())); - button.accessibleHelpProperty().bind(entry.getInformation()); + wrapper.nameProperty())); + button.accessibleHelpProperty().bind(wrapper.getInformation()); button.setOnAction(event -> { event.consume(); ThreadHelper.runFailableAsync(() -> { - entry.executeDefaultAction(); + wrapper.executeDefaultAction(); }); }); - new ContextMenuAugment<>(() -> this.createContextMenu()) - .augment(new SimpleCompStructure<>(button)); + new ContextMenuAugment<>(() -> this.createContextMenu()).augment(new SimpleCompStructure<>(button)); - HBox.setHgrow(button, Priority.ALWAYS); - var hbox = new HBox(button, new Spacer(25)); - - var loading = new LoadingOverlayComp(Comp.of(() -> hbox), entry.getLoading()); + var loading = new LoadingOverlayComp(Comp.of(() -> button), wrapper.getLoading()); var region = loading.createRegion(); return region; } @@ -98,11 +89,13 @@ public abstract class StoreEntryComp extends SimpleComp { protected Label createInformation() { var information = new Label(); information.setGraphicTextGap(7); - information.textProperty().bind(PlatformThread.sync(entry.getInformation())); + information.textProperty().bind(PlatformThread.sync(wrapper.getInformation())); information.getStyleClass().add("information"); AppFont.header(information); - var state = entry.getEntry().getProvider().stateDisplay(entry); + var state = wrapper.getEntry().getProvider() != null + ? wrapper.getEntry().getProvider().stateDisplay(wrapper) + : Comp.empty(); information.setGraphic(state.createRegion()); return information; @@ -110,14 +103,14 @@ public abstract class StoreEntryComp extends SimpleComp { protected Label createSummary() { var summary = new Label(); - summary.textProperty().bind(PlatformThread.sync(entry.getSummary())); + summary.textProperty().bind(PlatformThread.sync(wrapper.getSummary())); summary.getStyleClass().add("summary"); AppFont.small(summary); return summary; } protected void applyState(Node node) { - SimpleChangeListener.apply(PlatformThread.sync(entry.getState()), val -> { + SimpleChangeListener.apply(PlatformThread.sync(wrapper.getState()), val -> { switch (val) { case LOAD_FAILED -> { node.pseudoClassStateChanged(FAILED, true); @@ -136,24 +129,24 @@ public abstract class StoreEntryComp extends SimpleComp { } protected Comp createName() { - var filtered = BindingsHelper.filteredContentBinding( - StoreViewState.get().getAllEntries(), - other -> other.getEntry().getState().isUsable() - && entry.getEntry() - .getStore() - .equals(other.getEntry() - .getProvider() - .getLogicalParent(other.getEntry().getStore()))); + // var filtered = BindingsHelper.filteredContentBinding( + // StoreViewState.get().getAllEntries(), + // other -> other.getEntry().getState().isUsable() + // && entry.getEntry() + // .getStore() + // .equals(other.getEntry() + // .getProvider() + // .getLogicalParent(other.getEntry().getStore()))); LabelComp name = new LabelComp(Bindings.createStringBinding( () -> { - return entry.getName() - + (filtered.size() > 0 && entry.getEntry().getStore() instanceof FixedHierarchyStore - ? " (" + filtered.size() + ")" - : ""); + return wrapper.getName(); + // + (filtered.size() > 0 && entry.getEntry().getStore() instanceof + // FixedHierarchyStore + // ? " (" + filtered.size() + ")" + // : ""); }, - entry.nameProperty(), - entry.getInformation(), - filtered)); + wrapper.nameProperty(), + wrapper.getInformation())); name.apply(struc -> struc.get().setTextOverrun(OverrunStyle.CENTER_ELLIPSIS)) .apply(struc -> struc.get().setPadding(new Insets(5, 5, 5, 0))); name.apply(s -> AppFont.header(s.get())); @@ -161,17 +154,17 @@ public abstract class StoreEntryComp extends SimpleComp { } protected Node createIcon(int w, int h) { - var img = entry.isDisabled() + var img = wrapper.isDisabled() ? "disabled_icon.png" - : entry.getEntry() + : wrapper.getEntry() .getProvider() - .getDisplayIconFileName(entry.getEntry().getStore()); + .getDisplayIconFileName(wrapper.getEntry().getStore()); var imageComp = new PrettyImageComp(new SimpleStringProperty(img), w, h); var storeIcon = imageComp.createRegion(); storeIcon.getStyleClass().add("icon"); - if (entry.getState().getValue().isUsable()) { + if (wrapper.getState().getValue().isUsable()) { new FancyTooltipAugment<>(new SimpleStringProperty( - entry.getEntry().getProvider().getDisplayName())) + wrapper.getEntry().getProvider().getDisplayName())) .augment(storeIcon); } storeIcon.setPadding(new Insets(3, 0, 0, 0)); @@ -180,23 +173,23 @@ public abstract class StoreEntryComp extends SimpleComp { protected Comp createButtonBar() { var list = new ArrayList>(); - for (var p : entry.getActionProviders().entrySet()) { + for (var p : wrapper.getActionProviders().entrySet()) { var actionProvider = p.getKey().getDataStoreCallSite(); if (!actionProvider.isMajor() - || p.getKey().equals(entry.getDefaultActionProvider().getValue())) { + || p.getKey().equals(wrapper.getDefaultActionProvider().getValue())) { continue; } var button = new IconButtonComp( - actionProvider.getIcon(entry.getEntry().getStore().asNeeded()), () -> { + actionProvider.getIcon(wrapper.getEntry().getStore().asNeeded()), () -> { ThreadHelper.runFailableAsync(() -> { var action = actionProvider.createAction( - entry.getEntry().getStore().asNeeded()); + wrapper.getEntry().getStore().asNeeded()); action.execute(); }); }); button.apply(new FancyTooltipAugment<>( - actionProvider.getName(entry.getEntry().getStore().asNeeded()))); + actionProvider.getName(wrapper.getEntry().getStore().asNeeded()))); if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ONLY_SHOW_IF_ENABLED) { button.hide(Bindings.not(p.getValue())); } else if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ALWAYS_SHOW) { @@ -239,19 +232,19 @@ public abstract class StoreEntryComp extends SimpleComp { var contextMenu = new ContextMenu(); AppFont.normal(contextMenu.getStyleableNode()); - for (var p : entry.getActionProviders().entrySet()) { + for (var p : wrapper.getActionProviders().entrySet()) { var actionProvider = p.getKey().getDataStoreCallSite(); if (actionProvider.isMajor()) { continue; } - var name = actionProvider.getName(entry.getEntry().getStore().asNeeded()); - var icon = actionProvider.getIcon(entry.getEntry().getStore().asNeeded()); + var name = actionProvider.getName(wrapper.getEntry().getStore().asNeeded()); + var icon = actionProvider.getIcon(wrapper.getEntry().getStore().asNeeded()); var item = new MenuItem(null, new FontIcon(icon)); item.setOnAction(event -> { ThreadHelper.runFailableAsync(() -> { var action = actionProvider.createAction( - entry.getEntry().getStore().asNeeded()); + wrapper.getEntry().getStore().asNeeded()); action.execute(); }); }); @@ -264,27 +257,27 @@ public abstract class StoreEntryComp extends SimpleComp { contextMenu.getItems().add(item); } - if (entry.getActionProviders().size() > 0) { + if (wrapper.getActionProviders().size() > 0) { contextMenu.getItems().add(new SeparatorMenuItem()); } if (AppPrefs.get().developerMode().getValue()) { var browse = new MenuItem(AppI18n.get("browse"), new FontIcon("mdi2f-folder-open-outline")); browse.setOnAction( - event -> DesktopHelper.browsePath(entry.getEntry().getDirectory())); + event -> DesktopHelper.browsePath(wrapper.getEntry().getDirectory())); contextMenu.getItems().add(browse); } var refresh = new MenuItem(AppI18n.get("refresh"), new FontIcon("mdal-360")); - refresh.disableProperty().bind(entry.getRefreshable().not()); + refresh.disableProperty().bind(wrapper.getRefreshable().not()); refresh.setOnAction(event -> { - DataStorage.get().refreshAsync(entry.getEntry(), true); + DataStorage.get().refreshAsync(wrapper.getEntry(), true); }); contextMenu.getItems().add(refresh); var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline")); - del.disableProperty().bind(entry.getDeletable().not()); - del.setOnAction(event -> entry.delete()); + del.disableProperty().bind(wrapper.getDeletable().not()); + del.setOnAction(event -> wrapper.delete()); contextMenu.getItems().add(del); return contextMenu; diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListComp.java index b4055756a..a58954232 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListComp.java @@ -5,12 +5,15 @@ import io.xpipe.app.comp.base.MultiContentComp; import io.xpipe.app.core.AppState; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; +import io.xpipe.app.fxcomps.impl.HorizontalComp; import io.xpipe.app.fxcomps.util.BindingsHelper; import javafx.beans.binding.Bindings; import javafx.beans.value.ObservableBooleanValue; +import javafx.geometry.Insets; import javafx.scene.layout.Region; import java.util.LinkedHashMap; +import java.util.List; public class StoreEntryListComp extends SimpleComp { @@ -22,8 +25,9 @@ public class StoreEntryListComp extends SimpleComp { .getFilterString() .map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s)))); var content = new ListBoxViewComp<>(filtered, topLevel.getChildren(), (StoreSection e) -> { - return StoreSection.customSection(e).styleClass("top"); - }); + var custom = StoreSection.customSection(e).hgrow(); + return new HorizontalComp(List.of(Comp.spacer(20), custom, Comp.spacer(20))).styleClass("top"); + }).apply(struc -> ((Region) struc.get().getContent()).setPadding(new Insets(20, 0, 20, 0))); return content.styleClass("store-list-comp"); } diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntrySectionComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntrySectionComp.java index 209bafdb4..6cc8b4a16 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntrySectionComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntrySectionComp.java @@ -1,5 +1,6 @@ package io.xpipe.app.comp.storage.store; +import atlantafx.base.theme.Styles; import io.xpipe.app.comp.base.ListBoxViewComp; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; @@ -8,7 +9,9 @@ import io.xpipe.app.fxcomps.impl.HorizontalComp; import io.xpipe.app.fxcomps.impl.IconButtonComp; import io.xpipe.app.fxcomps.impl.VerticalComp; import io.xpipe.app.fxcomps.util.BindingsHelper; +import io.xpipe.app.fxcomps.util.SimpleChangeListener; import javafx.beans.binding.Bindings; +import javafx.css.PseudoClass; import javafx.scene.layout.*; import javafx.scene.paint.Color; @@ -16,6 +19,8 @@ import java.util.List; public class StoreEntrySectionComp extends Comp> { + public static final PseudoClass EXPANDED = PseudoClass.getPseudoClass("expanded"); + private final StoreSection section; public StoreEntrySectionComp(StoreSection section) { @@ -61,15 +66,28 @@ public class StoreEntrySectionComp extends Comp> { padding.setMaxWidth(25); return padding; }); + + var expanded = Bindings.createBooleanBinding(() -> { + return section.getWrapper().getExpanded().get() && section.getChildren().size() > 0; + }, section.getWrapper().getExpanded(), section.getChildren()); + return new VerticalComp(List.of( new HorizontalComp(topEntryList) .apply(struc -> struc.get().setFillHeight(true)), + Comp.separator().visible(expanded), new HorizontalComp(List.of(spacer, content)) .apply(struc -> struc.get().setFillHeight(true)) .hide(BindingsHelper.persist(Bindings.or( Bindings.not(section.getWrapper().getExpanded()), Bindings.size(section.getChildren()).isEqualTo(0)))))) .styleClass("store-entry-section-comp") + .styleClass(Styles.ELEVATED_1) + .apply(struc -> { + struc.get().setFillWidth(true); + SimpleChangeListener.apply(expanded, val -> { + struc.get().pseudoClassStateChanged(EXPANDED, val); + }); + }) .createStructure(); } } diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreLayoutComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreLayoutComp.java index 8c6b8eb98..8c9c1858a 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreLayoutComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreLayoutComp.java @@ -16,6 +16,7 @@ public class StoreLayoutComp extends SimpleComp { var groupHeader = new StoreSidebarComp().createRegion(); r.setLeft(groupHeader); r.setCenter(listR); + r.getStyleClass().add("layout"); return r; } } diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreSidebarComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreSidebarComp.java index 4b30800f6..d5dda0fd0 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreSidebarComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreSidebarComp.java @@ -1,5 +1,6 @@ package io.xpipe.app.comp.storage.store; +import atlantafx.base.theme.Styles; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.impl.VerticalComp; @@ -13,8 +14,8 @@ public class StoreSidebarComp extends SimpleComp { @Override protected Region createSimple() { var sideBar = new VerticalComp(List.of( - new StoreEntryListHeaderComp(), - new StoreCreationBarComp(), + new StoreEntryListHeaderComp().styleClass(Styles.ELEVATED_1), + new StoreCreationBarComp().styleClass(Styles.ELEVATED_1), Comp.of(() -> new Region()).styleClass("bar").styleClass("filler-bar"))); sideBar.apply(s -> VBox.setVgrow(s.get().getChildren().get(2), Priority.ALWAYS)); sideBar.styleClass("sidebar"); diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreViewState.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreViewState.java index 0b00c733b..ac387f55c 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreViewState.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreViewState.java @@ -12,8 +12,10 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.time.Instant; +import java.util.Arrays; import java.util.Comparator; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; public class StoreViewState { @@ -54,17 +56,19 @@ public class StoreViewState { DataStorage.get().addListener(new StorageListener() { @Override - public void onStoreAdd(DataStoreEntry entry) { + public void onStoreAdd(DataStoreEntry... entry) { + var l = Arrays.stream(entry).map(StoreEntryWrapper::new).toList(); Platform.runLater(() -> { - var sg = new StoreEntryWrapper(entry); - allEntries.add(sg); + allEntries.addAll(l); }); } @Override - public void onStoreRemove(DataStoreEntry entry) { + public void onStoreRemove(DataStoreEntry... entry) { + var a = Arrays.stream(entry).collect(Collectors.toSet()); + var l = StoreViewState.get().getAllEntries().stream().filter(storeEntryWrapper -> a.contains(storeEntryWrapper.getEntry())).toList(); Platform.runLater(() -> { - allEntries.removeIf(e -> e.getEntry().equals(entry)); + allEntries.removeAll(l); }); } }); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/Comp.java b/app/src/main/java/io/xpipe/app/fxcomps/Comp.java index c88225f95..7788c2443 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/Comp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/Comp.java @@ -1,12 +1,13 @@ package io.xpipe.app.fxcomps; +import atlantafx.base.controls.Spacer; import io.xpipe.app.fxcomps.augment.Augment; import io.xpipe.app.fxcomps.augment.GrowAugment; -import io.xpipe.app.fxcomps.impl.WrapperComp; import io.xpipe.app.fxcomps.util.Shortcuts; import io.xpipe.app.fxcomps.util.SimpleChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; +import javafx.geometry.Orientation; import javafx.scene.control.ButtonBase; import javafx.scene.control.Separator; import javafx.scene.control.Tooltip; @@ -26,19 +27,25 @@ public abstract class Comp> { private List> augments; + public static Comp> empty() { + return of(() -> new Region()); + } + + public static Comp> spacer(double size) { + return of(() -> new Spacer(size)); + } + public static Comp> of(Supplier r) { - return new WrapperComp<>(() -> { - var region = r.get(); - return () -> region; - }); + return new Comp<>() { + @Override + public CompStructure createBase() { + return new SimpleCompStructure<>(r.get()); + } + }; } public static Comp> separator() { - return Comp.of(() -> new Separator()); - } - - public static > Comp ofStructure(Supplier r) { - return new WrapperComp<>(r); + return of(() -> new Separator(Orientation.HORIZONTAL)); } @SuppressWarnings("unchecked") diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/WrapperComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/WrapperComp.java deleted file mode 100644 index 5c86ef690..000000000 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/WrapperComp.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.xpipe.app.fxcomps.impl; - -import io.xpipe.app.fxcomps.Comp; -import io.xpipe.app.fxcomps.CompStructure; - -import java.util.function.Supplier; - -public class WrapperComp> extends Comp { - - private final Supplier structureSupplier; - - public WrapperComp(Supplier structureSupplier) { - this.structureSupplier = structureSupplier; - } - - @Override - public S createBase() { - return structureSupplier.get(); - } -} diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index 00fa7f544..f9117f3b5 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -75,7 +75,10 @@ public abstract class DataStorage { try { deleteChildren(e, true); var newChildren = ((FixedHierarchyStore) e.getStore()).listChildren(); - newChildren.forEach((key, value) -> addStoreEntryIfNotPresent(key, value)); + addStoreEntries(newChildren.entrySet().stream() + .map(stringDataStoreEntry -> DataStoreEntry.createNew( + UUID.randomUUID(), stringDataStoreEntry.getKey(), stringDataStoreEntry.getValue())) + .toArray(DataStoreEntry[]::new)); return newChildren.size() > 0; } catch (Exception ex) { ErrorEvent.fromThrowable(ex).handle(); @@ -88,12 +91,10 @@ public abstract class DataStorage { var ordered = getStoreChildren(e, false, deep); Collections.reverse(ordered); - ordered.forEach(entry -> { - synchronized (this) { - this.storeEntries.remove(entry); - } - this.listeners.forEach(l -> l.onStoreRemove(entry)); - }); + synchronized (this) { + this.storeEntries.removeAll(ordered); + this.listeners.forEach(l -> l.onStoreRemove(ordered.toArray(DataStoreEntry[]::new))); + } save(); } @@ -103,22 +104,27 @@ public abstract class DataStorage { } var provider = entry.getProvider(); - var parent = display ? provider.getDisplayParent(entry.getStore()) : provider.getLogicalParent(entry.getStore()); + var parent = + display ? provider.getDisplayParent(entry.getStore()) : provider.getLogicalParent(entry.getStore()); return parent != null ? getStoreEntryIfPresent(parent) : Optional.empty(); } public synchronized List getStoreChildren(DataStoreEntry entry, boolean display, boolean deep) { var children = new ArrayList<>(getStoreEntries().stream() - .filter(other -> { - if (!other.getState().isUsable()) { - return false; - } + .filter(other -> { + if (!other.getState().isUsable()) { + return false; + } - var provider = other.getProvider(); - var parent = display ? provider.getDisplayParent(other.getStore()) : provider.getLogicalParent(other.getStore()); - return parent != null && entry.getStore().getClass().equals(parent.getClass()) && entry.getStore().equals(parent); - }) - .toList()); + var provider = other.getProvider(); + var parent = display + ? provider.getDisplayParent(other.getStore()) + : provider.getLogicalParent(other.getStore()); + return parent != null + && entry.getStore().getClass().equals(parent.getClass()) + && entry.getStore().equals(parent); + }) + .toList()); if (deep) { for (DataStoreEntry dataStoreEntry : new ArrayList<>(children)) { @@ -179,7 +185,7 @@ public abstract class DataStorage { public synchronized DataStoreEntry getStoreEntry(@NonNull DataStore store) { var entry = storeEntries.stream() - .filter(n -> Objects.equals(store.getClass(), n.getStore().getClass()) && store.equals(n.getStore())) + .filter(n -> n.getStore() != null && Objects.equals(store.getClass(), n.getStore().getClass()) && store.equals(n.getStore())) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Store not found")); return entry; @@ -242,6 +248,18 @@ public abstract class DataStorage { this.listeners.forEach(l -> l.onStoreAdd(e)); } + public void addStoreEntries(@NonNull DataStoreEntry... es) { + synchronized (this) { + for (DataStoreEntry e : es) { + e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); + this.storeEntries.add(e); + propagateUpdate(e); + } + this.listeners.forEach(l -> l.onStoreAdd(es)); + } + save(); + } + public DataStoreEntry addStoreEntryIfNotPresent(@NonNull String name, DataStore store) { var found = getStoreEntryIfPresent(store); if (found.isPresent()) { diff --git a/app/src/main/java/io/xpipe/app/storage/StorageListener.java b/app/src/main/java/io/xpipe/app/storage/StorageListener.java index 66ce0026f..c4b622824 100644 --- a/app/src/main/java/io/xpipe/app/storage/StorageListener.java +++ b/app/src/main/java/io/xpipe/app/storage/StorageListener.java @@ -2,7 +2,7 @@ package io.xpipe.app.storage; public interface StorageListener { - void onStoreAdd(DataStoreEntry entry); + void onStoreAdd(DataStoreEntry... entry); - void onStoreRemove(DataStoreEntry entry); + void onStoreRemove(DataStoreEntry... entry); } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css b/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css index 7621aaa0f..4f43e867d 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/header-bars.css @@ -1,6 +1,6 @@ .bar { -fx-padding: 0.8em 1.0em 0.8em 1.0em; --fx-background-color: -color-neutral-subtle; +-fx-background-color: -color-bg-subtle; -fx-border-color: -color-neutral-emphasis; } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index cfd7900d3..37fcf1413 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -24,21 +24,29 @@ } .store-entry-grid { --fx-padding: 6px 6px 6px 0; +-fx-padding: 6px 6px 6px 6px; } .store-entry-grid.dense { --fx-padding: 1px 6px 1px 0; +-fx-padding: 1px 6px 1px 6px; } -.store-entry-section-comp.top { +.store-list-comp .top { -fx-border-width: 0 0 1em 0; -fx-background-insets: 0 0 1em 0; -fx-border-color: transparent; } -.store-entry-section-comp.top:odd { --fx-background-color: -color-neutral-subtle; +.store-list-comp .top:odd .store-entry-section-comp { +-fx-background-color: -color-bg-subtle; +} + +.store-list-comp .top:even .store-entry-section-comp { +-fx-background-color: -color-bg-overlay; +} + +.store-list-comp .store-entry-section-comp { +-fx-background-color: -color-bg-default; } .store-entry-comp:hover { @@ -50,10 +58,26 @@ } .store-entry-comp .button-bar .button { --fx-padding: 8px; --fx-background-insets: 0; +-fx-padding: 6px; +} + +.store-entry-section-comp .separator { +-fx-padding: 0 0.5em 0 0.5em; +-fx-border-insets: 0px; +} + +.store-entry-section-comp .separator .line { +-fx-padding: 0; +-fx-border-insets: 0px; +} + +.store-entry-section-comp * { +-fx-spacing: 0.5em; +} + +.store-entry-section-comp:expanded { +-fx-background-color: -color-bg-default; } - diff --git a/app/src/main/resources/io/xpipe/app/resources/style/style.css b/app/src/main/resources/io/xpipe/app/resources/style/style.css index f936d83e7..5be7609fb 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/style.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/style.css @@ -13,6 +13,10 @@ -fx-padding: 1.2em; } +.layout { +-fx-background-color: -color-bg-default; +} + .text { -fx-font-smoothing-type: gray; }