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 new file mode 100644 index 00000000..f0c7fbf2 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/comp/base/ErrorOverlayComp.java @@ -0,0 +1,44 @@ +package io.xpipe.app.comp.base; + +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.SimpleComp; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.control.TextArea; +import javafx.scene.layout.Region; +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class ErrorOverlayComp extends SimpleComp { + + Comp background; + Property text; + + public ErrorOverlayComp(Comp background, Property text) { + this.background = background; + this.text = text; + } + + @Override + protected Region createSimple() { + var content = new SimpleObjectProperty(); + this.text.addListener((observable, oldValue, newValue) -> { + var comp = Comp.of(() -> { + var l = new TextArea(); + l.textProperty().bind(text); + l.setWrapText(true); + l.getStyleClass().add("error-overlay-comp"); + l.setEditable(false); + return l; + }); + content.set(new ModalOverlayComp.OverlayContent("error", comp, null, () -> {})); + }); + content.addListener((observable, oldValue, newValue) -> { + if (newValue == null) { + this.text.setValue(null); + } + }); + return new ModalOverlayComp(background, content).createRegion(); + } +} diff --git a/app/src/main/java/io/xpipe/app/comp/base/MessageComp.java b/app/src/main/java/io/xpipe/app/comp/base/MessageComp.java deleted file mode 100644 index 4e7e5b05..00000000 --- a/app/src/main/java/io/xpipe/app/comp/base/MessageComp.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.xpipe.app.comp.base; - -import io.xpipe.app.fxcomps.SimpleComp; -import io.xpipe.app.fxcomps.util.PlatformThread; -import io.xpipe.app.fxcomps.util.SimpleChangeListener; -import io.xpipe.app.util.ThreadHelper; -import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.value.ObservableValue; -import javafx.scene.control.TextArea; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import lombok.AccessLevel; -import lombok.experimental.FieldDefaults; - -@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) -public class MessageComp extends SimpleComp { - - Property shown = new SimpleBooleanProperty(); - - ObservableValue text; - int msShown; - - public MessageComp(ObservableValue text, int msShown) { - this.text = PlatformThread.sync(text); - this.msShown = msShown; - } - - public void show() { - shown.setValue(true); - - if (msShown != -1) { - ThreadHelper.runAsync(() -> { - try { - Thread.sleep(msShown); - } catch (InterruptedException ignored) { - } - - shown.setValue(false); - }); - } - } - - @Override - protected Region createSimple() { - var l = new TextArea(); - l.textProperty().bind(text); - l.setWrapText(true); - l.getStyleClass().add("message"); - l.setEditable(false); - - var sp = new StackPane(l); - sp.getStyleClass().add("message-comp"); - - SimpleChangeListener.apply(PlatformThread.sync(shown), n -> { - if (n) { - l.setMinHeight(Region.USE_PREF_SIZE); - l.setPrefHeight(Region.USE_COMPUTED_SIZE); - l.setMaxHeight(Region.USE_PREF_SIZE); - - sp.setMinHeight(Region.USE_PREF_SIZE); - sp.setPrefHeight(Region.USE_COMPUTED_SIZE); - sp.setMaxHeight(Region.USE_PREF_SIZE); - } else { - l.setMinHeight(0); - l.setPrefHeight(0); - l.setMaxHeight(0); - - sp.setMinHeight(0); - sp.setPrefHeight(0); - sp.setMaxHeight(0); - } - }); - - return sp; - } -} 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 6ccb3838..8d6f738c 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 @@ -2,12 +2,12 @@ package io.xpipe.app.comp.base; import atlantafx.base.controls.ModalPane; import atlantafx.base.theme.Styles; +import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.Shortcuts; import javafx.beans.property.Property; -import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -33,8 +33,9 @@ public class ModalOverlayComp extends SimpleComp { @Value public static class OverlayContent { - ObservableValue title; + String titleKey; Comp content; + String finishKey; Runnable onFinish; } @@ -45,6 +46,7 @@ public class ModalOverlayComp extends SimpleComp { protected Region createSimple() { var bgRegion = background.createRegion(); var modal = new ModalPane(); + modal.getStyleClass().add("modal-overlay-comp"); var pane = new StackPane(bgRegion, modal); pane.setPickOnBounds(false); PlatformThread.sync(overlayContent).addListener((observable, oldValue, newValue) -> { @@ -54,23 +56,26 @@ public class ModalOverlayComp extends SimpleComp { if (newValue != null) { var r = newValue.content.createRegion(); - - var finishButton = new Button("Finish"); - Styles.toggleStyleClass(finishButton, Styles.FLAT); - finishButton.setOnAction(event -> { - newValue.onFinish.run(); - overlayContent.setValue(null); - }); - - var buttonBar = new ButtonBar(); - buttonBar.getButtons().addAll(finishButton); - var box = new VBox(r, buttonBar); + var box = new VBox(r); box.setSpacing(15); box.setPadding(new Insets(15)); - var tp = new TitledPane(newValue.title.getValue(), box); + + if (newValue.finishKey != null) { + var finishButton = new Button(AppI18n.get(newValue.finishKey)); + Styles.toggleStyleClass(finishButton, Styles.FLAT); + finishButton.setOnAction(event -> { + newValue.onFinish.run(); + overlayContent.setValue(null); + }); + + var buttonBar = new ButtonBar(); + buttonBar.getButtons().addAll(finishButton); + box.getChildren().add(buttonBar); + } + + var tp = new TitledPane(AppI18n.get(newValue.titleKey), box); tp.setMaxWidth(400); tp.setCollapsible(false); - tp.getStyleClass().add("elevated-3"); var closeButton = new Button(null, new FontIcon("mdi2w-window-close")); closeButton.setOnAction(event -> { @@ -84,6 +89,7 @@ public class ModalOverlayComp extends SimpleComp { AnchorPane.setRightAnchor(closeButton, 10.0); var stack = new StackPane(tp, close); + stack.setPadding(new Insets(10)); stack.setOnMouseClicked(event -> { if (overlayContent.getValue() != null) { overlayContent.setValue(null); diff --git a/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java b/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java index c59fe1e3..73df1e01 100644 --- a/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java +++ b/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java @@ -1,7 +1,6 @@ package io.xpipe.app.comp.source.store; -import io.xpipe.app.comp.base.InstallExtensionComp; -import io.xpipe.app.comp.base.MessageComp; +import io.xpipe.app.comp.base.ErrorOverlayComp; import io.xpipe.app.comp.base.MultiStepComp; import io.xpipe.app.core.AppExtensionManager; import io.xpipe.app.core.AppFont; @@ -9,7 +8,6 @@ import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppWindowHelper; import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.ext.DataStoreProvider; -import io.xpipe.app.ext.DownloadModuleInstall; import io.xpipe.app.fxcomps.Comp; import io.xpipe.app.fxcomps.CompStructure; import io.xpipe.app.fxcomps.augment.GrowAugment; @@ -25,6 +23,7 @@ import io.xpipe.app.util.*; import io.xpipe.core.store.DataStore; import javafx.application.Platform; import javafx.beans.property.*; +import javafx.geometry.Insets; import javafx.scene.control.Alert; import javafx.scene.control.Separator; import javafx.scene.layout.BorderPane; @@ -48,7 +47,6 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { BooleanProperty busy = new SimpleBooleanProperty(); Property validator = new SimpleObjectProperty<>(new SimpleValidator()); Property messageProp = new SimpleStringProperty(); - MessageComp message = new MessageComp(messageProp, 10000); BooleanProperty finished = new SimpleBooleanProperty(); Property entry = new SimpleObjectProperty<>(); BooleanProperty changedSinceError = new SimpleBooleanProperty(); @@ -188,7 +186,14 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { @Override public CompStructure createBase() { + var back = Comp.of(this::createLayout); + var message = new ErrorOverlayComp(back, messageProp); + return message.createStructure(); + } + + private Region createLayout() { var layout = new BorderPane(); + layout.setPadding(new Insets(20)); var providerChoice = new DsStoreProviderChoiceComp(filter, provider); if (provider.getValue() != null) { providerChoice.apply(struc -> struc.get().setDisable(true)); @@ -197,37 +202,33 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { SimpleChangeListener.apply(provider, n -> { if (n != null) { - var install = n.getRequiredAdditionalInstallation(); - if (install != null && AppExtensionManager.getInstance().isInstalled(install)) { - layout.setCenter(new InstallExtensionComp((DownloadModuleInstall) install).createRegion()); - validator.setValue(new SimpleValidator()); - return; - } + // var install = n.getRequiredAdditionalInstallation(); + // if (install != null && AppExtensionManager.getInstance().isInstalled(install)) { + // layout.setCenter(new InstallExtensionComp((DownloadModuleInstall) + // install).createRegion()); + // validator.setValue(new SimpleValidator()); + // return; + // } var d = n.guiDialog(input); var propVal = new SimpleValidator(); var propR = createStoreProperties(d == null || d.getComp() == null ? null : d.getComp(), propVal); - var box = new VBox(propR); - box.setSpacing(7); + layout.setCenter(propR); - layout.setCenter(box); - - validator.setValue(new ChainedValidator(List.of(d != null && d.getValidator() != null ? d.getValidator() : new SimpleValidator(), propVal))); + validator.setValue(new ChainedValidator(List.of( + d != null && d.getValidator() != null ? d.getValidator() : new SimpleValidator(), propVal))); } else { layout.setCenter(null); validator.setValue(new SimpleValidator()); } }); - layout.setBottom(message.createRegion()); - var sep = new Separator(); sep.getStyleClass().add("spacer"); var top = new VBox(providerChoice.createRegion(), sep); top.getStyleClass().add("top"); layout.setTop(top); - // layout.getStyleClass().add("data-input-creation-step"); - return Comp.of(() -> layout).createStructure(); + return layout; } @Override @@ -275,7 +276,6 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { .getText(); TrackEvent.info(msg); messageProp.setValue(msg); - message.show(); changedSinceError.setValue(false); return false; } @@ -287,7 +287,6 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { PlatformThread.runLaterIfNeeded(parent::next); } catch (Exception ex) { messageProp.setValue(ExceptionConverter.convertMessage(ex)); - message.show(); changedSinceError.setValue(false); ErrorEvent.fromThrowable(ex).omit().reportable(false).handle(); } diff --git a/app/src/main/java/io/xpipe/app/prefs/TranslatableComboBoxControl.java b/app/src/main/java/io/xpipe/app/prefs/TranslatableComboBoxControl.java index e4641dd5..266c87c8 100644 --- a/app/src/main/java/io/xpipe/app/prefs/TranslatableComboBoxControl.java +++ b/app/src/main/java/io/xpipe/app/prefs/TranslatableComboBoxControl.java @@ -42,7 +42,7 @@ public class TranslatableComboBoxControl readOnlyLabel.getStyleClass().add("read-only-label"); comboBox.setMaxWidth(Double.MAX_VALUE); - comboBox.setVisibleRowCount(4); + comboBox.setVisibleRowCount(10); node.setAlignment(Pos.CENTER_LEFT); node.getChildren().addAll(comboBox, readOnlyLabel); diff --git a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties index 207fa984..450224ae 100644 --- a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties +++ b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties @@ -9,6 +9,9 @@ setLock=Set lock changeLock=Change lock lockCreationAlertTitle=Create Lock lockCreationAlertHeader=Set your new lock password +finish=Finish +error=Error +ok=Ok password=Password unlockAlertTitle=Unlock workspace unlockAlertHeader=Enter your lock password to continue diff --git a/app/src/main/resources/io/xpipe/app/resources/style/data-store-step.css b/app/src/main/resources/io/xpipe/app/resources/style/data-store-step.css index 114c9538..cafd6e18 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/data-store-step.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/data-store-step.css @@ -1,5 +1,5 @@ .step { --fx-padding: 1em 1.5em 0.5em 1.5em; +-fx-padding: 0; } .spacer { diff --git a/app/src/main/resources/io/xpipe/app/resources/style/error-overlay-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/error-overlay-comp.css new file mode 100644 index 00000000..6bc56f69 --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/style/error-overlay-comp.css @@ -0,0 +1,5 @@ +.error-overlay-comp { +-fx-padding: 1.0em; +-fx-border-width: 1px; +-fx-border-radius: 2px; +} diff --git a/app/src/main/resources/io/xpipe/app/resources/style/modal-overlay-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/modal-overlay-comp.css new file mode 100644 index 00000000..d7316b39 --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/style/modal-overlay-comp.css @@ -0,0 +1,20 @@ +.modal-overlay-comp .titled-pane { +-fx-font-size: 1.6em; +-fx-padding: 0; +-fx-border-radius: 0; +} + +.modal-overlay-comp { +-fx-border-radius: 0; +-fx-background-radius: 0; +} + +.modal-overlay-comp .titled-pane > * { +-fx-font-size: 1.0em; +-fx-border-radius: 0; +} + +.modal-overlay-comp .titled-pane > * { +-fx-background-radius: 0; +} + diff --git a/app/src/main/resources/io/xpipe/app/resources/style/multi-step-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/multi-step-comp.css index ffc1ca8d..ba6a2a62 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/multi-step-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/multi-step-comp.css @@ -80,3 +80,7 @@ -fx-pref-height: 0; } +.multi-step-comp > .jfx-tab-pane .tab-content-area { +-fx-padding: 0; +} + diff --git a/app/src/main/resources/io/xpipe/app/resources/style/style.css b/app/src/main/resources/io/xpipe/app/resources/style/style.css index e4895d6f..22cd510f 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/style.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/style.css @@ -13,21 +13,6 @@ -fx-padding: 1.2em; } -.message-comp { --fx-background-color: #FF9999AA; --fx-border-width: 1px; --fx-border-color:-color-accent-fg; --fx-border-radius: 2px; -} - -.message { --fx-padding: 0.0em; --fx-background-color: transparent; --fx-border-width: 1px; --fx-border-color:-color-accent-fg; --fx-border-radius: 2px; -} - .radio-button { -fx-background-color:transparent; } 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 9fd19752..2d69ac1e 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,6 @@ import io.xpipe.app.browser.action.BrowserAction; import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.icon.FileBrowserIcons; import io.xpipe.app.comp.base.ModalOverlayComp; -import io.xpipe.app.core.AppI18n; import io.xpipe.app.fxcomps.Comp; import javafx.beans.property.SimpleStringProperty; import javafx.scene.Node; @@ -55,11 +54,11 @@ public class NewItemAction implements BrowserAction, BranchAction { @Override public void execute(OpenFileSystemModel model, List entries) throws Exception { var name = new SimpleStringProperty(); - model.getOverlay().setValue(new ModalOverlayComp.OverlayContent(AppI18n.observable("newFile"), Comp.of(() -> { + model.getOverlay().setValue(new ModalOverlayComp.OverlayContent("newFile", Comp.of(() -> { var creationName = new TextField(); creationName.textProperty().bindBidirectional(name); return creationName; - }), () -> { + }), "finish", () -> { model.createFileAsync(name.getValue()); })); } @@ -78,11 +77,11 @@ public class NewItemAction implements BrowserAction, BranchAction { @Override public void execute(OpenFileSystemModel model, List entries) throws Exception { var name = new SimpleStringProperty(); - model.getOverlay().setValue(new ModalOverlayComp.OverlayContent(AppI18n.observable("newDirectory"), Comp.of(() -> { + model.getOverlay().setValue(new ModalOverlayComp.OverlayContent("newDirectory", Comp.of(() -> { var creationName = new TextField(); creationName.textProperty().bindBidirectional(name); return creationName; - }), () -> { + }), "finish", () -> { model.createDirectoryAsync(name.getValue()); })); }