View.Drawing.Scheme.cs 11 KB

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