diff --git a/Examples/FluentExample/FluentExample.csproj b/Examples/FluentExample/FluentExample.csproj
index 2086af6edb..a95c7a0253 100644
--- a/Examples/FluentExample/FluentExample.csproj
+++ b/Examples/FluentExample/FluentExample.csproj
@@ -4,6 +4,7 @@
net8.0previewenable
+ $(DefineConstants);POST_4148
diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs
index 03797368dc..81dd7ac140 100644
--- a/Terminal.Gui/Views/Dialog.cs
+++ b/Terminal.Gui/Views/Dialog.cs
@@ -6,13 +6,21 @@ namespace Terminal.Gui.Views;
/// scheme.
///
///
-/// To run the modally, create the , and pass it to
-/// . This will execute the dialog until
-/// it terminates via the (`Esc` by default),
-/// or when one of the views or buttons added to the dialog calls
-/// .
+///
+/// To run the modally, create the , and pass it to
+/// . This will execute the dialog until
+/// it terminates via the (`Esc` by default),
+/// or when one of the views or buttons added to the dialog calls
+/// .
+///
+///
+/// Phase 2: now implements with
+/// int? as the result type, returning the index of the clicked button. The
+/// property replaces the need for manual result tracking. A result of indicates
+/// the dialog was canceled (ESC pressed, window closed without clicking a button).
+///
///
-public class Dialog : Window
+public class Dialog : Window, IRunnable
{
private static LineStyle _defaultBorderStyle = LineStyle.Heavy; // Resources/config.json overrides
private static Alignment _defaultButtonAlignment = Alignment.End; // Resources/config.json overrides
@@ -91,7 +99,13 @@ public Button [] Buttons
}
/// Gets a value indicating whether the was canceled.
- /// The default value is .
+ ///
+ /// The default value is .
+ ///
+ /// Deprecated: Use instead. This property is maintained for backward
+ /// compatibility. A indicates the dialog was canceled.
+ ///
+ ///
public bool Canceled
{
get { return _canceled; }
@@ -107,6 +121,21 @@ public bool Canceled
}
}
+ ///
+ /// Gets or sets the result data extracted when the dialog was accepted, or if not accepted.
+ ///
+ ///
+ ///
+ /// Returns the zero-based index of the button that was clicked, or if the
+ /// dialog was canceled (ESC pressed, window closed without clicking a button).
+ ///
+ ///
+ /// This property is automatically set in when the dialog is
+ /// closing. The result is extracted by finding which button has focus when the dialog stops.
+ ///
+ ///
+ public int? Result { get; set; }
+
///
/// Defines the default border styling for . Can be configured via
/// .
@@ -198,4 +227,67 @@ protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attri
return false;
}
+
+ #region IRunnable Implementation
+
+ ///
+ /// Called when the dialog is about to stop running. Extracts the button result before the dialog is removed
+ /// from the runnable stack.
+ ///
+ /// The current value of IsRunning.
+ /// The new value of IsRunning (true = starting, false = stopping).
+ /// to cancel; to proceed.
+ ///
+ /// This method is called by the IRunnable infrastructure when the dialog is stopping. It extracts
+ /// which button was clicked (if any) before views are disposed.
+ ///
+ protected virtual bool OnIsRunningChanging (bool oldIsRunning, bool newIsRunning)
+ {
+ if (!newIsRunning && oldIsRunning) // Stopping
+ {
+ // Extract result BEFORE disposal - find which button has focus or was last clicked
+ Result = null; // Default: canceled (null = no button clicked)
+
+ for (var i = 0; i < _buttons.Count; i++)
+ {
+ if (_buttons [i].HasFocus)
+ {
+ Result = i;
+ _canceled = false;
+ break;
+ }
+ }
+
+ // If no button has focus, check if any button was the last focused view
+ if (Result is null && MostFocused is Button btn && _buttons.Contains (btn))
+ {
+ Result = _buttons.IndexOf (btn);
+ _canceled = false;
+ }
+
+ // Update legacy Canceled property for backward compatibility
+ if (Result is null)
+ {
+ _canceled = true;
+ }
+ }
+ else if (newIsRunning) // Starting
+ {
+ // Clear result when starting
+ Result = null;
+ _canceled = true; // Default to canceled until a button is clicked
+ }
+
+ // Call base implementation (Toplevel.IRunnable.RaiseIsRunningChanging)
+ return ((IRunnable)this).RaiseIsRunningChanging (oldIsRunning, newIsRunning);
+ }
+
+ // Explicitly implement IRunnable to override the behavior from Toplevel's IRunnable
+ bool IRunnable.RaiseIsRunningChanging (bool oldIsRunning, bool newIsRunning)
+ {
+ // Call our virtual method so subclasses can override
+ return OnIsRunningChanging (oldIsRunning, newIsRunning);
+ }
+
+ #endregion
}
diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs
index e9e874285e..a80d0cca11 100644
--- a/Terminal.Gui/Views/MessageBox.cs
+++ b/Terminal.Gui/Views/MessageBox.cs
@@ -106,8 +106,14 @@ public static int DefaultMinimumHeight
/// The index of the selected button, or if the user pressed .
///
///
- /// This global variable is useful for web-based consoles without a SynchronizationContext or TaskScheduler.
- /// Warning: Not thread-safe.
+ ///
+ /// Warning: This is a global variable and should be used with caution. It is not thread safe.
+ ///
+ ///
+ /// Deprecated: This property is maintained for backward compatibility. The MessageBox methods
+ /// now return the button index directly, and provides a cleaner,
+ /// non-global alternative for custom dialog implementations.
+ ///
///
public static int? Clicked { get; private set; }
@@ -573,7 +579,6 @@ params string [] buttons
// Create button array for Dialog
var count = 0;
List