diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserFilterComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserFilterComp.java index e29b7a2c..88d1a3b8 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserFilterComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserFilterComp.java @@ -1,19 +1,21 @@ package io.xpipe.app.browser; +import atlantafx.base.theme.Styles; import io.xpipe.app.browser.fs.OpenFileSystemModel; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.impl.TextFieldComp; import io.xpipe.app.fxcomps.impl.TooltipAugment; - +import io.xpipe.app.util.InputHelper; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import javafx.scene.layout.HBox; - -import atlantafx.base.theme.Styles; import org.kordamp.ikonli.javafx.FontIcon; public class BrowserFilterComp extends Comp { @@ -29,9 +31,15 @@ public class BrowserFilterComp extends Comp { @Override public Structure createBase() { var expanded = new SimpleBooleanProperty(); - var text = new TextFieldComp(filterString, false).createRegion(); + var text = new TextFieldComp(filterString, false).createStructure().get(); var button = new Button(); - new TooltipAugment<>("app.search").augment(button); + button.setFocusTraversable(true); + InputHelper.onExactKeyCode(text, KeyCode.ESCAPE, true, keyEvent -> { + text.clear(); + button.fire(); + keyEvent.consume(); + }); + new TooltipAugment<>("app.search", new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN)).augment(button); text.focusedProperty().addListener((observable, oldValue, newValue) -> { if (!newValue && filterString.getValue() == null) { if (button.isFocused()) { diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java index 716f2acf..9339513d 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserNavBar.java @@ -1,19 +1,17 @@ package io.xpipe.app.browser; +import atlantafx.base.theme.Styles; import io.xpipe.app.browser.file.BrowserContextMenu; import io.xpipe.app.browser.fs.OpenFileSystemModel; import io.xpipe.app.browser.icon.FileIconManager; import io.xpipe.app.fxcomps.Comp; -import io.xpipe.app.fxcomps.SimpleComp; +import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.augment.ContextMenuAugment; -import io.xpipe.app.fxcomps.impl.HorizontalComp; import io.xpipe.app.fxcomps.impl.PrettyImageHelper; -import io.xpipe.app.fxcomps.impl.StackComp; import io.xpipe.app.fxcomps.impl.TextFieldComp; import io.xpipe.app.util.BooleanScope; import io.xpipe.app.util.ThreadHelper; - import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; @@ -25,25 +23,16 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.input.MouseButton; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; import javafx.scene.layout.Region; - -import atlantafx.base.theme.Styles; +import javafx.scene.layout.StackPane; import org.kordamp.ikonli.javafx.FontIcon; -import java.util.List; - -public class BrowserNavBar extends SimpleComp { - - private static final PseudoClass INVISIBLE = PseudoClass.getPseudoClass("invisible"); - - private final OpenFileSystemModel model; - - public BrowserNavBar(OpenFileSystemModel model) { - this.model = model; - } +public class BrowserNavBar extends Comp { @Override - protected Region createSimple() { + public Structure createBase() { var path = new SimpleStringProperty(model.getCurrentPath().get()); model.getCurrentPath().subscribe((newValue) -> { path.set(newValue); @@ -107,62 +96,61 @@ public class BrowserNavBar extends SimpleComp { homeButton.getStyleClass().add(Styles.LEFT_PILL); homeButton.getStyleClass().add("path-graphic-button"); new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, null, () -> { - return model.getInOverview().get() ? null : new BrowserContextMenu(model, null); - }) + return model.getInOverview().get() ? null : new BrowserContextMenu(model, null); + }) .augment(new SimpleCompStructure<>(homeButton)); var historyButton = new Button(null, new FontIcon("mdi2h-history")); historyButton.setAccessibleText("History"); historyButton.getStyleClass().add(Styles.RIGHT_PILL); - // historyButton.getStyleClass().add("path-graphic-button"); new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, null, this::createContextMenu) .augment(new SimpleCompStructure<>(historyButton)); var breadcrumbs = new BrowserBreadcrumbBar(model).grow(false, true); - var stack = new StackComp(List.of(pathBar, breadcrumbs)) - .apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT)) - .hgrow() - .apply(struc -> { - var t = struc.get().getChildren().get(0); - var b = struc.get().getChildren().get(1); - b.setOnMouseClicked(event -> { - t.requestFocus(); - event.consume(); - }); - b.visibleProperty() - .bind(Bindings.createBooleanBinding( - () -> { - return !t.isFocused() - && !model.getInOverview().get(); - }, - t.focusedProperty(), - model.getInOverview())); - }) - .grow(false, true); - var topBox = new HorizontalComp(List.of(Comp.of(() -> homeButton), stack, Comp.of(() -> historyButton))) - .apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT)) - .apply(struc -> { - ((Region) struc.get().getChildren().get(0)) - .minHeightProperty() - .bind(((Region) struc.get().getChildren().get(1)).heightProperty()); - ((Region) struc.get().getChildren().get(0)) - .maxHeightProperty() - .bind(((Region) struc.get().getChildren().get(1)).heightProperty()); + var pathRegion = pathBar.createStructure().get(); + var breadcrumbsRegion = breadcrumbs.createRegion(); + breadcrumbsRegion.setOnMouseClicked(event -> { + pathRegion.requestFocus(); + event.consume(); + }); + breadcrumbsRegion.visibleProperty() + .bind(Bindings.createBooleanBinding( + () -> { + return !pathRegion.isFocused() + && !model.getInOverview().get(); + }, + pathRegion.focusedProperty(), + model.getInOverview())); + var stack = new StackPane(pathRegion, breadcrumbsRegion); + stack.setAlignment(Pos.CENTER_LEFT); + HBox.setHgrow(stack, Priority.ALWAYS); - ((Region) struc.get().getChildren().get(2)) - .minHeightProperty() - .bind(((Region) struc.get().getChildren().get(1)).heightProperty()); - ((Region) struc.get().getChildren().get(2)) - .maxHeightProperty() - .bind(((Region) struc.get().getChildren().get(1)).heightProperty()); - }) - .apply(struc -> { - struc.get().setPickOnBounds(false); - }) - .hgrow(); + var topBox = new HBox(homeButton, stack, historyButton); + homeButton.minHeightProperty().bind(stack.heightProperty()); + homeButton.maxHeightProperty().bind(stack.heightProperty()); + historyButton.minHeightProperty().bind(stack.heightProperty()); + historyButton.maxHeightProperty().bind(stack.heightProperty()); + topBox.setPickOnBounds(false); + HBox.setHgrow(topBox, Priority.ALWAYS); - return topBox.createRegion(); + return new Structure(topBox,pathRegion, historyButton); + } + + public record Structure(HBox box, TextField textField, Button historyButton) implements CompStructure { + + @Override + public HBox get() { + return box; + } + } + + private static final PseudoClass INVISIBLE = PseudoClass.getPseudoClass("invisible"); + + private final OpenFileSystemModel model; + + public BrowserNavBar(OpenFileSystemModel model) { + this.model = model; } private ContextMenu createContextMenu() { diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java index 2d511272..182e5411 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserTransferComp.java @@ -78,7 +78,7 @@ public class BrowserTransferComp extends SimpleComp { }) .hide(Bindings.isEmpty(syncItems)) .disable(syncAllDownloaded) - .apply(new TooltipAugment<>("downloadStageDescription")); + .tooltipKey("downloadStageDescription"); var clearButton = new IconButtonComp("mdi2c-close", () -> { model.clear(); }) diff --git a/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java b/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java index 8e75b43d..a1fffade 100644 --- a/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java +++ b/app/src/main/java/io/xpipe/app/browser/action/LeafAction.java @@ -4,14 +4,13 @@ import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.fs.OpenFileSystemModel; import io.xpipe.app.fxcomps.impl.TooltipAugment; import io.xpipe.app.fxcomps.util.BindingsHelper; -import io.xpipe.app.fxcomps.util.Shortcuts; import io.xpipe.app.util.BooleanScope; import io.xpipe.app.util.LicenseProvider; import io.xpipe.app.util.ThreadHelper; - import javafx.scene.control.Button; import javafx.scene.control.MenuItem; - +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.Region; import org.kordamp.ikonli.javafx.FontIcon; import java.util.List; @@ -20,7 +19,7 @@ public interface LeafAction extends BrowserAction { void execute(OpenFileSystemModel model, List entries) throws Exception; - default Button toButton(OpenFileSystemModel model, List selected) { + default Button toButton(Region root, OpenFileSystemModel model, List selected) { var b = new Button(); b.setOnAction(event -> { // Only accept shortcut actions in the current tab @@ -37,17 +36,20 @@ public interface LeafAction extends BrowserAction { }); event.consume(); }); - if (getShortcut() != null) { - Shortcuts.addShortcut(b, getShortcut()); - } var name = getName(model, selected); - new TooltipAugment<>(name).augment(b); + new TooltipAugment<>(name, getShortcut()).augment(b); var graphic = getIcon(model, selected); if (graphic != null) { b.setGraphic(graphic); } b.setMnemonicParsing(false); b.accessibleTextProperty().bind(name); + root.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (getShortcut() != null && getShortcut().match(event)) { + b.fire(); + event.consume(); + } + }); b.setDisable(!isActive(model, selected)); model.getCurrentPath().addListener((observable, oldValue, newValue) -> { diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserContextMenu.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserContextMenu.java index 08e3e515..9db73e32 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserContextMenu.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserContextMenu.java @@ -5,6 +5,7 @@ import io.xpipe.app.browser.action.BrowserAction; import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.fs.OpenFileSystemModel; import io.xpipe.app.core.AppFont; +import io.xpipe.app.util.InputHelper; import io.xpipe.app.util.LicenseProvider; import javafx.scene.control.ContextMenu; @@ -38,6 +39,11 @@ public final class BrowserContextMenu extends ContextMenu { } private void createMenu() { + InputHelper.onLeft(this, false, e -> { + hide(); + e.consume(); + }); + AppFont.normal(this.getStyleableNode()); var empty = source == null; diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java index b22d5b37..038db691 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListComp.java @@ -10,9 +10,7 @@ import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.augment.ContextMenuAugment; import io.xpipe.app.fxcomps.impl.PrettyImageHelper; import io.xpipe.app.fxcomps.util.PlatformThread; -import io.xpipe.app.util.BooleanScope; -import io.xpipe.app.util.HumanReadableFormat; -import io.xpipe.app.util.ThreadHelper; +import io.xpipe.app.util.*; import io.xpipe.core.process.OsType; import io.xpipe.core.store.FileKind; import io.xpipe.core.store.FileNames; @@ -26,12 +24,16 @@ import javafx.collections.ListChangeListener; import javafx.css.PseudoClass; import javafx.geometry.Bounds; import javafx.geometry.Pos; +import javafx.geometry.Side; import javafx.scene.AccessibleRole; import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.control.skin.TableViewSkin; import javafx.scene.control.skin.VirtualFlow; -import javafx.scene.input.*; +import javafx.scene.input.DragEvent; +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; @@ -128,7 +130,6 @@ public final class BrowserFileListComp extends SimpleComp { } else { table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); } - table.getSelectionModel().setCellSelectionEnabled(false); table.getSelectionModel().getSelectedItems().addListener((ListChangeListener) c -> { @@ -158,18 +159,24 @@ public final class BrowserFileListComp extends SimpleComp { private void prepareTableShortcuts(TableView table) { table.setOnKeyPressed(event -> { var selected = fileList.getSelection(); - BrowserAction.getFlattened(fileList.getFileSystemModel(), selected).stream() - .filter(browserAction -> browserAction.isApplicable(fileList.getFileSystemModel(), selected) - && browserAction.isActive(fileList.getFileSystemModel(), selected)) - .filter(browserAction -> browserAction.getShortcut() != null) - .filter(browserAction -> browserAction.getShortcut().match(event)) - .findAny() - .ifPresent(browserAction -> { + var action = BrowserAction.getFlattened(fileList.getFileSystemModel(), selected).stream().filter( + browserAction -> browserAction.isApplicable(fileList.getFileSystemModel(), selected) && + browserAction.isActive(fileList.getFileSystemModel(), selected)).filter( + browserAction -> browserAction.getShortcut() != null).filter(browserAction -> browserAction.getShortcut().match(event)).findAny(); + action.ifPresent(browserAction -> { ThreadHelper.runFailableAsync(() -> { browserAction.execute(fileList.getFileSystemModel(), selected); }); event.consume(); }); + if (action.isPresent()) { + return; + } + + if (event.getCode() == KeyCode.ESCAPE) { + table.getSelectionModel().clearSelection(); + event.consume(); + } }); } @@ -344,7 +351,6 @@ public final class BrowserFileListComp extends SimpleComp { // Sort the list ourselves as sorting the table would incur a lot of cell updates var obs = FXCollections.observableList(newItems); table.getItems().setAll(obs); - // table.sort(); } var currentDirectory = fileList.getFileSystemModel().getCurrentDirectory(); @@ -488,9 +494,6 @@ public final class BrowserFileListComp extends SimpleComp { .not() .not()) .focusTraversable(false) - .apply(struc -> struc.get().focusedProperty().addListener((observable, oldValue, newValue) -> { - getTableRow().requestFocus(); - })) .createRegion(); editing.addListener((observable, oldValue, newValue) -> { @@ -520,13 +523,20 @@ public final class BrowserFileListComp extends SimpleComp { graphic.setAlignment(Pos.CENTER_LEFT); setGraphic(graphic); - tableView.addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (event.getCode() == KeyCode.RIGHT) { - var selected = fileList.getSelection(); - if (selected.size() == 1 && selected.getFirst() == getTableRow().getItem()) { - ((ButtonBase) quickAccess).fire(); - event.consume(); - } + InputHelper.onExactKeyCode(tableView, KeyCode.RIGHT, true, event -> { + var selected = fileList.getSelection(); + if (selected.size() == 1 && selected.getFirst() == getTableRow().getItem()) { + ((ButtonBase) quickAccess).fire(); + event.consume(); + } + }); + InputHelper.onExactKeyCode(tableView, KeyCode.SPACE, true, event -> { + var selected = fileList.getSelection(); + // Only show one menu across all selected entries + if (selected.size() > 0 && selected.getLast() == getTableRow().getItem()) { + var cm = new BrowserContextMenu(fileList.getFileSystemModel(), getTableRow().getItem()); + ContextMenuHelper.toggleShow(cm, this, Side.RIGHT); + event.consume(); } }); } diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListModel.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListModel.java index 3012d44e..8ff3074e 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListModel.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileListModel.java @@ -1,19 +1,16 @@ package io.xpipe.app.browser.file; import io.xpipe.app.browser.fs.OpenFileSystemModel; -import io.xpipe.app.fxcomps.util.ListBindingsHelper; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.core.store.FileKind; import io.xpipe.core.store.FileNames; import io.xpipe.core.store.FileSystem; - import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; - import lombok.Getter; import java.util.ArrayList; @@ -37,8 +34,6 @@ public final class BrowserFileListModel { private final Property> shown = new SimpleObjectProperty<>(new ArrayList<>()); private final ObservableList previousSelection = FXCollections.observableArrayList(); private final ObservableList selection = FXCollections.observableArrayList(); - private final ObservableList selectedRaw = - ListBindingsHelper.mappedContentBinding(selection, entry -> entry.getRawFileEntry()); private final Property draggedOverDirectory = new SimpleObjectProperty<>(); private final Property draggedOverEmpty = new SimpleBooleanProperty(); diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessButtonComp.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessButtonComp.java index dd1c8a1b..629c094b 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessButtonComp.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserQuickAccessButtonComp.java @@ -4,9 +4,6 @@ import io.xpipe.app.browser.fs.OpenFileSystemModel; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.impl.IconButtonComp; import io.xpipe.app.util.InputHelper; - -import javafx.application.Platform; -import javafx.scene.control.Menu; import javafx.scene.layout.Region; import java.util.function.Supplier; @@ -34,11 +31,6 @@ public class BrowserQuickAccessButtonComp extends SimpleComp { } event.consume(); }); - cm.addEventFilter(Menu.ON_HIDDEN, e -> { - Platform.runLater(() -> { - struc.get().requestFocus(); - }); - }); InputHelper.onRight(struc.get(), false, keyEvent -> { cm.showMenu(struc.get()); keyEvent.consume(); diff --git a/app/src/main/java/io/xpipe/app/browser/fs/OpenFileSystemComp.java b/app/src/main/java/io/xpipe/app/browser/fs/OpenFileSystemComp.java index 20cf63c2..3a59da1f 100644 --- a/app/src/main/java/io/xpipe/app/browser/fs/OpenFileSystemComp.java +++ b/app/src/main/java/io/xpipe/app/browser/fs/OpenFileSystemComp.java @@ -1,5 +1,6 @@ package io.xpipe.app.browser.fs; +import atlantafx.base.controls.Spacer; import io.xpipe.app.browser.BrowserFilterComp; import io.xpipe.app.browser.BrowserNavBar; import io.xpipe.app.browser.BrowserOverviewComp; @@ -13,9 +14,9 @@ import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.fxcomps.augment.ContextMenuAugment; +import io.xpipe.app.fxcomps.impl.TooltipAugment; import io.xpipe.app.fxcomps.impl.VerticalComp; -import io.xpipe.app.fxcomps.util.Shortcuts; - +import io.xpipe.app.util.InputHelper; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -28,8 +29,6 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; - -import atlantafx.base.controls.Spacer; import org.kordamp.ikonli.javafx.FontIcon; import java.util.ArrayList; @@ -53,17 +52,24 @@ public class OpenFileSystemComp extends SimpleComp { } private Region createContent() { + var root = new VBox(); var overview = new Button(null, new FontIcon("mdi2m-monitor")); overview.setOnAction(e -> model.cdAsync(null)); + new TooltipAugment<>("overview", new KeyCodeCombination(KeyCode.HOME, KeyCombination.ALT_DOWN)).augment(overview); overview.disableProperty().bind(model.getInOverview()); overview.setAccessibleText("System overview"); + InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.HOME, KeyCombination.ALT_DOWN), true, keyEvent -> { + overview.fire(); + keyEvent.consume(); + }); - var backBtn = BrowserAction.byId("back", model, List.of()).toButton(model, List.of()); - var forthBtn = BrowserAction.byId("forward", model, List.of()).toButton(model, List.of()); - var refreshBtn = BrowserAction.byId("refresh", model, List.of()).toButton(model, List.of()); - var terminalBtn = BrowserAction.byId("openTerminal", model, List.of()).toButton(model, List.of()); + var backBtn = BrowserAction.byId("back", model, List.of()).toButton(root, model, List.of()); + var forthBtn = BrowserAction.byId("forward", model, List.of()).toButton(root, model, List.of()); + var refreshBtn = BrowserAction.byId("refresh", model, List.of()).toButton(root, model, List.of()); + var terminalBtn = BrowserAction.byId("openTerminal", model, List.of()).toButton(root, model, List.of()); - 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, null, @@ -73,18 +79,18 @@ public class OpenFileSystemComp extends SimpleComp { menuButton.setAccessibleText("Directory options"); var filter = new BrowserFilterComp(model, model.getFilter()).createStructure(); - Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN)); var topBar = new HBox(); topBar.setAlignment(Pos.CENTER); topBar.getStyleClass().add("top-bar"); + var navBar = new BrowserNavBar(model).createStructure(); topBar.getChildren() .setAll( overview, backBtn, forthBtn, new Spacer(10), - new BrowserNavBar(model).hgrow().createRegion(), + navBar.get(), new Spacer(5), filter.get(), refreshBtn, @@ -92,9 +98,18 @@ public class OpenFileSystemComp extends SimpleComp { menuButton); var content = createFileListContent(); - var root = new VBox(topBar, content); + root.getChildren().addAll(topBar, content); VBox.setVgrow(content, Priority.ALWAYS); root.setPadding(Insets.EMPTY); + InputHelper.onCtrlKeyCode(root, KeyCode.F, true, keyEvent -> { + filter.toggleButton().fire(); + filter.textField().requestFocus(); + keyEvent.consume(); + }); + InputHelper.onCtrlKeyCode(root, KeyCode.L, true, keyEvent -> { + navBar.textField().requestFocus(); + keyEvent.consume(); + }); return root; } diff --git a/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionTabsComp.java b/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionTabsComp.java index dc50cc75..007f2708 100644 --- a/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionTabsComp.java +++ b/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionTabsComp.java @@ -10,6 +10,7 @@ import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.util.BooleanScope; +import io.xpipe.app.util.InputHelper; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleBooleanProperty; @@ -21,6 +22,7 @@ import javafx.scene.control.Label; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.input.DragEvent; +import javafx.scene.input.KeyCode; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -164,6 +166,24 @@ public class BrowserSessionTabsComp extends SimpleComp { } } }); + + InputHelper.onInput(tabs, true, keyEvent -> { + var current = tabs.getSelectionModel().getSelectedItem(); + if (current == null) { + return; + } + + if (keyEvent.getCode() == KeyCode.W && keyEvent.isShortcutDown()) { + tabs.getTabs().remove(current); + keyEvent.consume(); + } + + if (keyEvent.getCode() == KeyCode.W && keyEvent.isShortcutDown() && keyEvent.isShiftDown()) { + tabs.getTabs().clear(); + keyEvent.consume(); + } + }); + return tabs; } @@ -216,7 +236,7 @@ public class BrowserSessionTabsComp extends SimpleComp { if (color != null) { c.getStyleClass().add(color.getId()); } - new TooltipAugment<>(new SimpleStringProperty(model.getTooltip())).augment(c); + new TooltipAugment<>(new SimpleStringProperty(model.getTooltip()), null).augment(c); c.addEventHandler( DragEvent.DRAG_ENTERED, mouseEvent -> Platform.runLater( diff --git a/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java b/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java index 9b1def5f..d7090c60 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/SideMenuBarComp.java @@ -70,8 +70,8 @@ public class SideMenuBarComp extends Comp> { for (int i = 0; i < entries.size(); i++) { var e = entries.get(i); var b = new IconButtonComp(e.icon(), () -> value.setValue(e)); - b.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + i])); - b.apply(new TooltipAugment<>(e.name())); + var shortcut = new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + i]); + b.apply(new TooltipAugment<>(e.name(), shortcut)); b.apply(struc -> { AppFont.setSize(struc.get(), 2); struc.get().pseudoClassStateChanged(selected, value.getValue().equals(e)); @@ -123,9 +123,9 @@ public class SideMenuBarComp extends Comp> { }; { + var shortcut = new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size()]); var b = new IconButtonComp("mdi2g-github", () -> Hyperlinks.open(Hyperlinks.GITHUB)) - .shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size()])) - .apply(new TooltipAugment<>("visitGithubRepository")) + .tooltipKey("visitGithubRepository", shortcut) .apply(simpleBorders) .accessibleTextKey("visitGithubRepository"); b.apply(struc -> { @@ -135,9 +135,9 @@ public class SideMenuBarComp extends Comp> { } { + var shortcut = new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 1]); var b = new IconButtonComp("mdi2d-discord", () -> Hyperlinks.open(Hyperlinks.DISCORD)) - .shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 1])) - .apply(new TooltipAugment<>("discord")) + .tooltipKey("discord", shortcut) .apply(simpleBorders) .accessibleTextKey("discord"); b.apply(struc -> { @@ -147,9 +147,9 @@ public class SideMenuBarComp extends Comp> { } { + var shortcut = new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 2]); var b = new IconButtonComp("mdi2t-translate", () -> Hyperlinks.open(Hyperlinks.TRANSLATE)) - .shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 2])) - .apply(new TooltipAugment<>("translate")) + .tooltipKey("translate", shortcut) .apply(simpleBorders) .accessibleTextKey("translate"); b.apply(struc -> { @@ -160,7 +160,7 @@ public class SideMenuBarComp extends Comp> { { var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded()) - .apply(new TooltipAugment<>("updateAvailableTooltip")) + .tooltipKey("updateAvailableTooltip") .accessibleTextKey("updateAvailableTooltip"); b.apply(struc -> { AppFont.setSize(struc.get(), 2); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java index f7d34080..a26a6956 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryComp.java @@ -190,7 +190,7 @@ public abstract class StoreEntryComp extends SimpleComp { var imageComp = PrettyImageHelper.ofFixedSize(img, w, h); var storeIcon = imageComp.createRegion(); if (wrapper.getValidity().getValue().isUsable()) { - new TooltipAugment<>(wrapper.getEntry().getProvider().displayName()).augment(storeIcon); + new TooltipAugment<>(wrapper.getEntry().getProvider().displayName(), null).augment(storeIcon); } var stack = new StackPane(storeIcon); @@ -227,7 +227,7 @@ public abstract class StoreEntryComp extends SimpleComp { button.accessibleText( actionProvider.getName(wrapper.getEntry().ref()).getValue()); button.apply(new TooltipAugment<>( - actionProvider.getName(wrapper.getEntry().ref()))); + actionProvider.getName(wrapper.getEntry().ref()), null)); if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ONLY_SHOW_IF_ENABLED) { button.hide(Bindings.not(p.getValue())); } else if (actionProvider.activeType() == ActionProvider.DataStoreCallSite.ActiveType.ALWAYS_SHOW) { @@ -266,7 +266,7 @@ public abstract class StoreEntryComp extends SimpleComp { event -> event.getButton() == MouseButton.PRIMARY, null, () -> StoreEntryComp.this.createContextMenu())); - settingsButton.apply(new TooltipAugment<>("more")); + settingsButton.tooltipKey("more"); return settingsButton; } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusComp.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusComp.java index 962d0648..31e1a539 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusComp.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryListStatusComp.java @@ -7,12 +7,10 @@ import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.impl.FilterComp; import io.xpipe.app.fxcomps.impl.IconButtonComp; -import io.xpipe.app.fxcomps.impl.TooltipAugment; import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.ListBindingsHelper; import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.process.OsType; - import javafx.beans.binding.Bindings; import javafx.beans.property.Property; import javafx.beans.property.SimpleObjectProperty; @@ -29,7 +27,6 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; - import org.kordamp.ikonli.javafx.FontIcon; public class StoreEntryListStatusComp extends SimpleComp { @@ -186,7 +183,7 @@ public class StoreEntryListStatusComp extends SimpleComp { sortMode)); }); alphabetical.accessibleTextKey("sortAlphabetical"); - alphabetical.apply(new TooltipAugment<>("sortAlphabetical")); + alphabetical.tooltipKey("sortAlphabetical"); return alphabetical; } @@ -225,7 +222,7 @@ public class StoreEntryListStatusComp extends SimpleComp { sortMode)); }); date.accessibleTextKey("sortLastUsed"); - date.apply(new TooltipAugment<>("sortLastUsed")); + date.tooltipKey("sortLastUsed"); return date; } diff --git a/app/src/main/java/io/xpipe/app/core/AppMainWindow.java b/app/src/main/java/io/xpipe/app/core/AppMainWindow.java index 42fd4951..3ceab3a8 100644 --- a/app/src/main/java/io/xpipe/app/core/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/AppMainWindow.java @@ -7,7 +7,6 @@ import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.CloseBehaviourAlert; import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.process.OsType; - import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.geometry.Rectangle2D; @@ -17,18 +16,17 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.Region; import javafx.stage.Screen; import javafx.stage.Stage; - import lombok.Builder; import lombok.Getter; import lombok.Value; import lombok.extern.jackson.Jacksonized; +import javax.imageio.ImageIO; import java.io.IOException; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; -import javax.imageio.ImageIO; public class AppMainWindow { @@ -157,6 +155,13 @@ public class AppMainWindow { e.consume(); }); + stage.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (event.getCode().equals(KeyCode.Q) && event.isShortcutDown()) { + stage.close(); + event.consume(); + } + }); + TrackEvent.debug("Window listeners added"); } diff --git a/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java b/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java index fe887086..8a0978f3 100644 --- a/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java +++ b/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java @@ -161,12 +161,10 @@ public class AppWindowHelper { event.consume(); }); a.getDialogPane().getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (OsType.getLocal().equals(OsType.LINUX) || OsType.getLocal().equals(OsType.MACOS)) { - if (event.getCode().equals(KeyCode.W) && event.isShortcutDown()) { - s.close(); - event.consume(); - return; - } + if (event.getCode().equals(KeyCode.W) && event.isShortcutDown()) { + s.close(); + event.consume(); + return; } if (event.getCode().equals(KeyCode.ESCAPE)) { @@ -274,11 +272,9 @@ public class AppWindowHelper { }); scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (OsType.getLocal().equals(OsType.LINUX) || OsType.getLocal().equals(OsType.MACOS)) { - if (event.getCode().equals(KeyCode.W) && event.isShortcutDown()) { - stage.close(); - event.consume(); - } + if (event.getCode().equals(KeyCode.W) && event.isShortcutDown()) { + stage.close(); + event.consume(); } }); } diff --git a/app/src/main/java/io/xpipe/app/fxcomps/Comp.java b/app/src/main/java/io/xpipe/app/fxcomps/Comp.java index 7bf1fc00..fb2b79dc 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/Comp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/Comp.java @@ -1,5 +1,6 @@ package io.xpipe.app.fxcomps; +import atlantafx.base.controls.Spacer; import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.augment.Augment; import io.xpipe.app.fxcomps.augment.GrowAugment; @@ -7,12 +8,10 @@ import io.xpipe.app.fxcomps.impl.TooltipAugment; import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.Shortcuts; - import javafx.application.Platform; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.geometry.Orientation; -import javafx.scene.control.ButtonBase; import javafx.scene.control.Separator; import javafx.scene.input.KeyCombination; import javafx.scene.layout.HBox; @@ -20,8 +19,6 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import atlantafx.base.controls.Spacer; - import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -186,20 +183,20 @@ public abstract class Comp> { return apply(struc -> Shortcuts.addShortcut(struc.get(), shortcut, r -> con.accept(struc))); } - public Comp shortcut(KeyCombination shortcut) { - return apply(struc -> Shortcuts.addShortcut((ButtonBase) struc.get(), shortcut)); - } - public Comp displayOnlyShortcut(KeyCombination shortcut) { return apply(struc -> Shortcuts.addDisplayShortcut(struc.get(), shortcut)); } public Comp tooltip(ObservableValue text) { - return apply(new TooltipAugment<>(text)); + return apply(new TooltipAugment<>(text, null)); } public Comp tooltipKey(String key) { - return apply(new TooltipAugment<>(key)); + return apply(new TooltipAugment<>(key, null)); + } + + public Comp tooltipKey(String key, KeyCombination shortcut) { + return apply(new TooltipAugment<>(key, shortcut)); } public Region createRegion() { diff --git a/app/src/main/java/io/xpipe/app/fxcomps/augment/ContextMenuAugment.java b/app/src/main/java/io/xpipe/app/fxcomps/augment/ContextMenuAugment.java index ea914050..87b86d4e 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/augment/ContextMenuAugment.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/augment/ContextMenuAugment.java @@ -42,7 +42,7 @@ public class ContextMenuAugment> implements Augment { + r.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { if (mouseEventCheck != null && mouseEventCheck.test(event)) { if (!hide.get()) { var cm = contextMenu.get(); @@ -55,18 +55,18 @@ public class ContextMenuAugment> implements Augment { + r.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> { if (mouseEventCheck != null && mouseEventCheck.test(event)) { event.consume(); } }); - r.addEventFilter(KeyEvent.KEY_RELEASED, event -> { + r.addEventHandler(KeyEvent.KEY_RELEASED, event -> { if (keyEventCheck != null && keyEventCheck.test(event)) { event.consume(); } }); - r.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + r.addEventHandler(KeyEvent.KEY_PRESSED, event -> { if (keyEventCheck != null && keyEventCheck.test(event)) { if (!hide.get()) { var cm = contextMenu.get(); @@ -80,7 +80,7 @@ public class ContextMenuAugment> implements Augment { + buttonBase.addEventHandler(ActionEvent.ACTION, event -> { if (buttonBase.getOnAction() != null) { return; } diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java index cc629fe4..60de3eb5 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/ContextualFileReferenceChoiceComp.java @@ -124,7 +124,7 @@ public class ContextualFileReferenceChoiceComp extends Comp> ErrorEvent.fromThrowable(e).handle(); } }); - gitShareButton.apply(new TooltipAugment<>("gitShareFileTooltip")); + gitShareButton.tooltipKey("gitShareFileTooltip"); gitShareButton.styleClass(Styles.RIGHT_PILL).grow(false, true); var layout = new HorizontalComp(List.of(fileNameComp, fileBrowseButton, gitShareButton)) diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/TooltipAugment.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/TooltipAugment.java index c498f208..cc1bbc47 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/TooltipAugment.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/TooltipAugment.java @@ -4,47 +4,46 @@ import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.augment.Augment; import io.xpipe.app.fxcomps.util.PlatformThread; -import io.xpipe.app.fxcomps.util.Shortcuts; - import javafx.beans.binding.Bindings; import javafx.beans.value.ObservableValue; import javafx.scene.control.Tooltip; +import javafx.scene.input.KeyCombination; import javafx.stage.Window; public class TooltipAugment> implements Augment { private final ObservableValue text; + private final KeyCombination shortcut; - public TooltipAugment(ObservableValue text) { - this.text = PlatformThread.sync(text); + public TooltipAugment(ObservableValue text, KeyCombination shortcut) { + this.text = text; + this.shortcut = shortcut; } - public TooltipAugment(String key) { + public TooltipAugment(String key, KeyCombination shortcut) { this.text = AppI18n.observable(key); + this.shortcut = shortcut; } @Override public void augment(S struc) { - var region = struc.get(); var tt = new FixedTooltip(); - if (Shortcuts.getDisplayShortcut(region) != null) { + if (shortcut != null) { var s = AppI18n.observable("shortcut"); var binding = Bindings.createStringBinding( () -> { - return text.getValue() + "\n\n" + s.getValue() + ": " - + Shortcuts.getDisplayShortcut(region).getDisplayText(); + return text.getValue() + "\n\n" + s.getValue() + ": " + shortcut.getDisplayText(); }, text, s); - tt.textProperty().bind(binding); + tt.textProperty().bind(PlatformThread.sync(binding)); } else { - tt.textProperty().bind(text); + tt.textProperty().bind(PlatformThread.sync(text)); } tt.setStyle("-fx-font-size: 11pt;"); tt.setWrapText(true); tt.setMaxWidth(400); tt.getStyleClass().add("fancy-tooltip"); - Tooltip.install(struc.get(), tt); } diff --git a/app/src/main/java/io/xpipe/app/util/InputHelper.java b/app/src/main/java/io/xpipe/app/util/InputHelper.java index 31c8a239..da639041 100644 --- a/app/src/main/java/io/xpipe/app/util/InputHelper.java +++ b/app/src/main/java/io/xpipe/app/util/InputHelper.java @@ -3,6 +3,7 @@ package io.xpipe.app.util; import javafx.event.EventHandler; import javafx.event.EventTarget; import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; @@ -11,9 +12,17 @@ import java.util.function.Consumer; public class InputHelper { - public static void onLeft(EventTarget target, boolean filter, Consumer r) { + public static void onCtrlKeyCode(EventTarget target, KeyCode code, boolean filter, Consumer r) { EventHandler keyEventEventHandler = event -> { - if (event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.NUMPAD4) { + if (event.isAltDown() || event.isShiftDown()) { + return; + } + + if (!event.isShortcutDown()) { + return; + } + + if (code == event.getCode()) { r.accept(event); } }; @@ -24,9 +33,9 @@ public class InputHelper { } } - public static void onRight(EventTarget target, boolean filter, Consumer r) { + public static void onKeyCombination(EventTarget target, KeyCombination c, boolean filter, Consumer r) { EventHandler keyEventEventHandler = event -> { - if (event.getCode() == KeyCode.RIGHT || event.getCode() == KeyCode.NUMPAD6) { + if (c.match(event)) { r.accept(event); } }; @@ -37,6 +46,60 @@ public class InputHelper { } } + public static void onExactKeyCode(EventTarget target, KeyCode code, boolean filter, Consumer r) { + EventHandler keyEventEventHandler = event -> { + if (event.isAltDown() || event.isShiftDown() || event.isShortcutDown()) { + return; + } + + if (code == event.getCode()) { + r.accept(event); + } + }; + if (filter) { + target.addEventFilter(KeyEvent.KEY_PRESSED, keyEventEventHandler); + } else { + target.addEventHandler(KeyEvent.KEY_PRESSED, keyEventEventHandler); + } + } + + public static void onInput(EventTarget target, boolean filter, Consumer r) { + EventHandler keyEventEventHandler = event -> { + r.accept(event); + }; + if (filter) { + target.addEventFilter(KeyEvent.KEY_PRESSED, keyEventEventHandler); + } else { + target.addEventHandler(KeyEvent.KEY_PRESSED, keyEventEventHandler); + } + } + + public static void onLeft(EventTarget target, boolean filter, Consumer r) { + EventHandler e = event -> { + if (event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.NUMPAD4) { + r.accept(event); + } + }; + if (filter) { + target.addEventFilter(KeyEvent.KEY_PRESSED, e); + } else { + target.addEventHandler(KeyEvent.KEY_PRESSED, e); + } + } + + public static void onRight(EventTarget target, boolean filter, Consumer r) { + EventHandler e = event -> { + if (event.getCode() == KeyCode.RIGHT || event.getCode() == KeyCode.NUMPAD6) { + r.accept(event); + } + }; + if (filter) { + target.addEventFilter(KeyEvent.KEY_PRESSED, e); + } else { + target.addEventHandler(KeyEvent.KEY_PRESSED, e); + } + } + public static void onNavigationInput(EventTarget target, Consumer r) { target.addEventFilter(KeyEvent.KEY_PRESSED, event -> { var c = event.getCode(); diff --git a/app/src/main/resources/io/xpipe/app/resources/style/browser.css b/app/src/main/resources/io/xpipe/app/resources/style/browser.css index 6e147613..cb3957d1 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/browser.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/browser.css @@ -182,10 +182,14 @@ -fx-background-color: -color-accent-subtle; } -.browser .table-row-cell:selected, .browser .table-row-cell:hover:selected { +.browser .table-row-cell:selected, .browser .table-row-cell:hover:selected, .root:key-navigation .browser .table-row-cell:focused:selected { -fx-background-color: -color-success-subtle; } +.root:key-navigation .browser .table-row-cell:focused { + -fx-background-color: -color-warning-subtle; +} + .root.nord .browser .table-row-cell:selected, .root.nord .browser .table-row-cell:hover:selected { -fx-background-color: -color-success-7; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java index 01286ced..3f1b445e 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenDirectoryInNewTabAction.java @@ -10,6 +10,9 @@ import io.xpipe.core.store.FileKind; import javafx.beans.value.ObservableValue; import javafx.scene.Node; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import org.kordamp.ikonli.javafx.FontIcon; import java.util.List; @@ -34,6 +37,11 @@ public class OpenDirectoryInNewTabAction implements LeafAction { return Category.OPEN; } + @Override + public KeyCombination getShortcut() { + return new KeyCodeCombination(KeyCode.ENTER, KeyCombination.SHIFT_DOWN); + } + @Override public boolean acceptsEmptySelection() { return true; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenNativeFileDetailsAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenNativeFileDetailsAction.java index bda7c020..002ecdf0 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenNativeFileDetailsAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenNativeFileDetailsAction.java @@ -10,6 +10,9 @@ import io.xpipe.core.process.ShellControl; import io.xpipe.core.store.FileNames; import javafx.beans.value.ObservableValue; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import java.util.List; @@ -62,6 +65,11 @@ public class OpenNativeFileDetailsAction implements LeafAction { } } + @Override + public KeyCombination getShortcut() { + return new KeyCodeCombination(KeyCode.ENTER,KeyCombination.ALT_DOWN); + } + @Override public Category getCategory() { return Category.NATIVE;