View.Drawing.Scheme.cs 11 KB

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