mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-26 01:20:28 +00:00
File browser overview page improvements
This commit is contained in:
parent
40a5c6a306
commit
52cdcfa0aa
21 changed files with 367 additions and 113 deletions
|
@ -0,0 +1,41 @@
|
||||||
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
|
import io.xpipe.app.browser.icon.BrowserIcons;
|
||||||
|
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||||
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
|
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||||
|
import io.xpipe.core.store.FileSystem;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class BrowserFileOverviewComp extends SimpleComp {
|
||||||
|
|
||||||
|
OpenFileSystemModel model;
|
||||||
|
ObservableList<FileSystem.FileEntry> list;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Region createSimple() {
|
||||||
|
var c = new ListBoxViewComp<>(list, list, entry -> {
|
||||||
|
return Comp.of(() -> {
|
||||||
|
var icon = BrowserIcons.createIcon(entry);
|
||||||
|
var l = new Button(entry.getPath(), icon.createRegion());
|
||||||
|
l.setOnAction(event -> {
|
||||||
|
model.cd(entry.getPath());
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
l.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
GrowAugment.create(true,false).augment(l);
|
||||||
|
return l;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.styleClass("overview-file-list");
|
||||||
|
return c.createRegion();
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,6 +48,10 @@ public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
||||||
var fi = new FontIcon("mdi2m-magnify");
|
var fi = new FontIcon("mdi2m-magnify");
|
||||||
button.setGraphic(fi);
|
button.setGraphic(fi);
|
||||||
button.setOnAction(event -> {
|
button.setOnAction(event -> {
|
||||||
|
if (model.getCurrentDirectory() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (expanded.get()) {
|
if (expanded.get()) {
|
||||||
if (filterString.getValue() == null) {
|
if (filterString.getValue() == null) {
|
||||||
expanded.set(false);
|
expanded.set(false);
|
||||||
|
@ -62,6 +66,7 @@ public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
||||||
|
|
||||||
text.setPrefWidth(0);
|
text.setPrefWidth(0);
|
||||||
button.getStyleClass().add(Styles.FLAT);
|
button.getStyleClass().add(Styles.FLAT);
|
||||||
|
button.disableProperty().bind(model.getInOverview());
|
||||||
expanded.addListener((observable, oldValue, val) -> {
|
expanded.addListener((observable, oldValue, val) -> {
|
||||||
if (val) {
|
if (val) {
|
||||||
text.setPrefWidth(250);
|
text.setPrefWidth(250);
|
||||||
|
@ -88,9 +93,11 @@ public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final OpenFileSystemModel model;
|
||||||
private final Property<String> filterString;
|
private final Property<String> filterString;
|
||||||
|
|
||||||
public BrowserFilterComp(Property<String> filterString) {
|
public BrowserFilterComp(OpenFileSystemModel model, Property<String> filterString) {
|
||||||
|
this.model = model;
|
||||||
this.filterString = filterString;
|
this.filterString = filterString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
@Override
|
@Override
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
var path = new SimpleStringProperty(model.getCurrentPath().get());
|
var path = new SimpleStringProperty(model.getCurrentPath().get());
|
||||||
model.getCurrentPath().addListener((observable, oldValue, newValue) -> {
|
SimpleChangeListener.apply(model.getCurrentPath(), (newValue) -> {
|
||||||
path.set(newValue);
|
path.set(newValue);
|
||||||
});
|
});
|
||||||
path.addListener((observable, oldValue, newValue) -> {
|
path.addListener((observable, oldValue, newValue) -> {
|
||||||
|
@ -49,7 +49,11 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
.styleClass("path-text")
|
.styleClass("path-text")
|
||||||
.apply(struc -> {
|
.apply(struc -> {
|
||||||
SimpleChangeListener.apply(struc.get().focusedProperty(), val -> {
|
SimpleChangeListener.apply(struc.get().focusedProperty(), val -> {
|
||||||
struc.get().pseudoClassStateChanged(INVISIBLE, !val);
|
struc.get().pseudoClassStateChanged(INVISIBLE, !val && !model.getInOverview().get());
|
||||||
|
});
|
||||||
|
|
||||||
|
SimpleChangeListener.apply(model.getInOverview(), val -> {
|
||||||
|
struc.get().pseudoClassStateChanged(INVISIBLE, !val && !struc.get().isFocused());
|
||||||
});
|
});
|
||||||
|
|
||||||
struc.get().setOnMouseClicked(event -> {
|
struc.get().setOnMouseClicked(event -> {
|
||||||
|
@ -61,13 +65,15 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
struc.get().selectAll();
|
struc.get().selectAll();
|
||||||
struc.get().requestFocus();
|
struc.get().requestFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
struc.get().setPromptText("Overview of " + model.getName());
|
||||||
});
|
});
|
||||||
|
|
||||||
var graphic = Bindings.createStringBinding(
|
var graphic = Bindings.createStringBinding(
|
||||||
() -> {
|
() -> {
|
||||||
var icon = model.getCurrentDirectory() != null
|
var icon = model.getCurrentDirectory() != null
|
||||||
? FileIconManager.getFileIcon(model.getCurrentDirectory(), false)
|
? FileIconManager.getFileIcon(model.getCurrentDirectory(), false)
|
||||||
: null;
|
: "home_icon.png";
|
||||||
return icon;
|
return icon;
|
||||||
},
|
},
|
||||||
model.getCurrentPath());
|
model.getCurrentPath());
|
||||||
|
@ -80,7 +86,9 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
graphicButton.getStyleClass().add(Styles.LEFT_PILL);
|
graphicButton.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
graphicButton.getStyleClass().add("path-graphic-button");
|
graphicButton.getStyleClass().add("path-graphic-button");
|
||||||
new ContextMenuAugment<>(
|
new ContextMenuAugment<>(
|
||||||
event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null))
|
event -> event.getButton() == MouseButton.PRIMARY, () -> {
|
||||||
|
return model.getInOverview().get() ? null : new BrowserContextMenu(model, null);
|
||||||
|
})
|
||||||
.augment(new SimpleCompStructure<>(graphicButton));
|
.augment(new SimpleCompStructure<>(graphicButton));
|
||||||
GrowAugment.create(false, true).augment(graphicButton);
|
GrowAugment.create(false, true).augment(graphicButton);
|
||||||
|
|
||||||
|
@ -92,7 +100,9 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
.apply(struc -> {
|
.apply(struc -> {
|
||||||
var t = struc.get().getChildren().get(0);
|
var t = struc.get().getChildren().get(0);
|
||||||
var b = struc.get().getChildren().get(1);
|
var b = struc.get().getChildren().get(1);
|
||||||
b.visibleProperty().bind(t.focusedProperty().not());
|
b.visibleProperty().bind(Bindings.createBooleanBinding(() -> {
|
||||||
|
return !t.isFocused() && !model.getInOverview().get();
|
||||||
|
}, t.focusedProperty(), model.getInOverview()));
|
||||||
})
|
})
|
||||||
.grow(false, true);
|
.grow(false, true);
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,13 @@ import io.xpipe.app.comp.base.SimpleTitledPaneComp;
|
||||||
import io.xpipe.app.core.AppI18n;
|
import io.xpipe.app.core.AppI18n;
|
||||||
import io.xpipe.app.fxcomps.SimpleComp;
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||||
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.store.FileSystem;
|
import io.xpipe.core.store.FileSystem;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BrowserOverviewComp extends SimpleComp {
|
public class BrowserOverviewComp extends SimpleComp {
|
||||||
|
@ -20,16 +22,23 @@ public class BrowserOverviewComp extends SimpleComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
var commonList = new BrowserSelectionListComp(FXCollections.observableArrayList(
|
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
|
||||||
new FileSystem.FileEntry(model.getFileSystem(), "C:\\", Instant.now(), true, false, false, 0, null)));
|
var common = sc.getOsType().determineInterestingPaths(sc).stream().map(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s)).toList();
|
||||||
var common = new SimpleTitledPaneComp(AppI18n.observable("a"), commonList);
|
var commonOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(common));
|
||||||
|
var commonPane = new SimpleTitledPaneComp(AppI18n.observable("common"), commonOverview);
|
||||||
|
|
||||||
var recentList = new BrowserSelectionListComp(FXCollections.observableArrayList(
|
var roots = sc.getShellDialect().listRoots(sc).map(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s)).toList();
|
||||||
new FileSystem.FileEntry(model.getFileSystem(), "C:\\", Instant.now(), true, false, false, 0, null)));
|
var rootsOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(roots));
|
||||||
var recent = new SimpleTitledPaneComp(AppI18n.observable("Recent"), recentList);
|
var rootsPane = new SimpleTitledPaneComp(AppI18n.observable("roots"), rootsOverview);
|
||||||
|
|
||||||
var vbox = new VerticalComp(List.of(common, recent)).styleClass("home");
|
|
||||||
|
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 vbox = new VerticalComp(List.of(commonPane, rootsPane, recentPane)).styleClass("overview");
|
||||||
return vbox.createRegion();
|
return vbox.createRegion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.xpipe.app.browser;
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import io.xpipe.app.browser.action.BrowserAction;
|
||||||
import io.xpipe.app.comp.base.ModalOverlayComp;
|
import io.xpipe.app.comp.base.ModalOverlayComp;
|
||||||
import io.xpipe.app.comp.base.MultiContentComp;
|
import io.xpipe.app.comp.base.MultiContentComp;
|
||||||
import io.xpipe.app.fxcomps.Comp;
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
|
@ -9,7 +10,6 @@ import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
|
||||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
@ -44,6 +44,7 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
private Region createContent() {
|
private Region createContent() {
|
||||||
var overview = new Button(null, new FontIcon("mdi2m-monitor"));
|
var overview = new Button(null, new FontIcon("mdi2m-monitor"));
|
||||||
overview.setOnAction(e -> model.cd(null));
|
overview.setOnAction(e -> model.cd(null));
|
||||||
|
overview.disableProperty().bind(model.getInOverview());
|
||||||
|
|
||||||
var backBtn = new Button(null, new FontIcon("fth-arrow-left"));
|
var backBtn = new Button(null, new FontIcon("fth-arrow-left"));
|
||||||
backBtn.setOnAction(e -> model.back());
|
backBtn.setOnAction(e -> model.back());
|
||||||
|
@ -56,18 +57,20 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
var refreshBtn = new Button(null, new FontIcon("mdmz-refresh"));
|
var refreshBtn = new Button(null, new FontIcon("mdmz-refresh"));
|
||||||
refreshBtn.setOnAction(e -> model.refresh());
|
refreshBtn.setOnAction(e -> model.refresh());
|
||||||
Shortcuts.addShortcut(refreshBtn, new KeyCodeCombination(KeyCode.F5));
|
Shortcuts.addShortcut(refreshBtn, new KeyCodeCombination(KeyCode.F5));
|
||||||
|
refreshBtn.disableProperty().bind(model.getInOverview());
|
||||||
|
|
||||||
var terminalBtn = new Button(null, new FontIcon("mdi2c-code-greater-than"));
|
var terminalBtn = BrowserAction.byId("openTerminal").toButton(model, List.of());
|
||||||
terminalBtn.setOnAction(
|
terminalBtn.setOnAction(
|
||||||
e -> model.openTerminalAsync(model.getCurrentPath().get()));
|
e -> model.openTerminalAsync(model.getCurrentPath().get()));
|
||||||
terminalBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
|
terminalBtn.disableProperty().bind(model.getInOverview());
|
||||||
|
|
||||||
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
|
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
|
||||||
new ContextMenuAugment<>(
|
new ContextMenuAugment<>(
|
||||||
event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null))
|
event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null))
|
||||||
.augment(new SimpleCompStructure<>(menuButton));
|
.augment(new SimpleCompStructure<>(menuButton));
|
||||||
|
menuButton.disableProperty().bind(model.getInOverview());
|
||||||
|
|
||||||
var filter = new BrowserFilterComp(model.getFilter()).createStructure();
|
var filter = new BrowserFilterComp(model, model.getFilter()).createStructure();
|
||||||
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
||||||
|
|
||||||
var topBar = new ToolBar();
|
var topBar = new ToolBar();
|
||||||
|
|
|
@ -8,11 +8,10 @@ import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
final class BrowserHistory {
|
final class OpenFileSystemHistory {
|
||||||
|
|
||||||
private final IntegerProperty cursor = new SimpleIntegerProperty(0);
|
private final IntegerProperty cursor = new SimpleIntegerProperty(-1);
|
||||||
private final List<String> history = new ArrayList<>();
|
private final List<String> history = new ArrayList<>();
|
||||||
private final BooleanBinding canGoBack = Bindings.createBooleanBinding(
|
private final BooleanBinding canGoBack = Bindings.createBooleanBinding(
|
||||||
() -> cursor.get() > 0 && history.size() > 1, cursor);
|
() -> cursor.get() > 0 && history.size() > 1, cursor);
|
||||||
|
@ -24,35 +23,33 @@ final class BrowserHistory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCurrent(String s) {
|
public void updateCurrent(String s) {
|
||||||
if (s == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var lastString = getCurrent();
|
var lastString = getCurrent();
|
||||||
if (Objects.equals(lastString, s)) {
|
if (cursor.get() != -1 && Objects.equals(lastString, s)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canGoForth.get()) {
|
if (canGoForth.get()) {
|
||||||
history.subList(cursor.get() + 1, history.size()).clear();
|
history.subList(cursor.get() + 1, history.size()).clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
history.add(s);
|
history.add(s);
|
||||||
cursor.set(history.size() - 1);
|
cursor.set(history.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> back() {
|
public String back() {
|
||||||
if (!canGoBack.get()) {
|
if (!canGoBack.get()) {
|
||||||
return Optional.empty();
|
return null;
|
||||||
}
|
}
|
||||||
cursor.set(cursor.get() - 1);
|
cursor.set(cursor.get() - 1);
|
||||||
return Optional.of(history.get(cursor.get()));
|
return history.get(cursor.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> forth() {
|
public String forth() {
|
||||||
if (!canGoForth.get()) {
|
if (!canGoForth.get()) {
|
||||||
return Optional.empty();
|
return null;
|
||||||
}
|
}
|
||||||
cursor.set(cursor.get() + 1);
|
cursor.set(cursor.get() + 1);
|
||||||
return Optional.of(history.get(cursor.get()));
|
return history.get(cursor.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BooleanBinding canGoBackProperty() {
|
public BooleanBinding canGoBackProperty() {
|
|
@ -1,7 +1,6 @@
|
||||||
package io.xpipe.app.browser;
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
import io.xpipe.app.comp.base.ModalOverlayComp;
|
import io.xpipe.app.comp.base.ModalOverlayComp;
|
||||||
import io.xpipe.app.core.AppCache;
|
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.storage.DataStorage;
|
import io.xpipe.app.storage.DataStorage;
|
||||||
import io.xpipe.app.util.BusyProperty;
|
import io.xpipe.app.util.BusyProperty;
|
||||||
|
@ -15,6 +14,7 @@ import io.xpipe.core.store.ConnectionFileSystem;
|
||||||
import io.xpipe.core.store.FileSystem;
|
import io.xpipe.core.store.FileSystem;
|
||||||
import io.xpipe.core.store.FileSystemStore;
|
import io.xpipe.core.store.FileSystemStore;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
@ -26,7 +26,6 @@ import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -37,13 +36,13 @@ public final class OpenFileSystemModel {
|
||||||
private final Property<String> filter = new SimpleStringProperty();
|
private final Property<String> filter = new SimpleStringProperty();
|
||||||
private final BrowserFileListModel fileList;
|
private final BrowserFileListModel fileList;
|
||||||
private final ReadOnlyObjectWrapper<String> currentPath = new ReadOnlyObjectWrapper<>();
|
private final ReadOnlyObjectWrapper<String> currentPath = new ReadOnlyObjectWrapper<>();
|
||||||
private final BrowserHistory history = new BrowserHistory();
|
private final OpenFileSystemHistory history = new OpenFileSystemHistory();
|
||||||
private final BooleanProperty busy = new SimpleBooleanProperty();
|
private final BooleanProperty busy = new SimpleBooleanProperty();
|
||||||
private final BrowserModel browserModel;
|
private final BrowserModel browserModel;
|
||||||
private final BooleanProperty noDirectory = new SimpleBooleanProperty();
|
private OpenFileSystemSavedState savedState;
|
||||||
private final Property<OpenFileSystemSavedState> savedState = new SimpleObjectProperty<>();
|
|
||||||
private final OpenFileSystemCache cache = new OpenFileSystemCache(this);
|
private final OpenFileSystemCache cache = new OpenFileSystemCache(this);
|
||||||
private final Property<ModalOverlayComp.OverlayContent> overlay = new SimpleObjectProperty<>();
|
private final Property<ModalOverlayComp.OverlayContent> overlay = new SimpleObjectProperty<>();
|
||||||
|
private final BooleanProperty inOverview = new SimpleBooleanProperty();
|
||||||
private final String name;
|
private final String name;
|
||||||
private boolean local;
|
private boolean local;
|
||||||
|
|
||||||
|
@ -51,6 +50,9 @@ public final class OpenFileSystemModel {
|
||||||
this.browserModel = browserModel;
|
this.browserModel = browserModel;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.name = name != null ? name : DataStorage.get().getStoreEntry(store).getName();
|
this.name = name != null ? name : DataStorage.get().getStoreEntry(store).getName();
|
||||||
|
this.inOverview.bind(Bindings.createBooleanBinding(() -> {
|
||||||
|
return currentPath.get() == null;
|
||||||
|
}, currentPath));
|
||||||
fileList = new BrowserFileListModel(this);
|
fileList = new BrowserFileListModel(this);
|
||||||
addListeners();
|
addListeners();
|
||||||
}
|
}
|
||||||
|
@ -73,18 +75,14 @@ public final class OpenFileSystemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addListeners() {
|
private void addListeners() {
|
||||||
savedState.addListener((observable, oldValue, newValue) -> {
|
// savedState.addListener((observable, oldValue, newValue) -> {
|
||||||
if (store == null) {
|
// if (store == null) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
var storageEntry = DataStorage.get().getStoreEntryIfPresent(store);
|
// var storageEntry = DataStorage.get().getStoreEntryIfPresent(store);
|
||||||
storageEntry.ifPresent(entry -> AppCache.update("browser-state-" + entry.getUuid(), newValue));
|
// storageEntry.ifPresent(entry -> AppCache.update("browser-state-" + entry.getUuid(), newValue));
|
||||||
});
|
// });
|
||||||
|
|
||||||
currentPath.addListener((observable, oldValue, newValue) -> {
|
|
||||||
savedState.setValue(savedState.getValue().withLastDirectory(newValue));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
|
@ -132,12 +130,15 @@ public final class OpenFileSystemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle commands typed into navigation bar
|
// Handle commands typed into navigation bar
|
||||||
if (normalizedPath != null && !FileNames.isAbsolute(normalizedPath) && fileSystem.getShell().isPresent()) {
|
if (normalizedPath != null
|
||||||
|
&& !FileNames.isAbsolute(normalizedPath)
|
||||||
|
&& fileSystem.getShell().isPresent()) {
|
||||||
var directory = currentPath.get();
|
var directory = currentPath.get();
|
||||||
var name = normalizedPath + " - "
|
var name = normalizedPath + " - "
|
||||||
+ XPipeDaemon.getInstance().getStoreName(store).orElse("?");
|
+ XPipeDaemon.getInstance().getStoreName(store).orElse("?");
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
if (ShellDialects.ALL.stream().anyMatch(dialect -> normalizedPath.startsWith(dialect.getOpenCommand()))) {
|
if (ShellDialects.ALL.stream()
|
||||||
|
.anyMatch(dialect -> normalizedPath.startsWith(dialect.getOpenCommand()))) {
|
||||||
var cmd = fileSystem
|
var cmd = fileSystem
|
||||||
.getShell()
|
.getShell()
|
||||||
.get()
|
.get()
|
||||||
|
@ -167,7 +168,7 @@ public final class OpenFileSystemModel {
|
||||||
dirPath = FileSystemHelper.validateDirectoryPath(this, normalizedPath);
|
dirPath = FileSystemHelper.validateDirectoryPath(this, normalizedPath);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ErrorEvent.fromThrowable(ex).handle();
|
ErrorEvent.fromThrowable(ex).handle();
|
||||||
return Optional.of(currentPath.get());
|
return Optional.ofNullable(currentPath.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Objects.equals(path, dirPath)) {
|
if (!Objects.equals(path, dirPath)) {
|
||||||
|
@ -194,7 +195,7 @@ public final class OpenFileSystemModel {
|
||||||
|
|
||||||
filter.setValue(null);
|
filter.setValue(null);
|
||||||
currentPath.set(path);
|
currentPath.set(path);
|
||||||
savedState.setValue(savedState.getValue().withLastDirectory(path));
|
savedState.cd(path);
|
||||||
history.updateCurrent(path);
|
history.updateCurrent(path);
|
||||||
loadFilesSync(path);
|
loadFilesSync(path);
|
||||||
}
|
}
|
||||||
|
@ -203,13 +204,11 @@ public final class OpenFileSystemModel {
|
||||||
try {
|
try {
|
||||||
if (dir != null) {
|
if (dir != null) {
|
||||||
var stream = getFileSystem().listFiles(dir);
|
var stream = getFileSystem().listFiles(dir);
|
||||||
noDirectory.set(false);
|
|
||||||
fileList.setAll(stream);
|
fileList.setAll(stream);
|
||||||
} else {
|
} else {
|
||||||
var stream = getFileSystem().listRoots().stream()
|
var stream = getFileSystem().listRoots().stream()
|
||||||
.map(s -> new FileSystem.FileEntry(
|
.map(s -> new FileSystem.FileEntry(
|
||||||
getFileSystem(), s, Instant.now(), true, false, false, 0, null));
|
getFileSystem(), s, Instant.now(), true, false, false, 0, null));
|
||||||
noDirectory.set(true);
|
|
||||||
fileList.setAll(stream);
|
fileList.setAll(stream);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -302,23 +301,6 @@ public final class OpenFileSystemModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteSelectionAsync() {
|
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
|
||||||
BusyProperty.execute(busy, () -> {
|
|
||||||
if (fileSystem == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BrowserAlerts.showDeleteAlert(fileList.getSelectedRaw())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemHelper.delete(fileList.getSelectedRaw());
|
|
||||||
refreshSync();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeSync() {
|
void closeSync() {
|
||||||
if (fileSystem == null) {
|
if (fileSystem == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -337,38 +319,25 @@ public final class OpenFileSystemModel {
|
||||||
var fs = store.createFileSystem();
|
var fs = store.createFileSystem();
|
||||||
fs.open();
|
fs.open();
|
||||||
this.fileSystem = fs;
|
this.fileSystem = fs;
|
||||||
this.local = fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false);
|
this.local =
|
||||||
|
fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false);
|
||||||
this.cache.init();
|
this.cache.init();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initWithGivenDirectory(String dir) throws Exception {
|
public void initWithGivenDirectory(String dir) throws Exception {
|
||||||
initSavedState(dir);
|
initState();
|
||||||
cdSyncWithoutCheck(dir);
|
cdSyncWithoutCheck(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initWithDefaultDirectory() throws Exception {
|
public void initWithDefaultDirectory() throws Exception {
|
||||||
var dir = FileSystemHelper.getStartDirectory(this);
|
initState();
|
||||||
initSavedState(dir);
|
savedState.cd(null);
|
||||||
cdSyncWithoutCheck(dir);
|
history.updateCurrent(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSavedState(String path) {
|
private void initState() {
|
||||||
var storageEntry = DataStorage.get()
|
this.savedState = OpenFileSystemSavedState.loadForStore(store);
|
||||||
.getStoreEntryIfPresent(store)
|
|
||||||
.map(entry -> entry.getUuid())
|
|
||||||
.orElse(UUID.randomUUID());
|
|
||||||
this.savedState.setValue(
|
|
||||||
AppCache.get("browser-state-" + storageEntry, OpenFileSystemSavedState.class, () -> {
|
|
||||||
try {
|
|
||||||
return OpenFileSystemSavedState.builder()
|
|
||||||
.lastDirectory(path)
|
|
||||||
.build();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorEvent.fromThrowable(e).handle();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openTerminalAsync(String directory) {
|
public void openTerminalAsync(String directory) {
|
||||||
|
@ -392,19 +361,19 @@ public final class OpenFileSystemModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public BrowserHistory getHistory() {
|
public OpenFileSystemHistory getHistory() {
|
||||||
return history;
|
return history;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void back() {
|
public void back() {
|
||||||
try (var ignored = new BusyProperty(busy)) {
|
try (var ignored = new BusyProperty(busy)) {
|
||||||
history.back().ifPresent(s -> cd(s));
|
cd(history.back());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forth() {
|
public void forth() {
|
||||||
try (var ignored = new BusyProperty(busy)) {
|
try (var ignored = new BusyProperty(busy)) {
|
||||||
history.forth().ifPresent(s -> cd(s));
|
cd(history.forth());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,147 @@
|
||||||
package io.xpipe.app.browser;
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
import lombok.Builder;
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
import lombok.Value;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import lombok.With;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JavaType;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||||
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
|
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;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import lombok.*;
|
||||||
import lombok.extern.jackson.Jacksonized;
|
import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
@Value
|
import java.io.IOException;
|
||||||
@With
|
import java.time.Instant;
|
||||||
@Jacksonized
|
import java.util.*;
|
||||||
@Builder
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@JsonSerialize(using = OpenFileSystemSavedState.Serializer.class)
|
||||||
|
@JsonDeserialize(using = OpenFileSystemSavedState.Deserializer.class)
|
||||||
public class OpenFileSystemSavedState {
|
public class OpenFileSystemSavedState {
|
||||||
|
|
||||||
String lastDirectory;
|
public static class Serializer extends StdSerializer<OpenFileSystemSavedState> {
|
||||||
|
|
||||||
|
protected Serializer() {
|
||||||
|
super(OpenFileSystemSavedState.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Deserializer extends StdDeserializer<OpenFileSystemSavedState> {
|
||||||
|
|
||||||
|
protected Deserializer() {
|
||||||
|
super(OpenFileSystemSavedState.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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<RecentEntry> recentDirectories = JacksonMapper.getDefault().treeToValue(tree.remove("recentDirectories"), javaType);
|
||||||
|
return new OpenFileSystemSavedState(null, FXCollections.observableList(recentDirectories));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static OpenFileSystemSavedState loadForStore(FileSystemStore store) {
|
||||||
|
var storageEntry = DataStorage.get()
|
||||||
|
.getStoreEntryIfPresent(store)
|
||||||
|
.map(entry -> entry.getUuid())
|
||||||
|
.orElse(UUID.randomUUID());
|
||||||
|
var state = AppCache.get("fs-state-" + storageEntry, OpenFileSystemSavedState.class, () -> {
|
||||||
|
return new OpenFileSystemSavedState();
|
||||||
|
});
|
||||||
|
state.store = store;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
public static class RecentEntry {
|
||||||
|
|
||||||
|
String directory;
|
||||||
|
Instant time;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSystemStore store;
|
||||||
|
private String lastDirectory;
|
||||||
|
@NonNull
|
||||||
|
private ObservableList<RecentEntry> recentDirectories;
|
||||||
|
|
||||||
|
public OpenFileSystemSavedState(String lastDirectory, @NonNull ObservableList<RecentEntry> recentDirectories) {
|
||||||
|
this.lastDirectory = lastDirectory;
|
||||||
|
this.recentDirectories = recentDirectories;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Timer TIMEOUT_TIMER = new Timer(true);
|
||||||
|
private static final int STORED = 10;
|
||||||
|
|
||||||
|
public OpenFileSystemSavedState() {
|
||||||
|
lastDirectory = null;
|
||||||
|
recentDirectories = FXCollections.observableList(new ArrayList<>(STORED));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
if (store == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var storageEntry = DataStorage.get().getStoreEntryIfPresent(store);
|
||||||
|
storageEntry.ifPresent(entry -> AppCache.update("fs-state-" + entry.getUuid(), this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cd(String dir) {
|
||||||
|
if (dir == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDirectory = dir;
|
||||||
|
TIMEOUT_TIMER.schedule(
|
||||||
|
new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Synchronize with platform thread
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (Objects.equals(lastDirectory, dir)) {
|
||||||
|
updateRecent(dir);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateRecent(String dir) {
|
||||||
|
recentDirectories.removeIf(recentEntry -> Objects.equals(recentEntry.directory, dir));
|
||||||
|
|
||||||
|
var o = new RecentEntry(dir, Instant.now());
|
||||||
|
if (recentDirectories.size() < STORED) {
|
||||||
|
recentDirectories.add(0, o);
|
||||||
|
} else {
|
||||||
|
recentDirectories.remove(recentDirectories.size() - 1);
|
||||||
|
recentDirectories.add(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,10 @@ public interface BrowserAction {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LeafAction byId(String id) {
|
||||||
|
return getFlattened().stream().filter(browserAction -> id.equals(browserAction.getId())).findAny().orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
default Node getIcon(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
default Node getIcon(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,12 @@ package io.xpipe.app.browser.action;
|
||||||
|
|
||||||
import io.xpipe.app.browser.BrowserEntry;
|
import io.xpipe.app.browser.BrowserEntry;
|
||||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||||
|
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||||
|
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||||
import io.xpipe.app.util.BusyProperty;
|
import io.xpipe.app.util.BusyProperty;
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
import io.xpipe.app.util.ThreadHelper;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -13,6 +17,29 @@ public interface LeafAction extends BrowserAction {
|
||||||
|
|
||||||
public abstract void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception;
|
public abstract void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception;
|
||||||
|
|
||||||
|
default Button toButton(OpenFileSystemModel model, List<BrowserEntry> selected) {
|
||||||
|
var b = new Button();
|
||||||
|
b.setOnAction(event -> {
|
||||||
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
|
BusyProperty.execute(model.getBusy(), () -> {
|
||||||
|
execute(model, selected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
if (getShortcut() != null) {
|
||||||
|
Shortcuts.addShortcut(b, getShortcut());
|
||||||
|
}
|
||||||
|
new FancyTooltipAugment<>(new SimpleStringProperty(getName(model, selected))).augment(b);
|
||||||
|
var graphic = getIcon(model, selected);
|
||||||
|
if (graphic != null) {
|
||||||
|
b.setGraphic(graphic);
|
||||||
|
}
|
||||||
|
b.setMnemonicParsing(false);
|
||||||
|
b.setDisable(!isActive(model, selected));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
default MenuItem toItem(OpenFileSystemModel model, List<BrowserEntry> selected, UnaryOperator<String> nameFunc) {
|
default MenuItem toItem(OpenFileSystemModel model, List<BrowserEntry> selected, UnaryOperator<String> nameFunc) {
|
||||||
var mi = new MenuItem(nameFunc.apply(getName(model, selected)));
|
var mi = new MenuItem(nameFunc.apply(getName(model, selected)));
|
||||||
mi.setOnAction(event -> {
|
mi.setOnAction(event -> {
|
||||||
|
@ -35,4 +62,7 @@ public interface LeafAction extends BrowserAction {
|
||||||
return mi;
|
return mi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default String getId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,23 @@ public interface DirectoryType {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadDefinitions() {
|
public static void loadDefinitions() {
|
||||||
ALL.add(new Simple(
|
ALL.add(new DirectoryType() {
|
||||||
"default", new IconVariant("default_root_folder.svg"), new IconVariant("default_root_folder_opened.svg"), ""));
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "root";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(FileSystem.FileEntry entry) {
|
||||||
|
return entry.getPath().equals("/") || entry.getPath().matches("\\w:\\\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIcon(FileSystem.FileEntry entry, boolean open) {
|
||||||
|
return open ? "default_root_folder_opened.svg" : "default_root_folder.svg";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
AppResources.with(AppResources.XPIPE_MODULE, "folder_list.txt", path -> {
|
AppResources.with(AppResources.XPIPE_MODULE, "folder_list.txt", path -> {
|
||||||
try (var reader =
|
try (var reader =
|
||||||
|
|
|
@ -13,6 +13,7 @@ import javafx.scene.input.KeyCombination;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -54,6 +55,10 @@ public abstract class Comp<S extends CompStructure<?>> {
|
||||||
return apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS));
|
return apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Comp<S> vgrow() {
|
||||||
|
return apply(struc -> VBox.setVgrow(struc.get(), Priority.ALWAYS));
|
||||||
|
}
|
||||||
|
|
||||||
public Comp<S> visible(ObservableValue<Boolean> o) {
|
public Comp<S> visible(ObservableValue<Boolean> o) {
|
||||||
return apply(struc -> struc.get().visibleProperty().bind(o));
|
return apply(struc -> struc.get().visibleProperty().bind(o));
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,7 @@ public class FancyTooltipAugment<S extends CompStructure<?>> implements Augment<
|
||||||
var tt = new JFXTooltip();
|
var tt = new JFXTooltip();
|
||||||
var toDisplay = text.getValue();
|
var toDisplay = text.getValue();
|
||||||
if (Shortcuts.getShortcut((Region) region) != null) {
|
if (Shortcuts.getShortcut((Region) region) != null) {
|
||||||
toDisplay =
|
toDisplay = toDisplay + " (" + Shortcuts.getShortcut((Region) region).getDisplayText() + ")";
|
||||||
toDisplay + " (" + Shortcuts.getShortcut((Region) region).getDisplayText() + ")";
|
|
||||||
}
|
}
|
||||||
tt.textProperty().setValue(toDisplay);
|
tt.textProperty().setValue(toDisplay);
|
||||||
tt.setStyle("-fx-font-size: 11pt;");
|
tt.setStyle("-fx-font-size: 11pt;");
|
||||||
|
|
|
@ -32,6 +32,7 @@ public class Shortcuts {
|
||||||
};
|
};
|
||||||
|
|
||||||
AtomicReference<Scene> scene = new AtomicReference<>();
|
AtomicReference<Scene> scene = new AtomicReference<>();
|
||||||
|
SHORTCUTS.put(region, comb);
|
||||||
SimpleChangeListener.apply(region.sceneProperty(), s -> {
|
SimpleChangeListener.apply(region.sceneProperty(), s -> {
|
||||||
if (Objects.equals(s, scene.get())) {
|
if (Objects.equals(s, scene.get())) {
|
||||||
return;
|
return;
|
||||||
|
@ -45,7 +46,6 @@ public class Shortcuts {
|
||||||
|
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
scene.set(s);
|
scene.set(s);
|
||||||
s.addEventHandler(KeyEvent.KEY_PRESSED, filter);
|
|
||||||
SHORTCUTS.put(region, comb);
|
SHORTCUTS.put(region, comb);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
BIN
app/src/main/resources/io/xpipe/app/resources/img/home_icon.png
Normal file
BIN
app/src/main/resources/io/xpipe/app/resources/img/home_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -30,6 +30,8 @@ mustNotBeEmpty=$NAME$ must not be empty
|
||||||
download=Drop to transfer
|
download=Drop to transfer
|
||||||
dragFiles=Drag files from here
|
dragFiles=Drag files from here
|
||||||
null=$VALUE$ must be not null
|
null=$VALUE$ must be not null
|
||||||
|
roots=Roots
|
||||||
|
recent=Recent
|
||||||
hostFeatureUnsupported=$FEATURE$ is not available on the host
|
hostFeatureUnsupported=$FEATURE$ is not available on the host
|
||||||
missingStore=$NAME$ does not exist
|
missingStore=$NAME$ does not exist
|
||||||
connectionName=Connection name
|
connectionName=Connection name
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
-fx-padding: 1em;
|
-fx-padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.browser .home {
|
.browser .overview {
|
||||||
-fx-spacing: 1em;
|
-fx-spacing: 1.5em;
|
||||||
-fx-padding: 1em;
|
-fx-padding: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected-file-list {
|
.selected-file-list {
|
||||||
|
@ -106,9 +106,18 @@
|
||||||
-fx-text-fill: transparent;
|
-fx-text-fill: transparent;
|
||||||
}
|
}
|
||||||
.browser .path-graphic-button {
|
.browser .path-graphic-button {
|
||||||
-fx-padding: 0 2px 0 7px;
|
-fx-padding: 0 5px 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.browser .overview-file-list {
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser .overview-file-list .button {
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.browser .context-menu .accelerator-text {
|
.browser .context-menu .accelerator-text {
|
||||||
-fx-padding: 3px 0px 3px 50px;
|
-fx-padding: 3px 0px 3px 50px;
|
||||||
|
|
|
@ -53,7 +53,7 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
|
||||||
@Override
|
@Override
|
||||||
public List<String> determineInterestingPaths(ShellControl pc) throws Exception {
|
public List<String> determineInterestingPaths(ShellControl pc) throws Exception {
|
||||||
var home = getHomeDirectory(pc);
|
var home = getHomeDirectory(pc);
|
||||||
return List.of(FileNames.join(home, "Desktop"));
|
return List.of(home, FileNames.join(home, "Documents"), FileNames.join(home, "Downloads"), FileNames.join(home, "Desktop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -42,6 +42,11 @@ public interface FileSystem extends Closeable, AutoCloseable {
|
||||||
this.executable = executable;
|
this.executable = executable;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FileEntry ofDirectory(FileSystem fileSystem, String path) {
|
||||||
|
return new FileEntry(fileSystem, path, Instant.now(), true, false, false, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystemStore getStore();
|
FileSystemStore getStore();
|
||||||
|
|
|
@ -7,6 +7,9 @@ import io.xpipe.app.browser.action.BrowserAction;
|
||||||
import io.xpipe.app.browser.action.BrowserActionFormatter;
|
import io.xpipe.app.browser.action.BrowserActionFormatter;
|
||||||
import io.xpipe.app.browser.action.LeafAction;
|
import io.xpipe.app.browser.action.LeafAction;
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
|
import javafx.scene.input.KeyCombination;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.datatransfer.Clipboard;
|
import java.awt.datatransfer.Clipboard;
|
||||||
|
@ -44,6 +47,11 @@ public class CopyPathAction implements BrowserAction, BranchAction {
|
||||||
return "Absolute Path";
|
return "Absolute Path";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyCombination getShortcut() {
|
||||||
|
return new KeyCodeCombination(KeyCode.C, KeyCombination.ALT_DOWN, KeyCombination.SHORTCUT_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
||||||
var s = entries.stream()
|
var s = entries.stream()
|
||||||
|
@ -89,6 +97,11 @@ public class CopyPathAction implements BrowserAction, BranchAction {
|
||||||
return "File Name";
|
return "File Name";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyCombination getShortcut() {
|
||||||
|
return new KeyCodeCombination(KeyCode.C, KeyCombination.SHIFT_DOWN, KeyCombination.SHORTCUT_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
||||||
var s = entries.stream()
|
var s = entries.stream()
|
||||||
|
|
|
@ -13,6 +13,10 @@ import java.util.List;
|
||||||
|
|
||||||
public class OpenTerminalAction implements LeafAction {
|
public class OpenTerminalAction implements LeafAction {
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return "openTerminal";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
||||||
if (entries.size() == 0) {
|
if (entries.size() == 0) {
|
||||||
|
|
Loading…
Reference in a new issue