RunnableWrapper.cs 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. namespace Terminal.Gui.Views;
  2. /// <summary>
  3. /// Wraps any <see cref="View"/> to make it runnable with a typed result, similar to how
  4. /// <see cref="FlagSelector{TFlagsEnum}"/> wraps <see cref="FlagSelector"/>.
  5. /// </summary>
  6. /// <typeparam name="TView">The type of view being wrapped.</typeparam>
  7. /// <typeparam name="TResult">The type of result data returned when the session completes.</typeparam>
  8. /// <remarks>
  9. /// <para>
  10. /// This class enables any View to be run as a blocking session with
  11. /// <see cref="IApplication.Run(IRunnable, Func{Exception, bool})"/>
  12. /// without requiring the View to implement <see cref="IRunnable{TResult}"/> or derive from
  13. /// <see cref="Runnable{TResult}"/>.
  14. /// </para>
  15. /// <para>
  16. /// Use <see cref="ViewRunnableExtensions.AsRunnable{TView, TResult}"/> for a fluent API approach,
  17. /// or <see cref="ApplicationRunnableExtensions.RunView{TView, TResult}"/> to run directly.
  18. /// </para>
  19. /// <example>
  20. /// <code>
  21. /// // Wrap a TextField to make it runnable with string result
  22. /// var textField = new TextField { Width = 40 };
  23. /// var runnable = new RunnableWrapper&lt;TextField, string&gt; { WrappedView = textField };
  24. ///
  25. /// // Extract result when stopping
  26. /// runnable.IsRunningChanging += (s, e) =&gt;
  27. /// {
  28. /// if (!e.NewValue) // Stopping
  29. /// {
  30. /// runnable.Result = runnable.WrappedView.Text;
  31. /// }
  32. /// };
  33. ///
  34. /// app.Run(runnable);
  35. /// Console.WriteLine($"User entered: {runnable.Result}");
  36. /// runnable.Dispose();
  37. /// </code>
  38. /// </example>
  39. /// </remarks>
  40. public class RunnableWrapper<TView, TResult> : Runnable<TResult> where TView : View
  41. {
  42. /// <summary>
  43. /// Initializes a new instance of <see cref="RunnableWrapper{TView, TResult}"/>.
  44. /// </summary>
  45. public RunnableWrapper ()
  46. {
  47. // Make the wrapper automatically size to fit the wrapped view
  48. Width = Dim.Fill ();
  49. Height = Dim.Fill ();
  50. }
  51. private TView? _wrappedView;
  52. /// <summary>
  53. /// Gets or sets the wrapped view that is being made runnable.
  54. /// </summary>
  55. /// <remarks>
  56. /// <para>
  57. /// This property must be set before the wrapper is initialized.
  58. /// Access this property to interact with the original view, extract its state,
  59. /// or configure result extraction logic.
  60. /// </para>
  61. /// </remarks>
  62. /// <exception cref="InvalidOperationException">Thrown if the property is set after initialization.</exception>
  63. public required TView WrappedView
  64. {
  65. get => _wrappedView ?? throw new InvalidOperationException ("WrappedView must be set before use.");
  66. init
  67. {
  68. if (IsInitialized)
  69. {
  70. throw new InvalidOperationException ("WrappedView cannot be changed after initialization.");
  71. }
  72. _wrappedView = value;
  73. }
  74. }
  75. /// <inheritdoc/>
  76. public override void EndInit ()
  77. {
  78. base.EndInit ();
  79. // Add the wrapped view as a subview after initialization
  80. if (_wrappedView is { })
  81. {
  82. Add (_wrappedView);
  83. }
  84. }
  85. }