mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-09-19 02:39:01 +12:00
File browser improvements
This commit is contained in:
parent
21da5bd828
commit
9cb0b7f494
11 changed files with 281 additions and 133 deletions
|
@ -1,19 +1,23 @@
|
||||||
package io.xpipe.app.browser;
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
import io.xpipe.app.comp.storage.store.StoreEntryTree;
|
||||||
import io.xpipe.app.comp.storage.store.StoreEntryFlatMiniSectionComp;
|
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
|
||||||
import io.xpipe.app.fxcomps.Comp;
|
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||||
import io.xpipe.app.fxcomps.SimpleComp;
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
||||||
import io.xpipe.app.fxcomps.augment.DragPseudoClassAugment;
|
|
||||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
|
||||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.TreeCell;
|
||||||
|
import javafx.scene.control.TreeItem;
|
||||||
|
import javafx.scene.control.TreeView;
|
||||||
import javafx.scene.input.DragEvent;
|
import javafx.scene.input.DragEvent;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
|
@ -33,34 +37,101 @@ final class BookmarkList extends SimpleComp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
var observableList = BindingsHelper.filteredContentBinding(StoreEntryFlatMiniSectionComp.ALL, e -> e.getEntry().getState().isUsable());
|
var root = StoreEntryTree.createTree();
|
||||||
var list = new ListBoxViewComp<>(observableList, observableList, e -> {
|
var view = new TreeView<StoreEntryWrapper>(root);
|
||||||
return Comp.of(() -> {
|
view.setShowRoot(false);
|
||||||
var button = new Button(null, e.createRegion());
|
view.getStyleClass().add("bookmark-list");
|
||||||
|
view.setCellFactory(param -> {
|
||||||
|
return new StoreCell();
|
||||||
|
});
|
||||||
|
|
||||||
if (!(e.getEntry().getStore() instanceof ShellStore)) {
|
model.getSelected().addListener((observable, oldValue, newValue) -> {
|
||||||
button.setDisable(true);
|
if (newValue == null) {
|
||||||
|
view.getSelectionModel().clearSelection();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.setOnAction(event -> {
|
view.getSelectionModel()
|
||||||
var fileSystem = ((ShellStore) e.getEntry().getStore());
|
.select(getTreeViewItem(
|
||||||
model.openFileSystemAsync(fileSystem);
|
root,
|
||||||
|
StoreViewState.get().getAllEntries().stream()
|
||||||
|
.filter(storeEntryWrapper -> storeEntryWrapper
|
||||||
|
.getState()
|
||||||
|
.getValue()
|
||||||
|
.isUsable()
|
||||||
|
&& storeEntryWrapper
|
||||||
|
.getEntry()
|
||||||
|
.getStore()
|
||||||
|
.equals(newValue.getStore()))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeItem<StoreEntryWrapper> getTreeViewItem(
|
||||||
|
TreeItem<StoreEntryWrapper> item, StoreEntryWrapper value) {
|
||||||
|
if (item.getValue() != null && item.getValue().equals(value)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TreeItem<StoreEntryWrapper> child : item.getChildren()) {
|
||||||
|
TreeItem<StoreEntryWrapper> s = getTreeViewItem(child, value);
|
||||||
|
if (s != null) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class StoreCell extends TreeCell<StoreEntryWrapper> {
|
||||||
|
|
||||||
|
private final StringProperty img = new SimpleStringProperty();
|
||||||
|
private final Node imageView = new PrettyImageComp(img, 20, 20).createRegion();
|
||||||
|
|
||||||
|
private StoreCell() {
|
||||||
|
setGraphic(imageView);
|
||||||
|
addEventHandler(DragEvent.DRAG_OVER, mouseEvent -> {
|
||||||
|
if (getItem() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHoverTimer(getItem().getEntry().getStore(), mouseEvent);
|
||||||
|
mouseEvent.consume();
|
||||||
|
});
|
||||||
|
addEventHandler(DragEvent.DRAG_EXITED, mouseEvent -> {
|
||||||
|
activeTask = null;
|
||||||
|
mouseEvent.consume();
|
||||||
|
});
|
||||||
|
addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||||
|
if (getItem() == null || event.getButton() != MouseButton.PRIMARY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileSystem = ((ShellStore) getItem().getEntry().getStore());
|
||||||
|
model.openFileSystemAsync(fileSystem, null);
|
||||||
event.consume();
|
event.consume();
|
||||||
});
|
});
|
||||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(button));
|
}
|
||||||
DragPseudoClassAugment.create().augment(new SimpleCompStructure<>(button));
|
|
||||||
|
|
||||||
button.addEventHandler(
|
@Override
|
||||||
DragEvent.DRAG_OVER,
|
public void updateItem(StoreEntryWrapper item, boolean empty) {
|
||||||
mouseEvent -> handleHoverTimer(e.getEntry().getStore(), mouseEvent));
|
super.updateItem(item, empty);
|
||||||
button.addEventHandler(
|
if (empty || item == null) {
|
||||||
DragEvent.DRAG_EXITED,
|
setText(null);
|
||||||
mouseEvent -> activeTask = null);
|
// Don't set image as that would trigger image comp update
|
||||||
|
// and cells are emptied on each change, leading to unnecessary changes
|
||||||
return button;
|
// img.set(null);
|
||||||
});
|
setGraphic(null);
|
||||||
}).styleClass("bookmark-list").createRegion();
|
} else {
|
||||||
return list;
|
setText(item.getName());
|
||||||
|
img.set(item.getEntry()
|
||||||
|
.getProvider()
|
||||||
|
.getDisplayIconFileName(item.getEntry().getStore()));
|
||||||
|
setGraphic(imageView);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleHoverTimer(DataStore store, DragEvent event) {
|
private void handleHoverTimer(DataStore store, DragEvent event) {
|
||||||
|
|
|
@ -11,13 +11,13 @@ import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||||
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
||||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
|
||||||
import io.xpipe.app.storage.DataStorage;
|
import io.xpipe.app.storage.DataStorage;
|
||||||
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.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
@ -52,21 +52,26 @@ public class FileBrowserComp extends SimpleComp {
|
||||||
|
|
||||||
var bookmarksList = new BookmarkList(model).createRegion();
|
var bookmarksList = new BookmarkList(model).createRegion();
|
||||||
VBox.setVgrow(bookmarksList, Priority.ALWAYS);
|
VBox.setVgrow(bookmarksList, Priority.ALWAYS);
|
||||||
var localDownloadStage = new LocalFileTransferComp(model.getLocalTransfersStage()).hide(Bindings.createBooleanBinding(() -> {
|
var localDownloadStage = new LocalFileTransferComp(model.getLocalTransfersStage())
|
||||||
|
.hide(PlatformThread.sync(Bindings.createBooleanBinding(
|
||||||
|
() -> {
|
||||||
if (model.getOpenFileSystems().size() == 0) {
|
if (model.getOpenFileSystems().size() == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !model.getMode().equals(FileBrowserModel.Mode.BROWSER);
|
if (!model.getMode().equals(FileBrowserModel.Mode.BROWSER)) {
|
||||||
}, PlatformThread.sync(model.getOpenFileSystems()))).createRegion();
|
return true;
|
||||||
SimpleChangeListener.apply(model.getSelected(), val -> {
|
|
||||||
localDownloadStage.visibleProperty().unbind();
|
|
||||||
if (val == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localDownloadStage.visibleProperty().bind(PlatformThread.sync(val.getLocal().not()));
|
if (model.getSelected().getValue() != null) {
|
||||||
});
|
return model.getSelected().getValue().isLocal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
model.getOpenFileSystems(),
|
||||||
|
model.getSelected())))
|
||||||
|
.createRegion();
|
||||||
var vertical = new VBox(bookmarksList, localDownloadStage);
|
var vertical = new VBox(bookmarksList, localDownloadStage);
|
||||||
vertical.setFillWidth(true);
|
vertical.setFillWidth(true);
|
||||||
|
|
||||||
|
@ -75,9 +80,10 @@ public class FileBrowserComp extends SimpleComp {
|
||||||
.widthProperty()
|
.widthProperty()
|
||||||
.addListener(
|
.addListener(
|
||||||
// set sidebar width in pixels depending on split pane width
|
// set sidebar width in pixels depending on split pane width
|
||||||
(obs, old, val) -> splitPane.setDividerPosition(0, 230 / splitPane.getWidth()));
|
(obs, old, val) -> splitPane.setDividerPosition(0, 280 / splitPane.getWidth()));
|
||||||
|
|
||||||
var r = addBottomBar(splitPane);
|
var r = addBottomBar(splitPane);
|
||||||
|
r.getStyleClass().add("browser");
|
||||||
// AppFont.small(r);
|
// AppFont.small(r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +134,8 @@ public class FileBrowserComp extends SimpleComp {
|
||||||
map.put(v, t);
|
map.put(v, t);
|
||||||
tabs.getTabs().add(t);
|
tabs.getTabs().add(t);
|
||||||
});
|
});
|
||||||
tabs.getSelectionModel().select(model.getOpenFileSystems().indexOf(model.getSelected().getValue()));
|
tabs.getSelectionModel()
|
||||||
|
.select(model.getOpenFileSystems().indexOf(model.getSelected().getValue()));
|
||||||
|
|
||||||
// Used for ignoring changes by the tabpane when new tabs are added. We want to perform the selections manually!
|
// Used for ignoring changes by the tabpane when new tabs are added. We want to perform the selections manually!
|
||||||
var modifying = new SimpleBooleanProperty();
|
var modifying = new SimpleBooleanProperty();
|
||||||
|
@ -195,8 +202,6 @@ public class FileBrowserComp extends SimpleComp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.getStyleClass().add("browser");
|
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,29 +233,14 @@ public class FileBrowserComp extends SimpleComp {
|
||||||
.bind(Bindings.createDoubleBinding(
|
.bind(Bindings.createDoubleBinding(
|
||||||
() -> model.getBusy().get() ? -1d : 0, PlatformThread.sync(model.getBusy())));
|
() -> model.getBusy().get() ? -1d : 0, PlatformThread.sync(model.getBusy())));
|
||||||
|
|
||||||
var name = Bindings.createStringBinding(
|
var name = DataStorage.get().getStoreEntry(model.getStore()).getName();
|
||||||
() -> {
|
var image = DataStorage.get()
|
||||||
return model.getStore().getValue() != null
|
.getStoreEntry(model.getStore())
|
||||||
? DataStorage.get()
|
|
||||||
.getStoreEntry(model.getStore().getValue())
|
|
||||||
.getName()
|
|
||||||
: null;
|
|
||||||
},
|
|
||||||
PlatformThread.sync(model.getStore()));
|
|
||||||
var image = Bindings.createStringBinding(
|
|
||||||
() -> {
|
|
||||||
return model.getStore().getValue() != null
|
|
||||||
? DataStorage.get()
|
|
||||||
.getStoreEntry(model.getStore().getValue())
|
|
||||||
.getProvider()
|
.getProvider()
|
||||||
.getDisplayIconFileName(model.getStore().getValue())
|
.getDisplayIconFileName(model.getStore());
|
||||||
: null;
|
var logo = new PrettyImageComp(new SimpleStringProperty(image), 20, 20).createRegion();
|
||||||
},
|
|
||||||
model.getStore());
|
|
||||||
var logo = new PrettyImageComp(image, 20, 20).createRegion();
|
|
||||||
|
|
||||||
var label = new Label();
|
var label = new Label(name);
|
||||||
label.textProperty().bind(name);
|
|
||||||
label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() {
|
label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(DragEvent mouseEvent) {
|
public void handle(DragEvent mouseEvent) {
|
||||||
|
|
|
@ -71,46 +71,43 @@ public class FileBrowserModel {
|
||||||
|
|
||||||
public void openExistingFileSystemIfPresent(ShellStore store) {
|
public void openExistingFileSystemIfPresent(ShellStore store) {
|
||||||
var found = openFileSystems.stream()
|
var found = openFileSystems.stream()
|
||||||
.filter(model -> Objects.equals(model.getStore().getValue(), store))
|
.filter(model -> Objects.equals(model.getStore(), store))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (found.isPresent()) {
|
if (found.isPresent()) {
|
||||||
selected.setValue(found.get());
|
selected.setValue(found.get());
|
||||||
} else {
|
} else {
|
||||||
openFileSystemAsync(store);
|
openFileSystemAsync(store, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openFileSystemSync(ShellStore store, String path) throws Exception {
|
public void openFileSystemAsync(ShellStore store, String path) {
|
||||||
var model = new OpenFileSystemModel(this);
|
// // Prevent multiple tabs in non browser modes
|
||||||
|
// if (!mode.equals(Mode.BROWSER)) {
|
||||||
|
// ThreadHelper.runFailableAsync(() -> {
|
||||||
|
// var open = openFileSystems.size() > 0 ? openFileSystems.get(0) : null;
|
||||||
|
// if (open != null) {
|
||||||
|
// open.closeSync();
|
||||||
|
// openFileSystems.remove(open);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var model = new OpenFileSystemModel(this, store);
|
||||||
|
// openFileSystems.add(model);
|
||||||
|
// selected.setValue(model);
|
||||||
|
// model.switchSync(store);
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
|
var model = new OpenFileSystemModel(this, store);
|
||||||
|
model.initFileSystem();
|
||||||
openFileSystems.add(model);
|
openFileSystems.add(model);
|
||||||
selected.setValue(model);
|
selected.setValue(model);
|
||||||
model.switchSync(store);
|
if (path != null) {
|
||||||
model.cd(path);
|
model.cd(path);
|
||||||
|
} else {
|
||||||
|
model.initDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openFileSystemAsync(ShellStore store) {
|
|
||||||
// Prevent multiple tabs in non browser modes
|
|
||||||
if (!mode.equals(Mode.BROWSER)) {
|
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
|
||||||
var open = openFileSystems.size() > 0 ? openFileSystems.get(0) : null;
|
|
||||||
if (open != null) {
|
|
||||||
open.closeSync();
|
|
||||||
openFileSystems.remove(open);
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new OpenFileSystemModel(this);
|
|
||||||
openFileSystems.add(model);
|
|
||||||
selected.setValue(model);
|
|
||||||
model.switchSync(store);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
|
||||||
var model = new OpenFileSystemModel(this);
|
|
||||||
openFileSystems.add(model);
|
|
||||||
selected.setValue(model);
|
|
||||||
model.switchSync(store);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class FileSystemHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionFileSystem fileSystem = (ConnectionFileSystem) model.getFileSystem();
|
ConnectionFileSystem fileSystem = (ConnectionFileSystem) model.getFileSystem();
|
||||||
var current = !(model.getStore().getValue() instanceof LocalStore)
|
var current = !model.isLocal()
|
||||||
? fileSystem
|
? fileSystem
|
||||||
.getShellControl()
|
.getShellControl()
|
||||||
.executeSimpleStringCommand(
|
.executeSimpleStringCommand(
|
||||||
|
|
|
@ -34,7 +34,7 @@ import java.util.stream.Stream;
|
||||||
@Getter
|
@Getter
|
||||||
public final class OpenFileSystemModel {
|
public final class OpenFileSystemModel {
|
||||||
|
|
||||||
private Property<FileSystemStore> store = new SimpleObjectProperty<>();
|
private final FileSystemStore store;
|
||||||
private FileSystem fileSystem;
|
private FileSystem fileSystem;
|
||||||
private final Property<String> filter = new SimpleStringProperty();
|
private final Property<String> filter = new SimpleStringProperty();
|
||||||
private final FileListModel fileList;
|
private final FileListModel fileList;
|
||||||
|
@ -46,10 +46,11 @@ public final class OpenFileSystemModel {
|
||||||
private final Property<OpenFileSystemSavedState> savedState = new SimpleObjectProperty<>();
|
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 local = new SimpleBooleanProperty();
|
private boolean local;
|
||||||
|
|
||||||
public OpenFileSystemModel(FileBrowserModel browserModel) {
|
public OpenFileSystemModel(FileBrowserModel browserModel, FileSystemStore store) {
|
||||||
this.browserModel = browserModel;
|
this.browserModel = browserModel;
|
||||||
|
this.store = store;
|
||||||
fileList = new FileListModel(this);
|
fileList = new FileListModel(this);
|
||||||
addListeners();
|
addListeners();
|
||||||
}
|
}
|
||||||
|
@ -61,7 +62,7 @@ public final class OpenFileSystemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
BusyProperty.execute(busy, () -> {
|
BusyProperty.execute(busy, () -> {
|
||||||
if (store.getValue() instanceof ShellStore s) {
|
if (store instanceof ShellStore s) {
|
||||||
c.accept(fileSystem.getShell().orElseThrow());
|
c.accept(fileSystem.getShell().orElseThrow());
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
refreshSync();
|
refreshSync();
|
||||||
|
@ -73,11 +74,11 @@ public final class OpenFileSystemModel {
|
||||||
|
|
||||||
private void addListeners() {
|
private void addListeners() {
|
||||||
savedState.addListener((observable, oldValue, newValue) -> {
|
savedState.addListener((observable, oldValue, newValue) -> {
|
||||||
if (store.getValue() == null) {
|
if (store == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var storageEntry = DataStorage.get().getStoreEntryIfPresent(store.getValue());
|
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));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ public final class OpenFileSystemModel {
|
||||||
if (!FileNames.isAbsolute(path) && fileSystem.getShell().isPresent()) {
|
if (!FileNames.isAbsolute(path) && fileSystem.getShell().isPresent()) {
|
||||||
var directory = currentPath.get();
|
var directory = currentPath.get();
|
||||||
var name = path + " - "
|
var name = path + " - "
|
||||||
+ XPipeDaemon.getInstance().getStoreName(store.getValue()).orElse("?");
|
+ XPipeDaemon.getInstance().getStoreName(store).orElse("?");
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
if (ShellDialects.ALL.stream().anyMatch(dialect -> path.startsWith(dialect.getOpenCommand()))) {
|
if (ShellDialects.ALL.stream().anyMatch(dialect -> path.startsWith(dialect.getOpenCommand()))) {
|
||||||
var cmd = fileSystem
|
var cmd = fileSystem
|
||||||
|
@ -177,7 +178,7 @@ public final class OpenFileSystemModel {
|
||||||
|
|
||||||
private void cdSyncWithoutCheck(String path) throws Exception {
|
private void cdSyncWithoutCheck(String path) throws Exception {
|
||||||
if (fileSystem == null) {
|
if (fileSystem == null) {
|
||||||
var fs = store.getValue().createFileSystem();
|
var fs = store.createFileSystem();
|
||||||
fs.open();
|
fs.open();
|
||||||
this.fileSystem = fs;
|
this.fileSystem = fs;
|
||||||
}
|
}
|
||||||
|
@ -323,21 +324,21 @@ public final class OpenFileSystemModel {
|
||||||
ErrorEvent.fromThrowable(e).handle();
|
ErrorEvent.fromThrowable(e).handle();
|
||||||
}
|
}
|
||||||
fileSystem = null;
|
fileSystem = null;
|
||||||
store = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchSync(FileSystemStore fileSystem) throws Exception {
|
public void initFileSystem() throws Exception {
|
||||||
BusyProperty.execute(busy, () -> {
|
BusyProperty.execute(busy, () -> {
|
||||||
closeSync();
|
var fs = store.createFileSystem();
|
||||||
this.store.setValue(fileSystem);
|
|
||||||
var fs = fileSystem.createFileSystem();
|
|
||||||
fs.open();
|
fs.open();
|
||||||
this.fileSystem = fs;
|
this.fileSystem = fs;
|
||||||
this.local.set(
|
this.local = fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false);
|
||||||
fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false));
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initDirectory() throws Exception {
|
||||||
|
BusyProperty.execute(busy, () -> {
|
||||||
var storageEntry = DataStorage.get()
|
var storageEntry = DataStorage.get()
|
||||||
.getStoreEntryIfPresent(fileSystem)
|
.getStoreEntryIfPresent(store)
|
||||||
.map(entry -> entry.getUuid())
|
.map(entry -> entry.getUuid())
|
||||||
.orElse(UUID.randomUUID());
|
.orElse(UUID.randomUUID());
|
||||||
this.savedState.setValue(
|
this.savedState.setValue(
|
||||||
|
@ -362,13 +363,13 @@ public final class OpenFileSystemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
BusyProperty.execute(busy, () -> {
|
BusyProperty.execute(busy, () -> {
|
||||||
if (store.getValue() instanceof ShellStore s) {
|
if (store instanceof ShellStore s) {
|
||||||
var connection = ((ConnectionFileSystem) fileSystem).getShellControl();
|
var connection = ((ConnectionFileSystem) fileSystem).getShellControl();
|
||||||
var command = s.control()
|
var command = s.control()
|
||||||
.initWith(connection.getShellDialect().getCdCommand(directory))
|
.initWith(connection.getShellDialect().getCdCommand(directory))
|
||||||
.prepareTerminalOpen(directory + " - "
|
.prepareTerminalOpen(directory + " - "
|
||||||
+ XPipeDaemon.getInstance()
|
+ XPipeDaemon.getInstance()
|
||||||
.getStoreName(store.getValue())
|
.getStoreName(store)
|
||||||
.orElse("?"));
|
.orElse("?"));
|
||||||
TerminalHelper.open(directory, command);
|
TerminalHelper.open(directory, command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.xpipe.app.comp.storage.store;
|
||||||
|
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.scene.control.TreeItem;
|
||||||
|
|
||||||
|
public class StoreEntryTree {
|
||||||
|
|
||||||
|
public static TreeItem<StoreEntryWrapper> createTree() {
|
||||||
|
var topLevel = StoreSection.createTopLevel();
|
||||||
|
var root = new TreeItem<StoreEntryWrapper>();
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
// Listen for any entry list change, not only top level changes
|
||||||
|
StoreViewState.get().getAllEntries().addListener((ListChangeListener<? super StoreEntryWrapper>) c -> {
|
||||||
|
root.getChildren().clear();
|
||||||
|
for (StoreSection v : topLevel.getChildren()) {
|
||||||
|
add(root, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (StoreSection v : topLevel.getChildren()) {
|
||||||
|
add(root, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void add(TreeItem<StoreEntryWrapper> parent, StoreSection section) {
|
||||||
|
var item = new TreeItem<>(section.getWrapper());
|
||||||
|
item.setExpanded(section.getWrapper().getExpanded().getValue());
|
||||||
|
parent.getChildren().add(item);
|
||||||
|
for (StoreSection child : section.getChildren()) {
|
||||||
|
add(item, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,30 @@ import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
|
import javafx.animation.Interpolator;
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.KeyValue;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
import javafx.util.Duration;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public class AppTheme {
|
public class AppTheme {
|
||||||
|
|
||||||
|
public record AccentColor(Color primaryColor, PseudoClass pseudoClass) {
|
||||||
|
|
||||||
|
public static AccentColor xpipeBlue() {
|
||||||
|
return new AccentColor(Color.web("#11B4B4"), PseudoClass.getPseudoClass("accent-primer-purple"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
if (AppPrefs.get() == null) {
|
if (AppPrefs.get() == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -60,6 +78,23 @@ public class AppTheme {
|
||||||
|
|
||||||
private static void changeTheme(Theme newTheme) {
|
private static void changeTheme(Theme newTheme) {
|
||||||
PlatformThread.runLaterIfNeeded(() -> {
|
PlatformThread.runLaterIfNeeded(() -> {
|
||||||
|
for (Window window : Window.getWindows()) {
|
||||||
|
var scene = window.getScene();
|
||||||
|
Image snapshot = scene.snapshot(null);
|
||||||
|
Pane root = (Pane) scene.getRoot();
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(snapshot);
|
||||||
|
root.getChildren().add(imageView);
|
||||||
|
|
||||||
|
// Animate!
|
||||||
|
var transition = new Timeline(
|
||||||
|
new KeyFrame(Duration.ZERO, new KeyValue(imageView.opacityProperty(), 1, Interpolator.EASE_OUT)),
|
||||||
|
new KeyFrame(
|
||||||
|
Duration.millis(1250), new KeyValue(imageView.opacityProperty(), 0, Interpolator.EASE_OUT)));
|
||||||
|
transition.setOnFinished(e -> root.getChildren().remove(imageView));
|
||||||
|
transition.play();
|
||||||
|
}
|
||||||
|
|
||||||
Application.setUserAgentStylesheet(newTheme.getTheme().getUserAgentStylesheet());
|
Application.setUserAgentStylesheet(newTheme.getTheme().getUserAgentStylesheet());
|
||||||
TrackEvent.debug("Set theme " + newTheme.getId() + " for scene");
|
TrackEvent.debug("Set theme " + newTheme.getId() + " for scene");
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class SvgView {
|
||||||
var widthProperty = new SimpleIntegerProperty();
|
var widthProperty = new SimpleIntegerProperty();
|
||||||
var heightProperty = new SimpleIntegerProperty();
|
var heightProperty = new SimpleIntegerProperty();
|
||||||
SimpleChangeListener.apply(content, val -> {
|
SimpleChangeListener.apply(content, val -> {
|
||||||
if (val == null) {
|
if (val == null || val.isBlank()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package io.xpipe.app.launcher;
|
package io.xpipe.app.launcher;
|
||||||
|
|
||||||
|
import io.xpipe.app.browser.FileBrowserModel;
|
||||||
import io.xpipe.app.core.mode.OperationMode;
|
import io.xpipe.app.core.mode.OperationMode;
|
||||||
import io.xpipe.app.ext.ActionProvider;
|
import io.xpipe.app.ext.ActionProvider;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
|
import io.xpipe.core.store.ShellStore;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
@ -114,7 +116,8 @@ public abstract class LauncherInput {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuiDsCreatorMultiStep.showForStore(DataSourceProvider.Category.STREAM, FileStore.local(file), null);
|
var dir = Files.isDirectory(file) ? file : file.getParent();
|
||||||
|
FileBrowserModel.DEFAULT.openFileSystemAsync(ShellStore.createLocal(), dir.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -49,7 +49,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.browser .bookmark-list {
|
.browser .bookmark-list {
|
||||||
-fx-border-width: 0 0 1 1;
|
-fx-border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.track,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.track-background,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.thumb,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.increment-button,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.decrement-button,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.increment-arrow,
|
||||||
|
.browser .bookmark-list *.scroll-bar:horizontal *.decrement-arrow {
|
||||||
|
-fx-background-color: null;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-padding: 0;
|
||||||
|
-fx-shape: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
.browser .tool-bar {
|
.browser .tool-bar {
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception {
|
public void execute(OpenFileSystemModel model, List<FileBrowserEntry> entries) throws Exception {
|
||||||
model.getBrowserModel().openFileSystemSync(model.getStore().getValue().asNeeded(), entries.get(0).getRawFileEntry().getPath());
|
model.getBrowserModel().openFileSystemAsync(model.getStore().asNeeded(), entries.get(0).getRawFileEntry().getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue