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 index 5e13adbc9..380604ddf 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ModalOverlay.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ModalOverlay.java @@ -71,6 +71,12 @@ public class ModalOverlay { }); } + public void hideable(AppLayoutModel.QueueEntry entry) { + setHideAction(() -> { + AppLayoutModel.get().getQueueEntries().add(entry); + }); + } + public void addButtonBarComp(Comp comp) { buttons.add(comp); } diff --git a/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java b/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java index 10a045f70..81bb8f03b 100644 --- a/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java +++ b/app/src/main/java/io/xpipe/app/core/AppLayoutModel.java @@ -9,6 +9,7 @@ import io.xpipe.app.prefs.AppPrefsComp; import io.xpipe.app.update.AppDistributionType; import io.xpipe.app.util.*; +import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; @@ -20,10 +21,8 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; -import lombok.Builder; -import lombok.Data; -import lombok.Getter; -import lombok.Value; +import lombok.*; +import lombok.experimental.NonFinal; import lombok.extern.jackson.Jacksonized; import java.time.Duration; @@ -189,10 +188,21 @@ public class AppLayoutModel { KeyCombination combination) {} @Value + @NonFinal public static class QueueEntry { ObservableValue name; LabelGraphic icon; Runnable action; + + public void show() { + ThreadHelper.runAsync(() -> { + try { + getAction().run(); + } finally { + AppLayoutModel.get().getQueueEntries().remove(this); + } + }); + } } } diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java index 7ffbf35a8..d53ceff3c 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java @@ -136,6 +136,12 @@ public class StoreCreationDialog { StoreCreationConsumer con, boolean staticDisplay, DataStoreEntry existingEntry) { + var ex = StoreCreationQueueEntry.findExisting(existingEntry); + if (ex.isPresent()) { + ex.get().show(); + return; + } + var prop = new SimpleObjectProperty<>(provider); var store = new SimpleObjectProperty<>(s); var model = new StoreCreationModel(prop, store, filter, initialName, existingEntry, staticDisplay, con); @@ -157,12 +163,7 @@ public class StoreCreationDialog { comp.prefWidth(650); var nameKey = model.storeTypeNameKey() + "Add"; var modal = ModalOverlay.of(nameKey, comp); - var provider = model.getProvider().getValue(); - var graphic = provider != null - && provider.getDisplayIconFileName(model.getStore().get()) != null - ? new LabelGraphic.ImageGraphic( - provider.getDisplayIconFileName(model.getStore().get()), 20) - : new LabelGraphic.IconGraphic("mdi2b-beaker-plus-outline"); + var queueEntry = StoreCreationQueueEntry.of(model, modal); comp.apply(struc -> { struc.get().addEventHandler(KeyEvent.KEY_PRESSED, e -> { if (e.getCode() == KeyCode.ESCAPE) { @@ -174,9 +175,7 @@ public class StoreCreationDialog { } }); }); - modal.hideable(AppI18n.observable(model.storeTypeNameKey() + "Add"), graphic, () -> { - modal.show(); - }); + modal.hideable(queueEntry); AppLayoutModel.get().getSelected().addListener((observable, oldValue, newValue) -> { if (model.getFinished().get() || !modal.isShowing()) { return; @@ -185,11 +184,7 @@ public class StoreCreationDialog { modal.hide(); AppLayoutModel.get() .getQueueEntries() - .add(new AppLayoutModel.QueueEntry( - AppI18n.observable(model.storeTypeNameKey() + "Add"), graphic, () -> { - AppLayoutModel.get().selectConnections(); - modal.show(); - })); + .add(queueEntry); }); modal.setRequireCloseButtonForClose(true); modal.addButton(new ModalButton( diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationQueueEntry.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationQueueEntry.java new file mode 100644 index 000000000..9052b536c --- /dev/null +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationQueueEntry.java @@ -0,0 +1,47 @@ +package io.xpipe.app.hub.comp; + +import io.xpipe.app.comp.base.ModalOverlay; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppLayoutModel; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.app.util.LabelGraphic; +import javafx.beans.value.ObservableValue; +import lombok.EqualsAndHashCode; +import lombok.Value; + +import java.util.Optional; + +@Value +@EqualsAndHashCode(callSuper = true) +public class StoreCreationQueueEntry extends AppLayoutModel.QueueEntry { + + public static StoreCreationQueueEntry of(StoreCreationModel model, ModalOverlay modal) { + var provider = model.getProvider().getValue(); + var graphic = provider != null + && provider.getDisplayIconFileName(model.getStore().get()) != null + ? new LabelGraphic.ImageGraphic( + provider.getDisplayIconFileName(model.getStore().get()), 20) + : new LabelGraphic.IconGraphic("mdi2b-beaker-plus-outline"); + return new StoreCreationQueueEntry(AppI18n.observable(model.storeTypeNameKey() + "Add"), graphic, () -> { + AppLayoutModel.get().selectConnections(); + modal.show(); + }, model.getExistingEntry()); + } + + public static Optional findExisting(DataStoreEntry entry) { + if (entry == null) { + return Optional.empty(); + } + + return AppLayoutModel.get().getQueueEntries().stream() + .filter(queueEntry -> queueEntry instanceof StoreCreationQueueEntry q && entry.equals(q.getEntry())) + .findFirst(); + } + + DataStoreEntry entry; + + public StoreCreationQueueEntry(ObservableValue name, LabelGraphic icon, Runnable action, DataStoreEntry entry) { + super(name, icon, action); + this.entry = entry; + } +}