diff --git a/src/general/base_stage/EditorComponentBase.cs b/src/general/base_stage/EditorComponentBase.cs
index 986896d18b8..92e4d6f0600 100644
--- a/src/general/base_stage/EditorComponentBase.cs
+++ b/src/general/base_stage/EditorComponentBase.cs
@@ -209,6 +209,12 @@ protected virtual void NextOrFinishClicked()
{
GUICommon.Instance.PlayButtonPressSound();
+ if (!ModalManager.Instance.TryCancelModals())
+ {
+ GD.PrintErr("Cannot close open modals before continuing, not triggering next / finish action");
+ return;
+ }
+
if (OnFinish != null)
{
if (OnFinish!.Invoke(null))
diff --git a/src/gui_common/ModalManager.cs b/src/gui_common/ModalManager.cs
index bce920b9056..71a04377d1e 100644
--- a/src/gui_common/ModalManager.cs
+++ b/src/gui_common/ModalManager.cs
@@ -121,7 +121,7 @@ public void MakeModal(TopLevelContainer popup)
}
///
- /// Closes the top-most modal popup if any.
+ /// Closes the top-most modal popup, if any.
///
[RunOnKeyDown("ui_cancel", Priority = Constants.POPUP_CANCEL_PRIORITY)]
public bool HideTopMostPopup()
@@ -134,15 +134,7 @@ public bool HideTopMostPopup()
if (popup.Exclusive && !popup.ExclusiveAllowCloseOnEscape)
return false;
- // This is emitted before closing to allow window using components to differentiate between "cancel" and
- // "any other reason for closing" in case some logic can be simplified by handling just those two situations.
- if (popup is CustomWindow dialog)
- dialog.EmitSignal(CustomWindow.SignalName.Canceled);
-
- popup.Close();
-
- // TODO: make sure removing this is not a problem (looks like this signal no longer exists at all)
- // popup.Notification(Control.NotificationModalClose);
+ ClosePopupDialog(popup);
return true;
}
@@ -163,6 +155,35 @@ public bool HideTopMostPopup()
return null;
}
+ ///
+ /// Tries to cancel all open modals (or close if the modals don't support cancellation)
+ ///
+ /// True if all modals are closed, false if some refused to close
+ public bool TryCancelModals()
+ {
+ // If no modals, we don't need to do anything
+ if (modalStack.Count <= 0)
+ return true;
+
+ while (modalStack.Count > 0)
+ {
+ var modal = modalStack.First();
+
+ // TODO: implement a way for important popups to ignore this close
+ ClosePopupDialog(modal);
+
+ // Fail if modal is still not closed
+ if (modal.IsVisibleInTree())
+ {
+ GD.Print("Modal doesn't want to cancel / close failing close operation of all modals");
+ return false;
+ }
+ }
+
+ // All modals are now closed
+ return true;
+ }
+
protected override void Dispose(bool disposing)
{
if (disposing)
@@ -173,6 +194,19 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
+ private void ClosePopupDialog(TopLevelContainer popup)
+ {
+ // This is emitted before closing to allow window-using components to differentiate between "cancel" and
+ // "any other reason for closing" in case some logic can be simplified by handling just those two situations.
+ if (popup is CustomWindow dialog)
+ dialog.EmitSignal(CustomWindow.SignalName.Canceled);
+
+ popup.Close();
+
+ // TODO: make sure removing this is not a problem (looks like this signal no longer exists at all)
+ // popup.Notification(Control.NotificationModalClose);
+ }
+
///
/// Parent lost signals are added when a modal is created so that the modal can be automatically deleted.
///