diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserComp.java index 471724c7..3deaf04e 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserComp.java @@ -165,7 +165,8 @@ public class BrowserComp extends SimpleComp { // Handle selection from model model.getSelected().addListener((observable, oldValue, newValue) -> { PlatformThread.runLaterIfNeeded(() -> { - var tab = tabs.getTabs().get(model.getOpenFileSystems().indexOf(newValue)); + var index = model.getOpenFileSystems().indexOf(newValue); + var tab = index != -1 ? tabs.getTabs().get(index) : null; tabs.getSelectionModel().select(tab); }); }); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemSavedState.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemSavedState.java index 63625ffe..88c8e8f8 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemSavedState.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemSavedState.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import io.xpipe.app.core.AppCache; import io.xpipe.app.storage.DataStorage; +import io.xpipe.core.impl.FileNames; import io.xpipe.core.util.JacksonMapper; import javafx.application.Platform; import javafx.collections.FXCollections; @@ -24,6 +25,9 @@ import lombok.extern.jackson.Jacksonized; import java.io.IOException; import java.time.Instant; import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; @AllArgsConstructor @Getter @@ -38,7 +42,8 @@ public class OpenFileSystemSavedState { } @Override - public void serialize(OpenFileSystemSavedState value, JsonGenerator gen, SerializerProvider provider) throws IOException { + public void serialize(OpenFileSystemSavedState value, JsonGenerator gen, SerializerProvider provider) + throws IOException { var node = JsonNodeFactory.instance.objectNode(); node.set("recentDirectories", JacksonMapper.getDefault().valueToTree(value.getRecentDirectories())); gen.writeTree(node); @@ -51,14 +56,26 @@ public class OpenFileSystemSavedState { super(OpenFileSystemSavedState.class); } + private static Predicate distinctBy(Function f) { + Set objects = new HashSet<>(); + return t -> objects.add(f.apply(t)); + } + @Override @SneakyThrows public OpenFileSystemSavedState deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { var tree = (ObjectNode) JacksonMapper.getDefault().readTree(p); - JavaType javaType = JacksonMapper.getDefault().getTypeFactory().constructCollectionLikeType(List.class, RecentEntry.class); - List recentDirectories = JacksonMapper.getDefault().treeToValue(tree.remove("recentDirectories"), javaType); - return new OpenFileSystemSavedState(null, FXCollections.observableList(recentDirectories)); + JavaType javaType = JacksonMapper.getDefault() + .getTypeFactory() + .constructCollectionLikeType(List.class, RecentEntry.class); + List recentDirectories = + JacksonMapper.getDefault().treeToValue(tree.remove("recentDirectories"), javaType); + var cleaned = recentDirectories.stream() + .map(recentEntry -> new RecentEntry(FileNames.toDirectory(recentEntry.directory), recentEntry.time)) + .filter(distinctBy(recentEntry -> recentEntry.getDirectory())) + .collect(Collectors.toCollection(ArrayList::new)); + return new OpenFileSystemSavedState(null, FXCollections.observableList(cleaned)); } } @@ -85,7 +102,9 @@ public class OpenFileSystemSavedState { @Setter private OpenFileSystemModel model; + private String lastDirectory; + @NonNull private ObservableList recentDirectories; @@ -138,14 +157,17 @@ public class OpenFileSystemSavedState { } private void updateRecent(String dir) { - recentDirectories.removeIf(recentEntry -> Objects.equals(recentEntry.directory, dir)); + var without = FileNames.removeTrailingSlash(dir); + var with = FileNames.toDirectory(dir); + recentDirectories.removeIf(recentEntry -> + Objects.equals(recentEntry.directory, without) || Objects.equals(recentEntry.directory, with)); - var o = new RecentEntry(dir, Instant.now()); + var o = new RecentEntry(with, Instant.now()); if (recentDirectories.size() < STORED) { recentDirectories.add(0, o); } else { recentDirectories.remove(recentDirectories.size() - 1); - recentDirectories.add(o); + recentDirectories.add(0, o); } } } diff --git a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java index 8d6f738c..e3b2737f 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java @@ -7,6 +7,7 @@ import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.Shortcuts; +import javafx.application.Platform; import javafx.beans.property.Property; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -24,7 +25,6 @@ import org.kordamp.ikonli.javafx.FontIcon; public class ModalOverlayComp extends SimpleComp { - public ModalOverlayComp(Comp background, Property overlayContent) { this.background = background; this.overlayContent = overlayContent; @@ -74,7 +74,6 @@ public class ModalOverlayComp extends SimpleComp { } var tp = new TitledPane(AppI18n.get(newValue.titleKey), box); - tp.setMaxWidth(400); tp.setCollapsible(false); var closeButton = new Button(null, new FontIcon("mdi2w-window-close")); @@ -98,8 +97,16 @@ public class ModalOverlayComp extends SimpleComp { stack.setAlignment(Pos.CENTER); close.maxWidthProperty().bind(tp.widthProperty()); close.maxHeightProperty().bind(tp.heightProperty()); + tp.maxWidthProperty().bind(stack.widthProperty().add(-100)); modal.show(stack); + + // Wait 2 pulses before focus so that the scene can be assigned to r + Platform.runLater(() -> { + Platform.runLater(() -> { + r.requestFocus(); + }); + }); } }); return pane; diff --git a/core/src/main/java/io/xpipe/core/impl/FileNames.java b/core/src/main/java/io/xpipe/core/impl/FileNames.java index 4589d555..75106318 100644 --- a/core/src/main/java/io/xpipe/core/impl/FileNames.java +++ b/core/src/main/java/io/xpipe/core/impl/FileNames.java @@ -34,6 +34,10 @@ public class FileNames { } public static String getFileName(String file) { + if (file == null) { + return null; + } + if (file.isEmpty()) { return ""; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenTerminalAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenTerminalAction.java index d529a6c2..78060ccd 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenTerminalAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenTerminalAction.java @@ -3,6 +3,7 @@ package io.xpipe.ext.base.browser; import io.xpipe.app.browser.BrowserEntry; import io.xpipe.app.browser.OpenFileSystemModel; import io.xpipe.app.browser.action.LeafAction; +import io.xpipe.app.util.TerminalHelper; import io.xpipe.core.store.FileKind; import javafx.scene.Node; import javafx.scene.input.KeyCode; @@ -20,6 +21,11 @@ public class OpenTerminalAction implements LeafAction { @Override public void execute(OpenFileSystemModel model, List entries) throws Exception { + if (model.getInOverview().get()) { + TerminalHelper.open(model.getName(), model.getFileSystem().getShell().orElseThrow().prepareTerminalOpen(model.getName())); + return; + } + if (entries.size() == 0) { model.openTerminalAsync(model.getCurrentDirectory().getPath()); return; @@ -30,11 +36,6 @@ public class OpenTerminalAction implements LeafAction { } } - @Override - public boolean isActive(OpenFileSystemModel model, List entries) { - return !model.getInOverview().get(); - } - @Override public Category getCategory() { return Category.OPEN; diff --git a/ext/base/src/main/java/module-info.java b/ext/base/src/main/java/module-info.java index 85cea3e3..41115dcc 100644 --- a/ext/base/src/main/java/module-info.java +++ b/ext/base/src/main/java/module-info.java @@ -52,7 +52,6 @@ open module io.xpipe.ext.base { DeleteStoreChildrenAction, AddStoreAction, EditStoreAction, - StreamExportAction, ShareStoreAction, FileBrowseAction, FileEditAction;