From 07472cad9a78bdcf48570a1947ef35327158f9d2 Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 26 Mar 2024 13:53:51 +0000 Subject: [PATCH] Add browser quick access --- .../app/browser/BrowserFileListComp.java | 24 +++- .../app/browser/BrowserOverviewComp.java | 2 +- .../browser/BrowserQuickAccessButtonComp.java | 129 ++++++++++++++++++ .../store/StoreQuickAccessButtonComp.java | 9 -- 4 files changed, 148 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/browser/BrowserQuickAccessButtonComp.java diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFileListComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserFileListComp.java index 0999c17e..fa0b1b66 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFileListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFileListComp.java @@ -1,5 +1,6 @@ package io.xpipe.app.browser; +import atlantafx.base.controls.Spacer; import atlantafx.base.theme.Styles; import io.xpipe.app.browser.action.BrowserAction; import io.xpipe.app.browser.icon.FileIconManager; @@ -36,7 +37,6 @@ import javafx.scene.input.MouseEvent; 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; @@ -485,8 +485,6 @@ final class BrowserFileListComp extends SimpleComp { private final StringProperty img = new SimpleStringProperty(); private final StringProperty text = new SimpleStringProperty(); - private final StackPane textField = - new LazyTextFieldComp(text).createStructure().get(); private final BooleanProperty updating = new SimpleBooleanProperty(); @@ -499,6 +497,17 @@ final class BrowserFileListComp extends SimpleComp { itemProperty())); setAccessibleRole(AccessibleRole.TEXT); + var textField = new LazyTextFieldComp(text).minWidth(USE_PREF_SIZE).createStructure().get(); + var quickAccess = new BrowserQuickAccessButtonComp(() -> getTableRow().getItem().getRawFileEntry(), fileList.getFileSystemModel(), fileEntry -> {}) + .hide(Bindings.createBooleanBinding(() -> { + var notDir = getTableRow().getItem().getRawFileEntry().getKind() != FileKind.DIRECTORY; + var isParentLink = getTableRow() + .getItem() + .getRawFileEntry() + .equals(fileList.getFileSystemModel().getCurrentParentDirectory()); + return notDir || isParentLink; + }, itemProperty())).createRegion(); + editing.addListener((observable, oldValue, newValue) -> { if (getTableRow().getItem() != null && getTableRow().getItem().equals(newValue)) { PlatformThread.runLaterIfNeeded(() -> textField.requestFocus()); @@ -518,10 +527,13 @@ final class BrowserFileListComp extends SimpleComp { text.addListener(listener); Node imageView = new PrettySvgComp(img, 24, 24).createRegion(); - HBox graphic = new HBox(imageView, textField); - graphic.setSpacing(10); - graphic.setAlignment(Pos.CENTER_LEFT); + HBox graphic = new HBox(imageView, + new Spacer(7), + quickAccess, + new Spacer(3), + textField); HBox.setHgrow(textField, Priority.ALWAYS); + graphic.setAlignment(Pos.CENTER_LEFT); setGraphic(graphic); } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java index 52b2579e..4622876b 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserOverviewComp.java @@ -72,7 +72,7 @@ public class BrowserOverviewComp extends SimpleComp { var recentOverview = new BrowserFileOverviewComp(model, recent, true); var recentPane = new SimpleTitledPaneComp(AppI18n.observable("recent"), recentOverview); - var vbox = new VerticalComp(List.of(commonPane, rootsPane, recentPane)).styleClass("overview"); + var vbox = new VerticalComp(List.of(recentPane, commonPane, rootsPane)).styleClass("overview"); return vbox.createRegion(); } } diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserQuickAccessButtonComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserQuickAccessButtonComp.java new file mode 100644 index 00000000..462d0ae5 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/browser/BrowserQuickAccessButtonComp.java @@ -0,0 +1,129 @@ +package io.xpipe.app.browser; + +import io.xpipe.app.browser.icon.FileIconManager; +import io.xpipe.app.fxcomps.SimpleComp; +import io.xpipe.app.fxcomps.impl.IconButtonComp; +import io.xpipe.app.fxcomps.impl.PrettyImageHelper; +import io.xpipe.app.util.ThreadHelper; +import io.xpipe.core.store.FileKind; +import io.xpipe.core.store.FileSystem; +import javafx.application.Platform; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import javafx.scene.layout.Region; + +import java.util.ArrayList; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class BrowserQuickAccessButtonComp extends SimpleComp { + + private final Supplier base; + private final OpenFileSystemModel model; + private final Consumer action; + + public BrowserQuickAccessButtonComp(Supplier base, OpenFileSystemModel model, Consumer action) { + this.base = base; + this.model = model; + this.action = action; + } + + @Override + protected Region createSimple() { + var button = new IconButtonComp("mdi2c-chevron-double-right"); + button.apply(struc -> { + struc.get().setOnAction(event -> { + showMenu(struc.get()); + }); + }); + return button.createRegion(); + } + + private void showMenu(Node anchor) { + ThreadHelper.runFailableAsync(() -> { + var children = model.getFileSystem().listFiles(base.get().getPath()); + try (var s = children) { + var list = s.toList(); + if (list.isEmpty()) { + return; + } + + Platform.runLater(() -> { + var cm = new ContextMenu(); + cm.addEventHandler(Menu.ON_SHOWING, e -> { + Node content = cm.getSkin().getNode(); + if (content instanceof Region r) { + r.setMaxWidth(500); + r.setMaxHeight(600); + } + }); + cm.setAutoHide(true); + cm.getStyleClass().add("condensed"); + cm.getItems().addAll(list.stream().map(e -> recurse(cm, e)).toList()); + cm.show(anchor, Side.RIGHT, 0, 0); + }); + } + }); + } + + private MenuItem recurse(ContextMenu contextMenu, FileSystem.FileEntry fileEntry) { + if (fileEntry.getKind() != FileKind.DIRECTORY) { + var m = new MenuItem( + fileEntry.getName(), + PrettyImageHelper.ofFixedSquare(FileIconManager.getFileIcon(fileEntry,false), 16).createRegion()); + m.setMnemonicParsing(false); + m.setOnAction(event -> { + action.accept(fileEntry); + event.consume(); + }); + return m; + } + + var m = new Menu( + fileEntry.getName(), + PrettyImageHelper.ofFixedSquare(FileIconManager.getFileIcon(fileEntry,false), 16).createRegion()); + m.setMnemonicParsing(false); + m.setOnAction(event -> { + if (event.getTarget() == m) { + if (m.isShowing()) { + event.consume(); + return; + } + + ThreadHelper.runFailableAsync(() -> { + updateMenuItems(m, fileEntry); + }); + action.accept(fileEntry); + event.consume(); + } + }); + return m; + } + + private void updateMenuItems(Menu m, FileSystem.FileEntry fileEntry) throws Exception { + var newFiles = model.getFileSystem().listFiles(fileEntry.getPath()); + try (var s = newFiles) { + var list = s.toList(); + + var newItems = new ArrayList(); + if (list.isEmpty()) { + newItems.add(new MenuItem("")); + } else if (list.size() == 1 && list.getFirst().getKind() == FileKind.DIRECTORY) { + var subMenu = recurse(m.getParentPopup(),list.getFirst()); + updateMenuItems(m, list.getFirst()); + newItems.add(subMenu); + } else { + newItems.addAll(list.stream().map(e -> recurse(m.getParentPopup(), e)).toList()); + } + + Platform.runLater(() -> { + m.getItems().setAll(newItems); + m.hide(); + m.show(); + }); + } + } +} diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreQuickAccessButtonComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreQuickAccessButtonComp.java index bb238a9a..737c88e5 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreQuickAccessButtonComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreQuickAccessButtonComp.java @@ -41,15 +41,6 @@ public class StoreQuickAccessButtonComp extends SimpleComp { } cm.show(anchor, Side.RIGHT, 0, 0); - - // App.getApp().getStage().getScene().addEventFilter(MouseEvent.MOUSE_MOVED, event -> { - // var stages = Stage.getWindows().stream().filter(window -> window instanceof ContextMenu).toList(); - // var hovered = stages.stream().anyMatch(window -> - // window.getScene().getRoot().hoverProperty().get()); - // if (!hovered) { - // stages.forEach(window -> window.hide()); - // } - // }); } private ContextMenu createMenu() {