mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-08-23 01:36:33 +00:00
Modal rework
This commit is contained in:
@@ -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<File
|
||||
private final BrowserFileListModel fileList;
|
||||
private final ReadOnlyObjectWrapper<String> currentPath = new ReadOnlyObjectWrapper<>();
|
||||
private final BrowserFileSystemHistory history = new BrowserFileSystemHistory();
|
||||
private final Property<ModalOverlayComp.OverlayContent> overlay = new SimpleObjectProperty<>();
|
||||
private final Property<ModalOverlay> overlay = new SimpleObjectProperty<>();
|
||||
private final BooleanProperty inOverview = new SimpleBooleanProperty();
|
||||
private final Property<BrowserTransferProgress> progress = new SimpleObjectProperty<>();
|
||||
private final ObservableList<UUID> terminalRequests = FXCollections.observableArrayList();
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ErrorOverlayComp extends SimpleComp {
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var content = new SimpleObjectProperty<ModalOverlayComp.OverlayContent>();
|
||||
var content = new SimpleObjectProperty<ModalOverlay>();
|
||||
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) -> {
|
||||
|
||||
@@ -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<ModalButton> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> overlayContent;
|
||||
private final Property<ModalOverlay> overlayContent;
|
||||
|
||||
public ModalOverlayComp(Comp<?> background, Property<OverlayContent> overlayContent) {
|
||||
public ModalOverlayComp(Comp<?> background, Property<ModalOverlay> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<BrowserEntry> 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
|
||||
|
||||
@@ -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<BrowserEntry> 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
|
||||
|
||||
@@ -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<BrowserEntry> 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
|
||||
|
||||
@@ -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<BrowserEntry> 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<BrowserEntry> 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<BrowserEntry> 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
|
||||
|
||||
@@ -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<BrowserEntry> 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
|
||||
|
||||
Reference in New Issue
Block a user