From 3de8cf7b5d35ac8e77a3dcff0cd045973b74490c Mon Sep 17 00:00:00 2001 From: crschnick Date: Fri, 20 Dec 2024 16:43:58 +0000 Subject: [PATCH] Modal rework --- .../file/BrowserFileSystemTabModel.java | 4 +- .../xpipe/app/comp/base/ErrorOverlayComp.java | 19 ++--- .../io/xpipe/app/comp/base/ModalOverlay.java | 65 +++++++++++++++ .../xpipe/app/comp/base/ModalOverlayComp.java | 69 ++++++++-------- .../java/io/xpipe/app/core/mode/BaseMode.java | 19 ++++- .../java/io/xpipe/app/core/mode/GuiMode.java | 15 +--- .../io/xpipe/app/core/mode/OperationMode.java | 6 ++ .../io/xpipe/app/core/window/AppDialog.java | 70 ++++++++++++++++ .../xpipe/app/core/window/AppMainWindow.java | 33 ++++---- .../app/update/UpdateAvailableAlert.java | 64 +++++++-------- .../java/io/xpipe/app/util/PlatformInit.java | 1 + .../xpipe/ext/base/browser/ChgrpAction.java | 49 +++++------- .../xpipe/ext/base/browser/ChmodAction.java | 49 +++++------- .../xpipe/ext/base/browser/ChownAction.java | 49 +++++------- .../xpipe/ext/base/browser/NewItemAction.java | 80 ++++++++----------- .../browser/compress/BaseCompressAction.java | 41 +++++----- 16 files changed, 362 insertions(+), 271 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/comp/base/ModalOverlay.java create mode 100644 app/src/main/java/io/xpipe/app/core/window/AppDialog.java diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java index de5636718..2d0ecf654 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java @@ -5,7 +5,7 @@ import io.xpipe.app.browser.BrowserFullSessionModel; import io.xpipe.app.browser.BrowserStoreSessionTab; import io.xpipe.app.browser.action.BrowserAction; import io.xpipe.app.comp.Comp; -import io.xpipe.app.comp.base.ModalOverlayComp; +import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.window.AppMainWindow; import io.xpipe.app.ext.ProcessControlProvider; import io.xpipe.app.ext.ShellStore; @@ -44,7 +44,7 @@ public final class BrowserFileSystemTabModel extends BrowserStoreSessionTab currentPath = new ReadOnlyObjectWrapper<>(); private final BrowserFileSystemHistory history = new BrowserFileSystemHistory(); - private final Property overlay = new SimpleObjectProperty<>(); + private final Property overlay = new SimpleObjectProperty<>(); private final BooleanProperty inOverview = new SimpleBooleanProperty(); private final Property progress = new SimpleObjectProperty<>(); private final ObservableList terminalRequests = FXCollections.observableArrayList(); diff --git a/app/src/main/java/io/xpipe/app/comp/base/ErrorOverlayComp.java b/app/src/main/java/io/xpipe/app/comp/base/ErrorOverlayComp.java index 8d12c07f1..77bb2a5aa 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ErrorOverlayComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ErrorOverlayComp.java @@ -28,7 +28,7 @@ public class ErrorOverlayComp extends SimpleComp { @Override protected Region createSimple() { - var content = new SimpleObjectProperty(); + var content = new SimpleObjectProperty(); this.text.addListener((observable, oldValue, newValue) -> { PlatformThread.runLaterIfNeeded(() -> { var comp = Comp.of(() -> { @@ -39,17 +39,12 @@ public class ErrorOverlayComp extends SimpleComp { l.setEditable(false); return l; }); - content.set(new ModalOverlayComp.OverlayContent( - "error", - comp, - Comp.of(() -> { - var graphic = new FontIcon("mdomz-warning"); - graphic.setIconColor(Color.RED); - return new StackPane(graphic); - }), - null, - () -> {}, - false)); + var overlay = ModalOverlay.of("error", comp, Comp.of(() -> { + var graphic = new FontIcon("mdomz-warning"); + graphic.setIconColor(Color.RED); + return new StackPane(graphic); + })).withDefaultButtons(); + content.set(overlay); }); }); content.addListener((observable, oldValue, newValue) -> { diff --git a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlay.java b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlay.java new file mode 100644 index 000000000..12d6bc45e --- /dev/null +++ b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlay.java @@ -0,0 +1,65 @@ +package io.xpipe.app.comp.base; + +import io.xpipe.app.comp.Comp; +import lombok.Builder; +import lombok.Singular; +import lombok.Value; +import lombok.With; + +import java.util.ArrayList; +import java.util.List; + +@Value +@With +@Builder(toBuilder = true) +public class ModalOverlay { + + public static ModalOverlay of(String titleKey, Comp content) { + return of(titleKey, content, null); + } + + public static ModalOverlay of(String titleKey, Comp content, Comp graphic) { + return new ModalOverlay(titleKey,content,graphic, new ArrayList<>()); + } + + public ModalOverlay withDefaultButtons(Runnable action) { + addButton(ModalButton.cancel()); + addButton(ModalButton.ok(action)); + return this; + } + + public ModalOverlay withDefaultButtons() { + return withDefaultButtons(() -> {}); + } + + String titleKey; + Comp content; + Comp graphic; + + @Singular + List buttons; + + public void addButton(ModalButton button) { + buttons.add(button); + } + + @Value + public static class ModalButton { + String key; + Runnable action; + boolean close; + boolean defaultButton; + + public static ModalButton finish(Runnable action) { + return new ModalButton("finish", action, true, true); + } + + public static ModalButton ok(Runnable action) { + return new ModalButton("ok", action, true, true); + } + + public static ModalButton cancel() { + return new ModalButton("cancel", () -> {}, true, false); + } + } +} diff --git a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java index 3c3cfb1b2..1f30320bb 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlayComp.java @@ -21,14 +21,13 @@ import javafx.scene.layout.VBox; import atlantafx.base.controls.ModalPane; import atlantafx.base.layout.ModalBox; import atlantafx.base.theme.Styles; -import lombok.Value; public class ModalOverlayComp extends SimpleComp { private final Comp background; - private final Property overlayContent; + private final Property overlayContent; - public ModalOverlayComp(Comp background, Property overlayContent) { + public ModalOverlayComp(Comp background, Property overlayContent) { this.background = background; this.overlayContent = overlayContent; } @@ -65,11 +64,11 @@ public class ModalOverlayComp extends SimpleComp { if (newValue != null) { var l = new Label( - AppI18n.get(newValue.titleKey), - newValue.graphic != null ? newValue.graphic.createRegion() : null); + AppI18n.get(newValue.getTitleKey()), + newValue.getGraphic() != null ? newValue.getGraphic().createRegion() : null); l.setGraphicTextGap(6); AppFont.normal(l); - var r = newValue.content.createRegion(); + var r = newValue.getContent().createRegion(); var box = new VBox(l, r); box.focusedProperty().addListener((o, old, n) -> { if (n) { @@ -79,19 +78,11 @@ public class ModalOverlayComp extends SimpleComp { box.setSpacing(10); box.setPadding(new Insets(10, 15, 15, 15)); - if (newValue.finishKey != null) { - var finishButton = new Button(AppI18n.get(newValue.finishKey)); - finishButton.getStyleClass().add(Styles.ACCENT); - finishButton.setOnAction(event -> { - newValue.onFinish.run(); - overlayContent.setValue(null); - event.consume(); - }); - - var buttonBar = new ButtonBar(); - buttonBar.getButtons().addAll(finishButton); - box.getChildren().add(buttonBar); + var buttonBar = new ButtonBar(); + for (var mb : newValue.getButtons()) { + buttonBar.getButtons().add(toButton(mb)); } + box.getChildren().add(buttonBar); var modalBox = new ModalBox(box); modalBox.setOnClose(event -> { @@ -110,15 +101,15 @@ public class ModalOverlayComp extends SimpleComp { }); modal.show(modalBox); - if (newValue.finishOnEnter) { - modalBox.addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (event.getCode() == KeyCode.ENTER) { - newValue.onFinish.run(); - overlayContent.setValue(null); - event.consume(); - } - }); - } +// if (newValue.isFinishOnEnter()) { +// modalBox.addEventFilter(KeyEvent.KEY_PRESSED, event -> { +// if (event.getCode() == KeyCode.ENTER) { +// newValue.getOnFinish().run(); +// overlayContent.setValue(null); +// event.consume(); +// } +// }); +// } // Wait 2 pulses before focus so that the scene can be assigned to r Platform.runLater(() -> { @@ -131,14 +122,20 @@ public class ModalOverlayComp extends SimpleComp { return pane; } - @Value - public static class OverlayContent { - - String titleKey; - Comp content; - Comp graphic; - String finishKey; - Runnable onFinish; - boolean finishOnEnter; + private Button toButton(ModalOverlay.ModalButton mb) { + var button = new Button(AppI18n.get(mb.getKey())); + if (mb.isDefaultButton()) { + button.getStyleClass().add(Styles.ACCENT); + } + button.setOnAction(event -> { + if (mb.getAction() != null) { + mb.getAction().run(); + } + if (mb.isClose()) { + overlayContent.setValue(null); + } + event.consume(); + }); + return button; } } diff --git a/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java b/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java index eecde2d7b..e2259bdee 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/BaseMode.java @@ -3,9 +3,13 @@ package io.xpipe.app.core.mode; import io.xpipe.app.beacon.AppBeaconServer; import io.xpipe.app.beacon.BlobManager; import io.xpipe.app.browser.BrowserFullSessionModel; +import io.xpipe.app.browser.file.BrowserLocalFileSystem; +import io.xpipe.app.browser.icon.BrowserIconManager; +import io.xpipe.app.comp.base.AppLayoutComp; import io.xpipe.app.comp.store.StoreViewState; import io.xpipe.app.core.*; import io.xpipe.app.core.check.*; +import io.xpipe.app.core.window.AppMainWindow; import io.xpipe.app.ext.ActionProvider; import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.ext.ProcessControlProvider; @@ -19,6 +23,7 @@ import io.xpipe.app.storage.DataStorageSyncHandler; import io.xpipe.app.terminal.TerminalLauncherManager; import io.xpipe.app.terminal.TerminalView; import io.xpipe.app.update.UpdateAvailableAlert; +import io.xpipe.app.update.UpdateChangelogAlert; import io.xpipe.app.update.XPipeDistributionType; import io.xpipe.app.util.*; @@ -66,9 +71,15 @@ public class BaseMode extends OperationMode { DataStorageSyncHandler.getInstance().init(); DataStorageSyncHandler.getInstance().retrieveSyncedData(); AppPrefs.initSharedRemote(); - SystemIcons.init(); DataStorage.init(); StoreViewState.init(); + AppLayoutModel.init(); + PlatformInit.init(true); + PlatformThread.runLaterIfNeededBlocking(() -> { + var content = new AppLayoutComp(); + var region = content.createRegion(); + AppMainWindow.getInstance().setLoadedContent(region); + }); }, () -> { AppFileWatcher.init(); FileBridge.init(); @@ -78,6 +89,7 @@ public class BaseMode extends OperationMode { }, () -> { PlatformInit.init(true); AppImages.init(); + SystemIcons.init(); }, () -> { // If we downloaded an update, and decided to no longer automatically update, don't remind us! // You can still update manually in the about tab @@ -85,6 +97,11 @@ public class BaseMode extends OperationMode { || AppPrefs.get().checkForSecurityUpdates().get()) { UpdateAvailableAlert.showIfNeeded(); } + UpdateChangelogAlert.showIfNeeded(); + }, () -> { + BrowserIconManager.loadIfNecessary(); + }, () -> { + BrowserLocalFileSystem.init(); }); ActionProvider.initProviders(); DataStoreProviders.init(); diff --git a/app/src/main/java/io/xpipe/app/core/mode/GuiMode.java b/app/src/main/java/io/xpipe/app/core/mode/GuiMode.java index 85a3722fc..759d01e74 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/GuiMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/GuiMode.java @@ -40,23 +40,10 @@ public class GuiMode extends PlatformMode { AppGreetings.showIfNeeded(); AppPtbCheck.check(); - NativeBridge.init(); - AppLayoutModel.init(); PlatformThread.runLaterIfNeededBlocking(() -> { - var content = new AppLayoutComp(); - AppMainWindow.initContent(content); + AppMainWindow.initContent(); }); - - // Can be loaded async - ThreadHelper.runFailableAsync(() -> { - BrowserIconManager.loadIfNecessary(); - }); - ThreadHelper.runFailableAsync(() -> { - BrowserLocalFileSystem.init(); - }); - - UpdateChangelogAlert.showIfNeeded(); } @Override diff --git a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java index 445b0c797..6719a71c8 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java @@ -40,6 +40,8 @@ public abstract class OperationMode { @Getter private static boolean inShutdownHook; + private static boolean windowRequested; + private static OperationMode CURRENT = null; public static OperationMode map(XPipeDaemonMode mode) { @@ -119,6 +121,10 @@ public abstract class OperationMode { } public static XPipeDaemonMode getStartupMode() { + if (windowRequested) { + return XPipeDaemonMode.GUI; + } + var arg = AppProperties.get().getArguments().getModeArg(); if (arg != null) { return arg; diff --git a/app/src/main/java/io/xpipe/app/core/window/AppDialog.java b/app/src/main/java/io/xpipe/app/core/window/AppDialog.java new file mode 100644 index 000000000..375f3a6c2 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/window/AppDialog.java @@ -0,0 +1,70 @@ +package io.xpipe.app.core.window; + +import io.xpipe.app.comp.base.DialogComp; +import io.xpipe.app.comp.base.ModalOverlay; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.util.PlatformInit; +import io.xpipe.app.util.ThreadHelper; +import javafx.application.Platform; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.control.Alert; +import javafx.stage.Stage; +import lombok.Getter; +import lombok.SneakyThrows; + +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +public class AppDialog { + + @Getter + private static final Property modalOverlay = new SimpleObjectProperty<>(); + + private static void showMainWindow() { + PlatformInit.init(true); + AppMainWindow.initEmpty(); + } + + private static void closeDialog() { + modalOverlay.setValue(null); + } + + private static void waitForClose() { + while (modalOverlay.getValue() != null) { + ThreadHelper.sleep(10); + } + } + + @SneakyThrows + public static void show(ModalOverlay o) { + showMainWindow(); + + if (!Platform.isFxApplicationThread()) { + CountDownLatch latch = new CountDownLatch(1); + Platform.runLater(() -> { + try { + modalOverlay.setValue(o); + } finally { + latch.countDown(); + } + }); + latch.await(); + waitForClose(); + try { + latch.await(); + } catch (InterruptedException ignored) { + } + } else { + modalOverlay.setValue(o); + var key = new Object(); + Platform.enterNestedEventLoop(key); + waitForClose(); + Platform.exitNestedEventLoop(key, null); + } + } +} diff --git a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java index 2bcc6479e..7e0b31bf7 100644 --- a/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java +++ b/app/src/main/java/io/xpipe/app/core/window/AppMainWindow.java @@ -2,6 +2,7 @@ package io.xpipe.app.core.window; import io.xpipe.app.comp.Comp; import io.xpipe.app.comp.base.AppWindowLoadComp; +import io.xpipe.app.comp.base.ModalOverlayComp; import io.xpipe.app.core.*; import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.issue.ErrorEvent; @@ -35,6 +36,7 @@ import javafx.stage.Stage; import lombok.Builder; import lombok.Getter; +import lombok.Setter; import lombok.Value; import lombok.extern.jackson.Jacksonized; @@ -56,6 +58,9 @@ public class AppMainWindow { private Thread thread; private volatile Instant lastUpdate; + @Setter + private Region loadedContent; + public AppMainWindow(Stage stage) { this.stage = stage; } @@ -67,7 +72,8 @@ public class AppMainWindow { var stage = App.getApp().getStage(); INSTANCE = new AppMainWindow(stage); - var scene = new Scene(new AppWindowLoadComp().createRegion(), -1, -1, false); + var emptyContent = new ModalOverlayComp(new AppWindowLoadComp(), AppDialog.getModalOverlay()); + var scene = new Scene(emptyContent.createRegion(), -1, -1, false); scene.setFill(Color.TRANSPARENT); ModifiedStage.prepareStage(stage); stage.setScene(scene); @@ -99,12 +105,12 @@ public class AppMainWindow { return getStage().outputScaleXProperty(); } - public static synchronized void initContent(Comp content) { + public static synchronized void initContent() { if (INSTANCE == null) { initEmpty(); } - INSTANCE.setupContent(content); + INSTANCE.setupContent(INSTANCE.loadedContent); } private static ObservableValue createTitle() { @@ -318,12 +324,12 @@ public class AppMainWindow { return inBounds ? state : null; } - private void setupContent(Comp content) { - var contentR = content.createRegion(); - stage.getScene().setRoot(contentR); + private void setupContent(Region content) { + var withOverlay = new ModalOverlayComp(Comp.of(() -> content), AppDialog.getModalOverlay()); + stage.getScene().setRoot(withOverlay.createRegion()); TrackEvent.debug("Set content scene"); - contentR.opacityProperty() + content.opacityProperty() .bind(Bindings.createDoubleBinding( () -> { if (OsType.getLocal() != OsType.MACOS) { @@ -333,8 +339,8 @@ public class AppMainWindow { }, stage.focusedProperty())); - contentR.prefWidthProperty().bind(stage.getScene().widthProperty()); - contentR.prefHeightProperty().bind(stage.getScene().heightProperty()); + content.prefWidthProperty().bind(stage.getScene().widthProperty()); + content.prefHeightProperty().bind(stage.getScene().heightProperty()); if (OsType.getLocal().equals(OsType.LINUX) || OsType.getLocal().equals(OsType.MACOS)) { stage.getScene().addEventHandler(KeyEvent.KEY_PRESSED, event -> { @@ -346,15 +352,6 @@ public class AppMainWindow { } stage.getScene().addEventHandler(KeyEvent.KEY_PRESSED, event -> { - if (AppProperties.get().isDeveloperMode() && event.getCode().equals(KeyCode.F6)) { - var newR = content.createRegion(); - stage.getScene().setRoot(newR); - newR.requestFocus(); - - TrackEvent.debug("Rebuilt content"); - event.consume(); - } - if (AppProperties.get().isShowcase() && event.getCode().equals(KeyCode.F12)) { var image = stage.getScene().snapshot(null); var awt = AppImages.toAwtImage(image); diff --git a/app/src/main/java/io/xpipe/app/update/UpdateAvailableAlert.java b/app/src/main/java/io/xpipe/app/update/UpdateAvailableAlert.java index 89b0f645c..121529c9d 100644 --- a/app/src/main/java/io/xpipe/app/update/UpdateAvailableAlert.java +++ b/app/src/main/java/io/xpipe/app/update/UpdateAvailableAlert.java @@ -1,16 +1,14 @@ package io.xpipe.app.update; +import io.xpipe.app.comp.Comp; import io.xpipe.app.comp.base.MarkdownComp; -import io.xpipe.app.core.AppI18n; -import io.xpipe.app.core.window.AppWindowHelper; +import io.xpipe.app.comp.base.ModalOverlay; +import io.xpipe.app.core.window.AppDialog; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.util.Hyperlinks; -import javafx.event.ActionEvent; import javafx.geometry.Insets; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonBar; -import javafx.scene.control.ButtonType; +import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; @@ -32,38 +30,30 @@ public class UpdateAvailableAlert { .tag("version", uh.getPreparedUpdate().getValue().getVersion()) .handle(); var u = uh.getPreparedUpdate().getValue(); - var update = AppWindowHelper.showBlockingAlert(alert -> { - alert.setTitle(AppI18n.get("updateReadyAlertTitle")); - alert.setAlertType(Alert.AlertType.NONE); - var markdown = new MarkdownComp(u.getBody() != null ? u.getBody() : "", s -> s).createRegion(); - alert.getButtonTypes().clear(); - var updaterContent = uh.createInterface(); - if (updaterContent != null) { - var stack = new StackPane(updaterContent); - stack.setPadding(new Insets(18)); - var box = new VBox(markdown, stack); - box.setFillWidth(true); - box.setPadding(Insets.EMPTY); - alert.getDialogPane().setContent(box); - } else { - alert.getDialogPane().setContent(markdown); - alert.getButtonTypes() - .add(new ButtonType(AppI18n.get("install"), ButtonBar.ButtonData.OK_DONE)); - var visit = new ButtonType(AppI18n.get("checkOutUpdate"), ButtonBar.ButtonData.FINISH); - alert.getButtonTypes().add(visit); - var button = alert.getDialogPane().lookupButton(visit); - button.addEventFilter(ActionEvent.ANY, event -> { - Hyperlinks.open(uh.getPreparedUpdate().getValue().getReleaseUrl()); - event.consume(); - }); - } - alert.getButtonTypes().add(new ButtonType(AppI18n.get("ignore"), ButtonBar.ButtonData.NO)); - }) - .map(buttonType -> buttonType.getButtonData().isDefaultButton()) - .orElse(false); - if (update) { - uh.executeUpdateAndClose(); + var markdown = new MarkdownComp(u.getBody() != null ? u.getBody() : "", s -> s).createRegion(); + var updaterContent = uh.createInterface(); + + Region region; + if (updaterContent != null) { + var stack = new StackPane(updaterContent); + stack.setPadding(new Insets(18)); + var box = new VBox(markdown, stack); + box.setFillWidth(true); + box.setPadding(Insets.EMPTY); + region = box; + } else { + region = markdown; } + + var modal = ModalOverlay.of("updateReadyAlertTitle", Comp.of(() -> region).prefWidth(600), null); + modal.addButton(new ModalOverlay.ModalButton("ignore",null,true,false)); + modal.addButton(new ModalOverlay.ModalButton("checkOutUpdate",() -> { + Hyperlinks.open(uh.getPreparedUpdate().getValue().getReleaseUrl()); + },false,false)); + modal.addButton(new ModalOverlay.ModalButton("install",() -> { + uh.executeUpdateAndClose(); + },false,true)); + AppDialog.show(modal); } } diff --git a/app/src/main/java/io/xpipe/app/util/PlatformInit.java b/app/src/main/java/io/xpipe/app/util/PlatformInit.java index 6613f2127..80db18150 100644 --- a/app/src/main/java/io/xpipe/app/util/PlatformInit.java +++ b/app/src/main/java/io/xpipe/app/util/PlatformInit.java @@ -64,6 +64,7 @@ public class PlatformInit { while (App.getApp() == null) { ThreadHelper.sleep(100); } + NativeBridge.init(); if (OperationMode.getStartupMode() == XPipeDaemonMode.GUI) { PlatformThread.runLaterIfNeededBlocking(() -> { AppMainWindow.initEmpty(); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/ChgrpAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/ChgrpAction.java index f34daa0f7..bfec368f3 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/ChgrpAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/ChgrpAction.java @@ -5,7 +5,7 @@ import io.xpipe.app.browser.action.BrowserLeafAction; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.comp.Comp; -import io.xpipe.app.comp.base.ModalOverlayComp; +import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.OsType; @@ -85,36 +85,31 @@ public class ChgrpAction implements BrowserBranchAction { } private static class Custom implements BrowserLeafAction { + @Override public void execute(BrowserFileSystemTabModel model, List entries) { var group = new SimpleStringProperty(); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "groupName", - Comp.of(() -> { - var creationName = new TextField(); - creationName.textProperty().bindBidirectional(group); - return creationName; - }) - .prefWidth(350), - null, - "finish", - () -> { - if (group.getValue() == null) { - return; - } + var modal = ModalOverlay.of("groupName", Comp.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(group); + return creationName; + }).prefWidth(350)); + modal.withDefaultButtons(() -> { + if (group.getValue() == null) { + return; + } - model.runCommandAsync( - CommandBuilder.of() - .add("chgrp", group.getValue()) - .addFiles(entries.stream() - .map(browserEntry -> browserEntry - .getRawFileEntry() - .getPath()) - .toList()), - false); - }, - true)); + model.runCommandAsync( + CommandBuilder.of() + .add("chgrp", group.getValue()) + .addFiles(entries.stream() + .map(browserEntry -> browserEntry + .getRawFileEntry() + .getPath()) + .toList()), + false); + }); + model.getOverlay().setValue(modal); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/ChmodAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/ChmodAction.java index a1bfb6046..f9f0980a1 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/ChmodAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/ChmodAction.java @@ -5,7 +5,7 @@ import io.xpipe.app.browser.action.BrowserLeafAction; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.comp.Comp; -import io.xpipe.app.comp.base.ModalOverlayComp; +import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.OsType; @@ -87,33 +87,28 @@ public class ChmodAction implements BrowserBranchAction { @Override public void execute(BrowserFileSystemTabModel model, List entries) { var permissions = new SimpleStringProperty(); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "chmodPermissions", - Comp.of(() -> { - var creationName = new TextField(); - creationName.textProperty().bindBidirectional(permissions); - return creationName; - }) - .prefWidth(350), - null, - "finish", - () -> { - if (permissions.getValue() == null) { - return; - } + var modal = ModalOverlay.of("chmodPermissions", Comp.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(permissions); + return creationName; + }) + .prefWidth(350)); + modal.withDefaultButtons(() -> { + if (permissions.getValue() == null) { + return; + } - model.runCommandAsync( - CommandBuilder.of() - .add("chmod", permissions.getValue()) - .addFiles(entries.stream() - .map(browserEntry -> browserEntry - .getRawFileEntry() - .getPath()) - .toList()), - false); - }, - true)); + model.runCommandAsync( + CommandBuilder.of() + .add("chmod", permissions.getValue()) + .addFiles(entries.stream() + .map(browserEntry -> browserEntry + .getRawFileEntry() + .getPath()) + .toList()), + false); + }); + model.getOverlay().setValue(modal); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/ChownAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/ChownAction.java index bd5f45bde..ed96227c1 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/ChownAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/ChownAction.java @@ -5,7 +5,7 @@ import io.xpipe.app.browser.action.BrowserLeafAction; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.comp.Comp; -import io.xpipe.app.comp.base.ModalOverlayComp; +import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.OsType; @@ -87,33 +87,28 @@ public class ChownAction implements BrowserBranchAction { @Override public void execute(BrowserFileSystemTabModel model, List entries) { var user = new SimpleStringProperty(); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "userName", - Comp.of(() -> { - var creationName = new TextField(); - creationName.textProperty().bindBidirectional(user); - return creationName; - }) - .prefWidth(350), - null, - "finish", - () -> { - if (user.getValue() == null) { - return; - } + var modal = ModalOverlay.of("userName", Comp.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(user); + return creationName; + }) + .prefWidth(350)); + modal.withDefaultButtons(() -> { + if (user.getValue() == null) { + return; + } - model.runCommandAsync( - CommandBuilder.of() - .add("chown", user.getValue()) - .addFiles(entries.stream() - .map(browserEntry -> browserEntry - .getRawFileEntry() - .getPath()) - .toList()), - false); - }, - true)); + model.runCommandAsync( + CommandBuilder.of() + .add("chown", user.getValue()) + .addFiles(entries.stream() + .map(browserEntry -> browserEntry + .getRawFileEntry() + .getPath()) + .toList()), + false); + }); + model.getOverlay().setValue(modal); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/NewItemAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/NewItemAction.java index 7dffeb5c0..874d316cd 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/NewItemAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/NewItemAction.java @@ -7,7 +7,7 @@ import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.browser.icon.BrowserIcons; import io.xpipe.app.comp.Comp; -import io.xpipe.app.comp.base.ModalOverlayComp; +import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.app.util.OptionsBuilder; import io.xpipe.core.process.OsType; @@ -59,21 +59,16 @@ public class NewItemAction implements BrowserAction, BrowserBranchAction { @Override public void execute(BrowserFileSystemTabModel model, List entries) { var name = new SimpleStringProperty(); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "newFile", - Comp.of(() -> { - var creationName = new TextField(); - creationName.textProperty().bindBidirectional(name); - return creationName; - }) - .prefWidth(350), - null, - "finish", - () -> { - model.createFileAsync(name.getValue()); - }, - true)); + var modal = ModalOverlay.of("newFile", Comp.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(name); + return creationName; + }) + .prefWidth(350)); + modal.withDefaultButtons(() -> { + model.createFileAsync(name.getValue()); + }); + model.getOverlay().setValue(modal); } @Override @@ -91,21 +86,16 @@ public class NewItemAction implements BrowserAction, BrowserBranchAction { @Override public void execute(BrowserFileSystemTabModel model, List entries) { var name = new SimpleStringProperty(); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "newDirectory", - Comp.of(() -> { - var creationName = new TextField(); - creationName.textProperty().bindBidirectional(name); - return creationName; - }) - .prefWidth(350), - null, - "finish", - () -> { - model.createDirectoryAsync(name.getValue()); - }, - true)); + var modal = ModalOverlay.of("newDirectory", Comp.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(name); + return creationName; + }) + .prefWidth(350)); + modal.withDefaultButtons(() -> { + model.createDirectoryAsync(name.getValue()); + }); + model.getOverlay().setValue(modal); } @Override @@ -124,22 +114,18 @@ public class NewItemAction implements BrowserAction, BrowserBranchAction { public void execute(BrowserFileSystemTabModel model, List entries) { var linkName = new SimpleStringProperty(); var target = new SimpleStringProperty(); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "base.newLink", - new OptionsBuilder() - .name("linkName") - .addString(linkName) - .name("targetPath") - .addString(target) - .buildComp() - .prefWidth(350), - null, - "finish", - () -> { - model.createLinkAsync(linkName.getValue(), target.getValue()); - }, - true)); + var modal = ModalOverlay.of("base.newLink", + new OptionsBuilder() + .name("linkName") + .addString(linkName) + .name("targetPath") + .addString(target) + .buildComp() + .prefWidth(350)); + modal.withDefaultButtons(() -> { + model.createLinkAsync(linkName.getValue(), target.getValue()); + }); + model.getOverlay().setValue(modal); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/browser/compress/BaseCompressAction.java b/ext/base/src/main/java/io/xpipe/ext/base/browser/compress/BaseCompressAction.java index dbfa83dd8..f82f3a4df 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/browser/compress/BaseCompressAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/browser/compress/BaseCompressAction.java @@ -6,7 +6,7 @@ import io.xpipe.app.browser.action.BrowserLeafAction; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.comp.Comp; -import io.xpipe.app.comp.base.ModalOverlayComp; +import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.app.util.CommandSupport; import io.xpipe.core.process.CommandBuilder; @@ -113,30 +113,25 @@ public abstract class BaseCompressAction implements BrowserAction, BrowserBranch @Override public void execute(BrowserFileSystemTabModel model, List entries) { var name = new SimpleStringProperty(directory ? entries.getFirst().getFileName() : null); - model.getOverlay() - .setValue(new ModalOverlayComp.OverlayContent( - "base.archiveName", - Comp.of(() -> { - var creationName = new TextField(); - creationName.textProperty().bindBidirectional(name); - return creationName; - }) - .prefWidth(350), - null, - "finish", - () -> { - var fixedName = name.getValue(); - if (fixedName == null) { - return; - } + var modal = ModalOverlay.of("base.archiveName", Comp.of(() -> { + var creationName = new TextField(); + creationName.textProperty().bindBidirectional(name); + return creationName; + }) + .prefWidth(350)); + modal.withDefaultButtons(() -> { + var fixedName = name.getValue(); + if (fixedName == null) { + return; + } - if (!fixedName.endsWith(getExtension())) { - fixedName = fixedName + "." + getExtension(); - } + if (!fixedName.endsWith(getExtension())) { + fixedName = fixedName + "." + getExtension(); + } - create(fixedName, model, entries); - }, - true)); + create(fixedName, model, entries); + }); + model.getOverlay().setValue(modal); } @Override