namespace Terminal.Gui.ViewBase; /// /// Extension methods for making any runnable with typed results. /// /// /// These extensions provide a fluent API for wrapping views in , /// enabling any View to be run as a blocking session without implementing . /// public static class ViewRunnableExtensions { /// /// Converts any View into a runnable with typed result extraction. /// /// The type of view to make runnable. /// The type of result data to extract. /// The view to wrap. Cannot be null. /// /// Function that extracts the result from the view when stopping. /// Called automatically when the runnable session ends. /// /// A that wraps the view. /// Thrown if or is null. /// /// /// This method wraps the view in a and automatically /// subscribes to to extract the result when the session stops. /// /// /// The result is extracted before the view is disposed, ensuring all data is still accessible. /// /// /// /// /// // Make a TextField runnable with string result /// var runnable = new TextField { Width = 40 } /// .AsRunnable(tf => tf.Text); /// /// app.Run(runnable); /// Console.WriteLine($"User entered: {runnable.Result}"); /// runnable.Dispose(); /// /// // Make a ColorPicker runnable with Color? result /// var colorRunnable = new ColorPicker() /// .AsRunnable(cp => cp.SelectedColor); /// /// app.Run(colorRunnable); /// Console.WriteLine($"Selected: {colorRunnable.Result}"); /// colorRunnable.Dispose(); /// /// // Make a FlagSelector runnable with enum result /// var flagsRunnable = new FlagSelector<SelectorStyles>() /// .AsRunnable(fs => fs.Value); /// /// app.Run(flagsRunnable); /// Console.WriteLine($"Selected styles: {flagsRunnable.Result}"); /// flagsRunnable.Dispose(); /// /// public static RunnableWrapper AsRunnable ( this TView view, Func resultExtractor) where TView : View { if (view is null) { throw new ArgumentNullException (nameof (view)); } if (resultExtractor is null) { throw new ArgumentNullException (nameof (resultExtractor)); } var wrapper = new RunnableWrapper { WrappedView = view }; // Subscribe to IsRunningChanging to extract result when stopping wrapper.IsRunningChanging += (s, e) => { if (!e.NewValue) // Stopping { wrapper.Result = resultExtractor (view); } }; return wrapper; } /// /// Converts any View into a runnable without result extraction. /// /// The type of view to make runnable. /// The view to wrap. Cannot be null. /// A that wraps the view. /// Thrown if is null. /// /// /// Use this overload when you don't need to extract a typed result, but still want to /// run the view as a blocking session. The wrapped view can still be accessed via /// after running. /// /// /// /// /// // Make a view runnable without result extraction /// var colorPicker = new ColorPicker(); /// var runnable = colorPicker.AsRunnable(); /// /// app.Run(runnable); /// /// // Access the wrapped view directly to get the result /// Console.WriteLine($"Selected: {runnable.WrappedView.SelectedColor}"); /// runnable.Dispose(); /// /// public static RunnableWrapper AsRunnable (this TView view) where TView : View { if (view is null) { throw new ArgumentNullException (nameof (view)); } return new RunnableWrapper { WrappedView = view }; } }