From daa011ffe6b10fd223b6a34be01a7c19295c200d Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 30 May 2023 01:59:08 +0000 Subject: [PATCH] Various fixes [stage] --- .../app/browser/BrowserFileOverviewComp.java | 18 ++++- .../io/xpipe/app/browser/BrowserNavBar.java | 5 ++ .../app/browser/BrowserOverviewComp.java | 37 ++++++--- .../xpipe/app/browser/OpenFileSystemComp.java | 19 +---- .../app/browser/OpenFileSystemHistory.java | 2 +- .../app/browser/OpenFileSystemModel.java | 38 +++------- .../app/browser/OpenFileSystemSavedState.java | 20 +++-- .../xpipe/app/browser/action/LeafAction.java | 5 ++ .../java/io/xpipe/app/comp/AppLayoutComp.java | 9 +-- .../io/xpipe/app/comp/base/VBoxViewComp.java | 75 +++++++++++++++++++ .../store/StoreEntryListHeaderComp.java | 4 +- .../main/java/io/xpipe/app/core/AppLogs.java | 11 ++- .../io/xpipe/app/fxcomps/impl/FilterComp.java | 1 + .../io/xpipe/app/fxcomps/util/Shortcuts.java | 2 +- .../io/xpipe/app/resources/style/browser.css | 3 - .../java/io/xpipe/core/process/OsType.java | 20 ++++- .../core/process/ProcessOutputException.java | 25 ++++--- dist/changelogs/1.0.2.md | 5 ++ .../io/xpipe/ext/base/browser/BackAction.java | 54 +++++++++++++ .../xpipe/ext/base/browser/ForwardAction.java | 54 +++++++++++++ .../ext/base/browser/OpenTerminalAction.java | 5 ++ .../xpipe/ext/base/browser/RefreshAction.java | 55 ++++++++++++++ ext/base/src/main/java/module-info.java | 3 + version | 2 +- 24 files changed, 380 insertions(+), 92 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/comp/base/VBoxViewComp.java create mode 100644 dist/changelogs/1.0.2.md create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/browser/BackAction.java create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/browser/ForwardAction.java create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/browser/RefreshAction.java diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java index 16f6f8174..3e0c0ebb0 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFileOverviewComp.java @@ -2,6 +2,7 @@ package io.xpipe.app.browser; import io.xpipe.app.browser.icon.BrowserIcons; import io.xpipe.app.comp.base.ListBoxViewComp; +import io.xpipe.app.comp.base.VBoxViewComp; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.augment.GrowAugment; @@ -13,16 +14,19 @@ import javafx.scene.layout.Region; import lombok.EqualsAndHashCode; import lombok.Value; +import java.util.function.Function; + @Value @EqualsAndHashCode(callSuper = true) public class BrowserFileOverviewComp extends SimpleComp { OpenFileSystemModel model; ObservableList list; + boolean grow; @Override protected Region createSimple() { - var c = new ListBoxViewComp<>(list, list, entry -> { + Function> factory = entry -> { return Comp.of(() -> { var icon = BrowserIcons.createIcon(entry); var l = new Button(entry.getPath(), icon.createRegion()); @@ -34,8 +38,14 @@ public class BrowserFileOverviewComp extends SimpleComp { GrowAugment.create(true,false).augment(l); return l; }); - }) - .styleClass("overview-file-list"); - return c.createRegion(); + }; + + if (grow) { + var c = new ListBoxViewComp<>(list, list, factory).styleClass("overview-file-list"); + return c.createRegion(); + } else { + var c = new VBoxViewComp<>(list, list, factory).styleClass("overview-file-list"); + return c.createRegion(); + } } } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java index 8cac3bb66..819d1ae2a 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java @@ -18,6 +18,9 @@ import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import javafx.scene.input.MouseButton; import javafx.scene.layout.Region; @@ -67,6 +70,8 @@ public class BrowserNavBar extends SimpleComp { }); struc.get().setPromptText("Overview of " + model.getName()); + }).shortcut(new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN), s -> { + s.get().requestFocus(); }); var graphic = Bindings.createStringBinding( diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java index 1e6392d8d..4e06fd587 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java @@ -5,10 +5,13 @@ import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.impl.VerticalComp; import io.xpipe.app.fxcomps.util.BindingsHelper; +import io.xpipe.app.issue.ErrorEvent; import io.xpipe.core.process.ShellControl; import io.xpipe.core.store.FileSystem; import javafx.collections.FXCollections; +import javafx.scene.layout.Priority; import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import lombok.SneakyThrows; import java.util.List; @@ -25,18 +28,34 @@ public class BrowserOverviewComp extends SimpleComp { @SneakyThrows protected Region createSimple() { ShellControl sc = model.getFileSystem().getShell().orElseThrow(); - var common = sc.getOsType().determineInterestingPaths(sc).stream().map(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s)).toList(); - var commonOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(common)); - var commonPane = new SimpleTitledPaneComp(AppI18n.observable("common"), commonOverview); + // TODO: May be move this into another thread + var common = sc.getOsType().determineInterestingPaths(sc).stream() + .map(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s)) + .filter(entry -> { + try { + var b = sc.getShellDialect().directoryExists(sc, entry.getPath()).executeAndCheck(); + return b; + } catch (Exception e) { + ErrorEvent.fromThrowable(e).handle(); + return false; + } + }) + .toList(); + var commonOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(common), false); + var commonPane = new SimpleTitledPaneComp(AppI18n.observable("common"), commonOverview).apply(struc -> VBox.setVgrow(struc.get(), Priority.NEVER)); - var roots = sc.getShellDialect().listRoots(sc).map(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s)).toList(); - var rootsOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(roots)); + var roots = sc.getShellDialect() + .listRoots(sc) + .map(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s)) + .toList(); + var rootsOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(roots), false); var rootsPane = new SimpleTitledPaneComp(AppI18n.observable("roots"), rootsOverview); - - var recent = BindingsHelper.mappedContentBinding(model.getSavedState().getRecentDirectories(), s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s.getDirectory())); - var recentOverview = new BrowserFileOverviewComp(model, recent); - var recentPane = new SimpleTitledPaneComp(AppI18n.observable("recent"), recentOverview).vgrow(); + var recent = BindingsHelper.mappedContentBinding( + model.getSavedState().getRecentDirectories(), + s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s.getDirectory())); + var recentOverview = new BrowserFileOverviewComp(model, recent, true); + var recentPane = new SimpleTitledPaneComp(AppI18n.observable("recent"), recentOverview); var vbox = new VerticalComp(List.of(commonPane, rootsPane, recentPane)).styleClass("overview"); return vbox.createRegion(); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java index 21a037d1b..a06ee5ecb 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemComp.java @@ -46,23 +46,10 @@ public class OpenFileSystemComp extends SimpleComp { overview.setOnAction(e -> model.cd(null)); overview.disableProperty().bind(model.getInOverview()); - var backBtn = new Button(null, new FontIcon("fth-arrow-left")); - backBtn.setOnAction(e -> model.back()); - backBtn.disableProperty().bind(model.getHistory().canGoBackProperty().not()); - - var forthBtn = new Button(null, new FontIcon("fth-arrow-right")); - forthBtn.setOnAction(e -> model.forth()); - forthBtn.disableProperty().bind(model.getHistory().canGoForthProperty().not()); - - var refreshBtn = new Button(null, new FontIcon("mdmz-refresh")); - refreshBtn.setOnAction(e -> model.refresh()); - Shortcuts.addShortcut(refreshBtn, new KeyCodeCombination(KeyCode.F5)); - refreshBtn.disableProperty().bind(model.getInOverview()); - + var backBtn = BrowserAction.byId("back").toButton(model, List.of()); + var forthBtn = BrowserAction.byId("forward").toButton(model, List.of()); + var refreshBtn = BrowserAction.byId("refresh").toButton(model, List.of()); var terminalBtn = BrowserAction.byId("openTerminal").toButton(model, List.of()); - terminalBtn.setOnAction( - e -> model.openTerminalAsync(model.getCurrentPath().get())); - terminalBtn.disableProperty().bind(model.getInOverview()); var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open")); new ContextMenuAugment<>( diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemHistory.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemHistory.java index 8cdd03686..ee7b9b5cc 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemHistory.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemHistory.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -final class OpenFileSystemHistory { +public final class OpenFileSystemHistory { private final IntegerProperty cursor = new SimpleIntegerProperty(-1); private final List history = new ArrayList<>(); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java index 446948b23..4477a49ce 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java @@ -22,7 +22,6 @@ import org.apache.commons.lang3.function.FailableConsumer; import java.io.IOException; import java.nio.file.Path; -import java.time.Instant; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -54,7 +53,6 @@ public final class OpenFileSystemModel { return currentPath.get() == null; }, currentPath)); fileList = new BrowserFileListModel(this); - addListeners(); } public void withShell(FailableConsumer c, boolean refresh) { @@ -74,17 +72,6 @@ public final class OpenFileSystemModel { }); } - private void addListeners() { - // savedState.addListener((observable, oldValue, newValue) -> { - // if (store == null) { - // return; - // } - // - // var storageEntry = DataStorage.get().getStoreEntryIfPresent(store); - // storageEntry.ifPresent(entry -> AppCache.update("browser-state-" + entry.getUuid(), newValue)); - // }); - } - @SneakyThrows public void refresh() { BusyProperty.execute(busy, () -> { @@ -194,9 +181,9 @@ public final class OpenFileSystemModel { // path = FileSystemHelper.normalizeDirectoryPath(this, path); filter.setValue(null); - currentPath.set(path); savedState.cd(path); history.updateCurrent(path); + currentPath.set(path); loadFilesSync(path); } @@ -206,10 +193,7 @@ public final class OpenFileSystemModel { var stream = getFileSystem().listFiles(dir); fileList.setAll(stream); } else { - var stream = getFileSystem().listRoots().stream() - .map(s -> new FileSystem.FileEntry( - getFileSystem(), s, Instant.now(), true, false, false, 0, null)); - fileList.setAll(stream); + fileList.setAll(Stream.of()); } return true; } catch (Exception e) { @@ -314,6 +298,10 @@ public final class OpenFileSystemModel { fileSystem = null; } + public boolean isClosed() { + return fileSystem == null; + } + public void initFileSystem() throws Exception { BusyProperty.execute(busy, () -> { var fs = store.createFileSystem(); @@ -337,7 +325,7 @@ public final class OpenFileSystemModel { } private void initState() { - this.savedState = OpenFileSystemSavedState.loadForStore(store); + this.savedState = OpenFileSystemSavedState.loadForStore(this); } public void openTerminalAsync(String directory) { @@ -365,15 +353,11 @@ public final class OpenFileSystemModel { return history; } - public void back() { - try (var ignored = new BusyProperty(busy)) { - cd(history.back()); - } + public void backSync() throws Exception { + cdSyncWithoutCheck(history.back()); } - public void forth() { - try (var ignored = new BusyProperty(busy)) { - cd(history.forth()); - } + public void forthSync() throws Exception { + cdSyncWithoutCheck(history.forth()); } } 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 8a85e335a..9c3a087db 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemSavedState.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemSavedState.java @@ -14,7 +14,6 @@ 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.store.FileSystemStore; import io.xpipe.core.util.JacksonMapper; import javafx.application.Platform; import javafx.collections.FXCollections; @@ -63,15 +62,15 @@ public class OpenFileSystemSavedState { } } - static OpenFileSystemSavedState loadForStore(FileSystemStore store) { + static OpenFileSystemSavedState loadForStore(OpenFileSystemModel model) { var storageEntry = DataStorage.get() - .getStoreEntryIfPresent(store) + .getStoreEntryIfPresent(model.getStore()) .map(entry -> entry.getUuid()) .orElse(UUID.randomUUID()); var state = AppCache.get("fs-state-" + storageEntry, OpenFileSystemSavedState.class, () -> { return new OpenFileSystemSavedState(); }); - state.store = store; + state.setModel(model); return state; } @@ -84,7 +83,8 @@ public class OpenFileSystemSavedState { Instant time; } - private FileSystemStore store; + @Setter + private OpenFileSystemModel model; private String lastDirectory; @NonNull private ObservableList recentDirectories; @@ -103,11 +103,11 @@ public class OpenFileSystemSavedState { } public void save() { - if (store == null) { + if (model == null) { return; } - var storageEntry = DataStorage.get().getStoreEntryIfPresent(store); + var storageEntry = DataStorage.get().getStoreEntryIfPresent(model.getStore()); storageEntry.ifPresent(entry -> AppCache.update("fs-state-" + entry.getUuid(), this)); } @@ -123,6 +123,10 @@ public class OpenFileSystemSavedState { public void run() { // Synchronize with platform thread Platform.runLater(() -> { + if (model.isClosed()) { + return; + } + if (Objects.equals(lastDirectory, dir)) { updateRecent(dir); save(); @@ -130,7 +134,7 @@ public class OpenFileSystemSavedState { }); } }, - 20000); + 200); } private void updateRecent(String dir) { diff --git a/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java b/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java index d9a7247d6..f4a45a496 100644 --- a/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java +++ b/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java @@ -36,7 +36,12 @@ public interface LeafAction extends BrowserAction { b.setGraphic(graphic); } b.setMnemonicParsing(false); + b.setDisable(!isActive(model, selected)); + model.getCurrentPath().addListener((observable, oldValue, newValue) -> { + b.setDisable(!isActive(model, selected)); + }); + return b; } diff --git a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java index a8b47494a..5050d150c 100644 --- a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java +++ b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java @@ -5,10 +5,7 @@ import io.xpipe.app.browser.BrowserModel; import io.xpipe.app.comp.about.AboutTabComp; import io.xpipe.app.comp.base.SideMenuBarComp; import io.xpipe.app.comp.storage.store.StoreLayoutComp; -import io.xpipe.app.core.AppActionLinkDetector; -import io.xpipe.app.core.AppFont; -import io.xpipe.app.core.AppI18n; -import io.xpipe.app.core.AppProperties; +import io.xpipe.app.core.*; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; @@ -33,7 +30,7 @@ public class AppLayoutComp extends Comp> { public AppLayoutComp() { entries = createEntryList(); - selected = new SimpleObjectProperty<>(entries.get(0)); + selected = new SimpleObjectProperty<>(AppState.get().isInitialLaunch() ? entries.get(1) : entries.get(0)); shortcut(new KeyCodeCombination(KeyCode.V, KeyCombination.SHORTCUT_DOWN), structure -> { AppActionLinkDetector.detectOnPaste(); @@ -43,11 +40,11 @@ public class AppLayoutComp extends Comp> { @SneakyThrows private List createEntryList() { var l = new ArrayList<>(List.of( - new SideMenuBarComp.Entry(AppI18n.observable("connections"), "mdi2c-connection", new StoreLayoutComp()), new SideMenuBarComp.Entry( AppI18n.observable("browser"), "mdi2f-file-cabinet", new BrowserComp(BrowserModel.DEFAULT)), + new SideMenuBarComp.Entry(AppI18n.observable("connections"), "mdi2c-connection", new StoreLayoutComp()), // new SideMenuBarComp.Entry(AppI18n.observable("data"), "mdsal-dvr", new SourceCollectionLayoutComp()), new SideMenuBarComp.Entry( AppI18n.observable("settings"), "mdsmz-miscellaneous_services", new PrefsComp(this)), diff --git a/app/src/main/java/io/xpipe/app/comp/base/VBoxViewComp.java b/app/src/main/java/io/xpipe/app/comp/base/VBoxViewComp.java new file mode 100644 index 000000000..66bd134d1 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/comp/base/VBoxViewComp.java @@ -0,0 +1,75 @@ +package io.xpipe.app.comp.base; + +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.CompStructure; +import io.xpipe.app.fxcomps.SimpleCompStructure; +import io.xpipe.app.fxcomps.util.PlatformThread; +import javafx.application.Platform; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class VBoxViewComp extends Comp> { + + private final ObservableList shown; + private final ObservableList all; + private final Function> compFunction; + + public VBoxViewComp(ObservableList shown, ObservableList all, Function> compFunction) { + this.shown = PlatformThread.sync(shown); + this.all = PlatformThread.sync(all); + this.compFunction = compFunction; + } + + @Override + public CompStructure createBase() { + Map cache = new HashMap<>(); + + VBox vbox = new VBox(); + vbox.setFocusTraversable(false); + + refresh(vbox, shown, cache, false); + vbox.requestLayout(); + + shown.addListener((ListChangeListener) (c) -> { + refresh(vbox, c.getList(), cache, true); + }); + + all.addListener((ListChangeListener) c -> { + cache.keySet().retainAll(c.getList()); + }); + + return new SimpleCompStructure<>(vbox); + } + + private void refresh(VBox listView, List c, Map cache, boolean asynchronous) { + Runnable update = () -> { + var newShown = c.stream() + .map(v -> { + if (!cache.containsKey(v)) { + cache.put(v, compFunction.apply(v).createRegion()); + } + + return cache.get(v); + }) + .toList(); + + if (!listView.getChildren().equals(newShown)) { + listView.getChildren().setAll(newShown); + listView.layout(); + } + }; + + if (asynchronous) { + Platform.runLater(update); + } else { + PlatformThread.runLaterIfNeeded(update); + } + } +} diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListHeaderComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListHeaderComp.java index a512abed5..d592b5168 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListHeaderComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryListHeaderComp.java @@ -32,8 +32,8 @@ public class StoreEntryListHeaderComp extends SimpleComp { } private Region createGroupListFilter() { - var filledHerProperty = new SimpleStringProperty(); - filledHerProperty.addListener((observable, oldValue, newValue) -> { + var filterProperty = new SimpleStringProperty(); + filterProperty.addListener((observable, oldValue, newValue) -> { ThreadHelper.runAsync(() -> { StoreViewState.get().getFilter().filterProperty().setValue(newValue); }); diff --git a/app/src/main/java/io/xpipe/app/core/AppLogs.java b/app/src/main/java/io/xpipe/app/core/AppLogs.java index 3ba589961..3857dc270 100644 --- a/app/src/main/java/io/xpipe/app/core/AppLogs.java +++ b/app/src/main/java/io/xpipe/app/core/AppLogs.java @@ -22,6 +22,7 @@ import java.nio.file.Path; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -84,7 +85,15 @@ public class AppLogs { var logDir = AppProperties.get().getDataDir().resolve("logs"); var shouldLogToFile = shouldWriteLogs(); - Path usedLogsDir = logDir.resolve(FORMATTER.format(Instant.now())); + var now = Instant.now(); + var name = FORMATTER.format(now); + Path usedLogsDir = logDir.resolve(name); + + // When two instances are being launched within the same second, add milliseconds + if (Files.exists(usedLogsDir)) { + usedLogsDir = logDir.resolve(name + "_" + now.get(ChronoField.MILLI_OF_SECOND)); + } + if (shouldLogToFile) { try { Files.createDirectories(usedLogsDir); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java index 8ef3215ff..93d0bf6a6 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/FilterComp.java @@ -28,6 +28,7 @@ public class FilterComp extends Comp { var bgLabel = new Label("Search ...", fi); bgLabel.getStyleClass().add("background"); var filter = new TextField(); + filter.setAccessibleText("Filter"); SimpleChangeListener.apply(filterText, val -> { PlatformThread.runLaterIfNeeded(() -> filter.setText(val)); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java b/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java index 9f493350a..b0a0129c7 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/util/Shortcuts.java @@ -46,7 +46,7 @@ public class Shortcuts { if (s != null) { scene.set(s); - SHORTCUTS.put(region, comb); + s.addEventHandler(KeyEvent.KEY_PRESSED, filter); } }); } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/browser.css b/app/src/main/resources/io/xpipe/app/resources/style/browser.css index 9add5c24f..56371befd 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/browser.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/browser.css @@ -105,9 +105,6 @@ .browser .path-text:invisible { -fx-text-fill: transparent; } -.browser .path-graphic-button { --fx-padding: 0 5px 0 5px; -} .browser .overview-file-list { -fx-border-width: 1px; diff --git a/core/src/main/java/io/xpipe/core/process/OsType.java b/core/src/main/java/io/xpipe/core/process/OsType.java index d88a80fa4..30749b30d 100644 --- a/core/src/main/java/io/xpipe/core/process/OsType.java +++ b/core/src/main/java/io/xpipe/core/process/OsType.java @@ -53,7 +53,11 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO @Override public List determineInterestingPaths(ShellControl pc) throws Exception { var home = getHomeDirectory(pc); - return List.of(home, FileNames.join(home, "Documents"), FileNames.join(home, "Downloads"), FileNames.join(home, "Desktop")); + return List.of( + home, + FileNames.join(home, "Documents"), + FileNames.join(home, "Downloads"), + FileNames.join(home, "Desktop")); } @Override @@ -111,7 +115,8 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO @Override public List determineInterestingPaths(ShellControl pc) throws Exception { var home = getHomeDirectory(pc); - return List.of(FileNames.join(home, "Desktop")); + return List.of( + home, FileNames.join(home, "Downloads"), FileNames.join(home, "Documents"), "/etc", "/tmp", "/var"); } @Override @@ -180,7 +185,16 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO @Override public List determineInterestingPaths(ShellControl pc) throws Exception { var home = getHomeDirectory(pc); - return List.of(FileNames.join(home, "Desktop")); + return List.of( + home, + FileNames.join(home, "Downloads"), + FileNames.join(home, "Documents"), + FileNames.join(home, "Desktop"), + "/Applications", + "/Library", + "/System", + "/etc" + ); } @Override diff --git a/core/src/main/java/io/xpipe/core/process/ProcessOutputException.java b/core/src/main/java/io/xpipe/core/process/ProcessOutputException.java index 7ab9a643a..33efb1e9c 100644 --- a/core/src/main/java/io/xpipe/core/process/ProcessOutputException.java +++ b/core/src/main/java/io/xpipe/core/process/ProcessOutputException.java @@ -6,35 +6,40 @@ import lombok.Getter; public class ProcessOutputException extends Exception { public static ProcessOutputException of(String customPrefix, ProcessOutputException ex) { - var messageSuffix = ex.getOutput() != null && ! ex.getOutput().isBlank()?": " + ex.getOutput() : ""; + var messageSuffix = ex.getOutput() != null && !ex.getOutput().isBlank() ? ": " + ex.getOutput() : ""; var message = customPrefix + messageSuffix; return new ProcessOutputException(message, ex.getExitCode(), ex.getOutput()); } public static ProcessOutputException of(int exitCode, String output, String accumulatedError) { - var combinedError = (accumulatedError != null ? accumulatedError.trim() + "\n" : "") + (output != null ? output.trim() : ""); - var message = switch (exitCode) { - case CommandControl.KILLED_EXIT_CODE -> "Process timed out (exit code " + exitCode + ") " + combinedError; - case CommandControl.TIMEOUT_EXIT_CODE -> "Process timed out (exit code " + exitCode + ") " + combinedError; - default -> "Process returned with exit code " + exitCode + ": " + combinedError; - }; + var combinedError = (accumulatedError != null ? accumulatedError.trim() + "\n" : "") + + (output != null ? output.trim() : ""); + var hasMessage = !combinedError.trim().isEmpty(); + var errorSuffix = hasMessage ? ": " + combinedError : ""; + var message = + switch (exitCode) { + case CommandControl.KILLED_EXIT_CODE -> "Process timed out and had to be killed" + errorSuffix; + case CommandControl.TIMEOUT_EXIT_CODE -> "Wait for process exit timed out" + + errorSuffix; + default -> "Process returned exit code " + exitCode + errorSuffix; + }; return new ProcessOutputException(message, exitCode, combinedError); } private final int exitCode; private final String output; - private ProcessOutputException(String message, int exitCode, String output) { + private ProcessOutputException(String message, int exitCode, String output) { super(message); this.exitCode = exitCode; this.output = output; } public boolean isTimeOut() { - return exitCode == CommandControl.TIMEOUT_EXIT_CODE; + return exitCode == CommandControl.TIMEOUT_EXIT_CODE; } public boolean isKill() { - return exitCode == CommandControl.KILLED_EXIT_CODE; + return exitCode == CommandControl.KILLED_EXIT_CODE; } } diff --git a/dist/changelogs/1.0.2.md b/dist/changelogs/1.0.2.md new file mode 100644 index 000000000..ede8dfcee --- /dev/null +++ b/dist/changelogs/1.0.2.md @@ -0,0 +1,5 @@ +## Changes in 1.0.2 + +- Add new overview page for file browser sessions +- Fix file IO being corrupted and freezing on Windows systems with global UTF8 enabled +- Many small miscellaneous fixes and improvements \ No newline at end of file diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/BackAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/BackAction.java new file mode 100644 index 000000000..ba3fbcd93 --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/BackAction.java @@ -0,0 +1,54 @@ +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 javafx.scene.Node; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.util.List; + +public class BackAction implements LeafAction { + + public String getId() { + return "back"; + } + + @Override + public void execute(OpenFileSystemModel model, List entries) throws Exception { + model.backSync(); + } + + @Override + public Category getCategory() { + return null; + } + + @Override + public Node getIcon(OpenFileSystemModel model, List entries) { + return new FontIcon("fth-arrow-left"); + } + + @Override + public boolean isApplicable(OpenFileSystemModel model, List entries) { + return false; + } + + @Override + public boolean isActive(OpenFileSystemModel model, List entries) { + return model.getHistory().canGoBackProperty().get(); + } + + @Override + public KeyCombination getShortcut() { + return new KeyCodeCombination(KeyCode.LEFT, KeyCombination.ALT_DOWN); + } + + @Override + public String getName(OpenFileSystemModel model, List entries) { + return "Back"; + } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/ForwardAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/ForwardAction.java new file mode 100644 index 000000000..a013a1959 --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/ForwardAction.java @@ -0,0 +1,54 @@ +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 javafx.scene.Node; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.util.List; + +public class ForwardAction implements LeafAction { + + public String getId() { + return "forward"; + } + + @Override + public void execute(OpenFileSystemModel model, List entries) throws Exception { + model.forthSync(); + } + + @Override + public Category getCategory() { + return null; + } + + @Override + public Node getIcon(OpenFileSystemModel model, List entries) { + return new FontIcon("fth-arrow-right"); + } + + @Override + public boolean isApplicable(OpenFileSystemModel model, List entries) { + return false; + } + + @Override + public boolean isActive(OpenFileSystemModel model, List entries) { + return model.getHistory().canGoForthProperty().get(); + } + + @Override + public KeyCombination getShortcut() { + return new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.ALT_DOWN); + } + + @Override + public String getName(OpenFileSystemModel model, List entries) { + return "Forward"; + } +} 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 ac1245213..7ee2847fc 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 @@ -29,6 +29,11 @@ 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/io/xpipe/ext/base/browser/RefreshAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/RefreshAction.java new file mode 100644 index 000000000..cf3e8a9bd --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/RefreshAction.java @@ -0,0 +1,55 @@ +package io.xpipe.ext.base.browser; + +import io.xpipe.app.browser.BrowserEntry; +import io.xpipe.app.browser.OpenFileSystemModel; +import io.xpipe.app.browser.action.BrowserAction; +import io.xpipe.app.browser.action.LeafAction; +import javafx.scene.Node; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.util.List; + +public class RefreshAction implements LeafAction { + + public String getId() { + return "refresh"; + } + + @Override + public void execute(OpenFileSystemModel model, List entries) throws Exception { + model.refreshSync(); + } + + @Override + public BrowserAction.Category getCategory() { + return null; + } + + @Override + public boolean isActive(OpenFileSystemModel model, List entries) { + return !model.getInOverview().get(); + } + + @Override + public Node getIcon(OpenFileSystemModel model, List entries) { + return new FontIcon("mdmz-refresh"); + } + + @Override + public boolean isApplicable(OpenFileSystemModel model, List entries) { + return false; + } + + @Override + public KeyCombination getShortcut() { + return new KeyCodeCombination(KeyCode.F5); + } + + @Override + public String getName(OpenFileSystemModel model, List entries) { + return "Refresh"; + } +} diff --git a/ext/base/src/main/java/module-info.java b/ext/base/src/main/java/module-info.java index d46862bc0..85cea3e3a 100644 --- a/ext/base/src/main/java/module-info.java +++ b/ext/base/src/main/java/module-info.java @@ -27,6 +27,9 @@ open module io.xpipe.ext.base { requires com.sun.jna.platform; provides BrowserAction with + BackAction, + ForwardAction, + RefreshAction, OpenFileDefaultAction, OpenFileWithAction, OpenDirectoryAction, diff --git a/version b/version index 7f207341d..e6d5cb833 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.0.1 \ No newline at end of file +1.0.2 \ No newline at end of file