mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-09-30 00:56:56 +13:00
More browser improvements
This commit is contained in:
parent
01a3177843
commit
e0b34ff480
11 changed files with 150 additions and 25 deletions
89
app/src/main/java/io/xpipe/app/browser/FileFilterComp.java
Normal file
89
app/src/main/java/io/xpipe/app/browser/FileFilterComp.java
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||||
|
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||||
|
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||||
|
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||||
|
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
|
import javafx.scene.input.KeyCombination;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
public class FileFilterComp extends SimpleComp {
|
||||||
|
|
||||||
|
private final Property<String> filterString;
|
||||||
|
|
||||||
|
public FileFilterComp(Property<String> filterString) {
|
||||||
|
this.filterString = filterString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Region createSimple() {
|
||||||
|
var expanded = new SimpleBooleanProperty();
|
||||||
|
var text = new TextFieldComp(filterString, false).createRegion();
|
||||||
|
text.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (!newValue && filterString.getValue() == null) {
|
||||||
|
expanded.set(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newValue) {
|
||||||
|
expanded.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
filterString.addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (newValue == null && !text.isFocused()) {
|
||||||
|
expanded.set(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
text.setMinWidth(0);
|
||||||
|
Styles.toggleStyleClass(text, Styles.LEFT_PILL);
|
||||||
|
|
||||||
|
SimpleChangeListener.apply(filterString, val -> {
|
||||||
|
if (val == null) {
|
||||||
|
text.getStyleClass().remove(Styles.SUCCESS);
|
||||||
|
} else {
|
||||||
|
text.getStyleClass().add(Styles.SUCCESS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var fi = new FontIcon("mdi2m-magnify");
|
||||||
|
var button = new Button();
|
||||||
|
GrowAugment.create(false, true).augment(new SimpleCompStructure<>(button));
|
||||||
|
Shortcuts.addShortcut(button, new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
||||||
|
button.setGraphic(fi);
|
||||||
|
button.setOnAction(event -> {
|
||||||
|
if (expanded.get() && filterString.getValue() == null) {
|
||||||
|
expanded.set(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
text.requestFocus();
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
|
||||||
|
SimpleChangeListener.apply(expanded, val -> {
|
||||||
|
if (val) {
|
||||||
|
text.setPrefWidth(250);
|
||||||
|
button.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
button.getStyleClass().remove(Styles.FLAT);
|
||||||
|
} else {
|
||||||
|
text.setPrefWidth(0);
|
||||||
|
button.getStyleClass().remove(Styles.RIGHT_PILL);
|
||||||
|
button.getStyleClass().add(Styles.FLAT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var box = new HBox(text, button);
|
||||||
|
box.setFillHeight(true);
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,10 +22,7 @@ import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.input.*;
|
import javafx.scene.input.*;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -83,6 +80,7 @@ final class FileListComp extends AnchorPane {
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
var table = new TableView<FileSystem.FileEntry>();
|
var table = new TableView<FileSystem.FileEntry>();
|
||||||
|
table.setPlaceholder(new Region());
|
||||||
table.getStyleClass().add(Styles.STRIPED);
|
table.getStyleClass().add(Styles.STRIPED);
|
||||||
table.getColumns().setAll(filenameCol, sizeCol, mtimeCol);
|
table.getColumns().setAll(filenameCol, sizeCol, mtimeCol);
|
||||||
table.getSortOrder().add(filenameCol);
|
table.getSortOrder().add(filenameCol);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import lombok.Getter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -34,6 +35,10 @@ final class FileListModel {
|
||||||
|
|
||||||
public FileListModel(OpenFileSystemModel model) {
|
public FileListModel(OpenFileSystemModel model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
|
||||||
|
model.getFilter().addListener((observable, oldValue, newValue) -> {
|
||||||
|
refreshShown();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAll(List<FileSystem.FileEntry> newFiles) {
|
public void setAll(List<FileSystem.FileEntry> newFiles) {
|
||||||
|
@ -47,11 +52,17 @@ final class FileListModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshShown() {
|
private void refreshShown() {
|
||||||
|
List<FileSystem.FileEntry> filtered = model.getFilter().getValue() != null ? all.getValue().stream().filter(entry -> {
|
||||||
|
var name = FileNames.getFileName(entry.getPath()).toLowerCase(Locale.ROOT);
|
||||||
|
var filterString = model.getFilter().getValue().toLowerCase(Locale.ROOT);
|
||||||
|
return name.contains(filterString);
|
||||||
|
}).toList() : all.getValue();
|
||||||
|
|
||||||
Comparator<FileSystem.FileEntry> tableComparator = comparatorProperty.getValue();
|
Comparator<FileSystem.FileEntry> tableComparator = comparatorProperty.getValue();
|
||||||
var comparator = tableComparator != null
|
var comparator = tableComparator != null
|
||||||
? FILE_TYPE_COMPARATOR.thenComparing(tableComparator)
|
? FILE_TYPE_COMPARATOR.thenComparing(tableComparator)
|
||||||
: FILE_TYPE_COMPARATOR;
|
: FILE_TYPE_COMPARATOR;
|
||||||
var listCopy = new ArrayList<>(all.getValue());
|
var listCopy = new ArrayList<>(filtered);
|
||||||
listCopy.sort(comparator);
|
listCopy.sort(comparator);
|
||||||
shown.setValue(listCopy);
|
shown.setValue(listCopy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import io.xpipe.core.store.ShellStore;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class FileSystemHelper {
|
public class FileSystemHelper {
|
||||||
|
@ -108,7 +108,7 @@ public class FileSystemHelper {
|
||||||
|
|
||||||
private static void dropFileAcrossFileSystems(FileSystem.FileEntry target, FileSystem.FileEntry source)
|
private static void dropFileAcrossFileSystems(FileSystem.FileEntry target, FileSystem.FileEntry source)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
var flatFiles = new HashMap<FileSystem.FileEntry, String>();
|
var flatFiles = new LinkedHashMap<FileSystem.FileEntry, String>();
|
||||||
|
|
||||||
// Prevent dropping directory into itself
|
// Prevent dropping directory into itself
|
||||||
if (source.getFileSystem().equals(target.getFileSystem())
|
if (source.getFileSystem().equals(target.getFileSystem())
|
||||||
|
|
|
@ -57,12 +57,15 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
creatingProperty.set(true);
|
creatingProperty.set(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var filter = new FileFilterComp(model.getFilter()).createRegion();
|
||||||
|
|
||||||
var topBar = new ToolBar();
|
var topBar = new ToolBar();
|
||||||
topBar.getItems().setAll(
|
topBar.getItems().setAll(
|
||||||
backBtn,
|
backBtn,
|
||||||
forthBtn,
|
forthBtn,
|
||||||
new Spacer(10),
|
new Spacer(10),
|
||||||
pathBar,
|
pathBar,
|
||||||
|
filter,
|
||||||
refreshBtn,
|
refreshBtn,
|
||||||
terminalBtn,
|
terminalBtn,
|
||||||
addBtn
|
addBtn
|
||||||
|
|
|
@ -25,6 +25,7 @@ final class OpenFileSystemModel {
|
||||||
|
|
||||||
private Property<FileSystemStore> store = new SimpleObjectProperty<>();
|
private Property<FileSystemStore> store = new SimpleObjectProperty<>();
|
||||||
private FileSystem fileSystem;
|
private FileSystem fileSystem;
|
||||||
|
private final Property<String> filter = new SimpleStringProperty();
|
||||||
private final FileListModel fileList;
|
private final FileListModel fileList;
|
||||||
private final ReadOnlyObjectWrapper<String> currentPath = new ReadOnlyObjectWrapper<>();
|
private final ReadOnlyObjectWrapper<String> currentPath = new ReadOnlyObjectWrapper<>();
|
||||||
private final FileBrowserNavigationHistory history = new FileBrowserNavigationHistory();
|
private final FileBrowserNavigationHistory history = new FileBrowserNavigationHistory();
|
||||||
|
@ -63,6 +64,7 @@ final class OpenFileSystemModel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter.setValue(null);
|
||||||
currentPath.set(path);
|
currentPath.set(path);
|
||||||
history.cd(path);
|
history.cd(path);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class AppInstaller {
|
||||||
} else {
|
} else {
|
||||||
targetFile = FileNames.join(
|
targetFile = FileNames.join(
|
||||||
s.getTemporaryDirectory(), localFile.getFileName().toString());
|
s.getTemporaryDirectory(), localFile.getFileName().toString());
|
||||||
try (CommandProcessControl c = s.command(s.getShellDialect().getStreamFileWriteCommand(targetFile))
|
try (CommandProcessControl c = s.getShellDialect().getStreamFileWriteCommand(s, targetFile)
|
||||||
.start()) {
|
.start()) {
|
||||||
c.discardOut();
|
c.discardOut();
|
||||||
c.discardErr();
|
c.discardErr();
|
||||||
|
|
|
@ -3,17 +3,22 @@ package io.xpipe.core.impl;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import io.xpipe.core.process.ProcessControlProvider;
|
import io.xpipe.core.process.ProcessControlProvider;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellProcessControl;
|
||||||
import io.xpipe.core.store.*;
|
import io.xpipe.core.store.FileSystem;
|
||||||
|
import io.xpipe.core.store.FileSystemStore;
|
||||||
|
import io.xpipe.core.store.MachineStore;
|
||||||
import io.xpipe.core.util.JacksonizedValue;
|
import io.xpipe.core.util.JacksonizedValue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.Optional;
|
import java.util.*;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@JsonTypeName("local")
|
@JsonTypeName("local")
|
||||||
public class LocalStore extends JacksonizedValue implements FileSystemStore, MachineStore {
|
public class LocalStore extends JacksonizedValue implements FileSystemStore, MachineStore {
|
||||||
|
@ -25,7 +30,6 @@ public class LocalStore extends JacksonizedValue implements FileSystemStore, Mac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileSystem createFileSystem() {
|
public FileSystem createFileSystem() {
|
||||||
if (true) return new ConnectionFileSystem(ShellStore.local().create());
|
|
||||||
return new FileSystem() {
|
return new FileSystem() {
|
||||||
@Override
|
@Override
|
||||||
public Optional<ShellProcessControl> getShell() {
|
public Optional<ShellProcessControl> getShell() {
|
||||||
|
@ -44,17 +48,17 @@ public class LocalStore extends JacksonizedValue implements FileSystemStore, Mac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String file) throws Exception {
|
public void delete(String file) throws Exception {
|
||||||
|
Files.delete(Path.of(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copy(String file, String newFile) throws Exception {
|
public void copy(String file, String newFile) throws Exception {
|
||||||
|
Files.copy(Path.of(file), Path.of(newFile), StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void move(String file, String newFile) throws Exception {
|
public void move(String file, String newFile) throws Exception {
|
||||||
|
Files.move(Path.of(file), Path.of(newFile), StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,6 +73,11 @@ public class LocalStore extends JacksonizedValue implements FileSystemStore, Mac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void touch(String file) throws Exception {
|
public void touch(String file) throws Exception {
|
||||||
|
if (exists(file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.createFile(Path.of(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -78,12 +87,27 @@ public class LocalStore extends JacksonizedValue implements FileSystemStore, Mac
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<FileEntry> listFiles(String file) throws Exception {
|
public Stream<FileEntry> listFiles(String file) throws Exception {
|
||||||
return null;
|
return Files.list(Path.of(file)).map(path -> {
|
||||||
|
try {
|
||||||
|
var date = Files.getLastModifiedTime(path);
|
||||||
|
var size = Files.isDirectory(path) ? 0 : Files.size(path);
|
||||||
|
return new FileEntry(
|
||||||
|
this,
|
||||||
|
path.toString(),
|
||||||
|
date.toInstant(),
|
||||||
|
Files.isDirectory(path),
|
||||||
|
Files.isHidden(path),
|
||||||
|
Files.isExecutable(path),
|
||||||
|
size);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> listRoots() throws Exception {
|
public List<String> listRoots() throws Exception {
|
||||||
return null;
|
return StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false).map(path -> path.toString()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,9 +123,7 @@ public class LocalStore extends JacksonizedValue implements FileSystemStore, Mac
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {}
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ public interface ShellDialect {
|
||||||
|
|
||||||
String getFileMoveCommand(String oldFile, String newFile);
|
String getFileMoveCommand(String oldFile, String newFile);
|
||||||
|
|
||||||
String getStreamFileWriteCommand(String file);
|
CommandProcessControl getStreamFileWriteCommand(ShellProcessControl processControl, String file);
|
||||||
|
|
||||||
String getTextFileWriteCommand(String content, String file);
|
String getTextFileWriteCommand(String content, String file);
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openOutput(String file) throws Exception {
|
public OutputStream openOutput(String file) throws Exception {
|
||||||
return shellProcessControl.command(proc -> proc.getShellDialect()
|
return shellProcessControl.getShellDialect()
|
||||||
.getStreamFileWriteCommand(proc.getOsType().normalizeFileName(file)))
|
.getStreamFileWriteCommand(shellProcessControl, shellProcessControl.getOsType().normalizeFileName(file))
|
||||||
.startExternalStdin();
|
.startExternalStdin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ public interface FileSystem extends Closeable, AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return listFilesRecursively(fileEntry.getPath());
|
return Stream.concat(Stream.of(fileEntry), listFilesRecursively(fileEntry.getPath()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue