View.Drawing.Scheme.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #nullable enable
  2. namespace Terminal.Gui.ViewBase;
  3. public partial class View
  4. {
  5. private string? _schemeName;
  6. /// <summary>
  7. /// Gets or sets the name of the scheme to use for this <see cref="View"/>. If set, it overrides the scheme
  8. /// inherited from the <see cref="SuperView"/>. If a scheme was explicitly set (<see cref="HasScheme"/> is
  9. /// true), this property is ignored.
  10. /// </summary>
  11. /// <remarks>
  12. /// <para>
  13. /// Setting this property raises pre- and post-change events via <see cref="CWPPropertyHelper"/>,
  14. /// allowing customization or cancellation of the change. The <see cref="SchemeNameChanging"/> event
  15. /// is raised before the change, and <see cref="SchemeNameChanged"/> is raised after.
  16. /// </para>
  17. /// </remarks>
  18. /// <value>The scheme name, or null if no scheme name is set.</value>
  19. /// <seealso cref="SchemeNameChanging"/>
  20. /// <seealso cref="SchemeNameChanged"/>
  21. public string? SchemeName
  22. {
  23. get => _schemeName;
  24. set
  25. {
  26. CWPPropertyHelper.ChangeProperty (
  27. ref _schemeName,
  28. value,
  29. OnSchemeNameChanging,
  30. SchemeNameChanging,
  31. newValue => _schemeName = newValue,
  32. OnSchemeNameChanged,
  33. SchemeNameChanged,
  34. out string? _);
  35. }
  36. }
  37. /// <summary>
  38. /// Called before the <see cref="SchemeName"/> property changes, allowing subclasses to cancel or modify the change.
  39. /// </summary>
  40. /// <param name="args">The event arguments containing the current and proposed new scheme name.</param>
  41. /// <returns>True to cancel the change, false to proceed.</returns>
  42. protected virtual bool OnSchemeNameChanging (ValueChangingEventArgs<string?> args) { return false; }
  43. /// <summary>
  44. /// Called after the <see cref="SchemeName"/> property changes, allowing subclasses to react to the change.
  45. /// </summary>
  46. /// <param name="args">The event arguments containing the old and new scheme name.</param>
  47. protected virtual void OnSchemeNameChanged (ValueChangedEventArgs<string?> args) { }
  48. /// <summary>
  49. /// Raised before the <see cref="SchemeName"/> property changes, allowing handlers to modify or cancel the change.
  50. /// </summary>
  51. /// <remarks>
  52. /// Set <see cref="ValueChangingEventArgs{T}.Handled"/> to true to cancel the change or modify
  53. /// <see cref="ValueChangingEventArgs{T}.NewValue"/> to adjust the proposed value.
  54. /// </remarks>
  55. /// <example>
  56. /// <code>
  57. /// view.SchemeNameChanging += (sender, args) =>
  58. /// {
  59. /// if (args.NewValue == "InvalidScheme")
  60. /// {
  61. /// args.Handled = true;
  62. /// Console.WriteLine("Invalid scheme name cancelled.");
  63. /// }
  64. /// };
  65. /// </code>
  66. /// </example>
  67. public event EventHandler<ValueChangingEventArgs<string?>>? SchemeNameChanging;
  68. /// <summary>
  69. /// Raised after the <see cref="SchemeName"/> property changes, notifying handlers of the completed change.
  70. /// </summary>
  71. /// <remarks>
  72. /// Provides the old and new scheme name via <see cref="ValueChangedEventArgs{T}.OldValue"/> and
  73. /// <see cref="ValueChangedEventArgs{T}.NewValue"/>, which may be null.
  74. /// </remarks>
  75. /// <example>
  76. /// <code>
  77. /// view.SchemeNameChanged += (sender, args) =>
  78. /// {
  79. /// Console.WriteLine($"SchemeName changed from {args.OldValue ?? "none"} to {args.NewValue ?? "none"}.");
  80. /// };
  81. /// </code>
  82. /// </example>
  83. public event EventHandler<ValueChangedEventArgs<string?>>? SchemeNameChanged;
  84. // Both holds the set Scheme and is used to determine if a Scheme has been set or not
  85. private Scheme? _scheme;
  86. /// <summary>
  87. /// Gets whether a Scheme has been explicitly set for this View, or if it will inherit the Scheme from its
  88. /// <see cref="SuperView"/>.
  89. /// </summary>
  90. public bool HasScheme => _scheme is { };
  91. /// <summary>
  92. /// Gets the scheme for the <see cref="View"/>. If the scheme has not been explicitly set
  93. /// (see <see cref="HasScheme"/>), gets the <see cref="SuperView"/>'s scheme or falls back to the base scheme.
  94. /// </summary>
  95. /// <returns>The resolved scheme, never null.</returns>
  96. /// <remarks>
  97. /// <para>
  98. /// This method uses the Cancellable Work Pattern (CWP) via
  99. /// <see cref="CWPWorkflowHelper.ExecuteWithResult{TResult}"/>
  100. /// to allow customization or cancellation of scheme resolution through the <see cref="OnGettingScheme"/> method
  101. /// and <see cref="GettingScheme"/> event.
  102. /// </para>
  103. /// </remarks>
  104. /// <example>
  105. /// <code>
  106. /// view.GettingScheme += (sender, args) =>
  107. /// {
  108. /// args.Result = SchemeManager.GetScheme("Custom");
  109. /// args.Handled = true;
  110. /// };
  111. /// Scheme scheme = view.GetScheme();
  112. /// </code>
  113. /// </example>
  114. public Scheme GetScheme ()
  115. {
  116. ResultEventArgs<Scheme?> args = new ();
  117. return CWPWorkflowHelper.ExecuteWithResult (
  118. resultEventArgs =>
  119. {
  120. bool cancelled = OnGettingScheme (out Scheme? newScheme);
  121. resultEventArgs.Result = newScheme;
  122. return cancelled;
  123. },
  124. GettingScheme,
  125. args,
  126. DefaultAction)!;
  127. Scheme DefaultAction ()
  128. {
  129. if (!HasScheme && !string.IsNullOrEmpty (SchemeName))
  130. {
  131. return SchemeManager.GetScheme (SchemeName);
  132. }
  133. if (!HasScheme)
  134. {
  135. return SuperView?.GetScheme () ?? SchemeManager.GetScheme (Schemes.Base);
  136. }
  137. return _scheme!;
  138. }
  139. }
  140. /// <summary>
  141. /// Called when the <see cref="Scheme"/> for the <see cref="View"/> is being retrieved. Subclasses can return
  142. /// true to stop further processing and optionally set <paramref name="scheme"/> to a different value.
  143. /// </summary>
  144. /// <param name="scheme">The scheme to use, or null to continue processing.</param>
  145. /// <returns>True to stop default behavior, false to proceed.</returns>
  146. protected virtual bool OnGettingScheme (out Scheme? scheme)
  147. {
  148. scheme = null;
  149. return false;
  150. }
  151. /// <summary>
  152. /// Raised when the <see cref="Scheme"/> for the <see cref="View"/> is being retrieved. Handlers can set
  153. /// <see cref="ResultEventArgs{T}.Handled"/> to true to stop further processing and optionally set
  154. /// <see cref="ResultEventArgs{T}.Result"/> to a different value.
  155. /// </summary>
  156. public event EventHandler<ResultEventArgs<Scheme?>>? GettingScheme;
  157. /// <summary>
  158. /// Sets the scheme for the <see cref="View"/>, marking it as explicitly set.
  159. /// </summary>
  160. /// <param name="scheme">The scheme to set, or null to clear the explicit scheme.</param>
  161. /// <returns>True if the scheme was set, false if unchanged or cancelled.</returns>
  162. /// <remarks>
  163. /// <para>
  164. /// This method uses the Cancellable Work Pattern (CWP) via <see cref="CWPPropertyHelper.ChangeProperty{T}"/>
  165. /// to allow customization or cancellation of the scheme change through the <see cref="OnSettingScheme"/> method
  166. /// and <see cref="SchemeChanging"/> event. The <see cref="SchemeChanged"/> event is raised after a successful
  167. /// change.
  168. /// </para>
  169. /// <para>
  170. /// If set to null, <see cref="HasScheme"/> will be false, and the view will inherit the scheme from its
  171. /// <see cref="SuperView"/> or fall back to the base scheme.
  172. /// </para>
  173. /// </remarks>
  174. /// <example>
  175. /// <code>
  176. /// view.SchemeChanging += (sender, args) =>
  177. /// {
  178. /// if (args.NewValue is null)
  179. /// {
  180. /// args.Handled = true;
  181. /// Console.WriteLine("Null scheme cancelled.");
  182. /// }
  183. /// };
  184. /// view.SchemeChanged += (sender, args) =>
  185. /// {
  186. /// Console.WriteLine($"Scheme changed to {args.NewValue?.Name ?? "none"}.");
  187. /// };
  188. /// bool set = view.SetScheme(SchemeManager.GetScheme("Base"));
  189. /// </code>
  190. /// </example>
  191. public bool SetScheme (Scheme? scheme)
  192. {
  193. return CWPPropertyHelper.ChangeProperty (
  194. ref _scheme,
  195. scheme,
  196. OnSettingScheme,
  197. SchemeChanging,
  198. newValue => _scheme = newValue,
  199. OnSchemeChanged,
  200. SchemeChanged,
  201. out Scheme? _);
  202. }
  203. /// <summary>
  204. /// Called before the scheme is set, allowing subclasses to cancel or modify the change.
  205. /// </summary>
  206. /// <param name="args">The event arguments containing the current and proposed new scheme.</param>
  207. /// <returns>True to cancel the change, false to proceed.</returns>
  208. protected virtual bool OnSettingScheme (ValueChangingEventArgs<Scheme?> args) { return false; }
  209. /// <summary>
  210. /// Called after the scheme is set, allowing subclasses to react to the change.
  211. /// </summary>
  212. /// <param name="args">The event arguments containing the old and new scheme.</param>
  213. protected virtual void OnSchemeChanged (ValueChangedEventArgs<Scheme?> args) { SetNeedsDraw (); }
  214. /// <summary>
  215. /// Raised before the scheme is set, allowing handlers to modify or cancel the change.
  216. /// </summary>
  217. /// <remarks>
  218. /// Set <see cref="ValueChangingEventArgs{T}.Handled"/> to true to cancel the change or modify
  219. /// <see cref="ValueChangingEventArgs{T}.NewValue"/> to adjust the proposed scheme.
  220. /// </remarks>
  221. public event EventHandler<ValueChangingEventArgs<Scheme?>>? SchemeChanging;
  222. /// <summary>
  223. /// Raised after the scheme is set, notifying handlers of the completed change.
  224. /// </summary>
  225. /// <remarks>
  226. /// Provides the old and new scheme via <see cref="ValueChangedEventArgs{T}.OldValue"/> and
  227. /// <see cref="ValueChangedEventArgs{T}.NewValue"/>, which may be null.
  228. /// </remarks>
  229. public event EventHandler<ValueChangedEventArgs<Scheme?>>? SchemeChanged;
  230. }