Runnable.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. namespace Terminal.Gui.ViewBase;
  2. /// <summary>
  3. /// Base implementation of <see cref="IRunnable"/> for views that can be run as sessions.
  4. /// </summary>
  5. /// <remarks>
  6. /// <para>
  7. /// Views can derive from this class or implement <see cref="IRunnable"/> directly.
  8. /// This base class provides a complete reference implementation of the <see cref="IRunnable"/>
  9. /// interface following Terminal.Gui's Cancellable Work Pattern.
  10. /// </para>
  11. /// <para>
  12. /// To customize lifecycle behavior, override the protected virtual methods:
  13. /// <see cref="OnStarting"/>, <see cref="OnStarted"/>, <see cref="OnStopping"/>, <see cref="OnStopped"/>.
  14. /// </para>
  15. /// </remarks>
  16. public class Runnable : View, IRunnable
  17. {
  18. /// <inheritdoc/>
  19. public bool Running { get; set; }
  20. #region IRunnable Implementation (RaiseXxxEvent Methods)
  21. /// <inheritdoc/>
  22. public virtual bool RaiseStartingEvent ()
  23. {
  24. // CWP Phase 1: Pre-notification via virtual method (can cancel)
  25. if (OnStarting ())
  26. {
  27. return true; // Starting canceled
  28. }
  29. // CWP Phase 2: Event notification (can cancel)
  30. var args = new System.ComponentModel.CancelEventArgs ();
  31. Starting?.Invoke (this, args);
  32. if (args.Cancel)
  33. {
  34. return true; // Starting canceled
  35. }
  36. // CWP Phase 3: Perform the work (mark as running)
  37. Running = true;
  38. // CWP Phase 4: Post-notification via virtual method
  39. OnStarted ();
  40. // CWP Phase 5: Post-notification event
  41. Started?.Invoke (this, EventArgs.Empty);
  42. return false; // Starting succeeded
  43. }
  44. /// <inheritdoc/>
  45. public virtual void RaiseStartedEvent ()
  46. {
  47. Started?.Invoke (this, EventArgs.Empty);
  48. }
  49. /// <inheritdoc/>
  50. public virtual void RaiseStoppingEvent ()
  51. {
  52. // CWP Phase 1: Pre-notification via virtual method (can cancel)
  53. if (OnStopping ())
  54. {
  55. return; // Stopping canceled
  56. }
  57. // CWP Phase 2: Event notification (can cancel)
  58. var args = new System.ComponentModel.CancelEventArgs ();
  59. Stopping?.Invoke (this, args);
  60. if (args.Cancel)
  61. {
  62. return; // Stopping canceled
  63. }
  64. // CWP Phase 3: Perform the work (stop the session)
  65. Running = false;
  66. // CWP Phase 4: Post-notification via virtual method
  67. OnStopped ();
  68. // CWP Phase 5: Post-notification event
  69. Stopped?.Invoke (this, EventArgs.Empty);
  70. }
  71. #endregion
  72. #region Protected Virtual Methods (Override Pattern)
  73. /// <summary>
  74. /// Called before <see cref="Starting"/> event. Override to cancel starting.
  75. /// </summary>
  76. /// <returns><see langword="true"/> to cancel; <see langword="false"/> to proceed.</returns>
  77. /// <remarks>
  78. /// <para>
  79. /// This is the first phase of the Cancellable Work Pattern for starting.
  80. /// Default implementation returns <see langword="false"/> (allow starting).
  81. /// </para>
  82. /// <para>
  83. /// Override this method to provide custom logic for determining whether the runnable
  84. /// should start (e.g., validating preconditions).
  85. /// </para>
  86. /// </remarks>
  87. protected virtual bool OnStarting ()
  88. {
  89. return false; // Default: allow starting
  90. }
  91. /// <summary>
  92. /// Called after session has started. Override for post-start work.
  93. /// </summary>
  94. /// <remarks>
  95. /// <para>
  96. /// This is the fourth phase of the Cancellable Work Pattern for starting.
  97. /// At this point, <see cref="Running"/> is <see langword="true"/>.
  98. /// Default implementation does nothing.
  99. /// </para>
  100. /// <para>
  101. /// Override this method to perform work that should occur after the session starts.
  102. /// </para>
  103. /// </remarks>
  104. protected virtual void OnStarted ()
  105. {
  106. // Default: do nothing
  107. }
  108. /// <summary>
  109. /// Called before <see cref="Stopping"/> event. Override to cancel stopping.
  110. /// </summary>
  111. /// <returns><see langword="true"/> to cancel; <see langword="false"/> to proceed.</returns>
  112. /// <remarks>
  113. /// <para>
  114. /// This is the first phase of the Cancellable Work Pattern for stopping.
  115. /// Default implementation returns <see langword="false"/> (allow stopping).
  116. /// </para>
  117. /// <para>
  118. /// Override this method to provide custom logic for determining whether the runnable
  119. /// should stop (e.g., prompting the user to save changes).
  120. /// </para>
  121. /// </remarks>
  122. protected virtual bool OnStopping ()
  123. {
  124. return false; // Default: allow stopping
  125. }
  126. /// <summary>
  127. /// Called after session has stopped. Override for post-stop cleanup.
  128. /// </summary>
  129. /// <remarks>
  130. /// <para>
  131. /// This is the fourth phase of the Cancellable Work Pattern for stopping.
  132. /// At this point, <see cref="Running"/> is <see langword="false"/>.
  133. /// Default implementation does nothing.
  134. /// </para>
  135. /// <para>
  136. /// Override this method to perform cleanup work that should occur after the session stops.
  137. /// </para>
  138. /// </remarks>
  139. protected virtual void OnStopped ()
  140. {
  141. // Default: do nothing
  142. }
  143. #endregion
  144. #region Events
  145. // Note: Initializing and Initialized events are inherited from View (ISupportInitialize pattern)
  146. /// <inheritdoc/>
  147. public event EventHandler<System.ComponentModel.CancelEventArgs>? Starting;
  148. /// <inheritdoc/>
  149. public event EventHandler? Started;
  150. /// <inheritdoc/>
  151. public event EventHandler<System.ComponentModel.CancelEventArgs>? Stopping;
  152. /// <inheritdoc/>
  153. public event EventHandler? Stopped;
  154. #endregion
  155. /// <summary>
  156. /// Stops and closes this runnable session.
  157. /// </summary>
  158. /// <remarks>
  159. /// <para>
  160. /// This method calls <see cref="RaiseStoppingEvent"/> to initiate the stopping process.
  161. /// The Application infrastructure will update this once IApplication supports IRunnable directly.
  162. /// </para>
  163. /// </remarks>
  164. public virtual void RequestStop ()
  165. {
  166. // TODO: Phase 3 - Update Application.RequestStop to accept IRunnable
  167. // For now, directly call RaiseStoppingEvent which follows CWP
  168. RaiseStoppingEvent ();
  169. }
  170. }