IRunnable.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. namespace Terminal.Gui.App;
  2. /// <summary>
  3. /// Non-generic base interface for runnable views. Provides common members without type parameter.
  4. /// </summary>
  5. /// <remarks>
  6. /// <para>
  7. /// This interface enables storing heterogeneous runnables in collections (e.g.,
  8. /// <see cref="IApplication.RunnableSessionStack"/>)
  9. /// while preserving type safety at usage sites via <see cref="IRunnable{TResult}"/>.
  10. /// </para>
  11. /// <para>
  12. /// Most code should use <see cref="IRunnable{TResult}"/> directly. This base interface is primarily
  13. /// for framework infrastructure (session management, stacking, etc.).
  14. /// </para>
  15. /// <para>
  16. /// A runnable view executes as a self-contained blocking session with its own lifecycle,
  17. /// event loop iteration, and focus management./>
  18. /// blocks until
  19. /// <see cref="IApplication.RequestStop()"/> is called.
  20. /// </para>
  21. /// <para>
  22. /// This interface follows the Terminal.Gui Cancellable Work Pattern (CWP) for all lifecycle events.
  23. /// </para>
  24. /// </remarks>
  25. /// <seealso cref="IRunnable{TResult}"/>
  26. /// <seealso cref="IApplication.Run(IRunnable, Func{Exception, bool})"/>
  27. public interface IRunnable
  28. {
  29. #region Running or not (added to/removed from RunnableSessionStack)
  30. /// <summary>
  31. /// Gets whether this runnable session is currently running (i.e., on the
  32. /// <see cref="IApplication.RunnableSessionStack"/>).
  33. /// </summary>
  34. /// <remarks>
  35. /// <para>
  36. /// Read-only property derived from stack state. Returns <see langword="true"/> if this runnable
  37. /// is currently on the <see cref="IApplication.RunnableSessionStack"/>, <see langword="false"/> otherwise.
  38. /// </para>
  39. /// <para>
  40. /// Runnables are added to the stack during <see cref="IApplication.Begin(IRunnable)"/> and removed in
  41. /// <see cref="IApplication.End(RunnableSessionToken)"/>.
  42. /// </para>
  43. /// </remarks>
  44. bool IsRunning { get; }
  45. /// <summary>
  46. /// Called by the framework to raise the <see cref="IsRunningChanging"/> event.
  47. /// </summary>
  48. /// <param name="oldIsRunning">The current value of <see cref="IsRunning"/>.</param>
  49. /// <param name="newIsRunning">The new value of <see cref="IsRunning"/> (true = starting, false = stopping).</param>
  50. /// <returns><see langword="true"/> if the change was canceled; otherwise <see langword="false"/>.</returns>
  51. /// <remarks>
  52. /// <para>
  53. /// This method implements the Cancellable Work Pattern. It calls the protected virtual method first,
  54. /// then raises the event if not canceled.
  55. /// </para>
  56. /// <para>
  57. /// When <paramref name="newIsRunning"/> is <see langword="false"/> (stopping), this is the ideal place
  58. /// for implementations to extract <c>Result</c> from views before the runnable is removed from the stack.
  59. /// </para>
  60. /// </remarks>
  61. bool RaiseIsRunningChanging (bool oldIsRunning, bool newIsRunning);
  62. /// <summary>
  63. /// Raised when <see cref="IsRunning"/> is changing (e.g., when <see cref="IApplication.Begin(IRunnable)"/> or
  64. /// <see cref="IApplication.End(RunnableSessionToken)"/> is called).
  65. /// Can be canceled by setting `args.Cancel` to <see langword="true"/>.
  66. /// </summary>
  67. /// <remarks>
  68. /// <para>
  69. /// Subscribe to this event to participate in the runnable lifecycle before state changes occur.
  70. /// When <see cref="CancelEventArgs{T}.NewValue"/> is <see langword="false"/> (stopping),
  71. /// this is the ideal place to extract <c>Result</c> before views are disposed and to optionally
  72. /// cancel the stop operation (e.g., prompt to save changes).
  73. /// </para>
  74. /// <para>
  75. /// This event follows the Terminal.Gui Cancellable Work Pattern (CWP).
  76. /// </para>
  77. /// </remarks>
  78. event EventHandler<CancelEventArgs<bool>>? IsRunningChanging;
  79. /// <summary>
  80. /// Called by the framework to raise the <see cref="IsRunningChanged"/> event.
  81. /// </summary>
  82. /// <param name="newIsRunning">The new value of <see cref="IsRunning"/> (true = started, false = stopped).</param>
  83. /// <remarks>
  84. /// This method is called after the state change has occurred and cannot be canceled.
  85. /// </remarks>
  86. void RaiseIsRunningChangedEvent (bool newIsRunning);
  87. /// <summary>
  88. /// Raised after <see cref="IsRunning"/> has changed (after the runnable has been added to or removed from the
  89. /// <see cref="IApplication.RunnableSessionStack"/>).
  90. /// </summary>
  91. /// <remarks>
  92. /// <para>
  93. /// Subscribe to this event to perform post-state-change logic. When <see cref="EventArgs{T}.Value"/> is
  94. /// <see langword="true"/>,
  95. /// the runnable has started and is on the stack. When <see langword="false"/>, the runnable has stopped and been
  96. /// removed from the stack.
  97. /// </para>
  98. /// <para>
  99. /// This event follows the Terminal.Gui Cancellable Work Pattern (CWP).
  100. /// </para>
  101. /// </remarks>
  102. event EventHandler<EventArgs<bool>>? IsRunningChanged;
  103. #endregion Running or not (added to/removed from RunnableSessionStack)
  104. #region Modal or not (top of RunnableSessionStack or not)
  105. /// <summary>
  106. /// Gets whether this runnable session is at the top of the <see cref="IApplication.RunnableSessionStack"/> and thus
  107. /// exclusively receiving mouse and keyboard input.
  108. /// </summary>
  109. /// <remarks>
  110. /// <para>
  111. /// Read-only property derived from stack state. Returns <see langword="true"/> if this runnable
  112. /// is at the top of the stack (i.e., <c>this == app.TopRunnable</c>), <see langword="false"/> otherwise.
  113. /// </para>
  114. /// <para>
  115. /// The runnable at the top of the stack gets all mouse/keyboard input and thus is running "modally".
  116. /// </para>
  117. /// </remarks>
  118. bool IsModal { get; }
  119. /// <summary>
  120. /// Called by the framework to raise the <see cref="IsModalChanging"/> event.
  121. /// </summary>
  122. /// <param name="oldIsModal">The current value of <see cref="IsModal"/>.</param>
  123. /// <param name="newIsModal">The new value of <see cref="IsModal"/> (true = becoming modal/top, false = no longer modal).</param>
  124. /// <returns><see langword="true"/> if the change was canceled; otherwise <see langword="false"/>.</returns>
  125. /// <remarks>
  126. /// This method implements the Cancellable Work Pattern. It calls the protected virtual method first,
  127. /// then raises the event if not canceled.
  128. /// </remarks>
  129. bool RaiseIsModalChanging (bool oldIsModal, bool newIsModal);
  130. /// <summary>
  131. /// Raised when this runnable is about to become modal (top of stack) or cease being modal.
  132. /// Can be canceled by setting `args.Cancel` to <see langword="true"/>.
  133. /// </summary>
  134. /// <remarks>
  135. /// <para>
  136. /// Subscribe to this event to participate in modal state transitions before they occur.
  137. /// When <see cref="CancelEventArgs{T}.NewValue"/> is <see langword="true"/>, the runnable is becoming modal (top
  138. /// of stack).
  139. /// When <see langword="false"/>, another runnable is becoming modal and this one will no longer receive input.
  140. /// </para>
  141. /// <para>
  142. /// This event follows the Terminal.Gui Cancellable Work Pattern (CWP).
  143. /// </para>
  144. /// </remarks>
  145. event EventHandler<CancelEventArgs<bool>>? IsModalChanging;
  146. /// <summary>
  147. /// Called by the framework to raise the <see cref="IsModalChanged"/> event.
  148. /// </summary>
  149. /// <param name="newIsModal">The new value of <see cref="IsModal"/> (true = became modal/top, false = no longer modal).</param>
  150. /// <remarks>
  151. /// This method is called after the modal state change has occurred and cannot be canceled.
  152. /// </remarks>
  153. void RaiseIsModalChangedEvent (bool newIsModal);
  154. /// <summary>
  155. /// Raised after this runnable has become modal (top of stack) or ceased being modal.
  156. /// </summary>
  157. /// <remarks>
  158. /// <para>
  159. /// Subscribe to this event to perform post-activation logic (e.g., setting focus, updating UI state).
  160. /// When <see cref="EventArgs{T}.Value"/> is <see langword="true"/>, the runnable became modal (top of
  161. /// stack).
  162. /// When <see langword="false"/>, the runnable is no longer modal (another runnable is on top).
  163. /// </para>
  164. /// <para>
  165. /// This event follows the Terminal.Gui Cancellable Work Pattern (CWP).
  166. /// </para>
  167. /// </remarks>
  168. event EventHandler<EventArgs<bool>>? IsModalChanged;
  169. #endregion Modal or not (top of RunnableSessionStack or not)
  170. }
  171. /// <summary>
  172. /// Defines a view that can be run as an independent blocking session with <see cref="IApplication.Run(IRunnable, Func{Exception, bool})"/>,
  173. /// returning a typed result.
  174. /// </summary>
  175. /// <typeparam name="TResult">
  176. /// The type of result data returned when the session completes.
  177. /// Common types: <see cref="int"/> for button indices, <see cref="string"/> for file paths,
  178. /// custom types for complex form data.
  179. /// </typeparam>
  180. /// <remarks>
  181. /// <para>
  182. /// A runnable view executes as a self-contained blocking session with its own lifecycle,
  183. /// event loop iteration, and focus management. <see cref="IApplication.Run(IRunnable, Func{Exception, bool})"/> blocks until
  184. /// <see cref="IApplication.RequestStop()"/> is called.
  185. /// </para>
  186. /// <para>
  187. /// When <see cref="Result"/> is <see langword="null"/>, the session was stopped without being accepted
  188. /// (e.g., ESC key pressed, window closed). When non-<see langword="null"/>, it contains the result data
  189. /// extracted in <see cref="IRunnable.RaiseIsRunningChanging"/> (when stopping) before views are disposed.
  190. /// </para>
  191. /// <para>
  192. /// Implementing <see cref="IRunnable{TResult}"/> does not require deriving from any specific
  193. /// base class or using <see cref="ViewArrangement.Overlapped"/>. These are orthogonal concerns.
  194. /// </para>
  195. /// <para>
  196. /// This interface follows the Terminal.Gui Cancellable Work Pattern (CWP) for all lifecycle events.
  197. /// </para>
  198. /// </remarks>
  199. /// <seealso cref="IRunnable"/>
  200. /// <seealso cref="IApplication.Run(IRunnable, Func{Exception, bool})"/>
  201. public interface IRunnable<TResult> : IRunnable
  202. {
  203. /// <summary>
  204. /// Gets or sets the result data extracted when the session was accepted, or <see langword="null"/> if not accepted.
  205. /// </summary>
  206. /// <remarks>
  207. /// <para>
  208. /// Implementations should set this in the <see cref="IRunnable.RaiseIsRunningChanging"/> method
  209. /// (when stopping, i.e., <c>newIsRunning == false</c>) by extracting data from
  210. /// views before they are disposed.
  211. /// </para>
  212. /// <para>
  213. /// <see langword="null"/> indicates the session was stopped without accepting (ESC key, close without action).
  214. /// Non-<see langword="null"/> contains the type-safe result data.
  215. /// </para>
  216. /// </remarks>
  217. TResult? Result { get; set; }
  218. }