From 2a16911dac25be56446d0bfd76d79e074be7a16d Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 15 Sep 2026 18:02:19 +0000 Subject: [PATCH] Various UI fixes --- app/build.gradle | 2 +- .../java/io/xpipe/app/comp/RegionBuilder.java | 5 +++ .../io/xpipe/app/ext/AuthModuleProvider.java | 2 + .../hub/action/impl/CloneHubLeafProvider.java | 3 ++ .../hub/category/StoreCategoryChoiceComp.java | 2 +- .../app/hub/category/StoreCategoryComp.java | 24 +++++++++-- .../hub/category/StoreCategoryWrapper.java | 40 ++++++++----------- .../xpipe/app/hub/entry/StoreEntryComp.java | 12 +++++- .../hub/list/StoreEntryBatchSelectComp.java | 14 +++++-- .../io/xpipe/app/hub/list/StoreViewState.java | 10 ++--- .../io/xpipe/app/storage/DataStorage.java | 2 - gradle/gradle_scripts/extension.gradle | 2 +- 12 files changed, 77 insertions(+), 41 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ce89b73c3..a9ef98aba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { api "org.slf4j:slf4j-jdk-platform-logging:2.0.18" api 'io.xpipe:modulefs:0.1.9' api 'net.synedra:validatorfx:0.4.2' - api 'io.xpipe:atlantafx-base:2.1.1' + api 'io.xpipe:atlantafx-base:2.1.2' api ("io.xpipe:atlantafx-themes:1.9.1") { exclude group: "io.github.mkpaz", module: "atlantafx-base" } diff --git a/app/src/main/java/io/xpipe/app/comp/RegionBuilder.java b/app/src/main/java/io/xpipe/app/comp/RegionBuilder.java index d65c3243b..80b672c18 100644 --- a/app/src/main/java/io/xpipe/app/comp/RegionBuilder.java +++ b/app/src/main/java/io/xpipe/app/comp/RegionBuilder.java @@ -1,5 +1,6 @@ package io.xpipe.app.comp; +import javafx.beans.value.ObservableDoubleValue; import javafx.geometry.Orientation; import javafx.scene.control.Separator; import javafx.scene.layout.Region; @@ -26,6 +27,10 @@ public abstract class RegionBuilder extends BaseRegionBuilder< return of(() -> new Spacer(size)); } + public static RegionBuilder hspacer(ObservableDoubleValue size) { + return of(() -> new Spacer(size, Orientation.HORIZONTAL)); + } + public static RegionBuilder vspacer() { return of(() -> new Spacer(Orientation.VERTICAL)); } diff --git a/app/src/main/java/io/xpipe/app/ext/AuthModuleProvider.java b/app/src/main/java/io/xpipe/app/ext/AuthModuleProvider.java index 10aec63c6..4d70e725e 100644 --- a/app/src/main/java/io/xpipe/app/ext/AuthModuleProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/AuthModuleProvider.java @@ -47,4 +47,6 @@ public abstract class AuthModuleProvider { public abstract void setWindowsCredential( String target, int type, int persist, String userName, SecretValue password); + + public abstract void deleteWindowsCredential(String target, int type); } diff --git a/app/src/main/java/io/xpipe/app/hub/action/impl/CloneHubLeafProvider.java b/app/src/main/java/io/xpipe/app/hub/action/impl/CloneHubLeafProvider.java index cfb06d2e5..9d69f6dfc 100644 --- a/app/src/main/java/io/xpipe/app/hub/action/impl/CloneHubLeafProvider.java +++ b/app/src/main/java/io/xpipe/app/hub/action/impl/CloneHubLeafProvider.java @@ -76,6 +76,9 @@ public class CloneHubLeafProvider implements HubLeafProvider { entry.setPinToTop(ref.get().isPinToTop()); entry.setOrderIndex(DataStorage.get().getNextOrderIndex()); entry.setNotes(ref.get().getNotes()); + for (String tag : ref.get().getTags()) { + entry.addTag(tag); + } var instant = ref.get().getLastAccess().plus(Duration.ofSeconds(1)); entry.setLastModified(instant); diff --git a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryChoiceComp.java b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryChoiceComp.java index 9b1edcea0..87777824f 100644 --- a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryChoiceComp.java @@ -90,7 +90,7 @@ public class StoreCategoryChoiceComp extends SimpleRegionBuilder { setGraphic(PrettyImageHelper.ofFixedSizeSquare(w.getIconFile().getValue(), 16) .build()); setText(w.getShownName().getValue()); - setPadding(new Insets(6, 6, 6, 8 + (indent ? w.getDepth() * 8 : 0))); + setPadding(new Insets(6, 6, 6, 8 + (indent ? w.getDepth().getValue() * 8 : 0))); } else { setGraphic(null); setText("None"); diff --git a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java index 6dc0c918c..3550d24d9 100644 --- a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java @@ -158,8 +158,11 @@ public class StoreCategoryComp extends SimpleRegionBuilder { var showStatus = hover.or(new SimpleBooleanProperty(DataStorage.get().syncEnabled())) .or(showing) .or(focus); + var indentSpacer = RegionBuilder.hspacer(Bindings.createDoubleBinding(() -> { + return category.getDepth().getValue() * 8.0; + }, category.getDepth())); var h = new HorizontalComp(List.of( - RegionBuilder.hspacer((category.getDepth() * 8)), + indentSpacer, expandButton, RegionBuilder.hspacer(3), iconButton, @@ -454,20 +457,27 @@ public class StoreCategoryComp extends SimpleRegionBuilder { if (AppPrefs.get().enableHttpApi().get()) { var copyId = new MenuItem(AppI18n.get("copyId"), new FontIcon("mdi2c-content-copy")); copyId.setOnAction(event -> - ClipboardHelper.copyText(category.getCategory().getUuid().toString())); + { + ClipboardHelper.copyText(category.getCategory().getUuid().toString()); + event.consume(); + }); contextMenu.getItems().add(copyId); } if (AppPrefs.get().developerMode().getValue()) { var browse = new MenuItem(AppI18n.get("browseInternalStorage"), new FontIcon("mdi2f-folder-open-outline")); browse.setOnAction( - event -> DesktopHelper.browseFile(category.getCategory().getDirectory())); + event -> { + DesktopHelper.browseFile(category.getCategory().getDirectory()); + event.consume(); + }); contextMenu.getItems().add(browse); } var newCategory = new MenuItem(AppI18n.get("createNewCategory"), new FontIcon("mdi2p-plus-thick")); newCategory.setOnAction(event -> { StoreViewState.get().createNewCategory(category); + event.consume(); }); newCategory.setDisable(!DataStorage.get().canCreateStoreCategoryWithin(category.getCategory())); contextMenu.getItems().add(newCategory); @@ -477,10 +487,16 @@ public class StoreCategoryComp extends SimpleRegionBuilder { var configure = new MenuItem(AppI18n.get("configure"), new FontIcon("mdi2w-wrench-outline")); configure.setOnAction(event -> { StoreCategoryConfigComp.show(category); + event.consume(); }); contextMenu.getItems().add(configure); var rename = new MenuItem(AppI18n.get("rename"), new FontIcon("mdal-edit")); + rename.setAccelerator(switch (OsType.ofLocal()) { + case OsType.Linux ignored -> new KeyCodeCombination(KeyCode.F2); + case OsType.MacOs ignored -> new KeyCodeCombination(KeyCode.ENTER); + case OsType.Windows ignored -> new KeyCodeCombination(KeyCode.F2); + }); rename.setOnAction(event -> { category.getRenameTrigger().fire(null); event.consume(); @@ -511,8 +527,10 @@ public class StoreCategoryComp extends SimpleRegionBuilder { contextMenu.getItems().add(new SeparatorMenuItem()); var del = new MenuItem(AppI18n.get("remove"), new FontIcon("mdal-delete_outline")); + del.setAccelerator(new KeyCodeCombination(KeyCode.DELETE)); del.setOnAction(event -> { category.delete(); + event.consume(); }); del.setDisable(!DataStorage.get().canDeleteStoreCategory(category.getCategory())); contextMenu.getItems().add(del); diff --git a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryWrapper.java b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryWrapper.java index 26d63eac1..d1c31960d 100644 --- a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryWrapper.java +++ b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryWrapper.java @@ -32,11 +32,11 @@ import java.util.Optional; public class StoreCategoryWrapper { private final DataStoreCategory root; - private final int depth; - private final Property name; + private final IntegerProperty depth = new SimpleIntegerProperty(); + private final Property name = new SimpleObjectProperty<>(); private final DataStoreCategory category; - private final Property lastAccess; - private final BooleanProperty sync; + private final Property lastAccess = new SimpleObjectProperty<>(); + private final BooleanProperty sync = new SimpleBooleanProperty(); private final DerivedObservableList children; private final DerivedObservableList shownChildren; private final DerivedObservableList directContainedEntries; @@ -50,29 +50,11 @@ public class StoreCategoryWrapper { private StoreCategoryWrapper cachedParent; public StoreCategoryWrapper(DataStoreCategory category) { - var d = 0; - DataStoreCategory last = category; - DataStoreCategory p = category; - while ((p = DataStorage.get() - .getStoreCategoryIfPresent(p.getParentCategory()) - .orElse(null)) - != null) { - d++; - last = p; - } - depth = d; - - this.root = last; + this.root = DataStorage.get().getRootCategory(category); this.category = category; - this.name = new SimpleStringProperty(category.getName()); - this.lastAccess = new SimpleObjectProperty<>(category.getLastAccess()); - this.sync = new SimpleBooleanProperty(Boolean.TRUE.equals( - DataStorage.get().getEffectiveCategoryConfig(category).getSync())); this.children = DerivedObservableList.arrayList(true); this.shownChildren = DerivedObservableList.arrayList(true); this.directContainedEntries = DerivedObservableList.arrayList(true); - this.color.setValue( - DataStorage.get().getEffectiveCategoryConfig(category).getColor()); setupListeners(); } @@ -192,6 +174,18 @@ public class StoreCategoryWrapper { name.setValue(catName); } + var d = 0; + DataStoreCategory last = category; + DataStoreCategory p = category; + while ((p = DataStorage.get() + .getStoreCategoryIfPresent(p.getParentCategory()) + .orElse(null)) + != null) { + d++; + last = p; + } + depth.setValue(d); + orderIndex.set(category.getOrderIndex()); lastAccess.setValue(category.getLastAccess().minus(Duration.ofMillis(500))); sync.setValue(Boolean.TRUE.equals( diff --git a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java index 999dd7c92..d8bd4eb1f 100644 --- a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryComp.java @@ -464,8 +464,14 @@ public abstract class StoreEntryComp extends SimpleRegionBuilder { if (cat == StoreActionCategory.CONFIGURATION && getWrapper().getEntry().getValidity() != DataStoreEntry.Validity.LOAD_FAILED) { var rename = new MenuItem(AppI18n.get("rename"), new FontIcon("mdal-edit")); + rename.setAccelerator(switch (OsType.ofLocal()) { + case OsType.Linux ignored -> new KeyCodeCombination(KeyCode.F2); + case OsType.MacOs ignored -> new KeyCodeCombination(KeyCode.ENTER); + case OsType.Windows ignored -> new KeyCodeCombination(KeyCode.F2); + }); rename.setOnAction(event -> { name.requestFocus(); + event.consume(); }); items.add(items.size(), rename); @@ -613,7 +619,11 @@ public abstract class StoreEntryComp extends SimpleRegionBuilder { return !getWrapper().getDeletable().get(); }, getWrapper().getDeletable())); - del.setOnAction(event -> getWrapper().delete()); + del.setOnAction(event -> { + getWrapper().delete(); + event.consume(); + }); + del.setAccelerator(new KeyCodeCombination(KeyCode.DELETE)); contextMenu.getItems().add(del); } diff --git a/app/src/main/java/io/xpipe/app/hub/list/StoreEntryBatchSelectComp.java b/app/src/main/java/io/xpipe/app/hub/list/StoreEntryBatchSelectComp.java index 899760d05..83394f84d 100644 --- a/app/src/main/java/io/xpipe/app/hub/list/StoreEntryBatchSelectComp.java +++ b/app/src/main/java/io/xpipe/app/hub/list/StoreEntryBatchSelectComp.java @@ -37,7 +37,7 @@ public class StoreEntryBatchSelectComp extends SimpleRegionBuilder { return; } - StoreViewState.get().unselectBatchMode(section); + StoreViewState.get().unselectBatchMode(section, false); } }); }); @@ -59,8 +59,14 @@ public class StoreEntryBatchSelectComp extends SimpleRegionBuilder { } BooleanScope.executeExclusive(selfUpdate, () -> { - if (cb.isSelected()) { - StoreViewState.get().selectBatchMode(section); + while (c.next()) { + if (c.wasRemoved()) { + for (StoreSection r : c.getRemoved()) { + Platform.runLater(() -> { + StoreViewState.get().unselectBatchMode(r, true); + }); + } + } } }); }); @@ -75,7 +81,7 @@ public class StoreEntryBatchSelectComp extends SimpleRegionBuilder { if (cb.isSelected()) { StoreViewState.get().selectBatchMode(section); } else { - StoreViewState.get().unselectBatchMode(section); + StoreViewState.get().unselectBatchMode(section, false); } } diff --git a/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java b/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java index 9c67bf582..daa13c92a 100644 --- a/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java +++ b/app/src/main/java/io/xpipe/app/hub/list/StoreViewState.java @@ -284,7 +284,7 @@ public class StoreViewState { } } - public void unselectBatchMode(StoreSection section) { + public void unselectBatchMode(StoreSection section, boolean recursive) { var wrapper = section.getWrapper(); if (wrapper != null && wrapper.getEntry().getValidity() == DataStoreEntry.Validity.LOAD_FAILED) { return; @@ -292,8 +292,8 @@ public class StoreViewState { if (wrapper != null) { batchModeSelection.getList().remove(wrapper); } - if (wrapper == null || wrapper.getEntry().getProvider().getUsageCategory() == DataStoreUsageCategory.GROUP) { - section.getShownChildren().getList().forEach(c -> unselectBatchMode(c)); + if (wrapper == null || wrapper.getEntry().getProvider().getUsageCategory() == DataStoreUsageCategory.GROUP || recursive) { + section.getAllChildren().getList().forEach(c -> unselectBatchMode(c, recursive)); } } @@ -717,7 +717,7 @@ public class StoreViewState { return 1; } - if (o1.getDepth() > o2.getDepth()) { + if (o1.getDepth().getValue() > o2.getDepth().getValue()) { if (p1 == o2) { return 1; } @@ -725,7 +725,7 @@ public class StoreViewState { return compare(p1, o2); } - if (o1.getDepth() < o2.getDepth()) { + if (o1.getDepth().getValue() < o2.getDepth().getValue()) { if (p2 == o1) { return -1; } 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 8bcce0d9c..1fd7afc19 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -689,8 +689,6 @@ public abstract class DataStorage { } cat.setParentCategory(newParent.getUuid()); - listeners.forEach(storageListener -> storageListener.onCategoryRemove(cat)); - listeners.forEach(storageListener -> storageListener.onCategoryAdd(cat)); listeners.forEach(storageListener -> storageListener.onCategoryListUpdate()); listeners.forEach(storageListener -> storageListener.onStoreListUpdate()); saveAsync(); diff --git a/gradle/gradle_scripts/extension.gradle b/gradle/gradle_scripts/extension.gradle index ae190ef8d..61aa7afe7 100644 --- a/gradle/gradle_scripts/extension.gradle +++ b/gradle/gradle_scripts/extension.gradle @@ -43,7 +43,7 @@ configurations { dependencies { compileOnly "tools.jackson.core:jackson-databind:3.2.0" compileOnly project(':app') - compileOnly 'io.xpipe:atlantafx-base:2.1.0' + compileOnly 'io.xpipe:atlantafx-base:2.1.2' compileOnly 'commons-io:commons-io:2.22.0' compileOnly "org.kordamp.ikonli:ikonli-javafx:12.4.0"