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. ///