From 6df970db2f0beedeb58b812f613f2630a361ab04 Mon Sep 17 00:00:00 2001 From: crschnick Date: Mon, 30 Jun 2025 09:03:18 +0000 Subject: [PATCH] Rework --- .../java/io/xpipe/app/ext}/GroupStore.java | 2 +- .../xpipe/app/ext}/SelfReferentialStore.java | 2 +- .../io/xpipe/app/hub/comp/StoreEntryComp.java | 4 +- .../xpipe/app/hub/comp/StoreEntryWrapper.java | 20 ++++++ .../io/xpipe/app/hub/comp/StoreViewState.java | 6 +- .../io/xpipe/app/storage/DataStorage.java | 72 +++++++++++++++---- .../io/xpipe/app/storage/StorageListener.java | 2 +- .../ext/base/identity/IdentitySelectComp.java | 4 +- .../ext/base/identity/IdentityStore.java | 2 +- .../ext/base/identity/IdentityValue.java | 21 ++---- .../ext/base/script/ScriptGroupStore.java | 4 +- .../ext/base/script/SimpleScriptStore.java | 2 +- .../service/AbstractServiceGroupStore.java | 2 +- ext/base/src/main/java/module-info.java | 1 - .../ext/system/incus/IncusCommandView.java | 3 +- .../ext/system/incus/IncusInstallStore.java | 2 +- .../io/xpipe/ext/system/lxd/LxdCmdStore.java | 2 +- .../xpipe/ext/system/lxd/LxdCommandView.java | 2 + .../LxdContainerConsoleActionProvider.java | 4 +- .../LxdContainerEditConfigActionProvider.java | 4 +- ...dContainerEditRunConfigActionProvider.java | 2 +- .../ext/system/podman/PodmanCmdStore.java | 2 +- .../system/podman/PodmanContainerStore.java | 2 +- 23 files changed, 116 insertions(+), 51 deletions(-) rename {ext/base/src/main/java/io/xpipe/ext/base => app/src/main/java/io/xpipe/app/ext}/GroupStore.java (93%) rename {ext/base/src/main/java/io/xpipe/ext/base => app/src/main/java/io/xpipe/app/ext}/SelfReferentialStore.java (97%) diff --git a/ext/base/src/main/java/io/xpipe/ext/base/GroupStore.java b/app/src/main/java/io/xpipe/app/ext/GroupStore.java similarity index 93% rename from ext/base/src/main/java/io/xpipe/ext/base/GroupStore.java rename to app/src/main/java/io/xpipe/app/ext/GroupStore.java index 315b0b0f1..eff149123 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/GroupStore.java +++ b/app/src/main/java/io/xpipe/app/ext/GroupStore.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base; +package io.xpipe.app.ext; import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.core.store.DataStore; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/SelfReferentialStore.java b/app/src/main/java/io/xpipe/app/ext/SelfReferentialStore.java similarity index 97% rename from ext/base/src/main/java/io/xpipe/ext/base/SelfReferentialStore.java rename to app/src/main/java/io/xpipe/app/ext/SelfReferentialStore.java index 124901a2e..54ca834bd 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/SelfReferentialStore.java +++ b/app/src/main/java/io/xpipe/app/ext/SelfReferentialStore.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base; +package io.xpipe.app.ext; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntry; diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryComp.java index c8d560fd8..fee62b7c7 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryComp.java @@ -575,9 +575,9 @@ public abstract class StoreEntryComp extends SimpleComp { items.add(pinToTop); } - if (getWrapper().getStore().getValue() instanceof FixedHierarchyStore) { + if (getWrapper().canBreakOutCategory()) { var breakOut = new MenuItem(); - var is = getWrapper().getEntry().getBreakOutCategory() != null; + var is = getWrapper().getBreakoutCategory().isPresent(); if (is) { breakOut.textProperty().bind(AppI18n.observable("mergeCategory")); breakOut.setGraphic(new FontIcon("mdi2c-collapse-all-outline")); diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryWrapper.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryWrapper.java index 2ae66ccfb..8314f9ff2 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryWrapper.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreEntryWrapper.java @@ -1,6 +1,7 @@ package io.xpipe.app.hub.comp; import io.xpipe.app.action.*; +import io.xpipe.app.ext.GroupStore; import io.xpipe.app.ext.LocalStore; import io.xpipe.app.ext.ShellStore; import io.xpipe.app.ext.SingletonSessionStore; @@ -14,6 +15,7 @@ import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreCategory; import io.xpipe.app.storage.DataStoreColor; import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.app.util.FixedHierarchyStore; import io.xpipe.app.util.PlatformThread; import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.store.DataStore; @@ -337,6 +339,11 @@ public class StoreEntryWrapper { return false; } + public boolean canBreakOutCategory() { + return (getStore().getValue() instanceof FixedHierarchyStore || getStore().getValue() instanceof GroupStore) && + StoreViewState.get().getParentSectionForWrapper(this).isPresent(); + } + public void breakOutCategory() { ThreadHelper.runAsync(() -> { var cat = DataStorage.get().breakOutCategory(entry); @@ -350,6 +357,19 @@ public class StoreEntryWrapper { }); } + public Optional getBreakoutCategory() { + if (entry.getBreakOutCategory() == null) { + return Optional.empty(); + } + + var cat = DataStorage.get().getStoreCategoryIfPresent(entry.getBreakOutCategory()); + if (cat.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(StoreViewState.get().getCategoryWrapper(cat.get())); + } + public void mergeBreakOutCategory() { ThreadHelper.runAsync(() -> { DataStorage.get().mergeBreakOutCategory(entry); diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreViewState.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreViewState.java index fcbf5faaa..c67c7ac03 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreViewState.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreViewState.java @@ -416,6 +416,10 @@ public class StoreViewState { return; } + if (found.get().equals(activeCategory.getValue())) { + activeCategory.setValue(found.get().getParent()); + } + synchronized (this) { categories.getList().remove(found.get()); } @@ -427,7 +431,7 @@ public class StoreViewState { } @Override - public void onEntryCategoryChange(DataStoreCategory from, DataStoreCategory to) { + public void onEntryCategoryChange() { Platform.runLater(() -> { synchronized (this) { categories.getList().forEach(storeCategoryWrapper -> storeCategoryWrapper.update()); 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 20a0ed16c..cb88558de 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -1,6 +1,7 @@ package io.xpipe.app.storage; import io.xpipe.app.core.AppProperties; +import io.xpipe.app.ext.GroupStore; import io.xpipe.app.ext.LocalStore; import io.xpipe.app.ext.NameableStore; import io.xpipe.app.issue.ErrorEventFactory; @@ -402,7 +403,12 @@ public abstract class DataStorage { } public DataStoreCategory breakOutCategory(DataStoreEntry entry) { - if (!(entry.getStore() instanceof FixedHierarchyStore)) { + if (!(entry.getStore() instanceof FixedHierarchyStore) && !(entry.getStore() instanceof GroupStore)) { + return null; + } + + var parent = getDefaultDisplayParent(entry).or(() -> getSyntheticParent(entry)); + if (parent.isEmpty()) { return null; } @@ -419,22 +425,46 @@ public abstract class DataStorage { DataStoreCategoryConfig.empty()); addStoreCategory(breakOut); entry.setBreakOutCategory(breakOut); + entry.setExpanded(true); - var children = getStoreChildren(entry); - children.forEach(child -> { - child.setCategoryUuid(breakOut.getUuid()); + var children = getDeepStoreChildren(entry); + var childrenToKeep = new HashSet(); + children.forEach(c -> { + if (c.getBreakOutCategory() != null) { + childrenToKeep.addAll(getDeepStoreChildren(c)); + childrenToKeep.add(c); + } }); - listeners.forEach(storageListener -> storageListener.onEntryCategoryChange(cat, breakOut)); + children.forEach(child -> { + if (!childrenToKeep.contains(child)) { + child.setCategoryUuid(breakOut.getUuid()); + } + }); + entry.setCategoryUuid(breakOut.getUuid()); + + var categoriesToMove = new ArrayList(); + children.forEach(child -> { + if (child.getBreakOutCategory() != null) { + var childBreakOut = getStoreCategoryIfPresent(child.getBreakOutCategory()); + if (childBreakOut.isPresent() && childBreakOut.get().getParentCategory().equals(cat.getUuid())) { + categoriesToMove.add(childBreakOut.get()); + } + } + }); + categoriesToMove.forEach(toMove -> { + toMove.setParentCategory(breakOut.getUuid()); + listeners.forEach(storageListener -> storageListener.onCategoryRemove(toMove)); + listeners.forEach(storageListener -> storageListener.onCategoryAdd(toMove)); + }); + + listeners.forEach(storageListener -> storageListener.onEntryCategoryChange()); listeners.forEach(storageListener -> storageListener.onStoreListUpdate()); + saveAsync(); return breakOut; } public void mergeBreakOutCategory(DataStoreEntry entry) { - if (!(entry.getStore() instanceof FixedHierarchyStore)) { - return; - } - if (entry.getBreakOutCategory() == null) { return; } @@ -445,12 +475,30 @@ public abstract class DataStorage { return; } + var parent = getDefaultDisplayParent(entry).or(() -> getSyntheticParent(entry)); + if (parent.isEmpty()) { + return; + } + + var moveCategories = new ArrayList(); var children = getStoreChildren(entry); children.forEach(child -> { - child.setCategoryUuid(entry.getCategoryUuid()); + if (child.getBreakOutCategory() == null) { + child.setCategoryUuid(parent.get().getCategoryUuid()); + } else { + var cbo = getStoreCategoryIfPresent(child.getBreakOutCategory()); + if (cbo.isPresent() && cbo.get().getParentCategory().equals(breakOut.get().getUuid())) { + moveCategories.add(cbo.get()); + } + } }); + moveCategories.forEach(mc -> { + mc.setParentCategory(parent.get().getCategoryUuid()); + }); + entry.setCategoryUuid(parent.get().getCategoryUuid()); + listeners.forEach( - storageListener -> storageListener.onEntryCategoryChange(breakOut.get(), getStoreCategory(entry))); + storageListener -> storageListener.onEntryCategoryChange()); deleteStoreCategory(breakOut.get(), false, false); entry.setBreakOutCategory(null); listeners.forEach(storageListener -> storageListener.onStoreListUpdate()); @@ -472,7 +520,7 @@ public abstract class DataStorage { child.setCategoryUuid(newCategory.getUuid()); }); - listeners.forEach(storageListener -> storageListener.onEntryCategoryChange(oldCat, newCategory)); + listeners.forEach(storageListener -> storageListener.onEntryCategoryChange()); listeners.forEach(storageListener -> storageListener.onStoreListUpdate()); saveAsync(); } 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 d3a237a90..b9624325e 100644 --- a/app/src/main/java/io/xpipe/app/storage/StorageListener.java +++ b/app/src/main/java/io/xpipe/app/storage/StorageListener.java @@ -12,5 +12,5 @@ public interface StorageListener { void onCategoryRemove(DataStoreCategory category); - void onEntryCategoryChange(DataStoreCategory from, DataStoreCategory to); + void onEntryCategoryChange(); } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentitySelectComp.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentitySelectComp.java index 7b921b177..b074bcee0 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentitySelectComp.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentitySelectComp.java @@ -139,7 +139,9 @@ public class IdentitySelectComp extends Comp> { layout.apply(struc -> { struc.get().focusedProperty().addListener((observable, oldValue, newValue) -> { - struc.get().getChildren().getFirst().requestFocus(); + Platform.runLater(() -> { + struc.get().getChildren().getFirst().requestFocus(); + }); }); }); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityStore.java index 701188a86..d609470cf 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityStore.java @@ -2,7 +2,7 @@ package io.xpipe.ext.base.identity; import io.xpipe.app.util.*; import io.xpipe.core.store.*; -import io.xpipe.ext.base.SelfReferentialStore; +import io.xpipe.app.ext.SelfReferentialStore; import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityValue.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityValue.java index 9b74c3df1..976c97981 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityValue.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityValue.java @@ -30,28 +30,17 @@ public interface IdentityValue { } var found = DataStorage.get().getStoreEntryIfPresent(effective.getDefaultIdentityStore()); - if (found.isEmpty() || !(found.get().getStore() instanceof IdentityStore identityStore)) { + if (found.isEmpty() || !(found.get().getStore() instanceof IdentityStore)) { return null; } return new Ref(found.get().ref()); } - static IdentityValue ofCategory(DataStoreEntry e) { - var target = e.getBreakOutCategory() != null - ? DataStorage.get() - .getStoreCategoryIfPresent(e.getBreakOutCategory()) - .orElse(null) - : null; - if (target == null) { - target = DataStorage.get().getStoreCategory(e); - } - var effective = DataStorage.get().getEffectiveCategoryConfig(target); - if (effective.getDefaultIdentityStore() == null) { - return null; - } - - var found = DataStorage.get().getStoreEntryIfPresent(effective.getDefaultIdentityStore()); + static IdentityValue ofBreakout(DataStoreEntry e) { + var cat = DataStorage.get().getStoreCategory(e); + var uuid = cat.getConfig().getDefaultIdentityStore(); + var found = DataStorage.get().getStoreEntryIfPresent(uuid); if (found.isEmpty() || !(found.get().getStore() instanceof IdentityStore)) { return null; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStore.java b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStore.java index b5e58d65b..f25830d8a 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/ScriptGroupStore.java @@ -2,8 +2,8 @@ package io.xpipe.ext.base.script; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStoreEntryRef; -import io.xpipe.ext.base.GroupStore; -import io.xpipe.ext.base.SelfReferentialStore; +import io.xpipe.app.ext.GroupStore; +import io.xpipe.app.ext.SelfReferentialStore; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.EqualsAndHashCode; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java index c1b974450..25d255a1a 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/script/SimpleScriptStore.java @@ -7,7 +7,7 @@ import io.xpipe.app.util.Validators; import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellDialect; import io.xpipe.core.util.ValidationException; -import io.xpipe.ext.base.SelfReferentialStore; +import io.xpipe.app.ext.SelfReferentialStore; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.EqualsAndHashCode; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStore.java b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStore.java index f5e51fbf0..d67a97bf9 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceGroupStore.java @@ -3,7 +3,7 @@ package io.xpipe.ext.base.service; import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.util.Validators; import io.xpipe.core.store.DataStore; -import io.xpipe.ext.base.GroupStore; +import io.xpipe.app.ext.GroupStore; import lombok.AccessLevel; import lombok.EqualsAndHashCode; diff --git a/ext/base/src/main/java/module-info.java b/ext/base/src/main/java/module-info.java index db43d4152..ce045376b 100644 --- a/ext/base/src/main/java/module-info.java +++ b/ext/base/src/main/java/module-info.java @@ -11,7 +11,6 @@ import io.xpipe.ext.base.store.StoreStartActionProvider; import io.xpipe.ext.base.store.StoreStopActionProvider; open module io.xpipe.ext.base { - exports io.xpipe.ext.base; exports io.xpipe.ext.base.script; exports io.xpipe.ext.base.store; exports io.xpipe.ext.base.desktop; diff --git a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusCommandView.java b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusCommandView.java index 00bd8b9a0..9e3fe46b9 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusCommandView.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusCommandView.java @@ -7,6 +7,7 @@ import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.util.CommandViewBase; import io.xpipe.core.process.*; +import io.xpipe.ext.base.identity.IdentityValue; import lombok.NonNull; import java.util.*; @@ -112,7 +113,7 @@ public class IncusCommandView extends CommandViewBase { return listContainersAndStates().entrySet().stream() .map(s -> { boolean running = s.getValue().toLowerCase(Locale.ROOT).equals("running"); - var c = new IncusContainerStore(store, s.getKey(), null); + var c = new IncusContainerStore(store, s.getKey(), IdentityValue.ofBreakout(store.get())); var entry = DataStoreEntry.createNew(c.getContainerName(), c); entry.setStorePersistentState(ContainerStoreState.builder() .containerState(s.getValue()) diff --git a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusInstallStore.java b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusInstallStore.java index c24ae2c6d..bb1cfb83e 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusInstallStore.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/incus/IncusInstallStore.java @@ -8,7 +8,7 @@ import io.xpipe.app.util.Validators; import io.xpipe.core.store.DataStoreState; import io.xpipe.core.store.FixedChildStore; import io.xpipe.core.store.StatefulDataStore; -import io.xpipe.ext.base.SelfReferentialStore; +import io.xpipe.app.ext.SelfReferentialStore; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.EqualsAndHashCode; diff --git a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCmdStore.java b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCmdStore.java index f43556182..7a80ae94c 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCmdStore.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCmdStore.java @@ -8,7 +8,7 @@ import io.xpipe.app.util.Validators; import io.xpipe.core.store.DataStoreState; import io.xpipe.core.store.FixedChildStore; import io.xpipe.core.store.StatefulDataStore; -import io.xpipe.ext.base.SelfReferentialStore; +import io.xpipe.app.ext.SelfReferentialStore; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.EqualsAndHashCode; diff --git a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCommandView.java b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCommandView.java index 092e60187..e8a9ca0ee 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCommandView.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdCommandView.java @@ -7,6 +7,7 @@ import io.xpipe.app.storage.DataStoreEntryRef; import io.xpipe.app.util.CommandViewBase; import io.xpipe.core.process.*; +import io.xpipe.ext.base.identity.IdentityValue; import lombok.NonNull; import java.util.*; @@ -123,6 +124,7 @@ public class LxdCommandView extends CommandViewBase { var c = LxdContainerStore.builder() .cmd(store) .containerName(s.getKey()) + .identity(IdentityValue.ofBreakout(store.get())) .build(); var entry = DataStoreEntry.createNew(c.getContainerName(), c); entry.setStorePersistentState(ContainerStoreState.builder() diff --git a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerConsoleActionProvider.java b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerConsoleActionProvider.java index ef8cd8f24..0dad4b5d7 100644 --- a/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerConsoleActionProvider.java +++ b/ext/system/src/main/java/io/xpipe/ext/system/lxd/LxdContainerConsoleActionProvider.java @@ -52,8 +52,8 @@ public class LxdContainerConsoleActionProvider implements HubLeafProvider