Modal rework

This commit is contained in:
crschnick
2024-12-20 16:43:58 +00:00
parent ad13f52c10
commit 3de8cf7b5d
16 changed files with 362 additions and 271 deletions
@@ -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