From 313cc922ee064e905377d4f0cdceee97c05c6c7f Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 4 Jul 2023 01:28:33 +0000 Subject: [PATCH] Various small improvements --- README.md | 2 +- .../xpipe/app/browser/FileSystemHelper.java | 10 ++ .../java/io/xpipe/app/comp/AppLayoutComp.java | 79 +++--------- .../xpipe/app/comp/base/SideMenuBarComp.java | 14 +-- .../storage/store/DenseStoreEntryComp.java | 10 +- .../storage/store/StandardStoreEntryComp.java | 10 +- .../comp/storage/store/StoreEntryComp.java | 11 +- .../comp/storage/store/StoreEntryWrapper.java | 10 -- .../main/java/io/xpipe/app/core/AppI18n.java | 2 +- .../io/xpipe/app/core/AppLayoutModel.java | 74 +++++++++++ .../java/io/xpipe/app/core/AppMainWindow.java | 3 + .../main/java/io/xpipe/app/core/AppTheme.java | 3 - .../io/xpipe/app/core/mode/PlatformMode.java | 1 + .../app/exchange/MessageExchangeImpls.java | 8 +- .../java/io/xpipe/app/ext/ActionProvider.java | 2 +- .../io/xpipe/app/ext/DataStoreProvider.java | 8 +- .../java/io/xpipe/app/ext/PrefsProvider.java | 8 +- .../app/fxcomps/impl/DataStoreChoiceComp.java | 7 +- .../io/xpipe/app/fxcomps/impl/SvgView.java | 2 +- .../java/io/xpipe/app/prefs/AppPrefs.java | 44 ++++--- .../java/io/xpipe/app/prefs/PrefsComp.java | 6 +- .../io/xpipe/app/storage/StorageElement.java | 5 +- .../io/xpipe/app/util/OptionsBuilder.java | 6 + .../resources/lang/dscreation_en.properties | 2 +- .../resources/lang/preferences_en.properties | 4 + .../app/resources/style/store-entry-comp.css | 2 +- .../beacon/exchange/MessageExchanges.java | 8 +- .../java/io/xpipe/core/impl/FileNames.java | 4 + .../io/xpipe/core/store/LeafShellStore.java | 3 - .../{actions => action}/AddStoreAction.java | 2 +- .../ext/base/action/BrowseStoreAction.java | 63 ++++++++++ .../DeleteStoreChildrenAction.java | 4 +- .../{actions => action}/EditStoreAction.java | 9 +- .../{actions => action}/FileBrowseAction.java | 2 +- .../{actions => action}/FileEditAction.java | 2 +- .../xpipe/ext/base/action/LaunchAction.java | 117 ++++++++++++++++++ .../ext/base/action/LaunchShortcutAction.java | 60 +++++++++ .../RefreshStoreAction.java | 4 +- .../{actions => action}/SampleAction.java | 2 +- .../io/xpipe/ext/base/action/ScanAction.java | 61 +++++++++ .../{actions => action}/ShareStoreAction.java | 2 +- .../StreamExportAction.java | 2 +- ext/base/src/main/java/module-info.java | 8 +- .../resources/lang/translations_en.properties | 3 + 44 files changed, 523 insertions(+), 166 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/core/AppLayoutModel.java delete mode 100644 core/src/main/java/io/xpipe/core/store/LeafShellStore.java rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/AddStoreAction.java (97%) create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/action/BrowseStoreAction.java rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/DeleteStoreChildrenAction.java (95%) rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/EditStoreAction.java (90%) rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/FileBrowseAction.java (97%) rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/FileEditAction.java (97%) create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/action/LaunchShortcutAction.java rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/RefreshStoreAction.java (95%) rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/SampleAction.java (99%) create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/action/ScanAction.java rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/ShareStoreAction.java (98%) rename ext/base/src/main/java/io/xpipe/ext/base/{actions => action}/StreamExportAction.java (98%) diff --git a/README.md b/README.md index 8f28ebd64..9cc871546 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ bash <(curl -sL https://raw.githubusercontent.com/xpipe-io/xpipe/master/get-xpip ##### Windows (Experimental) ``` -powershell -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/xpipe-io/xpipe/master/get-xpipe.ps1'))" +powershell -ExecutionPolicy Bypass -Command iwr "https://raw.githubusercontent.com/xpipe-io/xpipe/master/get-xpipe.ps1" -OutFile "$env:TEMP\get-xpipe.ps1" ";" "&" "$env:TEMP\get-xpipe.ps1" ``` ## Further information diff --git a/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java b/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java index 2b0ce5715..7e3b8e30c 100644 --- a/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java +++ b/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java @@ -47,6 +47,12 @@ public class FileSystemHelper { return null; } + if (path.startsWith("\"") && path.endsWith("\"")) { + path = path.substring(1, path.length() - 1); + } else if (path.startsWith("'") && path.endsWith("'")) { + path = path.substring(1, path.length() - 1); + } + // Handle special case when file system creation has failed if (model.getFileSystem() == null) { return path; @@ -100,6 +106,10 @@ public class FileSystemHelper { throw new IllegalArgumentException(String.format("Directory %s is not absolute", resolved)); } + if (model.getFileSystem().fileExists(path)) { + return FileNames.toDirectory(FileNames.getParent(path)); + } + return FileNames.toDirectory(resolved); } 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 d24761f2c..5d9424c6d 100644 --- a/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java +++ b/app/src/main/java/io/xpipe/app/comp/AppLayoutComp.java @@ -1,93 +1,58 @@ package io.xpipe.app.comp; -import io.xpipe.app.browser.BrowserComp; -import io.xpipe.app.browser.BrowserModel; import io.xpipe.app.comp.base.SideMenuBarComp; -import io.xpipe.app.comp.storage.store.StoreLayoutComp; -import io.xpipe.app.core.*; +import io.xpipe.app.core.AppActionLinkDetector; +import io.xpipe.app.core.AppFont; +import io.xpipe.app.core.AppLayoutModel; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; +import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.prefs.AppPrefs; -import io.xpipe.app.prefs.PrefsComp; -import javafx.beans.property.Property; -import javafx.beans.property.SimpleObjectProperty; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Region; -import lombok.SneakyThrows; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; public class AppLayoutComp extends Comp> { - private final List entries; - private final Property selected; + private final AppLayoutModel model = AppLayoutModel.get(); public AppLayoutComp() { - entries = createEntryList(); - selected = new SimpleObjectProperty<>(AppState.get().isInitialLaunch() ? entries.get(1) : entries.get(0)); - shortcut(new KeyCodeCombination(KeyCode.V, KeyCombination.SHORTCUT_DOWN), structure -> { AppActionLinkDetector.detectOnPaste(); }); } - @SneakyThrows - private List createEntryList() { - var l = new ArrayList<>(List.of( - new SideMenuBarComp.Entry( - AppI18n.observable("browser"), "mdi2f-file-cabinet", new BrowserComp(BrowserModel.DEFAULT)), - new SideMenuBarComp.Entry(AppI18n.observable("connections"), "mdi2c-connection", new StoreLayoutComp()), - // new SideMenuBarComp.Entry(AppI18n.observable("data"), "mdsal-dvr", new SourceCollectionLayoutComp()), - new SideMenuBarComp.Entry( - AppI18n.observable("settings"), "mdsmz-miscellaneous_services", new PrefsComp(this)))); - // new SideMenuBarComp.Entry(AppI18n.observable("help"), "mdi2b-book-open-variant", new - // StorageLayoutComp()), - // new SideMenuBarComp.Entry(AppI18n.observable("account"), "mdi2a-account", new StorageLayoutComp()) - if (AppProperties.get().isDeveloperMode() && !AppProperties.get().isImage()) { - l.add(new SideMenuBarComp.Entry( - AppI18n.observable("developer"), "mdi2b-book-open-variant", new DeveloperTabComp())); - } - - // l.add(new SideMenuBarComp.Entry(AppI18n.observable("abc"), "mdi2b-book-open-variant", Comp.of(() -> { - // var fi = new FontIcon("mdsal-dvr"); - // fi.setIconSize(30); - // fi.setIconColor(Color.valueOf("#111C")); - // JfxHelper.addEffect(fi); - // return new StackPane(fi); - // }))); - - return l; - } @Override public CompStructure createBase() { - var map = new HashMap(); - getRegion(entries.get(0), map); - getRegion(entries.get(1), map); + var map = new HashMap(); + getRegion(model.getEntries().get(0), map); + getRegion(model.getEntries().get(1), map); var pane = new BorderPane(); - var sidebar = new SideMenuBarComp(selected, entries); - pane.setCenter(getRegion(selected.getValue(), map)); + var sidebar = new SideMenuBarComp(model.getSelected(), model.getEntries()); + pane.setCenter(getRegion(model.getSelected().getValue(), map)); pane.setRight(sidebar.createRegion()); - selected.addListener((c, o, n) -> { - if (o != null && o.equals(entries.get(2))) { + model.getSelected().addListener((c, o, n) -> { + if (o != null && o.equals(model.getEntries().get(2))) { AppPrefs.get().save(); } - pane.setCenter(getRegion(n, map)); + PlatformThread.runLaterIfNeeded(() -> { + pane.setCenter(getRegion(n, map)); + }); }); AppFont.normal(pane); return new SimpleCompStructure<>(pane); } - private Region getRegion(SideMenuBarComp.Entry entry, Map map) { + private Region getRegion(AppLayoutModel.Entry entry, Map map) { if (map.containsKey(entry)) { return map.get(entry); } @@ -96,16 +61,4 @@ public class AppLayoutComp extends Comp> { map.put(entry, r); return r; } - - public List getEntries() { - return entries; - } - - public SideMenuBarComp.Entry getSelected() { - return selected.getValue(); - } - - public Property selectedProperty() { - return selected; - } } 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 f5928ef88..bd163f90b 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 @@ -1,6 +1,7 @@ package io.xpipe.app.comp.base; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppLayoutModel; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.SimpleCompStructure; @@ -10,7 +11,6 @@ import io.xpipe.app.update.UpdateAvailableAlert; import io.xpipe.app.update.XPipeDistributionType; import javafx.beans.binding.Bindings; import javafx.beans.property.Property; -import javafx.beans.value.ObservableValue; import javafx.css.PseudoClass; import javafx.scene.control.Button; import javafx.scene.layout.Priority; @@ -21,10 +21,10 @@ import java.util.List; public class SideMenuBarComp extends Comp> { - private final Property value; - private final List entries; + private final Property value; + private final List entries; - public SideMenuBarComp(Property value, List entries) { + public SideMenuBarComp(Property value, List entries) { this.value = value; this.entries = entries; } @@ -42,14 +42,15 @@ public class SideMenuBarComp extends Comp> { b.apply(struc -> { struc.get().pseudoClassStateChanged(selected, value.getValue().equals(e)); value.addListener((c, o, n) -> { - struc.get().pseudoClassStateChanged(selected, n.equals(e)); + PlatformThread.runLaterIfNeeded(() -> { + struc.get().pseudoClassStateChanged(selected, n.equals(e)); + }); }); }); vbox.getChildren().add(b.createRegion()); }); { - // vbox.getChildren().add(new Spacer(Orientation.VERTICAL)); var fi = new FontIcon("mdi2u-update"); var b = new BigIconButton(AppI18n.observable("update"), fi, () -> UpdateAvailableAlert.showIfNeeded()); b.apply(GrowAugment.create(true, false)); @@ -76,5 +77,4 @@ public class SideMenuBarComp extends Comp> { return new SimpleCompStructure<>(vbox); } - public record Entry(ObservableValue name, String icon, Comp comp) {} } diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java index ce3b1901c..bfcb88dcb 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/DenseStoreEntryComp.java @@ -31,15 +31,13 @@ public class DenseStoreEntryComp extends StoreEntryComp { grid.getColumnConstraints().add(new ColumnConstraints(0)); } - var custom = new ColumnConstraints(content != null ? 300 : 0); + var customSize = content != null ? 300 : 0; + var custom = new ColumnConstraints(0, customSize, customSize); custom.setHalignment(HPos.RIGHT); - custom.setMinWidth(Region.USE_PREF_SIZE); - custom.setMaxWidth(Region.USE_PREF_SIZE); - var info = new ColumnConstraints(content != null ? 300 : 600); + var infoSize = content != null ? 300 : 600; + var info = new ColumnConstraints(0, infoSize, infoSize); info.setHalignment(HPos.LEFT); - info.setMinWidth(Region.USE_PREF_SIZE); - info.setMaxWidth(Region.USE_PREF_SIZE); var nameCC = new ColumnConstraints(); nameCC.setMinWidth(100); diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java index fc20a5b90..6e9f0a1da 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StandardStoreEntryComp.java @@ -31,16 +31,14 @@ public class StandardStoreEntryComp extends StoreEntryComp { grid.getColumnConstraints().addAll(nameCC); grid.add(createInformation(), 2, 0, 1, 2); - var info = new ColumnConstraints(content != null ? 300 : 600); + var infoSize = content != null ? 300 : 600; + var info = new ColumnConstraints(0, infoSize, infoSize); info.setHalignment(HPos.LEFT); - info.setMinWidth(Region.USE_PREF_SIZE); - info.setMaxWidth(Region.USE_PREF_SIZE); grid.getColumnConstraints().add(info); - var custom = new ColumnConstraints(content != null ? 300 : 0); + var customSize = content != null ? 300 : 0; + var custom = new ColumnConstraints(0, customSize, customSize); custom.setHalignment(HPos.RIGHT); - custom.setMinWidth(Region.USE_PREF_SIZE); - custom.setMaxWidth(Region.USE_PREF_SIZE); var cr = content != null ? content.createRegion() : new Region(); var bb = createButtonBar().createRegion(); var controls = new HBox(cr, bb); diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java index 1773ee85c..28629d3a8 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java @@ -175,8 +175,12 @@ public abstract class StoreEntryComp extends SimpleComp { var list = new ArrayList>(); for (var p : wrapper.getActionProviders().entrySet()) { var actionProvider = p.getKey().getDataStoreCallSite(); - if (!actionProvider.isMajor() - || p.getKey().equals(wrapper.getDefaultActionProvider().getValue())) { + if (!actionProvider.isMajor(wrapper.getEntry().getStore().asNeeded())) { + continue; + } + + var def = p.getKey().getDefaultDataStoreCallSite(); + if (def != null && def.equals(wrapper.getDefaultActionProvider().getValue())) { continue; } @@ -234,7 +238,7 @@ public abstract class StoreEntryComp extends SimpleComp { for (var p : wrapper.getActionProviders().entrySet()) { var actionProvider = p.getKey().getDataStoreCallSite(); - if (actionProvider.isMajor()) { + if (actionProvider.isMajor(wrapper.getEntry().getStore().asNeeded())) { continue; } @@ -269,7 +273,6 @@ public abstract class StoreEntryComp extends SimpleComp { } var refresh = new MenuItem(AppI18n.get("refresh"), new FontIcon("mdal-360")); - refresh.disableProperty().bind(wrapper.getRefreshable().not()); refresh.setOnAction(event -> { DataStorage.get().refreshAsync(wrapper.getEntry(), true); }); diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java index 19ff9fb7e..2c8a2c04e 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryWrapper.java @@ -32,9 +32,6 @@ public class StoreEntryWrapper implements StorageFilter.Filterable { private final StringProperty summary = new SimpleStringProperty(); private final Map actionProviders; private final Property> defaultActionProvider; - private final BooleanProperty editable = new SimpleBooleanProperty(); - private final BooleanProperty renamable = new SimpleBooleanProperty(); - private final BooleanProperty refreshable = new SimpleBooleanProperty(); private final BooleanProperty deletable = new SimpleBooleanProperty(); private final BooleanProperty expanded = new SimpleBooleanProperty(); @@ -108,13 +105,6 @@ public class StoreEntryWrapper implements StorageFilter.Filterable { } } - editable.setValue(entry.getState() != DataStoreEntry.State.LOAD_FAILED - && (entry.getConfiguration().isEditable() - || AppPrefs.get().developerDisableGuiRestrictions().get())); - renamable.setValue(entry.getConfiguration().isRenameable() - || AppPrefs.get().developerDisableGuiRestrictions().getValue()); - refreshable.setValue(entry.getConfiguration().isRefreshable() - || AppPrefs.get().developerDisableGuiRestrictions().getValue()); deletable.setValue(entry.getConfiguration().isDeletable() || AppPrefs.get().developerDisableGuiRestrictions().getValue()); diff --git a/app/src/main/java/io/xpipe/app/core/AppI18n.java b/app/src/main/java/io/xpipe/app/core/AppI18n.java index 04da41886..34df6c68a 100644 --- a/app/src/main/java/io/xpipe/app/core/AppI18n.java +++ b/app/src/main/java/io/xpipe/app/core/AppI18n.java @@ -168,7 +168,7 @@ public class AppI18n { var key = getKey(s); if (translations == null) { - TrackEvent.warn("Translations not initialized for" + key); + TrackEvent.warn("Translations not initialized for " + key); return s; } diff --git a/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java b/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java new file mode 100644 index 000000000..152bbbf1e --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java @@ -0,0 +1,74 @@ +package io.xpipe.app.core; + +import io.xpipe.app.browser.BrowserComp; +import io.xpipe.app.browser.BrowserModel; +import io.xpipe.app.comp.DeveloperTabComp; +import io.xpipe.app.comp.storage.store.StoreLayoutComp; +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.prefs.PrefsComp; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ObservableValue; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class AppLayoutModel { + + private static AppLayoutModel INSTANCE; + + public static AppLayoutModel get() { + return INSTANCE; + } + + public static void init() { + INSTANCE = new AppLayoutModel(); + } + + private final List entries; + private final Property selected; + + public AppLayoutModel() { + this.entries = createEntryList(); + this.selected = new SimpleObjectProperty<>(entries.get(1)); + } + + public void selectBrowser() { + selected.setValue(entries.get(0)); + } + + public void selectConnections() { + selected.setValue(entries.get(1)); + } + + private List createEntryList() { + var l = new ArrayList<>(List.of( + new Entry( + AppI18n.observable("browser"), "mdi2f-file-cabinet", new BrowserComp(BrowserModel.DEFAULT)), + new Entry(AppI18n.observable("connections"), "mdi2c-connection", new StoreLayoutComp()), + // new SideMenuBarComp.Entry(AppI18n.observable("data"), "mdsal-dvr", new SourceCollectionLayoutComp()), + new Entry( + AppI18n.observable("settings"), "mdsmz-miscellaneous_services", new PrefsComp(this)))); + // new SideMenuBarComp.Entry(AppI18n.observable("help"), "mdi2b-book-open-variant", new + // StorageLayoutComp()), + // new SideMenuBarComp.Entry(AppI18n.observable("account"), "mdi2a-account", new StorageLayoutComp()) + if (AppProperties.get().isDeveloperMode() && !AppProperties.get().isImage()) { + l.add(new Entry( + AppI18n.observable("developer"), "mdi2b-book-open-variant", new DeveloperTabComp())); + } + + // l.add(new SideMenuBarComp.Entry(AppI18n.observable("abc"), "mdi2b-book-open-variant", Comp.of(() -> { + // var fi = new FontIcon("mdsal-dvr"); + // fi.setIconSize(30); + // fi.setIconColor(Color.valueOf("#111C")); + // JfxHelper.addEffect(fi); + // return new StackPane(fi); + // }))); + + return l; + } + + public record Entry(ObservableValue name, String icon, Comp comp) {} +} 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 291d10bf8..0515f5de4 100644 --- a/app/src/main/java/io/xpipe/app/core/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/AppMainWindow.java @@ -240,6 +240,9 @@ public class AppMainWindow { stage.getScene().setRoot(contentR); TrackEvent.debug("Set content scene"); + contentR.prefWidthProperty().bind(stage.getScene().widthProperty()); + contentR.prefHeightProperty().bind(stage.getScene().heightProperty()); + stage.getScene().addEventHandler(KeyEvent.KEY_PRESSED, event -> { if (AppProperties.get().isDeveloperMode() && event.getCode().equals(KeyCode.F6)) { var newR = content.createRegion(); diff --git a/app/src/main/java/io/xpipe/app/core/AppTheme.java b/app/src/main/java/io/xpipe/app/core/AppTheme.java index 3ce3f6a1b..1034c86ea 100644 --- a/app/src/main/java/io/xpipe/app/core/AppTheme.java +++ b/app/src/main/java/io/xpipe/app/core/AppTheme.java @@ -83,9 +83,6 @@ public class AppTheme { PlatformThread.runLaterIfNeeded(() -> { for (Window window : Window.getWindows()) { - // Fix scene content not taking the correct size after style seed change - window.setWidth(window.getWidth() - 1); - var scene = window.getScene(); Image snapshot = scene.snapshot(null); Pane root = (Pane) scene.getRoot(); diff --git a/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java b/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java index 1cebc546a..309b6f94f 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java @@ -62,6 +62,7 @@ public abstract class PlatformMode extends OperationMode { AppTheme.init(); AppStyle.init(); AppImages.init(); + AppLayoutModel.init(); TrackEvent.info("mode", "Finished essential component initialization before platform"); TrackEvent.info("mode", "Launching application ..."); diff --git a/app/src/main/java/io/xpipe/app/exchange/MessageExchangeImpls.java b/app/src/main/java/io/xpipe/app/exchange/MessageExchangeImpls.java index 18f570941..31e0f3391 100644 --- a/app/src/main/java/io/xpipe/app/exchange/MessageExchangeImpls.java +++ b/app/src/main/java/io/xpipe/app/exchange/MessageExchangeImpls.java @@ -4,14 +4,14 @@ import io.xpipe.beacon.RequestMessage; import io.xpipe.beacon.ResponseMessage; import io.xpipe.beacon.exchange.MessageExchanges; +import java.util.List; import java.util.Optional; import java.util.ServiceLoader; -import java.util.Set; import java.util.stream.Collectors; public class MessageExchangeImpls { - private static Set> ALL; + private static List> ALL; public static void loadAll() { ALL = ServiceLoader.load(MessageExchangeImpl.class).stream() @@ -20,7 +20,7 @@ public class MessageExchangeImpls { // TrackEvent.trace("init", "Loaded exchange implementation " + ex.getId()); return ex; }) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); ALL.forEach(messageExchange -> { if (MessageExchanges.byId(messageExchange.getId()).isEmpty()) { @@ -51,7 +51,7 @@ public class MessageExchangeImpls { return Optional.ofNullable((MessageExchangeImpl) r.orElse(null)); } - public static Set> getAll() { + public static List> getAll() { return ALL; } } diff --git a/app/src/main/java/io/xpipe/app/ext/ActionProvider.java b/app/src/main/java/io/xpipe/app/ext/ActionProvider.java index b8934b91b..9dab3d733 100644 --- a/app/src/main/java/io/xpipe/app/ext/ActionProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/ActionProvider.java @@ -99,7 +99,7 @@ public interface ActionProvider { Class getApplicableClass(); - default boolean isMajor() { + default boolean isMajor(T o) { return false; } diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java index 65f50359b..3fc6bf554 100644 --- a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java @@ -42,8 +42,12 @@ public interface DataStoreProvider { return new StoreSectionComp(section); } - default String failureInfo() { - return null; + default boolean canHaveSubShells() { + return true; + } + + default boolean shouldHaveSubShells() { + return canHaveSubShells(); } default Comp stateDisplay(StoreEntryWrapper w) { diff --git a/app/src/main/java/io/xpipe/app/ext/PrefsProvider.java b/app/src/main/java/io/xpipe/app/ext/PrefsProvider.java index 54954cb40..b540dc590 100644 --- a/app/src/main/java/io/xpipe/app/ext/PrefsProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/PrefsProvider.java @@ -4,13 +4,13 @@ import com.dlsc.formsfx.model.structure.Field; import io.xpipe.core.util.ModuleLayerLoader; import javafx.beans.value.ObservableBooleanValue; +import java.util.List; import java.util.ServiceLoader; -import java.util.Set; import java.util.stream.Collectors; public abstract class PrefsProvider { - private static Set ALL; + private static List ALL; public static class Loader implements ModuleLayerLoader { @@ -18,7 +18,7 @@ public abstract class PrefsProvider { public void init(ModuleLayer layer) { ALL = ServiceLoader.load(layer, PrefsProvider.class).stream() .map(ServiceLoader.Provider::get) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } @Override @@ -32,7 +32,7 @@ public abstract class PrefsProvider { } } - public static Set getAll() { + public static List getAll() { return ALL; } diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java index 9d1651eae..92231389a 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/DataStoreChoiceComp.java @@ -7,7 +7,6 @@ import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.storage.DataStorage; import io.xpipe.app.util.CustomComboBoxBuilder; import io.xpipe.core.store.DataStore; -import io.xpipe.core.store.LeafShellStore; import io.xpipe.core.store.ShellStore; import javafx.beans.property.Property; import javafx.beans.property.SimpleStringProperty; @@ -23,6 +22,10 @@ import java.util.function.Predicate; @AllArgsConstructor public class DataStoreChoiceComp extends SimpleComp { + public static DataStoreChoiceComp other(Property selected, Class clazz, Predicate filter) { + return new DataStoreChoiceComp(Mode.OTHER, null, selected, clazz, filter); + } + public static DataStoreChoiceComp proxy(Property selected) { return new DataStoreChoiceComp<>(Mode.PROXY, null, selected, ShellStore.class, shellStore -> true); } @@ -107,7 +110,7 @@ public class DataStoreChoiceComp extends SimpleComp { } var s = e.getEntry().getStore(); - if (!(mode == Mode.ENVIRONMENT) && s instanceof LeafShellStore) { + if (!(mode == Mode.ENVIRONMENT) && e.getEntry().getProvider() != null && !e.getEntry().getProvider().canHaveSubShells()) { continue; } diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java index f66a5a256..8b4aa1ba2 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/SvgView.java @@ -102,7 +102,7 @@ public class SvgView { wv.setFocusTraversable(false); wv.setAccessibleRole(AccessibleRole.IMAGE_VIEW); - wv.getEngine().loadContent(getHtml(svgContent.getValue())); + wv.getEngine().loadContent(svgContent.getValue() != null ? getHtml(svgContent.getValue()) : null); svgContent.addListener((c, o, n) -> { if (n == null) { wv.getEngine().loadContent(""); diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index 21e4fa084..6309ff4f3 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -149,6 +149,19 @@ public class AppPrefs { StringField.ofStringType(customTerminalCommand).render(() -> new SimpleTextControl()), terminalType.isEqualTo(ExternalTerminalType.CUSTOM)); + private final BooleanProperty preferTerminalTabs = typed(new SimpleBooleanProperty(true), Boolean.class); + private final BooleanField preferTerminalTabsField = + BooleanField.ofBooleanType(preferTerminalTabs).render(() -> new CustomToggleControl()); + + // Start behaviour + // =============== + private final ObjectProperty externalStartupBehaviour = + typed(new SimpleObjectProperty<>(ExternalStartupBehaviour.TRAY), ExternalStartupBehaviour.class); + + private final SingleSelectionField externalStartupBehaviourControl = + Field.ofSingleSelectionType(externalStartupBehaviourList, externalStartupBehaviour) + .render(() -> new TranslatableComboBoxControl<>()); + // Close behaviour // =============== private final ObjectProperty closeBehaviour = @@ -170,12 +183,10 @@ public class AppPrefs { StringField.ofStringType(customEditorCommand).render(() -> new SimpleTextControl()), externalEditor.isEqualTo(ExternalEditorType.CUSTOM)); private final IntegerProperty editorReloadTimeout = typed(new SimpleIntegerProperty(1000), Integer.class); - private final ObjectProperty externalStartupBehaviour = - typed(new SimpleObjectProperty<>(ExternalStartupBehaviour.TRAY), ExternalStartupBehaviour.class); - private final SingleSelectionField externalStartupBehaviourControl = - Field.ofSingleSelectionType(externalStartupBehaviourList, externalStartupBehaviour) - .render(() -> new TranslatableComboBoxControl<>()); + private final BooleanProperty preferEditorTabs = typed(new SimpleBooleanProperty(true), Boolean.class); + private final BooleanField preferEditorTabsField = + BooleanField.ofBooleanType(preferEditorTabs).render(() -> new CustomToggleControl()); // Automatically update // ==================== @@ -529,15 +540,14 @@ public class AppPrefs { "appearance", Group.of( "uiOptions", - Setting.of("language", languageControl, languageInternal), Setting.of("theme", themeControl, theme), Setting.of("useSystemFont", useSystemFontInternal), - Setting.of("tooltipDelay", tooltipDelayInternal, tooltipDelayMin, tooltipDelayMax)), + Setting.of("tooltipDelay", tooltipDelayInternal, tooltipDelayMin, tooltipDelayMax), + Setting.of("language", languageControl, languageInternal)), Group.of("windowOptions", Setting.of("saveWindowLocation", saveWindowLocationInternal))), Category.of( - "integrations", + "editor", Group.of( - "editor", Setting.of("editorProgram", externalEditorControl, externalEditor), Setting.of("customEditorCommand", customEditorCommandControl, customEditorCommand) .applyVisibility(VisibilityProperty.of( @@ -546,13 +556,15 @@ public class AppPrefs { "editorReloadTimeout", editorReloadTimeout, editorReloadTimeoutMin, - editorReloadTimeoutMax)), - Group.of( - "terminal", - Setting.of("terminalProgram", terminalTypeControl, terminalType), - Setting.of("customTerminalCommand", customTerminalCommandControl, customTerminalCommand) - .applyVisibility(VisibilityProperty.of( - terminalType.isEqualTo(ExternalTerminalType.CUSTOM))))), + editorReloadTimeoutMax), + Setting.of("preferEditorTabs", preferEditorTabsField, preferEditorTabs))), + Category.of("terminal", + Group.of( + Setting.of("terminalProgram", terminalTypeControl, terminalType), + Setting.of("customTerminalCommand", customTerminalCommandControl, customTerminalCommand) + .applyVisibility(VisibilityProperty.of( + terminalType.isEqualTo(ExternalTerminalType.CUSTOM))), + Setting.of("preferTerminalTabs", preferTerminalTabsField, preferTerminalTabs))), Category.of( "developer", Setting.of( diff --git a/app/src/main/java/io/xpipe/app/prefs/PrefsComp.java b/app/src/main/java/io/xpipe/app/prefs/PrefsComp.java index 7b0ad5090..65d70e9c1 100644 --- a/app/src/main/java/io/xpipe/app/prefs/PrefsComp.java +++ b/app/src/main/java/io/xpipe/app/prefs/PrefsComp.java @@ -1,7 +1,7 @@ package io.xpipe.app.prefs; -import io.xpipe.app.comp.AppLayoutComp; import io.xpipe.app.core.AppFont; +import io.xpipe.app.core.AppLayoutModel; import io.xpipe.app.fxcomps.SimpleComp; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -9,9 +9,9 @@ import org.controlsfx.control.MasterDetailPane; public class PrefsComp extends SimpleComp { - private final AppLayoutComp layout; + private final AppLayoutModel layout; - public PrefsComp(AppLayoutComp layout) { + public PrefsComp(AppLayoutModel layout) { this.layout = layout; } diff --git a/app/src/main/java/io/xpipe/app/storage/StorageElement.java b/app/src/main/java/io/xpipe/app/storage/StorageElement.java index e565df681..361ca8bd0 100644 --- a/app/src/main/java/io/xpipe/app/storage/StorageElement.java +++ b/app/src/main/java/io/xpipe/app/storage/StorageElement.java @@ -122,12 +122,9 @@ public abstract class StorageElement { @Value public static class Configuration { boolean deletable; - boolean renameable; - boolean editable; - boolean refreshable; public static Configuration defaultConfiguration() { - return new Configuration(true, true, true, true); + return new Configuration(true); } } } diff --git a/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java b/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java index 51d2f5fa1..9a8d3775f 100644 --- a/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java +++ b/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java @@ -6,6 +6,7 @@ import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.impl.*; import io.xpipe.core.util.SecretValue; import javafx.beans.property.Property; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.geometry.Orientation; @@ -64,6 +65,11 @@ public class OptionsBuilder { return this; } + public OptionsBuilder disable() { + comp.disable(new SimpleBooleanProperty(true)); + return this; + } + public OptionsBuilder nonNull(Validator v) { var e = name; var p = props.get(props.size() - 1); diff --git a/app/src/main/resources/io/xpipe/app/resources/lang/dscreation_en.properties b/app/src/main/resources/io/xpipe/app/resources/lang/dscreation_en.properties index 2febd9fdf..189754425 100644 --- a/app/src/main/resources/io/xpipe/app/resources/lang/dscreation_en.properties +++ b/app/src/main/resources/io/xpipe/app/resources/lang/dscreation_en.properties @@ -42,7 +42,7 @@ clean=Clean refresh=Refresh remove=Remove addDatabase=Add Database ... -addHost=Add Host ... +addHost=Add Remote Host ... addShell=Add Environment ... addCommand=Add Command ... addOther=Add Other ... diff --git a/app/src/main/resources/io/xpipe/app/resources/lang/preferences_en.properties b/app/src/main/resources/io/xpipe/app/resources/lang/preferences_en.properties index 575aef422..448012593 100644 --- a/app/src/main/resources/io/xpipe/app/resources/lang/preferences_en.properties +++ b/app/src/main/resources/io/xpipe/app/resources/lang/preferences_en.properties @@ -64,6 +64,8 @@ developerMode=Developer mode developerModeDescription=When enabled, you will have access to a variety of additional options that are useful for development. editor=Editor custom=Custom +preferEditorTabs=Prefer to open new tabs +preferEditorTabsDescription=Controls whether XPipe will try to open new tabs in your chosen editor instead of new windows. customEditorCommand=Custom editor command customEditorCommandDescription=The command to execute to open the custom editor. The placeholder string $file will be replaced by the quoted absolute file name when called. editorReloadTimeout=Editor reload timeout @@ -97,6 +99,8 @@ terminalProgramDescription=The default terminal to use when opening any kind of program=Program customTerminalCommand=Custom terminal command customTerminalCommandDescription=The command to execute to open the custom terminal. The placeholder string $cmd will be replaced by the quoted shell script file name when called. +preferTerminalTabs=Prefer to open new tabs +preferTerminalTabsDescription=Controls whether XPipe will try to open new tabs in your chosen terminal instead of new windows. cmd=cmd.exe powershell=Powershell pwsh=Powershell Core diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index 3542824a8..fa6c5c14a 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -43,7 +43,7 @@ } .store-entry-section-comp:expanded:even-depth { --fx-background-color: -color-bg-subtle; +-fx-background-color: -color-neutral-subtle; -fx-background-radius: 4px; } diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java b/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java index 2dc0737ac..b235cfa65 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java @@ -3,14 +3,14 @@ package io.xpipe.beacon.exchange; import io.xpipe.beacon.RequestMessage; import io.xpipe.beacon.ResponseMessage; +import java.util.List; import java.util.Optional; import java.util.ServiceLoader; -import java.util.Set; import java.util.stream.Collectors; public class MessageExchanges { - private static Set ALL; + private static List ALL; public static void loadAll() { if (ALL == null) { @@ -19,7 +19,7 @@ public class MessageExchanges { var ex = (MessageExchange) s.get(); return ex; }) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } } @@ -42,7 +42,7 @@ public class MessageExchanges { .findAny(); } - public static Set getAll() { + public static List getAll() { loadAll(); return ALL; } diff --git a/core/src/main/java/io/xpipe/core/impl/FileNames.java b/core/src/main/java/io/xpipe/core/impl/FileNames.java index d2a66cfa6..53b6c8a85 100644 --- a/core/src/main/java/io/xpipe/core/impl/FileNames.java +++ b/core/src/main/java/io/xpipe/core/impl/FileNames.java @@ -11,6 +11,10 @@ public class FileNames { } public static String toDirectory(String path) { + if (path == null) { + return null; + } + if (path.endsWith("/") || path.endsWith("\\")) { return path; } diff --git a/core/src/main/java/io/xpipe/core/store/LeafShellStore.java b/core/src/main/java/io/xpipe/core/store/LeafShellStore.java deleted file mode 100644 index 5674907bd..000000000 --- a/core/src/main/java/io/xpipe/core/store/LeafShellStore.java +++ /dev/null @@ -1,3 +0,0 @@ -package io.xpipe.core.store; - -public interface LeafShellStore extends DataStore {} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/AddStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/AddStoreAction.java similarity index 97% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/AddStoreAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/AddStoreAction.java index 10d78a4c2..befc3f66a 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/AddStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/AddStoreAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.comp.store.GuiDsStoreCreator; import io.xpipe.app.ext.ActionProvider; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/BrowseStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/BrowseStoreAction.java new file mode 100644 index 000000000..1286290df --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/BrowseStoreAction.java @@ -0,0 +1,63 @@ +package io.xpipe.ext.base.action; + +import io.xpipe.app.browser.BrowserModel; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppLayoutModel; +import io.xpipe.app.ext.ActionProvider; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.core.store.ShellStore; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.value.ObservableValue; +import lombok.Value; + +public class BrowseStoreAction implements ActionProvider { + + @Value + static class Action implements ActionProvider.Action { + + DataStoreEntry entry; + + @Override + public boolean requiresJavaFXPlatform() { + return true; + } + + @Override + public void execute() { + BrowserModel.DEFAULT.openFileSystemAsync(entry.getName(), entry.getStore().asNeeded(),null, new SimpleBooleanProperty()); + AppLayoutModel.get().selectBrowser(); + } + } + + @Override + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { + + @Override + public boolean isMajor(ShellStore o) { + return true; + } + + @Override + public ObservableValue getName(ShellStore store) { + return AppI18n.observable("browseFiles"); + } + + @Override + public String getIcon(ShellStore store) { + return "mdi2f-folder-open-outline"; + } + + @Override + public ActionProvider.Action createAction(ShellStore store) { + return new Action(DataStorage.get().getStoreEntry(store)); + } + + @Override + public Class getApplicableClass() { + return ShellStore.class; + } + }; + } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/DeleteStoreChildrenAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/DeleteStoreChildrenAction.java similarity index 95% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/DeleteStoreChildrenAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/DeleteStoreChildrenAction.java index fa39082fc..d22918579 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/DeleteStoreChildrenAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/DeleteStoreChildrenAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.ActionProvider; @@ -32,7 +32,7 @@ public class DeleteStoreChildrenAction implements ActionProvider { return new DataStoreCallSite<>() { @Override - public boolean isMajor() { + public boolean isMajor(DataStore o) { return false; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/EditStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/EditStoreAction.java similarity index 90% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/EditStoreAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/EditStoreAction.java index 5d1f55c69..2a66da6d4 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/EditStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/EditStoreAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.comp.store.GuiDsStoreCreator; import io.xpipe.app.core.AppI18n; @@ -57,7 +57,7 @@ public class EditStoreAction implements ActionProvider { return new DataStoreCallSite<>() { @Override - public boolean isMajor() { + public boolean isMajor(DataStore o) { return false; } @@ -76,11 +76,6 @@ public class EditStoreAction implements ActionProvider { return DataStore.class; } - @Override - public boolean isApplicable(DataStore o) { - return DataStorage.get().getStoreEntry(o).getConfiguration().isEditable(); - } - @Override public ObservableValue getName(DataStore store) { return AppI18n.observable("base.edit"); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/FileBrowseAction.java similarity index 97% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/FileBrowseAction.java index 628154748..51df0a40f 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/FileBrowseAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.ActionProvider; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/FileEditAction.java similarity index 97% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/FileEditAction.java index 0c0902966..5fcd97d7f 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/FileEditAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.ActionProvider; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java new file mode 100644 index 000000000..338e8ae9b --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchAction.java @@ -0,0 +1,117 @@ +package io.xpipe.ext.base.action; + +import io.xpipe.app.ext.ActionProvider; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.app.util.TerminalHelper; +import io.xpipe.core.store.LaunchableStore; +import lombok.Value; + +import java.util.List; +import java.util.UUID; + +public class LaunchAction implements ActionProvider { + + @Value + static class Action implements ActionProvider.Action { + + DataStoreEntry entry; + + @Override + public boolean requiresJavaFXPlatform() { + return false; + } + + @Override + public void execute() throws Exception { + var storeName = entry.getName(); + if (entry.getStore() instanceof LaunchableStore s) { + String command = s.prepareLaunchCommand(storeName); + if (command == null) { + return; + } + + TerminalHelper.open(storeName, command); + } + } + } + + @Override + public LauncherCallSite getLauncherCallSite() { + return new LauncherCallSite() { + @Override + public String getId() { + return "launch"; + } + + @Override + public ActionProvider.Action createAction(List args) { + var entry = DataStorage.get() + .getStoreEntry(UUID.fromString(args.get(0))) + .orElseThrow(); + return new Action(entry); + } + }; + } + + @Override + public DefaultDataStoreCallSite getDefaultDataStoreCallSite() { + return new DefaultDataStoreCallSite() { + @Override + public boolean isApplicable(LaunchableStore o) { + return DataStorage.get() + .getStoreEntryIfPresent(o) + .orElseThrow() + .getState() + .isUsable(); + } + + @Override + public ActionProvider.Action createAction(LaunchableStore store) { + return new Action( + DataStorage.get().getStoreEntryIfPresent(store).orElseThrow()); + } + + @Override + public Class getApplicableClass() { + return LaunchableStore.class; + } + }; + } + + // @Override + // public DataStoreCallSite getDataStoreCallSite() { + // return new DataStoreCallSite() { + // + // @Override + // public boolean isApplicable(LaunchableStore o) throws Exception { + // return DataStorage.get().getStoreEntryIfPresent(o).orElseThrow().getState().isUsable(); + // } + // + // @Override + // public ObservableValue getName(LaunchableStore store) { + // return AppI18n.observable("openShell"); + // } + // + // @Override + // public String getIcon(LaunchableStore store) { + // return "mdi2c-code-greater-than"; + // } + // + // @Override + // public ActionProvider.Action createAction(LaunchableStore store) { + // return new Action(DataStorage.get().getStoreEntryIfPresent(store).orElseThrow()); + // } + // + // @Override + // public Class getApplicableClass() { + // return LaunchableStore.class; + // } + // + // @Override + // public boolean isMajor() { + // return true; + // } + // }; + // } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchShortcutAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchShortcutAction.java new file mode 100644 index 000000000..5eaa6da9e --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/LaunchShortcutAction.java @@ -0,0 +1,60 @@ +package io.xpipe.ext.base.action; + +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.ext.ActionProvider; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.app.util.DesktopShortcuts; +import io.xpipe.core.store.ShellStore; +import javafx.beans.value.ObservableValue; +import lombok.Value; + +public class LaunchShortcutAction implements ActionProvider { + + @Value + static class Action implements ActionProvider.Action { + + DataStoreEntry entry; + + @Override + public boolean requiresJavaFXPlatform() { + return false; + } + + @Override + public void execute() throws Exception { + DesktopShortcuts.create("xpipe://launch/" + entry.getUuid().toString(), entry.getName()); + } + } + + @Override + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { + + @Override + public Action createAction(ShellStore store) { + return new Action(DataStorage.get().getStoreEntry(store)); + } + + @Override + public Class getApplicableClass() { + return ShellStore.class; + } + + @Override + public ObservableValue getName(ShellStore store) { + return AppI18n.observable("createShortcut"); + } + + @Override + public String getIcon(ShellStore store) { + return "mdi2c-code-greater-than"; + } + + @Override + public boolean isMajor(ShellStore o) { + return false; + } + }; + } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/RefreshStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/RefreshStoreAction.java similarity index 95% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/RefreshStoreAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/RefreshStoreAction.java index 5bca605d9..ae10f9249 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/RefreshStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/RefreshStoreAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.ActionProvider; @@ -31,7 +31,7 @@ public class RefreshStoreAction implements ActionProvider { return new ActionProvider.DataStoreCallSite<>() { @Override - public boolean isMajor() { + public boolean isMajor(DataStore o) { return true; } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/SampleAction.java similarity index 99% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/SampleAction.java index e3d311864..de9ce4609 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/SampleAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/SampleAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.ActionProvider; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/action/ScanAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/ScanAction.java new file mode 100644 index 000000000..aefc0e288 --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/ScanAction.java @@ -0,0 +1,61 @@ +package io.xpipe.ext.base.action; + +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.ext.ActionProvider; +import io.xpipe.app.ext.DataStoreProviders; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.app.util.ScanAlert; +import io.xpipe.core.store.ShellStore; +import javafx.beans.value.ObservableValue; +import lombok.Value; + +public class ScanAction implements ActionProvider { + + @Value + static class Action implements ActionProvider.Action { + + DataStoreEntry entry; + + @Override + public boolean requiresJavaFXPlatform() { + return true; + } + + @Override + public void execute() { + ScanAlert.showAsync(entry, false); + } + } + + @Override + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { + + @Override + public boolean isMajor(ShellStore o) { + return DataStoreProviders.byStore(o).shouldHaveSubShells(); + } + + @Override + public ObservableValue getName(ShellStore store) { + return AppI18n.observable("scanConnections"); + } + + @Override + public String getIcon(ShellStore store) { + return "mdi2p-playlist-plus"; + } + + @Override + public ActionProvider.Action createAction(ShellStore store) { + return new Action(DataStorage.get().getStoreEntry(store)); + } + + @Override + public Class getApplicableClass() { + return ShellStore.class; + } + }; + } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/ShareStoreAction.java similarity index 98% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/ShareStoreAction.java index 2d63833b0..e868fd015 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/ShareStoreAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.core.AppActionLinkDetector; import io.xpipe.app.core.AppI18n; diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java b/ext/base/src/main/java/io/xpipe/ext/base/action/StreamExportAction.java similarity index 98% rename from ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java rename to ext/base/src/main/java/io/xpipe/ext/base/action/StreamExportAction.java index a74d78832..57d30e5db 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/action/StreamExportAction.java @@ -1,4 +1,4 @@ -package io.xpipe.ext.base.actions; +package io.xpipe.ext.base.action; import io.xpipe.app.browser.StandaloneFileBrowser; import io.xpipe.app.core.AppI18n; diff --git a/ext/base/src/main/java/module-info.java b/ext/base/src/main/java/module-info.java index 86fcb0ce8..0998c9e46 100644 --- a/ext/base/src/main/java/module-info.java +++ b/ext/base/src/main/java/module-info.java @@ -3,12 +3,12 @@ import io.xpipe.app.ext.ActionProvider; import io.xpipe.app.ext.DataSourceProvider; import io.xpipe.app.ext.DataStoreProvider; import io.xpipe.ext.base.*; -import io.xpipe.ext.base.actions.*; +import io.xpipe.ext.base.action.*; import io.xpipe.ext.base.browser.*; open module io.xpipe.ext.base { exports io.xpipe.ext.base; - exports io.xpipe.ext.base.actions; + exports io.xpipe.ext.base.action; requires java.desktop; requires io.xpipe.core; @@ -48,10 +48,14 @@ open module io.xpipe.ext.base { JavapAction, JarAction; provides ActionProvider with + ScanAction, + LaunchAction, + LaunchShortcutAction, AddStoreAction, EditStoreAction, ShareStoreAction, FileBrowseAction, + BrowseStoreAction, FileEditAction; provides DataSourceProvider with TextSourceProvider, diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties index 33834a19b..207dd0d18 100644 --- a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties +++ b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties @@ -12,6 +12,9 @@ options=Options newFile=New file newLink=New link linkName=Link name +scanConnections=Add connections ... +createShortcut=Create desktop shortcut +browseFiles=Browse Files targetPath=Target path newDirectory=New directory terminator=Terminator