mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-07-01 04:21:11 +12:00
Browser improvements
This commit is contained in:
parent
ff2956ef29
commit
8d9b4c9d4e
|
@ -9,9 +9,7 @@ import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
||||||
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.SimpleObjectProperty;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.collections.SetChangeListener;
|
import javafx.collections.SetChangeListener;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
|
@ -93,8 +91,10 @@ final class BrowserBookmarkList extends SimpleComp {
|
||||||
|
|
||||||
private final StringProperty img = new SimpleStringProperty();
|
private final StringProperty img = new SimpleStringProperty();
|
||||||
private final Node imageView = new PrettyImageComp(img, 20, 20).createRegion();
|
private final Node imageView = new PrettyImageComp(img, 20, 20).createRegion();
|
||||||
|
private final BooleanProperty busy = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
private StoreCell() {
|
private StoreCell() {
|
||||||
|
disableProperty().bind(busy);
|
||||||
setGraphic(imageView);
|
setGraphic(imageView);
|
||||||
addEventHandler(DragEvent.DRAG_OVER, mouseEvent -> {
|
addEventHandler(DragEvent.DRAG_OVER, mouseEvent -> {
|
||||||
if (getItem() == null) {
|
if (getItem() == null) {
|
||||||
|
@ -114,7 +114,7 @@ final class BrowserBookmarkList extends SimpleComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileSystem = ((ShellStore) getItem().getEntry().getStore());
|
var fileSystem = ((ShellStore) getItem().getEntry().getStore());
|
||||||
model.openFileSystemAsync(null, fileSystem, null);
|
model.openFileSystemAsync(null, fileSystem, null, busy);
|
||||||
event.consume();
|
event.consume();
|
||||||
});
|
});
|
||||||
var icon = new SimpleObjectProperty<String>("mdal-keyboard_arrow_right");
|
var icon = new SimpleObjectProperty<String>("mdal-keyboard_arrow_right");
|
||||||
|
|
|
@ -26,11 +26,9 @@ final class BrowserContextMenu extends ContextMenu {
|
||||||
AppFont.normal(this.getStyleableNode());
|
AppFont.normal(this.getStyleableNode());
|
||||||
|
|
||||||
var empty = source == null;
|
var empty = source == null;
|
||||||
var selected = new ArrayList<>(empty ? List.of() : model.getFileList().getSelection());
|
var selected = new ArrayList<>(empty ? List.of(new BrowserEntry(model.getCurrentDirectory(), model.getFileList(), false)) : model.getFileList().getSelection());
|
||||||
if (source != null && !selected.contains(source)) {
|
if (source != null && !selected.contains(source)) {
|
||||||
selected.add(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()) {
|
for (BrowserAction.Category cat : BrowserAction.Category.values()) {
|
||||||
|
|
|
@ -204,6 +204,12 @@ final class BrowserFileListComp extends SimpleComp {
|
||||||
|
|
||||||
private void prepareTableEntries(TableView<BrowserEntry> table) {
|
private void prepareTableEntries(TableView<BrowserEntry> table) {
|
||||||
var emptyEntry = new BrowserFileListCompEntry(table, null, fileList);
|
var emptyEntry = new BrowserFileListCompEntry(table, null, fileList);
|
||||||
|
table.setOnMouseClicked(e -> {
|
||||||
|
emptyEntry.onMouseClick(e);
|
||||||
|
});
|
||||||
|
table.setOnMouseDragEntered(event -> {
|
||||||
|
emptyEntry.onMouseDragEntered(event);
|
||||||
|
});
|
||||||
table.setOnDragOver(event -> {
|
table.setOnDragOver(event -> {
|
||||||
emptyEntry.onDragOver(event);
|
emptyEntry.onDragOver(event);
|
||||||
});
|
});
|
||||||
|
@ -223,6 +229,10 @@ final class BrowserFileListComp extends SimpleComp {
|
||||||
table.setRowFactory(param -> {
|
table.setRowFactory(param -> {
|
||||||
TableRow<BrowserEntry> row = new TableRow<>();
|
TableRow<BrowserEntry> row = new TableRow<>();
|
||||||
new ContextMenuAugment<>(event -> {
|
new ContextMenuAugment<>(event -> {
|
||||||
|
if (row.getItem() == null) {
|
||||||
|
return event.getButton() == MouseButton.SECONDARY;
|
||||||
|
}
|
||||||
|
|
||||||
if (row.getItem() != null && row.getItem().getRawFileEntry().isDirectory()) {
|
if (row.getItem() != null && row.getItem().getRawFileEntry().isDirectory()) {
|
||||||
return event.getButton() == MouseButton.SECONDARY;
|
return event.getButton() == MouseButton.SECONDARY;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ public class BrowserFileListCompEntry {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void onMouseClick(MouseEvent t) {
|
public void onMouseClick(MouseEvent t) {
|
||||||
|
t.consume();
|
||||||
|
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
model.getSelection().clear();
|
model.getSelection().clear();
|
||||||
return;
|
return;
|
||||||
|
@ -38,7 +40,6 @@ public class BrowserFileListCompEntry {
|
||||||
|
|
||||||
if (t.getClickCount() == 2 && t.getButton() == MouseButton.PRIMARY) {
|
if (t.getClickCount() == 2 && t.getButton() == MouseButton.PRIMARY) {
|
||||||
model.onDoubleClick(item);
|
model.onDoubleClick(item);
|
||||||
t.consume();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +55,6 @@ public class BrowserFileListCompEntry {
|
||||||
var end = tv.getSelectionModel().getFocusedIndex();
|
var end = tv.getSelectionModel().getFocusedIndex();
|
||||||
var start = end > min ? min : max;
|
var start = end > min ? min : max;
|
||||||
tv.getSelectionModel().selectRange(Math.min(start, end), Math.max(start, end) + 1);
|
tv.getSelectionModel().selectRange(Math.min(start, end), Math.max(start, end) + 1);
|
||||||
t.consume();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
import io.xpipe.core.store.FileSystem;
|
import io.xpipe.core.store.FileSystem;
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
@ -33,8 +32,6 @@ public final class BrowserFileListModel {
|
||||||
new SimpleObjectProperty<>(FILE_TYPE_COMPARATOR);
|
new SimpleObjectProperty<>(FILE_TYPE_COMPARATOR);
|
||||||
private final Property<List<BrowserEntry>> all = new SimpleObjectProperty<>(new ArrayList<>());
|
private final Property<List<BrowserEntry>> all = new SimpleObjectProperty<>(new ArrayList<>());
|
||||||
private final Property<List<BrowserEntry>> shown = new SimpleObjectProperty<>(new ArrayList<>());
|
private final Property<List<BrowserEntry>> shown = new SimpleObjectProperty<>(new ArrayList<>());
|
||||||
private final ObjectProperty<Predicate<BrowserEntry>> predicateProperty =
|
|
||||||
new SimpleObjectProperty<>(path -> true);
|
|
||||||
private final ObservableList<BrowserEntry> previousSelection = FXCollections.observableArrayList();
|
private final ObservableList<BrowserEntry> previousSelection = FXCollections.observableArrayList();
|
||||||
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
|
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
|
||||||
private final ObservableList<FileSystem.FileEntry> selectedRaw =
|
private final ObservableList<FileSystem.FileEntry> selectedRaw =
|
||||||
|
@ -129,8 +126,4 @@ public final class BrowserFileListModel {
|
||||||
// FileOpener.openInTextEditor(entry.getRawFileEntry());
|
// FileOpener.openInTextEditor(entry.getRawFileEntry());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectProperty<Predicate<BrowserEntry>> predicateProperty() {
|
|
||||||
return predicateProperty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package io.xpipe.app.browser;
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
|
import io.xpipe.app.util.BusyProperty;
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
import io.xpipe.app.util.ThreadHelper;
|
||||||
import io.xpipe.core.impl.FileStore;
|
import io.xpipe.core.impl.FileStore;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
@ -104,11 +107,11 @@ public class BrowserModel {
|
||||||
if (found.isPresent()) {
|
if (found.isPresent()) {
|
||||||
selected.setValue(found.get());
|
selected.setValue(found.get());
|
||||||
} else {
|
} else {
|
||||||
openFileSystemAsync(name, store, null);
|
openFileSystemAsync(name, store, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openFileSystemAsync(String name, ShellStore store, String path) {
|
public void openFileSystemAsync(String name, ShellStore store, String path, BooleanProperty busy) {
|
||||||
// // Prevent multiple tabs in non browser modes
|
// // Prevent multiple tabs in non browser modes
|
||||||
// if (!mode.equals(Mode.BROWSER)) {
|
// if (!mode.equals(Mode.BROWSER)) {
|
||||||
// ThreadHelper.runFailableAsync(() -> {
|
// ThreadHelper.runFailableAsync(() -> {
|
||||||
|
@ -127,8 +130,13 @@ public class BrowserModel {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
var model = new OpenFileSystemModel(name, this, store);
|
OpenFileSystemModel model;
|
||||||
model.initFileSystem();
|
|
||||||
|
try (var b = new BusyProperty(busy != null ? busy : new SimpleBooleanProperty())) {
|
||||||
|
model = new OpenFileSystemModel(name, this, store);
|
||||||
|
model.initFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
openFileSystems.add(model);
|
openFileSystems.add(model);
|
||||||
selected.setValue(model);
|
selected.setValue(model);
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package io.xpipe.app.browser;
|
||||||
|
|
||||||
|
import io.xpipe.app.comp.base.SimpleTitledPaneComp;
|
||||||
|
import io.xpipe.app.core.AppI18n;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
|
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||||
|
import io.xpipe.core.store.FileSystem;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BrowserOverviewComp extends SimpleComp {
|
||||||
|
|
||||||
|
private final OpenFileSystemModel model;
|
||||||
|
|
||||||
|
public BrowserOverviewComp(OpenFileSystemModel model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Region createSimple() {
|
||||||
|
var commonList = new BrowserSelectionListComp(FXCollections.observableArrayList(
|
||||||
|
new FileSystem.FileEntry(model.getFileSystem(), "C:\\", Instant.now(), true, false, false, 0, null)));
|
||||||
|
var common = new SimpleTitledPaneComp(AppI18n.observable("a"), commonList);
|
||||||
|
|
||||||
|
var recentList = new BrowserSelectionListComp(FXCollections.observableArrayList(
|
||||||
|
new FileSystem.FileEntry(model.getFileSystem(), "C:\\", Instant.now(), true, false, false, 0, null)));
|
||||||
|
var recent = new SimpleTitledPaneComp(AppI18n.observable("Recent"), recentList);
|
||||||
|
|
||||||
|
var vbox = new VerticalComp(List.of(common, recent)).styleClass("home");
|
||||||
|
return vbox.createRegion();
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,9 +59,36 @@ public class BrowserStatusBarComp extends SimpleComp {
|
||||||
});
|
});
|
||||||
AppFont.small(bar);
|
AppFont.small(bar);
|
||||||
|
|
||||||
// Use status bar as an extension of file list
|
simulateEmptyCell(bar);
|
||||||
new ContextMenuAugment<>(() -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(bar));
|
|
||||||
|
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void simulateEmptyCell(Region r) {
|
||||||
|
var emptyEntry = new BrowserFileListCompEntry(r, null, model.getFileList());
|
||||||
|
r.setOnMouseClicked(e -> {
|
||||||
|
emptyEntry.onMouseClick(e);
|
||||||
|
});
|
||||||
|
r.setOnMouseDragEntered(event -> {
|
||||||
|
emptyEntry.onMouseDragEntered(event);
|
||||||
|
});
|
||||||
|
r.setOnDragOver(event -> {
|
||||||
|
emptyEntry.onDragOver(event);
|
||||||
|
});
|
||||||
|
r.setOnDragEntered(event -> {
|
||||||
|
emptyEntry.onDragEntered(event);
|
||||||
|
});
|
||||||
|
r.setOnDragDetected(event -> {
|
||||||
|
emptyEntry.startDrag(event);
|
||||||
|
});
|
||||||
|
r.setOnDragExited(event -> {
|
||||||
|
emptyEntry.onDragExited(event);
|
||||||
|
});
|
||||||
|
r.setOnDragDropped(event -> {
|
||||||
|
emptyEntry.onDragDrop(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use status bar as an extension of file list
|
||||||
|
new ContextMenuAugment<>(() -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(r));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,13 @@ package io.xpipe.app.browser;
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import io.xpipe.app.comp.base.ModalOverlayComp;
|
import io.xpipe.app.comp.base.ModalOverlayComp;
|
||||||
|
import io.xpipe.app.comp.base.MultiContentComp;
|
||||||
import io.xpipe.app.fxcomps.Comp;
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
import io.xpipe.app.fxcomps.SimpleComp;
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||||
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
||||||
|
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||||
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
@ -21,7 +24,8 @@ import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
import static io.xpipe.app.browser.BrowserFileListModel.PREDICATE_NOT_HIDDEN;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class OpenFileSystemComp extends SimpleComp {
|
public class OpenFileSystemComp extends SimpleComp {
|
||||||
|
|
||||||
|
@ -33,9 +37,7 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
var alertOverlay = new ModalOverlayComp(
|
var alertOverlay = new ModalOverlayComp(Comp.of(() -> createContent()), model.getOverlay());
|
||||||
Comp.of(() -> createContent()),
|
|
||||||
model.getOverlay());
|
|
||||||
return alertOverlay.createRegion();
|
return alertOverlay.createRegion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,22 +60,43 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
terminalBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
|
terminalBtn.disableProperty().bind(PlatformThread.sync(model.getNoDirectory()));
|
||||||
|
|
||||||
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
|
var menuButton = new MenuButton(null, new FontIcon("mdral-folder_open"));
|
||||||
new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null)).augment(new SimpleCompStructure<>(menuButton));
|
new ContextMenuAugment<>(
|
||||||
|
event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null))
|
||||||
|
.augment(new SimpleCompStructure<>(menuButton));
|
||||||
|
|
||||||
var filter = new BrowserFilterComp(model.getFilter()).createStructure();
|
var filter = new BrowserFilterComp(model.getFilter()).createStructure();
|
||||||
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
||||||
|
|
||||||
var topBar = new ToolBar();
|
var topBar = new ToolBar();
|
||||||
topBar.getItems()
|
topBar.getItems()
|
||||||
.setAll(backBtn, forthBtn, new Spacer(10), new BrowserNavBar(model).createRegion(), filter.get(), refreshBtn, terminalBtn, menuButton);
|
.setAll(
|
||||||
|
backBtn,
|
||||||
|
forthBtn,
|
||||||
|
new Spacer(10),
|
||||||
|
new BrowserNavBar(model).createRegion(),
|
||||||
|
filter.get(),
|
||||||
|
refreshBtn,
|
||||||
|
terminalBtn,
|
||||||
|
menuButton);
|
||||||
|
|
||||||
var directoryView = new BrowserFileListComp(model.getFileList()).createRegion();
|
var content = createFileListContent();
|
||||||
|
var root = new VBox(topBar, content);
|
||||||
var root = new VBox(topBar, directoryView);
|
VBox.setVgrow(content, Priority.ALWAYS);
|
||||||
root.getChildren().add(new BrowserStatusBarComp(model).createRegion());
|
|
||||||
VBox.setVgrow(directoryView, Priority.ALWAYS);
|
|
||||||
root.setPadding(Insets.EMPTY);
|
root.setPadding(Insets.EMPTY);
|
||||||
model.getFileList().predicateProperty().set(PREDICATE_NOT_HIDDEN);
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Region createFileListContent() {
|
||||||
|
var directoryView = new BrowserFileListComp(model.getFileList()).apply(struc -> VBox.setVgrow(struc.get(), Priority.ALWAYS));
|
||||||
|
var statusBar = new BrowserStatusBarComp(model);
|
||||||
|
var fileList = new VerticalComp(List.of(directoryView, statusBar));
|
||||||
|
|
||||||
|
var home = new BrowserOverviewComp(model);
|
||||||
|
var stack = new MultiContentComp(Map.of(
|
||||||
|
home,
|
||||||
|
model.getCurrentPath().isNull(),
|
||||||
|
fileList,
|
||||||
|
BindingsHelper.persist(model.getCurrentPath().isNull().not())));
|
||||||
|
return stack.createRegion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package io.xpipe.app.comp.base;
|
package io.xpipe.app.comp.base;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
|
||||||
import io.xpipe.app.fxcomps.CompStructure;
|
import io.xpipe.app.fxcomps.CompStructure;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
@ -34,7 +33,7 @@ public class BigIconButton extends ButtonComp {
|
||||||
label.getStyleClass().add("name");
|
label.getStyleClass().add("name");
|
||||||
vbox.getChildren().add(label);
|
vbox.getChildren().add(label);
|
||||||
|
|
||||||
var b = new JFXButton(null);
|
var b = new Button(null);
|
||||||
b.setGraphic(vbox);
|
b.setGraphic(vbox);
|
||||||
b.setOnAction(e -> getListener().run());
|
b.setOnAction(e -> getListener().run());
|
||||||
b.getStyleClass().add("big-icon-button-comp");
|
b.getStyleClass().add("big-icon-button-comp");
|
||||||
|
@ -50,14 +49,14 @@ public class BigIconButton extends ButtonComp {
|
||||||
@Value
|
@Value
|
||||||
@Builder
|
@Builder
|
||||||
public static class Structure implements CompStructure<Button> {
|
public static class Structure implements CompStructure<Button> {
|
||||||
JFXButton button;
|
Button button;
|
||||||
VBox stack;
|
VBox stack;
|
||||||
Node graphic;
|
Node graphic;
|
||||||
StackPane graphicPane;
|
StackPane graphicPane;
|
||||||
Label text;
|
Label text;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JFXButton get() {
|
public Button get() {
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
@ -57,6 +59,13 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
||||||
vbox.getChildren().add(b.createRegion());
|
vbox.getChildren().add(b.createRegion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var filler = new Button();
|
||||||
|
filler.setDisable(true);
|
||||||
|
filler.setMaxHeight(3000);
|
||||||
|
vbox.getChildren().add(filler);
|
||||||
|
VBox.setVgrow(filler, Priority.ALWAYS);
|
||||||
|
filler.prefWidthProperty().bind(vbox.widthProperty());
|
||||||
|
|
||||||
vbox.getStyleClass().add("sidebar-comp");
|
vbox.getStyleClass().add("sidebar-comp");
|
||||||
return new SimpleCompStructure<>(vbox);
|
return new SimpleCompStructure<>(vbox);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package io.xpipe.app.comp.base;
|
||||||
|
|
||||||
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
|
import io.xpipe.app.fxcomps.CompStructure;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.scene.control.TitledPane;
|
||||||
|
|
||||||
|
public class SimpleTitledPaneComp extends Comp<CompStructure<TitledPane>> {
|
||||||
|
|
||||||
|
private final ObservableValue<String> name;
|
||||||
|
private final Comp<?> content;
|
||||||
|
|
||||||
|
public SimpleTitledPaneComp(ObservableValue<String> name, Comp<?> content) {
|
||||||
|
this.name = name;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompStructure<TitledPane> createBase() {
|
||||||
|
var tp = new TitledPane(null, content.createRegion());
|
||||||
|
tp.textProperty().bind(name);
|
||||||
|
tp.getStyleClass().add("simple-titled-pane-comp");
|
||||||
|
tp.setExpanded(true);
|
||||||
|
tp.setCollapsible(false);
|
||||||
|
return new SimpleCompStructure<>(tp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ public class ContextMenuAugment<S extends CompStructure<?>> implements Augment<S
|
||||||
@Override
|
@Override
|
||||||
public void augment(S struc) {
|
public void augment(S struc) {
|
||||||
var r = struc.get();
|
var r = struc.get();
|
||||||
r.setOnMousePressed(event -> {
|
r.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
|
||||||
if (currentContextMenu != null && currentContextMenu.isShowing()) {
|
if (currentContextMenu != null && currentContextMenu.isShowing()) {
|
||||||
currentContextMenu.hide();
|
currentContextMenu.hide();
|
||||||
currentContextMenu = null;
|
currentContextMenu = null;
|
||||||
|
|
|
@ -1,9 +1,31 @@
|
||||||
package io.xpipe.app.issue;
|
package io.xpipe.app.issue;
|
||||||
|
|
||||||
import io.xpipe.app.core.AppI18n;
|
import io.xpipe.app.core.AppI18n;
|
||||||
|
import io.xpipe.app.util.Hyperlinks;
|
||||||
|
|
||||||
public interface ErrorAction {
|
public interface ErrorAction {
|
||||||
|
|
||||||
|
public static ErrorAction reportOnGithub() {
|
||||||
|
return new ErrorAction() {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return AppI18n.get("reportOnGithub");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return AppI18n.get("reportOnGithubDescription");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(ErrorEvent event) {
|
||||||
|
var url = "https://github.com/xpipe-io/xpipe/issues/new";
|
||||||
|
Hyperlinks.open(url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static ErrorAction sendDiagnostics() {
|
public static ErrorAction sendDiagnostics() {
|
||||||
return new ErrorAction() {
|
return new ErrorAction() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,14 +11,15 @@ import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||||
import io.xpipe.app.util.JfxHelper;
|
import io.xpipe.app.util.JfxHelper;
|
||||||
import io.xpipe.app.util.PlatformState;
|
import io.xpipe.app.util.PlatformState;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Separator;
|
import javafx.scene.control.Separator;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
@ -145,11 +146,7 @@ public class ErrorHandlerComp extends SimpleComp {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Region createTop() {
|
||||||
protected Region createSimple() {
|
|
||||||
var graphic = new FontIcon("mdomz-warning");
|
|
||||||
graphic.setIconColor(Color.RED);
|
|
||||||
|
|
||||||
var headerId = event.isTerminal() ? "terminalErrorOccured" : "errorOccured";
|
var headerId = event.isTerminal() ? "terminalErrorOccured" : "errorOccured";
|
||||||
var desc = event.getDescription();
|
var desc = event.getDescription();
|
||||||
if (desc == null && event.getThrowable() != null) {
|
if (desc == null && event.getThrowable() != null) {
|
||||||
|
@ -160,8 +157,36 @@ public class ErrorHandlerComp extends SimpleComp {
|
||||||
desc = AppI18n.get("errorNoDetail");
|
desc = AppI18n.get("errorNoDetail");
|
||||||
}
|
}
|
||||||
var limitedDescription = desc.substring(0, Math.min(1000, desc.length()));
|
var limitedDescription = desc.substring(0, Math.min(1000, desc.length()));
|
||||||
var top = JfxHelper.createNamedEntry(AppI18n.get(headerId), limitedDescription, graphic);
|
|
||||||
|
|
||||||
|
var header = new Label(AppI18n.get(headerId));
|
||||||
|
AppFont.header(header);
|
||||||
|
var descriptionField = new TextField(limitedDescription);
|
||||||
|
descriptionField.setEditable(false);
|
||||||
|
descriptionField.setPadding(Insets.EMPTY);
|
||||||
|
AppFont.small(descriptionField);
|
||||||
|
var text = new VBox(header, descriptionField);
|
||||||
|
text.setSpacing(2);
|
||||||
|
|
||||||
|
var graphic = new FontIcon("mdomz-warning");
|
||||||
|
graphic.setIconColor(Color.RED);
|
||||||
|
var pane = new StackPane(graphic);
|
||||||
|
var hbox = new HBox(pane, text);
|
||||||
|
hbox.setSpacing(8);
|
||||||
|
pane.prefHeightProperty()
|
||||||
|
.bind(Bindings.createDoubleBinding(
|
||||||
|
() -> header.getHeight() + descriptionField.getHeight() + 2,
|
||||||
|
header.heightProperty(),
|
||||||
|
descriptionField.heightProperty()));
|
||||||
|
pane.prefHeightProperty().addListener((c, o, n) -> {
|
||||||
|
var size = Math.min(n.intValue(), 100);
|
||||||
|
graphic.setIconSize(size);
|
||||||
|
});
|
||||||
|
return hbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Region createSimple() {
|
||||||
|
var top = createTop();
|
||||||
var content = new VBox(top, new Separator(Orientation.HORIZONTAL));
|
var content = new VBox(top, new Separator(Orientation.HORIZONTAL));
|
||||||
if (event.isReportable()) {
|
if (event.isReportable()) {
|
||||||
var header = new Label(AppI18n.get("possibleActions"));
|
var header = new Label(AppI18n.get("possibleActions"));
|
||||||
|
@ -170,7 +195,7 @@ public class ErrorHandlerComp extends SimpleComp {
|
||||||
actionBox.getStyleClass().add("actions");
|
actionBox.getStyleClass().add("actions");
|
||||||
actionBox.setFillWidth(true);
|
actionBox.setFillWidth(true);
|
||||||
|
|
||||||
for (var action : List.of(ErrorAction.sendDiagnostics(), ErrorAction.ignore())) {
|
for (var action : List.of(ErrorAction.sendDiagnostics(), ErrorAction.reportOnGithub(), ErrorAction.ignore())) {
|
||||||
var ac = createActionComp(action);
|
var ac = createActionComp(action);
|
||||||
actionBox.getChildren().add(ac);
|
actionBox.getChildren().add(ac);
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ public abstract class LauncherInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
var dir = Files.isDirectory(file) ? file : file.getParent();
|
var dir = Files.isDirectory(file) ? file : file.getParent();
|
||||||
BrowserModel.DEFAULT.openFileSystemAsync(null, ShellStore.createLocal(), dir.toString());
|
BrowserModel.DEFAULT.openFileSystemAsync(null, ShellStore.createLocal(), dir.toString(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -84,6 +84,8 @@ install=Install ...
|
||||||
ignore=Ignore
|
ignore=Ignore
|
||||||
possibleActions=Possible actions
|
possibleActions=Possible actions
|
||||||
reportError=Report error
|
reportError=Report error
|
||||||
|
reportOnGithub=Report on GitHub
|
||||||
|
reportOnGithubDescription=Open a new issue in the GitHub repository
|
||||||
reportErrorDescription=Send an error report with optional user feedback and diagnostics info
|
reportErrorDescription=Send an error report with optional user feedback and diagnostics info
|
||||||
ignoreError=Ignore error
|
ignoreError=Ignore error
|
||||||
ignoreErrorDescription=Ignore this error and continue like nothing happened
|
ignoreErrorDescription=Ignore this error and continue like nothing happened
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
-fx-padding: 1em;
|
-fx-padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.browser .home {
|
||||||
|
-fx-spacing: 1em;
|
||||||
|
-fx-padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.selected-file-list {
|
.selected-file-list {
|
||||||
-fx-spacing: 5px;
|
-fx-spacing: 5px;
|
||||||
-fx-padding: 8px;
|
-fx-padding: 8px;
|
||||||
|
@ -40,12 +45,8 @@
|
||||||
-fx-background-color: -color-success-muted;
|
-fx-background-color: -color-success-muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bookmark-list .button:disabled {
|
.bookmark-list > *:disabled {
|
||||||
-fx-opacity: 1.0;
|
-fx-opacity: 0.5;
|
||||||
}
|
|
||||||
|
|
||||||
.bookmark-list .button:disabled * {
|
|
||||||
-fx-opacity: 0.9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.browser .bookmark-list {
|
.browser .bookmark-list {
|
||||||
|
@ -169,7 +170,7 @@
|
||||||
-fx-background-color: -color-accent-subtle;
|
-fx-background-color: -color-accent-subtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.browser .table-row-cell:selected {
|
.browser .table-row-cell:selected, .browser .table-row-cell:hover:selected {
|
||||||
-fx-background-color: -color-success-subtle;
|
-fx-background-color: -color-success-subtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,14 @@
|
||||||
-fx-spacing: 1em;
|
-fx-spacing: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-handler-comp .text-field {
|
||||||
|
-fx-padding: 0;
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-border-radius: 0;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.error-handler-comp .actions {
|
.error-handler-comp .actions {
|
||||||
-fx-spacing: 0.3em;
|
-fx-spacing: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,28 @@
|
||||||
.sidebar-comp {
|
.sidebar-comp {
|
||||||
-fx-pref-width: 7em;
|
-fx-pref-width: 7em;
|
||||||
-fx-background-color: -color-neutral-muted;
|
-fx-border-width: 0 0 0 1px;
|
||||||
-fx-border-width: 0 0 0 0.05em;
|
|
||||||
-fx-border-color: -color-neutral-emphasis;
|
-fx-border-color: -color-neutral-emphasis;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
|
-fx-background-insets: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-comp .button:hover {
|
.sidebar-comp .big-icon-button-comp, .sidebar-comp .button {
|
||||||
-fx-background-color: -color-neutral-muted;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-comp .big-icon-button-comp {
|
|
||||||
-fx-background-radius: 0;
|
-fx-background-radius: 0;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-comp .button:disabled {
|
||||||
|
-fx-opacity: 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-comp .big-icon-button-comp:selected {
|
.sidebar-comp .big-icon-button-comp:selected {
|
||||||
-fx-background-color: -color-accent-muted;
|
-fx-background-color: -color-accent-subtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-comp .big-icon-button-comp .icon {
|
.sidebar-comp .big-icon-button-comp .icon {
|
||||||
-fx-font-size: 1.5em;
|
-fx-font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-comp .big-icon-button-comp * {
|
|
||||||
-fx-text-fill: -color-fg-default;
|
|
||||||
-fx-icon-color: -color-fg-default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-comp .big-icon-button-comp {
|
.sidebar-comp .big-icon-button-comp {
|
||||||
-fx-padding: 1.1em 0.6em 1.1em 0.6em;
|
-fx-padding: 1.1em 0.6em 1.1em 0.6em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.core.process;
|
||||||
|
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -33,6 +34,8 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
|
||||||
return FileNames.join(getXPipeHomeDirectory(pc), "system_id");
|
return FileNames.join(getXPipeHomeDirectory(pc), "system_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> determineInterestingPaths(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
String getHomeDirectory(ShellControl pc) throws Exception;
|
String getHomeDirectory(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
String getFileSystemSeparator();
|
String getFileSystemSeparator();
|
||||||
|
@ -47,6 +50,12 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
|
||||||
|
|
||||||
static final class Windows implements OsType {
|
static final class Windows implements OsType {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> determineInterestingPaths(ShellControl pc) throws Exception {
|
||||||
|
var home = getHomeDirectory(pc);
|
||||||
|
return List.of(FileNames.join(home, "Desktop"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHomeDirectory(ShellControl pc) throws Exception {
|
public String getHomeDirectory(ShellControl pc) throws Exception {
|
||||||
return pc.executeSimpleStringCommand(
|
return pc.executeSimpleStringCommand(
|
||||||
|
@ -99,6 +108,12 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
|
||||||
|
|
||||||
static final class Linux implements OsType {
|
static final class Linux implements OsType {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> determineInterestingPaths(ShellControl pc) throws Exception {
|
||||||
|
var home = getHomeDirectory(pc);
|
||||||
|
return List.of(FileNames.join(home, "Desktop"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHomeDirectory(ShellControl pc) throws Exception {
|
public String getHomeDirectory(ShellControl pc) throws Exception {
|
||||||
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
|
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
|
||||||
|
@ -162,6 +177,12 @@ public sealed interface OsType permits OsType.Windows, OsType.Linux, OsType.MacO
|
||||||
|
|
||||||
static final class MacOs implements OsType {
|
static final class MacOs implements OsType {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> determineInterestingPaths(ShellControl pc) throws Exception {
|
||||||
|
var home = getHomeDirectory(pc);
|
||||||
|
return List.of(FileNames.join(home, "Desktop"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHomeDirectory(ShellControl pc) throws Exception {
|
public String getHomeDirectory(ShellControl pc) throws Exception {
|
||||||
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
|
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
||||||
model.getBrowserModel().openFileSystemAsync(model.getName(), model.getStore().asNeeded(), entries.get(0).getRawFileEntry().getPath());
|
model.getBrowserModel().openFileSystemAsync(model.getName(), model.getStore().asNeeded(), entries.get(0).getRawFileEntry().getPath(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue