File browser improvements

This commit is contained in:
crschnick
2023-05-06 12:28:18 +00:00
parent 240d6698d6
commit 2a828721db
7 changed files with 158 additions and 32 deletions

View File

@@ -1,6 +1,8 @@
package io.xpipe.app.browser;
import io.xpipe.core.store.FileSystem;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import lombok.SneakyThrows;
@@ -19,7 +21,7 @@ public class FileBrowserClipboard {
List<FileSystem.FileEntry> entries;
}
public static Instance currentCopyClipboard;
public static Property<Instance> currentCopyClipboard = new SimpleObjectProperty<>();
public static Instance currentDragClipboard;
@SneakyThrows
@@ -34,12 +36,12 @@ public class FileBrowserClipboard {
@SneakyThrows
public static void startCopy(FileSystem.FileEntry base, List<FileSystem.FileEntry> selected) {
var id = UUID.randomUUID();
currentCopyClipboard = new Instance(id, base, new ArrayList<>(selected));
currentCopyClipboard.setValue(new Instance(id, base, new ArrayList<>(selected)));
}
public static Instance retrieveCopy() {
var current = currentCopyClipboard;
return current;
return current.getValue();
}
public static Instance retrieveDrag(Dragboard dragboard) {

View File

@@ -0,0 +1,55 @@
package io.xpipe.app.browser;
import atlantafx.base.controls.Spacer;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.LabelComp;
import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.binding.Bindings;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.Region;
import lombok.Value;
@Value
public class FileBrowserStatusBarComp extends SimpleComp {
OpenFileSystemModel model;
@Override
protected Region createSimple() {
var cc = PlatformThread.sync(FileBrowserClipboard.currentCopyClipboard);
var ccCount = Bindings.createStringBinding(() -> {
if (cc.getValue() != null && cc.getValue().getEntries().size() > 0) {
return String.valueOf(cc.getValue().getEntries().size()) + " file" + (cc.getValue().getEntries().size() > 1 ? "s" : "") + " in clipboard";
} else {
return null;
}
}, cc);
var selectedCount = PlatformThread.sync(Bindings.createIntegerBinding(() -> {
return model.getFileList().getSelected().size();
}, model.getFileList().getSelected()));
var allCount = PlatformThread.sync(Bindings.createIntegerBinding(() -> {
return model.getFileList().getAll().getValue().size();
}, model.getFileList().getAll()));
var selectedComp = new LabelComp(Bindings.createStringBinding(() -> {
if (selectedCount.getValue().intValue() == 0) {
return null;
} else {
return selectedCount.getValue() + " / " + allCount.getValue() + " selected";
}
}, selectedCount, allCount));
var bar = new ToolBar();
bar.getItems().setAll(
new LabelComp(ccCount).createRegion(),
new Spacer(),
selectedComp.createRegion()
);
bar.getStyleClass().add("status-bar");
AppFont.small(bar);
return bar;
}
}

View File

@@ -133,6 +133,31 @@ final class FileContextMenu extends ContextMenu {
getItems().add(new SeparatorMenuItem());
{
var copy = new MenuItem("Copy");
copy.setOnAction(event -> {
FileBrowserClipboard.startCopy(
model.getCurrentDirectory(), model.getFileList().getSelected());
event.consume();
});
getItems().add(copy);
var paste = new MenuItem("Paste");
paste.setOnAction(event -> {
var clipboard = FileBrowserClipboard.retrieveCopy();
if (clipboard != null) {
var files = clipboard.getEntries();
var target = model.getCurrentDirectory();
model.dropFilesIntoAsync(target, files, true);
}
event.consume();
});
getItems().add(paste);
}
getItems().add(new SeparatorMenuItem());
var copyName = new MenuItem("Copy name");
copyName.setOnAction(event -> {
var selection = new StringSelection(FileNames.getFileName(entry.getPath()));

View File

@@ -15,6 +15,7 @@ import io.xpipe.app.util.HumanReadableFormat;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileSystem;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
@@ -87,7 +88,6 @@ final class FileListComp extends AnchorPane {
mtimeCol.setCellFactory(col -> new FileTimeCell());
mtimeCol.getStyleClass().add(Tweaks.ALIGN_RIGHT);
var modeCol = new TableColumn<FileSystem.FileEntry, String>("Attributes");
modeCol.setCellValueFactory(
param -> new SimpleObjectProperty<>(param.getValue().getMode()));
@@ -117,7 +117,8 @@ final class FileListComp extends AnchorPane {
};
var dirsFirst = Comparator.<FileSystem.FileEntry, Boolean>comparing(path -> !path.isDirectory());
Comparator<? super FileSystem.FileEntry> us = parentFirst.thenComparing(dirsFirst).thenComparing(comp);
Comparator<? super FileSystem.FileEntry> us =
parentFirst.thenComparing(dirsFirst).thenComparing(comp);
FXCollections.sort(table.getItems(), us);
return true;
});
@@ -133,11 +134,27 @@ final class FileListComp extends AnchorPane {
table.getSelectionModel().getSelectedItems().addListener((ListChangeListener<? super FileSystem.FileEntry>)
c -> {
fileList.getSelected().setAll(c.getList());
// Explicitly unselect synthetic entries since we can't use a custom selection model as that is bugged in JavaFX
var toSelect = c.getList().stream()
.filter(entry -> fileList.getFileSystemModel().getCurrentParentDirectory() == null
|| !entry.getPath()
.equals(fileList.getFileSystemModel()
.getCurrentParentDirectory()
.getPath()))
.toList();
fileList.getSelected().setAll(toSelect);
fileList.getFileSystemModel()
.getBrowserModel()
.getSelectedFiles()
.setAll(c.getList());
.setAll(toSelect);
Platform.runLater(() -> {
var toUnselect = table.getSelectionModel().getSelectedItems().stream()
.filter(entry -> !toSelect.contains(entry))
.toList();
toUnselect.forEach(entry -> table.getSelectionModel()
.clearSelection(table.getItems().indexOf(entry)));
});
});
table.setOnKeyPressed(event -> {
@@ -183,12 +200,6 @@ final class FileListComp extends AnchorPane {
var listEntry = Bindings.createObjectBinding(
() -> new FileListCompEntry(row, row.getItem(), fileList), row.itemProperty());
row.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue && listEntry.get().isSynthetic()) {
row.updateSelected(false);
}
});
row.itemProperty().addListener((observable, oldValue, newValue) -> {
row.pseudoClassStateChanged(DRAG, false);
row.pseudoClassStateChanged(DRAG_OVER, false);
@@ -252,7 +263,13 @@ final class FileListComp extends AnchorPane {
}
}
var hasAttributes = fileList.getFileSystemModel().getFileSystem() != null && !fileList.getFileSystemModel().getFileSystem().getShell().orElseThrow().getOsType().equals(OsType.WINDOWS);
var hasAttributes = fileList.getFileSystemModel().getFileSystem() != null
&& !fileList.getFileSystemModel()
.getFileSystem()
.getShell()
.orElseThrow()
.getOsType()
.equals(OsType.WINDOWS);
if (!hasAttributes) {
table.getColumns().remove(modeCol);
} else {
@@ -310,7 +327,9 @@ final class FileListComp extends AnchorPane {
private final StringProperty img = new SimpleStringProperty();
private final StringProperty text = new SimpleStringProperty();
private final Node imageView = new SvgCacheComp(new SimpleDoubleProperty(24), new SimpleDoubleProperty(24), img, FileIconManager.getSvgCache()).createRegion();
private final Node imageView = new SvgCacheComp(
new SimpleDoubleProperty(24), new SimpleDoubleProperty(24), img, FileIconManager.getSvgCache())
.createRegion();
private final StackPane textField =
new LazyTextFieldComp(text).createStructure().get();
private final ChangeListener<String> listener;

View File

@@ -84,7 +84,7 @@ public class OpenFileSystemComp extends SimpleComp {
FileListComp directoryView = new FileListComp(model.getFileList());
var root = new VBox(topBar, directoryView);
var root = new VBox(topBar, directoryView, new FileBrowserStatusBarComp(model).createRegion());
VBox.setVgrow(directoryView, Priority.ALWAYS);
root.setPadding(Insets.EMPTY);
model.getFileList().predicateProperty().set(PREDICATE_NOT_HIDDEN);