mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-09-15 16:59:00 +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;
|
||||
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.storage.store.StoreEntryFlatMiniSectionComp;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.comp.storage.store.StoreEntryTree;
|
||||
import io.xpipe.app.comp.storage.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.DragPseudoClassAugment;
|
||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
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.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.Timer;
|
||||
|
@ -33,34 +37,101 @@ final class BookmarkList extends SimpleComp {
|
|||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var observableList = BindingsHelper.filteredContentBinding(StoreEntryFlatMiniSectionComp.ALL, e -> e.getEntry().getState().isUsable());
|
||||
var list = new ListBoxViewComp<>(observableList, observableList, e -> {
|
||||
return Comp.of(() -> {
|
||||
var button = new Button(null, e.createRegion());
|
||||
var root = StoreEntryTree.createTree();
|
||||
var view = new TreeView<StoreEntryWrapper>(root);
|
||||
view.setShowRoot(false);
|
||||
view.getStyleClass().add("bookmark-list");
|
||||
view.setCellFactory(param -> {
|
||||
return new StoreCell();
|
||||
});
|
||||
|
||||
if (!(e.getEntry().getStore() instanceof ShellStore)) {
|
||||
button.setDisable(true);
|
||||
model.getSelected().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue == null) {
|
||||
view.getSelectionModel().clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
view.getSelectionModel()
|
||||
.select(getTreeViewItem(
|
||||
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;
|
||||
}
|
||||
|
||||
button.setOnAction(event -> {
|
||||
var fileSystem = ((ShellStore) e.getEntry().getStore());
|
||||
model.openFileSystemAsync(fileSystem);
|
||||
event.consume();
|
||||
});
|
||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(button));
|
||||
DragPseudoClassAugment.create().augment(new SimpleCompStructure<>(button));
|
||||
|
||||
button.addEventHandler(
|
||||
DragEvent.DRAG_OVER,
|
||||
mouseEvent -> handleHoverTimer(e.getEntry().getStore(), mouseEvent));
|
||||
button.addEventHandler(
|
||||
DragEvent.DRAG_EXITED,
|
||||
mouseEvent -> activeTask = null);
|
||||
|
||||
return button;
|
||||
handleHoverTimer(getItem().getEntry().getStore(), mouseEvent);
|
||||
mouseEvent.consume();
|
||||
});
|
||||
}).styleClass("bookmark-list").createRegion();
|
||||
return list;
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(StoreEntryWrapper item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) {
|
||||
setText(null);
|
||||
// Don't set image as that would trigger image comp update
|
||||
// and cells are emptied on each change, leading to unnecessary changes
|
||||
// img.set(null);
|
||||
setGraphic(null);
|
||||
} else {
|
||||
setText(item.getName());
|
||||
img.set(item.getEntry()
|
||||
.getProvider()
|
||||
.getDisplayIconFileName(item.getEntry().getStore()));
|
||||
setGraphic(imageView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.impl.PrettyImageComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.BusyProperty;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -46,27 +46,32 @@ public class FileBrowserComp extends SimpleComp {
|
|||
protected Region createSimple() {
|
||||
FileType.loadDefinitions();
|
||||
DirectoryType.loadDefinitions();
|
||||
ThreadHelper.runAsync( () -> {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
FileIconManager.loadIfNecessary();
|
||||
});
|
||||
|
||||
var bookmarksList = new BookmarkList(model).createRegion();
|
||||
VBox.setVgrow(bookmarksList, Priority.ALWAYS);
|
||||
var localDownloadStage = new LocalFileTransferComp(model.getLocalTransfersStage()).hide(Bindings.createBooleanBinding(() -> {
|
||||
if (model.getOpenFileSystems().size() == 0) {
|
||||
return true;
|
||||
}
|
||||
var localDownloadStage = new LocalFileTransferComp(model.getLocalTransfersStage())
|
||||
.hide(PlatformThread.sync(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
if (model.getOpenFileSystems().size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !model.getMode().equals(FileBrowserModel.Mode.BROWSER);
|
||||
}, PlatformThread.sync(model.getOpenFileSystems()))).createRegion();
|
||||
SimpleChangeListener.apply(model.getSelected(), val -> {
|
||||
localDownloadStage.visibleProperty().unbind();
|
||||
if (val == null) {
|
||||
return;
|
||||
}
|
||||
if (!model.getMode().equals(FileBrowserModel.Mode.BROWSER)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
vertical.setFillWidth(true);
|
||||
|
||||
|
@ -75,9 +80,10 @@ public class FileBrowserComp extends SimpleComp {
|
|||
.widthProperty()
|
||||
.addListener(
|
||||
// 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);
|
||||
r.getStyleClass().add("browser");
|
||||
// AppFont.small(r);
|
||||
return r;
|
||||
}
|
||||
|
@ -92,14 +98,14 @@ public class FileBrowserComp extends SimpleComp {
|
|||
var selected = new HBox();
|
||||
selected.setAlignment(Pos.CENTER_LEFT);
|
||||
selected.setSpacing(10);
|
||||
// model.getSelected().addListener((ListChangeListener<? super FileSystem.FileEntry>) c -> {
|
||||
// selected.getChildren().setAll(c.getList().stream().map(s -> {
|
||||
// var field = new TextField(s.getPath());
|
||||
// field.setEditable(false);
|
||||
// field.setPrefWidth(400);
|
||||
// return field;
|
||||
// }).toList());
|
||||
// });
|
||||
// model.getSelected().addListener((ListChangeListener<? super FileSystem.FileEntry>) c -> {
|
||||
// selected.getChildren().setAll(c.getList().stream().map(s -> {
|
||||
// var field = new TextField(s.getPath());
|
||||
// field.setEditable(false);
|
||||
// field.setPrefWidth(400);
|
||||
// return field;
|
||||
// }).toList());
|
||||
// });
|
||||
var spacer = new Spacer(Orientation.HORIZONTAL);
|
||||
var button = new Button("Select");
|
||||
button.setOnAction(event -> model.finishChooser());
|
||||
|
@ -128,7 +134,8 @@ public class FileBrowserComp extends SimpleComp {
|
|||
map.put(v, 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!
|
||||
var modifying = new SimpleBooleanProperty();
|
||||
|
@ -195,8 +202,6 @@ public class FileBrowserComp extends SimpleComp {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
stack.getStyleClass().add("browser");
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
@ -228,29 +233,14 @@ public class FileBrowserComp extends SimpleComp {
|
|||
.bind(Bindings.createDoubleBinding(
|
||||
() -> model.getBusy().get() ? -1d : 0, PlatformThread.sync(model.getBusy())));
|
||||
|
||||
var name = Bindings.createStringBinding(
|
||||
() -> {
|
||||
return model.getStore().getValue() != null
|
||||
? 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()
|
||||
.getDisplayIconFileName(model.getStore().getValue())
|
||||
: null;
|
||||
},
|
||||
model.getStore());
|
||||
var logo = new PrettyImageComp(image, 20, 20).createRegion();
|
||||
var name = DataStorage.get().getStoreEntry(model.getStore()).getName();
|
||||
var image = DataStorage.get()
|
||||
.getStoreEntry(model.getStore())
|
||||
.getProvider()
|
||||
.getDisplayIconFileName(model.getStore());
|
||||
var logo = new PrettyImageComp(new SimpleStringProperty(image), 20, 20).createRegion();
|
||||
|
||||
var label = new Label();
|
||||
label.textProperty().bind(name);
|
||||
var label = new Label(name);
|
||||
label.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() {
|
||||
@Override
|
||||
public void handle(DragEvent mouseEvent) {
|
||||
|
|
|
@ -71,46 +71,43 @@ public class FileBrowserModel {
|
|||
|
||||
public void openExistingFileSystemIfPresent(ShellStore store) {
|
||||
var found = openFileSystems.stream()
|
||||
.filter(model -> Objects.equals(model.getStore().getValue(), store))
|
||||
.filter(model -> Objects.equals(model.getStore(), store))
|
||||
.findFirst();
|
||||
if (found.isPresent()) {
|
||||
selected.setValue(found.get());
|
||||
} else {
|
||||
openFileSystemAsync(store);
|
||||
openFileSystemAsync(store, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void openFileSystemSync(ShellStore store, String path) throws Exception {
|
||||
var model = new OpenFileSystemModel(this);
|
||||
openFileSystems.add(model);
|
||||
selected.setValue(model);
|
||||
model.switchSync(store);
|
||||
model.cd(path);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
public void openFileSystemAsync(ShellStore store, String path) {
|
||||
// // 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);
|
||||
var model = new OpenFileSystemModel(this, store);
|
||||
model.initFileSystem();
|
||||
openFileSystems.add(model);
|
||||
selected.setValue(model);
|
||||
model.switchSync(store);
|
||||
if (path != null) {
|
||||
model.cd(path);
|
||||
} else {
|
||||
model.initDirectory();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public class FileSystemHelper {
|
|||
}
|
||||
|
||||
ConnectionFileSystem fileSystem = (ConnectionFileSystem) model.getFileSystem();
|
||||
var current = !(model.getStore().getValue() instanceof LocalStore)
|
||||
var current = !model.isLocal()
|
||||
? fileSystem
|
||||
.getShellControl()
|
||||
.executeSimpleStringCommand(
|
||||
|
|
|
@ -34,7 +34,7 @@ import java.util.stream.Stream;
|
|||
@Getter
|
||||
public final class OpenFileSystemModel {
|
||||
|
||||
private Property<FileSystemStore> store = new SimpleObjectProperty<>();
|
||||
private final FileSystemStore store;
|
||||
private FileSystem fileSystem;
|
||||
private final Property<String> filter = new SimpleStringProperty();
|
||||
private final FileListModel fileList;
|
||||
|
@ -46,10 +46,11 @@ public final class OpenFileSystemModel {
|
|||
private final Property<OpenFileSystemSavedState> savedState = new SimpleObjectProperty<>();
|
||||
private final OpenFileSystemCache cache = new OpenFileSystemCache(this);
|
||||
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.store = store;
|
||||
fileList = new FileListModel(this);
|
||||
addListeners();
|
||||
}
|
||||
|
@ -61,7 +62,7 @@ public final class OpenFileSystemModel {
|
|||
}
|
||||
|
||||
BusyProperty.execute(busy, () -> {
|
||||
if (store.getValue() instanceof ShellStore s) {
|
||||
if (store instanceof ShellStore s) {
|
||||
c.accept(fileSystem.getShell().orElseThrow());
|
||||
if (refresh) {
|
||||
refreshSync();
|
||||
|
@ -73,11 +74,11 @@ public final class OpenFileSystemModel {
|
|||
|
||||
private void addListeners() {
|
||||
savedState.addListener((observable, oldValue, newValue) -> {
|
||||
if (store.getValue() == null) {
|
||||
if (store == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var storageEntry = DataStorage.get().getStoreEntryIfPresent(store.getValue());
|
||||
var storageEntry = DataStorage.get().getStoreEntryIfPresent(store);
|
||||
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()) {
|
||||
var directory = currentPath.get();
|
||||
var name = path + " - "
|
||||
+ XPipeDaemon.getInstance().getStoreName(store.getValue()).orElse("?");
|
||||
+ XPipeDaemon.getInstance().getStoreName(store).orElse("?");
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
if (ShellDialects.ALL.stream().anyMatch(dialect -> path.startsWith(dialect.getOpenCommand()))) {
|
||||
var cmd = fileSystem
|
||||
|
@ -177,7 +178,7 @@ public final class OpenFileSystemModel {
|
|||
|
||||
private void cdSyncWithoutCheck(String path) throws Exception {
|
||||
if (fileSystem == null) {
|
||||
var fs = store.getValue().createFileSystem();
|
||||
var fs = store.createFileSystem();
|
||||
fs.open();
|
||||
this.fileSystem = fs;
|
||||
}
|
||||
|
@ -323,21 +324,21 @@ public final class OpenFileSystemModel {
|
|||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
fileSystem = null;
|
||||
store = null;
|
||||
}
|
||||
|
||||
public void switchSync(FileSystemStore fileSystem) throws Exception {
|
||||
public void initFileSystem() throws Exception {
|
||||
BusyProperty.execute(busy, () -> {
|
||||
closeSync();
|
||||
this.store.setValue(fileSystem);
|
||||
var fs = fileSystem.createFileSystem();
|
||||
var fs = store.createFileSystem();
|
||||
fs.open();
|
||||
this.fileSystem = fs;
|
||||
this.local.set(
|
||||
fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false));
|
||||
this.local = fs.getShell().map(shellControl -> shellControl.isLocal()).orElse(false);
|
||||
});
|
||||
}
|
||||
|
||||
public void initDirectory() throws Exception {
|
||||
BusyProperty.execute(busy, () -> {
|
||||
var storageEntry = DataStorage.get()
|
||||
.getStoreEntryIfPresent(fileSystem)
|
||||
.getStoreEntryIfPresent(store)
|
||||
.map(entry -> entry.getUuid())
|
||||
.orElse(UUID.randomUUID());
|
||||
this.savedState.setValue(
|
||||
|
@ -362,13 +363,13 @@ public final class OpenFileSystemModel {
|
|||
}
|
||||
|
||||
BusyProperty.execute(busy, () -> {
|
||||
if (store.getValue() instanceof ShellStore s) {
|
||||
if (store instanceof ShellStore s) {
|
||||
var connection = ((ConnectionFileSystem) fileSystem).getShellControl();
|
||||
var command = s.control()
|
||||
.initWith(connection.getShellDialect().getCdCommand(directory))
|
||||
.prepareTerminalOpen(directory + " - "
|
||||
+ XPipeDaemon.getInstance()
|
||||
.getStoreName(store.getValue())
|
||||
.getStoreName(store)
|
||||
.orElse("?"));
|
||||
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.prefs.AppPrefs;
|
||||
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.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.Getter;
|
||||
|
||||
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() {
|
||||
if (AppPrefs.get() == null) {
|
||||
return;
|
||||
|
@ -60,6 +78,23 @@ public class AppTheme {
|
|||
|
||||
private static void changeTheme(Theme newTheme) {
|
||||
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());
|
||||
TrackEvent.debug("Set theme " + newTheme.getId() + " for scene");
|
||||
});
|
||||
|
|
|
@ -52,7 +52,7 @@ public class SvgView {
|
|||
var widthProperty = new SimpleIntegerProperty();
|
||||
var heightProperty = new SimpleIntegerProperty();
|
||||
SimpleChangeListener.apply(content, val -> {
|
||||
if (val == null) {
|
||||
if (val == null || val.isBlank()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package io.xpipe.app.launcher;
|
||||
|
||||
import io.xpipe.app.browser.FileBrowserModel;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.ext.ActionProvider;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -114,7 +116,8 @@ public abstract class LauncherInput {
|
|||
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
|
||||
|
|
|
@ -49,7 +49,22 @@
|
|||
}
|
||||
|
||||
.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 {
|
||||
|
|
|
@ -16,7 +16,7 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
|
|||
|
||||
@Override
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue