diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java index 75806e7c..d24e67a5 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserWelcomeComp.java @@ -1,9 +1,10 @@ package io.xpipe.app.browser; +import atlantafx.base.controls.Spacer; +import atlantafx.base.theme.Styles; import io.xpipe.app.browser.session.BrowserSessionModel; import io.xpipe.app.comp.base.ButtonComp; import io.xpipe.app.comp.base.ListBoxViewComp; -import io.xpipe.app.comp.base.LoadingOverlayComp; import io.xpipe.app.comp.base.TileButtonComp; import io.xpipe.app.core.AppFont; import io.xpipe.app.core.AppI18n; @@ -17,7 +18,6 @@ import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.DerivedObservableList; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.util.ThreadHelper; - import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -31,9 +31,6 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import atlantafx.base.controls.Spacer; -import atlantafx.base.theme.Styles; - import java.util.List; public class BrowserWelcomeComp extends SimpleComp { @@ -57,8 +54,7 @@ public class BrowserWelcomeComp extends SimpleComp { .padding(new Insets(5, 0, 0, 0)) .createRegion(); - var loading = LoadingOverlayComp.noProgress(Comp.empty(),model.getBusy()).createRegion(); - var hbox = new HBox(img, vbox, new Spacer(), loading); + var hbox = new HBox(img, vbox); hbox.setAlignment(Pos.CENTER_LEFT); hbox.setSpacing(15); diff --git a/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionComp.java b/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionComp.java index ae37e2c6..98c1dc81 100644 --- a/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionComp.java +++ b/app/src/main/java/io/xpipe/app/browser/session/BrowserSessionComp.java @@ -3,21 +3,24 @@ package io.xpipe.app.browser.session; import io.xpipe.app.browser.BrowserBookmarkComp; import io.xpipe.app.browser.BrowserBookmarkHeaderComp; import io.xpipe.app.browser.BrowserTransferComp; +import io.xpipe.app.comp.base.LoadingOverlayComp; import io.xpipe.app.comp.base.SideSplitPaneComp; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.core.AppLayoutModel; +import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; +import io.xpipe.app.fxcomps.impl.AnchorComp; import io.xpipe.app.fxcomps.impl.StackComp; 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.util.ThreadHelper; import io.xpipe.core.store.ShellStore; - import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleDoubleProperty; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; @@ -101,9 +104,22 @@ public class BrowserSessionComp extends SimpleComp { var split = new SimpleDoubleProperty(); var tabs = new BrowserSessionTabsComp(model, split) - .apply(struc -> struc.get().setViewOrder(1)) - .apply(struc -> struc.get().setPickOnBounds(false)); - var splitPane = new SideSplitPaneComp(vertical, tabs) + .apply(struc -> { + struc.get().setViewOrder(1); + struc.get().setPickOnBounds(false); + AnchorPane.setTopAnchor(struc.get(), 0.0); + AnchorPane.setBottomAnchor(struc.get(), 0.0); + AnchorPane.setLeftAnchor(struc.get(), 0.0); + AnchorPane.setRightAnchor(struc.get(), 0.0); + }); + var loadingIndicator = LoadingOverlayComp.noProgress(Comp.empty(),model.getBusy()) + .apply(struc -> { + AnchorPane.setTopAnchor(struc.get(), 0.0); + AnchorPane.setRightAnchor(struc.get(), 0.0); + }) + .styleClass("tab-loading-indicator"); + var loadingStack = new AnchorComp(List.of(tabs, loadingIndicator)); + var splitPane = new SideSplitPaneComp(vertical, loadingStack) .withInitialWidth(AppLayoutModel.get().getSavedState().getBrowserConnectionsWidth()) .withOnDividerChange(d -> { AppLayoutModel.get().getSavedState().setBrowserConnectionsWidth(d); 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 3809c234..b1948437 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 @@ -26,6 +26,8 @@ import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.input.DragEvent; import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -207,12 +209,39 @@ public class BrowserSessionTabsComp extends SimpleComp { if (keyEvent.getCode() == KeyCode.W && keyEvent.isShortcutDown()) { tabs.getTabs().remove(current); keyEvent.consume(); + return; } if (keyEvent.getCode() == KeyCode.W && keyEvent.isShortcutDown() && keyEvent.isShiftDown()) { tabs.getTabs().clear(); keyEvent.consume(); } + + if (keyEvent.getCode().isFunctionKey()) { + var start = KeyCode.F1.getCode(); + var index = keyEvent.getCode().getCode() - start; + if (index < tabs.getTabs().size()) { + tabs.getSelectionModel().select(index); + keyEvent.consume(); + return; + } + } + + var forward = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN); + if (forward.match(keyEvent)) { + var next = (tabs.getSelectionModel().getSelectedIndex() + 1) % tabs.getTabs().size(); + tabs.getSelectionModel().select(next); + keyEvent.consume(); + return; + } + + var back = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN); + if (back.match(keyEvent)) { + var previous = (tabs.getTabs().size() + tabs.getSelectionModel().getSelectedIndex() - 1) % tabs.getTabs().size(); + tabs.getSelectionModel().select(previous); + keyEvent.consume(); + return; + } }); return tabs; diff --git a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java index 8c352964..e1735b85 100644 --- a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java +++ b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java @@ -10,13 +10,10 @@ import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.storage.DataStorage; - import javafx.beans.binding.Bindings; import javafx.beans.value.ObservableValue; import javafx.scene.control.ButtonBase; -import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; -import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; @@ -68,23 +65,6 @@ public class AppLayoutComp extends Comp> { return; } }); - if (event.isConsumed()) { - return; - } - - var forward = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN); - if (forward.match(event)) { - var next = (model.getEntries().indexOf(model.getSelected().getValue()) + 1) % 3; - model.getSelected().setValue(model.getEntries().get(next)); - return; - } - - var back = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN); - if (back.match(event)) { - var next = (model.getEntries().indexOf(model.getSelected().getValue()) + 2) % 3; - model.getSelected().setValue(model.getEntries().get(next)); - return; - } }); AppFont.normal(pane); pane.getStyleClass().add("layout"); diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/AnchorComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/AnchorComp.java new file mode 100644 index 00000000..0a2691f8 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/AnchorComp.java @@ -0,0 +1,27 @@ +package io.xpipe.app.fxcomps.impl; + +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.CompStructure; +import io.xpipe.app.fxcomps.SimpleCompStructure; +import javafx.scene.layout.AnchorPane; + +import java.util.List; + +public class AnchorComp extends Comp> { + + private final List> comps; + + public AnchorComp(List> comps) { + this.comps = List.copyOf(comps); + } + + @Override + public CompStructure createBase() { + var pane = new AnchorPane(); + for (var c : comps) { + pane.getChildren().add(c.createRegion()); + } + pane.setPickOnBounds(false); + return new SimpleCompStructure<>(pane); + } +} 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 1d6b3dc1..860741ad 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 @@ -208,6 +208,20 @@ } +.browser .tab-header-area .control-buttons-tab { + -fx-opacity: 0; +} + +.browser .tab-loading-indicator { + -fx-min-width: 2.5em; + -fx-pref-width: 2.5em; + -fx-max-width: 2.5em; + -fx-min-height: 2.5em; + -fx-pref-height: 2.5em; + -fx-max-height: 2.5em; + -fx-background-color: transparent; +} + .browser .tab-pane.floating > .tab-header-area { -fx-border-width: 0 0 0.05em 0; -fx-border-color: -color-border-default; diff --git a/core/src/main/java/io/xpipe/core/process/ShellOpenFunction.java b/core/src/main/java/io/xpipe/core/process/ShellOpenFunction.java index ee620918..07a4b6de 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellOpenFunction.java +++ b/core/src/main/java/io/xpipe/core/process/ShellOpenFunction.java @@ -27,7 +27,7 @@ public interface ShellOpenFunction { @Override public CommandBuilder prepareWithInitCommand(@NonNull String command) { - throw new UnsupportedOperationException(); + return CommandBuilder.of().add(command); } }; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenFileDefaultAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenFileDefaultAction.java index 64ddec42..4b63270f 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenFileDefaultAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/OpenFileDefaultAction.java @@ -48,6 +48,6 @@ public class OpenFileDefaultAction implements LeafAction { @Override public boolean isApplicable(OpenFileSystemModel model, List entries) { - return entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE); + return model.getFileList().getEditing().getValue() == null && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE); } }