Various fixes [stage]

This commit is contained in:
crschnick 2023-05-30 01:59:08 +00:00
parent 52cdcfa0aa
commit daa011ffe6
24 changed files with 380 additions and 92 deletions

View file

@ -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<FileSystem.FileEntry> list;
boolean grow;
@Override
protected Region createSimple() {
var c = new ListBoxViewComp<>(list, list, entry -> {
Function<FileSystem.FileEntry, Comp<?>> 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();
}
}
}

View file

@ -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(

View file

@ -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();

View file

@ -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<>(

View file

@ -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<String> history = new ArrayList<>();

View file

@ -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<ShellControl, Exception> 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());
}
}

View file

@ -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<RecentEntry> 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) {

View file

@ -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;
}

View file

@ -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<CompStructure<BorderPane>> {
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<CompStructure<BorderPane>> {
@SneakyThrows
private List<SideMenuBarComp.Entry> 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)),

View file

@ -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<T> extends Comp<CompStructure<VBox>> {
private final ObservableList<T> shown;
private final ObservableList<T> all;
private final Function<T, Comp<?>> compFunction;
public VBoxViewComp(ObservableList<T> shown, ObservableList<T> all, Function<T, Comp<?>> compFunction) {
this.shown = PlatformThread.sync(shown);
this.all = PlatformThread.sync(all);
this.compFunction = compFunction;
}
@Override
public CompStructure<VBox> createBase() {
Map<T, Region> cache = new HashMap<>();
VBox vbox = new VBox();
vbox.setFocusTraversable(false);
refresh(vbox, shown, cache, false);
vbox.requestLayout();
shown.addListener((ListChangeListener<? super T>) (c) -> {
refresh(vbox, c.getList(), cache, true);
});
all.addListener((ListChangeListener<? super T>) c -> {
cache.keySet().retainAll(c.getList());
});
return new SimpleCompStructure<>(vbox);
}
private void refresh(VBox listView, List<? extends T> c, Map<T, Region> 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);
}
}
}

View file

@ -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);
});

View file

@ -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);

View file

@ -28,6 +28,7 @@ public class FilterComp extends Comp<FilterComp.Structure> {
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));

View file

@ -46,7 +46,7 @@ public class Shortcuts {
if (s != null) {
scene.set(s);
SHORTCUTS.put(region, comb);
s.addEventHandler(KeyEvent.KEY_PRESSED, filter);
}
});
}

View file

@ -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;

View file

@ -53,7 +53,11 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
@Override
public List<String> 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<String> 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<String> 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

View file

@ -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;
}
}

5
dist/changelogs/1.0.2.md vendored Normal file
View file

@ -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

View file

@ -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<BrowserEntry> entries) throws Exception {
model.backSync();
}
@Override
public Category getCategory() {
return null;
}
@Override
public Node getIcon(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new FontIcon("fth-arrow-left");
}
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return false;
}
@Override
public boolean isActive(OpenFileSystemModel model, List<BrowserEntry> 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<BrowserEntry> entries) {
return "Back";
}
}

View file

@ -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<BrowserEntry> entries) throws Exception {
model.forthSync();
}
@Override
public Category getCategory() {
return null;
}
@Override
public Node getIcon(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new FontIcon("fth-arrow-right");
}
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return false;
}
@Override
public boolean isActive(OpenFileSystemModel model, List<BrowserEntry> 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<BrowserEntry> entries) {
return "Forward";
}
}

View file

@ -29,6 +29,11 @@ public class OpenTerminalAction implements LeafAction {
}
}
@Override
public boolean isActive(OpenFileSystemModel model, List<BrowserEntry> entries) {
return !model.getInOverview().get();
}
@Override
public Category getCategory() {
return Category.OPEN;

View file

@ -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<BrowserEntry> entries) throws Exception {
model.refreshSync();
}
@Override
public BrowserAction.Category getCategory() {
return null;
}
@Override
public boolean isActive(OpenFileSystemModel model, List<BrowserEntry> entries) {
return !model.getInOverview().get();
}
@Override
public Node getIcon(OpenFileSystemModel model, List<BrowserEntry> entries) {
return new FontIcon("mdmz-refresh");
}
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return false;
}
@Override
public KeyCombination getShortcut() {
return new KeyCodeCombination(KeyCode.F5);
}
@Override
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
return "Refresh";
}
}

View file

@ -27,6 +27,9 @@ open module io.xpipe.ext.base {
requires com.sun.jna.platform;
provides BrowserAction with
BackAction,
ForwardAction,
RefreshAction,
OpenFileDefaultAction,
OpenFileWithAction,
OpenDirectoryAction,

View file

@ -1 +1 @@
1.0.1
1.0.2