Browser mode fixes

This commit is contained in:
crschnick 2023-05-20 13:43:41 +00:00
parent 8e7f3f5aab
commit 9d2122831d
12 changed files with 110 additions and 79 deletions

View file

@ -59,7 +59,7 @@ public class BrowserComp extends SimpleComp {
return true;
}
if (!model.getMode().equals(BrowserModel.Mode.BROWSER)) {
if (model.getMode().isChooser()) {
return true;
}
@ -89,7 +89,7 @@ public class BrowserComp extends SimpleComp {
}
private Region addBottomBar(Region r) {
if (model.getMode().equals(BrowserModel.Mode.BROWSER)) {
if (!model.getMode().isChooser()) {
return r;
}
@ -98,14 +98,19 @@ public class BrowserComp 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.getSelection().addListener((ListChangeListener<? super BrowserEntry>) c -> {
PlatformThread.runLaterIfNeeded(() -> {
selected.getChildren()
.setAll(c.getList().stream()
.map(s -> {
var field = new TextField(s.getRawFileEntry().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());
@ -198,7 +203,7 @@ public class BrowserComp extends SimpleComp {
continue;
}
model.closeFileSystem(source.getKey());
model.closeFileSystemAsync(source.getKey());
}
}
});
@ -209,16 +214,9 @@ public class BrowserComp extends SimpleComp {
var tabs = new TabPane();
tabs.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
tabs.setTabMinWidth(Region.USE_COMPUTED_SIZE);
if (!model.getMode().equals(BrowserModel.Mode.BROWSER)) {
tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
tabs.getStyleClass().add("singular");
} else {
tabs.setTabClosingPolicy(ALL_TABS);
Styles.toggleStyleClass(tabs, TabPane.STYLE_CLASS_FLOATING);
toggleStyleClass(tabs, DENSE);
}
tabs.setTabClosingPolicy(ALL_TABS);
Styles.toggleStyleClass(tabs, TabPane.STYLE_CLASS_FLOATING);
toggleStyleClass(tabs, DENSE);
return tabs;
}
@ -257,12 +255,6 @@ public class BrowserComp extends SimpleComp {
tab.setGraphic(label);
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(label));
if (!this.model.getMode().equals(BrowserModel.Mode.BROWSER)) {
label.setManaged(false);
label.setVisible(false);
}
tab.setContent(new OpenFileSystemComp(model).createSimple());
return tab;
}

View file

@ -4,30 +4,34 @@ import io.xpipe.app.browser.action.BranchAction;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.core.AppFont;
import javafx.collections.FXCollections;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Menu;
import javafx.scene.control.SeparatorMenuItem;
import java.util.ArrayList;
import java.util.List;
final class BrowserContextMenu extends ContextMenu {
private final OpenFileSystemModel model;
private final boolean empty;
private final BrowserEntry source;
public BrowserContextMenu(OpenFileSystemModel model, boolean empty) {
super();
public BrowserContextMenu(OpenFileSystemModel model, BrowserEntry source) {
this.model = model;
this.empty = empty;
this.source = source;
createMenu();
}
private void createMenu() {
AppFont.normal(this.getStyleableNode());
var selected = empty || model.getFileList().getSelected().isEmpty()
? FXCollections.observableArrayList(
new BrowserEntry(model.getCurrentDirectory(), model.getFileList(), false))
: model.getFileList().getSelected();
var empty = source == null;
var selected = new ArrayList<>(empty ? List.of() : model.getFileList().getSelection());
if (source != null && !selected.contains(source)) {
selected.add(source);
} else if (source == null && model.getFileList().getSelection().isEmpty()) {
selected.add(new BrowserEntry(model.getCurrentDirectory(), model.getFileList(), false));
}
for (BrowserAction.Category cat : BrowserAction.Category.values()) {
var all = BrowserAction.ALL.stream()

View file

@ -30,7 +30,10 @@ import javafx.scene.control.*;
import javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.skin.VirtualFlow;
import javafx.scene.input.DragEvent;
import javafx.scene.layout.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import java.time.Instant;
import java.time.ZoneId;
@ -126,25 +129,28 @@ final class BrowserFileListComp extends SimpleComp {
}
private void prepareTableSelectionModel(TableView<BrowserEntry> table) {
if (fileList.getMode().equals(BrowserModel.Mode.SINGLE_FILE_CHOOSER)
|| fileList.getMode().equals(BrowserModel.Mode.DIRECTORY_CHOOSER)) {
if (!fileList.getMode().isMultiple()) {
table.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
} else {
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
}
table.getSelectionModel().getSelectedItems().addListener((ListChangeListener<? super BrowserEntry>) c -> {
var toSelect = new ArrayList<>(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.getRawFileEntry()
.getPath()
.equals(fileList.getFileSystemModel()
.getCurrentParentDirectory()
.getPath()))
.toList();
fileList.getSelected().setAll(toSelect);
toSelect.removeIf(entry -> fileList.getFileSystemModel().getCurrentParentDirectory() != null
&& entry.getRawFileEntry()
.getPath()
.equals(fileList.getFileSystemModel()
.getCurrentParentDirectory()
.getPath()));
// Remove unsuitable selection
toSelect.removeIf(browserEntry -> (browserEntry.getRawFileEntry().isDirectory()
&& !fileList.getMode().isAcceptsDirectories())
|| (!browserEntry.getRawFileEntry().isDirectory()
&& !fileList.getMode().isAcceptsFiles()));
fileList.getSelection().setAll(toSelect);
Platform.runLater(() -> {
var toUnselect = table.getSelectionModel().getSelectedItems().stream()
@ -155,7 +161,7 @@ final class BrowserFileListComp extends SimpleComp {
});
});
fileList.getSelected().addListener((ListChangeListener<? super BrowserEntry>) c -> {
fileList.getSelection().addListener((ListChangeListener<? super BrowserEntry>) c -> {
if (c.getList().equals(table.getSelectionModel().getSelectedItems())) {
return;
}
@ -178,7 +184,7 @@ final class BrowserFileListComp extends SimpleComp {
private void prepareTableShortcuts(TableView<BrowserEntry> table) {
table.setOnKeyPressed(event -> {
var selected = fileList.getSelected();
var selected = fileList.getSelection();
BrowserAction.getFlattened().stream()
.filter(browserAction -> browserAction.isApplicable(fileList.getFileSystemModel(), selected)
&& browserAction.isActive(fileList.getFileSystemModel(), selected))
@ -218,7 +224,7 @@ final class BrowserFileListComp extends SimpleComp {
return null;
}
return new BrowserContextMenu(fileList.getFileSystemModel(), row.getItem() == null);
return new BrowserContextMenu(fileList.getFileSystemModel(), row.getItem());
})
.augment(new SimpleCompStructure<>(row));
var listEntry = Bindings.createObjectBinding(

View file

@ -32,7 +32,7 @@ public class BrowserFileListCompEntry {
@SuppressWarnings("unchecked")
public void onMouseClick(MouseEvent t) {
if (item == null) {
model.getSelected().clear();
model.getSelection().clear();
return;
}
@ -53,7 +53,7 @@ public class BrowserFileListCompEntry {
var max = tv.getSelectionModel().getSelectedItems().stream().mapToInt(entry -> all.indexOf(entry)).max().orElse(all.size() - 1);
var end = all.indexOf(item);
var start = end > min ? min : max;
model.getSelected().setAll(all.subList(Math.min(start, end), Math.max(start, end) + 1));
model.getSelection().setAll(all.subList(Math.min(start, end), Math.max(start, end) + 1));
t.consume();
return;
}

View file

@ -35,9 +35,9 @@ public final class BrowserFileListModel {
private final Property<List<BrowserEntry>> shown = new SimpleObjectProperty<>(new ArrayList<>());
private final ObjectProperty<Predicate<BrowserEntry>> predicateProperty =
new SimpleObjectProperty<>(path -> true);
private final ObservableList<BrowserEntry> selected = FXCollections.observableArrayList();
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
private final ObservableList<FileSystem.FileEntry> selectedRaw =
BindingsHelper.mappedContentBinding(selected, entry -> entry.getRawFileEntry());
BindingsHelper.mappedContentBinding(selection, entry -> entry.getRawFileEntry());
private final Property<BrowserEntry> draggedOverDirectory = new SimpleObjectProperty<BrowserEntry>();
private final Property<Boolean> draggedOverEmpty = new SimpleBooleanProperty();

View file

@ -1,5 +1,6 @@
package io.xpipe.app.browser;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.impl.FileStore;
import io.xpipe.core.store.ShellStore;
@ -10,6 +11,7 @@ import javafx.collections.ObservableList;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@ -19,14 +21,36 @@ public class BrowserModel {
public BrowserModel(Mode mode) {
this.mode = mode;
selected.addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
return;
}
BindingsHelper.bindContent(selection, newValue.getFileList().getSelection());
});
}
@Getter
public static enum Mode {
BROWSER,
SINGLE_FILE_CHOOSER,
SINGLE_FILE_SAVE,
MULTIPLE_FILE_CHOOSER,
DIRECTORY_CHOOSER
BROWSER(false, true, true, true),
SINGLE_FILE_CHOOSER(true, false, true, false),
SINGLE_FILE_SAVE(true, false, true, false),
MULTIPLE_FILE_CHOOSER(true, true, true, false),
SINGLE_DIRECTORY_CHOOSER(true, false, false, true),
MULTIPLE_DIRECTORY_CHOOSER(true, true, false, true);
private final boolean chooser;
private final boolean multiple;
private final boolean acceptsFiles;
private final boolean acceptsDirectories;
Mode(boolean chooser, boolean multiple, boolean acceptsFiles, boolean acceptsDirectories) {
this.chooser = chooser;
this.multiple = multiple;
this.acceptsFiles = acceptsFiles;
this.acceptsDirectories = acceptsDirectories;
}
}
public static final BrowserModel DEFAULT = new BrowserModel(Mode.BROWSER);
@ -39,19 +63,23 @@ public class BrowserModel {
private final ObservableList<OpenFileSystemModel> openFileSystems = FXCollections.observableArrayList();
private final Property<OpenFileSystemModel> selected = new SimpleObjectProperty<>();
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel();
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
public void finishChooser() {
if (getMode().equals(Mode.BROWSER)) {
if (!getMode().isChooser()) {
throw new IllegalStateException();
}
var selectedFiles = openFileSystems.get(0).getFileList().getSelected();
closeFileSystem(openFileSystems.get(0));
var chosen = new ArrayList<>(selection);
for (OpenFileSystemModel openFileSystem : openFileSystems) {
closeFileSystemAsync(openFileSystem);
}
if (selectedFiles.size() == 0) {
if (chosen.size() == 0) {
return;
}
var stores = selectedFiles.stream()
var stores = chosen.stream()
.map(entry -> new FileStore(
entry.getRawFileEntry().getFileSystem().getStore(),
entry.getRawFileEntry().getPath()))
@ -59,7 +87,7 @@ public class BrowserModel {
onFinish.accept(stores);
}
public void closeFileSystem(OpenFileSystemModel open) {
public void closeFileSystemAsync(OpenFileSystemModel open) {
ThreadHelper.runAsync(() -> {
if (Objects.equals(selected.getValue(), open)) {
selected.setValue(null);

View file

@ -31,8 +31,8 @@ public class BrowserStatusBarComp extends SimpleComp {
}, cc);
var selectedCount = PlatformThread.sync(Bindings.createIntegerBinding(() -> {
return model.getFileList().getSelected().size();
}, model.getFileList().getSelected()));
return model.getFileList().getSelection().size();
}, model.getFileList().getSelection()));
var allCount = PlatformThread.sync(Bindings.createIntegerBinding(() -> {
return (int) model.getFileList().getAll().getValue().stream().filter(entry -> !entry.isSynthetic()).count();
@ -60,7 +60,7 @@ public class BrowserStatusBarComp extends SimpleComp {
AppFont.small(bar);
// Use status bar as an extension of file list
new ContextMenuAugment<>(false, () -> new BrowserContextMenu(model, true)).augment(new SimpleCompStructure<>(bar));
new ContextMenuAugment<>(false, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(bar));
return bar;
}

View file

@ -57,7 +57,7 @@ public class OpenFileSystemComp extends SimpleComp {
terminalBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
new ContextMenuAugment<>(true, () -> new BrowserContextMenu(model, true)).augment(new SimpleCompStructure<>(menuButton));
new ContextMenuAugment<>(true, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(menuButton));
var filter = new BrowserFilterComp(model.getFilter()).createStructure();
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
@ -69,9 +69,7 @@ public class OpenFileSystemComp extends SimpleComp {
var directoryView = new BrowserFileListComp(model.getFileList()).createRegion();
var root = new VBox(topBar, directoryView);
if (model.getBrowserModel().getMode() == BrowserModel.Mode.BROWSER) {
root.getChildren().add(new BrowserStatusBarComp(model).createRegion());
}
root.getChildren().add(new BrowserStatusBarComp(model).createRegion());
VBox.setVgrow(directoryView, Priority.ALWAYS);
root.setPadding(Insets.EMPTY);
model.getFileList().predicateProperty().set(PREDICATE_NOT_HIDDEN);

View file

@ -73,7 +73,7 @@ public class App extends Application {
var titleBinding = Bindings.createStringBinding(
() -> {
var base = String.format(
"xpipe Desktop (%s)", AppProperties.get().getVersion());
"XPipe Desktop (%s)", AppProperties.get().getVersion());
var prefix = AppProperties.get().isStaging() ? "[STAGE] " : "";
var suffix = XPipeDistributionType.get().getUpdateHandler().getPreparedUpdate().getValue() != null
? String.format(

View file

@ -9,7 +9,7 @@ import io.xpipe.core.process.ShellDialect;
import java.util.List;
public class OpenInNativeManagerAction implements LeafAction {
public class BrowseInNativeManagerAction implements LeafAction {
@Override
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
@ -19,7 +19,11 @@ public class OpenInNativeManagerAction implements LeafAction {
var e = entry.getRawFileEntry().getPath();
switch (OsType.getLocal()) {
case OsType.Windows windows -> {
sc.executeSimpleCommand("explorer " + d.fileArgument(e));
if (entry.getRawFileEntry().isDirectory()) {
sc.executeSimpleCommand("explorer " + d.fileArgument(e));
} else {
sc.executeSimpleCommand("explorer /select," + d.fileArgument(e));
}
}
case OsType.Linux linux -> {
var action = entry.getRawFileEntry().isDirectory() ? "org.freedesktop.FileManager1.ShowFolders" : "org.freedesktop.FileManager1.ShowItems";

View file

@ -1,7 +1,6 @@
package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.BrowserModel;
import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction;
import javafx.scene.Node;
@ -31,7 +30,7 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().isDirectory()) && model.getBrowserModel().getMode() == BrowserModel.Mode.BROWSER;
return entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().isDirectory());
}
@Override

View file

@ -33,7 +33,7 @@ open module io.xpipe.ext.base {
OpenDirectoryInNewTabAction,
OpenTerminalAction,
OpenNativeFileDetailsAction,
OpenInNativeManagerAction,
BrowseInNativeManagerAction,
EditFileAction,
RunAction,
CopyAction,