View.Drawing.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. using System.ComponentModel;
  2. using System.Diagnostics;
  3. namespace Terminal.Gui.ViewBase;
  4. public partial class View // Drawing APIs
  5. {
  6. /// <summary>
  7. /// Draws a set of views.
  8. /// </summary>
  9. /// <param name="views">The peer views to draw.</param>
  10. /// <param name="force">If <see langword="true"/>, <see cref="View.SetNeedsDraw()"/> will be called on each view to force it to be drawn.</param>
  11. internal static void Draw (IEnumerable<View> views, bool force)
  12. {
  13. // **Snapshot once** — every recursion level gets its own frozen array
  14. View [] viewsArray = views.Snapshot ();
  15. // The draw context is used to track the region drawn by each view.
  16. DrawContext context = new DrawContext ();
  17. foreach (View view in viewsArray)
  18. {
  19. if (force)
  20. {
  21. view.SetNeedsDraw ();
  22. }
  23. view.Draw (context);
  24. }
  25. // Draw the margins (those with Shadows) last to ensure they are drawn on top of the content.
  26. Margin.DrawMargins (viewsArray);
  27. // DrawMargins may have caused some views have NeedsDraw/NeedsSubViewDraw set; clear them all.
  28. foreach (View view in viewsArray)
  29. {
  30. view.ClearNeedsDraw ();
  31. // ClearNeedsDraw does not clear view.SuperView.SubViewsNeedDraw, so we have to do it here
  32. if (view.SuperView is { })
  33. {
  34. view.SuperView.SubViewNeedsDraw = false;
  35. }
  36. }
  37. }
  38. /// <summary>
  39. /// Draws the view if it needs to be drawn.
  40. /// </summary>
  41. /// <remarks>
  42. /// <para>
  43. /// The view will only be drawn if it is visible, and has any of <see cref="NeedsDraw"/>,
  44. /// <see cref="SubViewNeedsDraw"/>,
  45. /// or <see cref="NeedsLayout"/> set.
  46. /// </para>
  47. /// <para>
  48. /// See the View Drawing Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.Gui/docs/drawing.html"/>.
  49. /// </para>
  50. /// </remarks>
  51. public void Draw (DrawContext? context = null)
  52. {
  53. if (!CanBeVisible (this))
  54. {
  55. return;
  56. }
  57. Region? originalClip = GetClip ();
  58. // TODO: This can be further optimized by checking NeedsDraw below and only
  59. // TODO: clearing, drawing text, drawing content, etc. if it is true.
  60. if (NeedsDraw || SubViewNeedsDraw)
  61. {
  62. // ------------------------------------
  63. // Draw the Border and Padding.
  64. // Note Margin with a Shadow is special-cased and drawn in a separate pass to support
  65. // transparent shadows.
  66. DoDrawAdornments (originalClip);
  67. SetClip (originalClip);
  68. // ------------------------------------
  69. // Clear the Viewport
  70. // By default, we clip to the viewport preventing drawing outside the viewport
  71. // We also clip to the content, but if a developer wants to draw outside the viewport, they can do
  72. // so via settings. SetClip honors the ViewportSettings.DisableVisibleContentClipping flag.
  73. // Get our Viewport in screen coordinates
  74. originalClip = AddViewportToClip ();
  75. // If no context ...
  76. context ??= new ();
  77. SetAttributeForRole (Enabled ? VisualRole.Normal : VisualRole.Disabled);
  78. DoClearViewport (context);
  79. // ------------------------------------
  80. // Draw the subviews first (order matters: SubViews, Text, Content)
  81. if (SubViewNeedsDraw)
  82. {
  83. DoDrawSubViews (context);
  84. }
  85. // ------------------------------------
  86. // Draw the text
  87. SetAttributeForRole (Enabled ? VisualRole.Normal : VisualRole.Disabled);
  88. DoDrawText (context);
  89. // ------------------------------------
  90. // Draw the content
  91. DoDrawContent (context);
  92. // ------------------------------------
  93. // Draw the line canvas
  94. // Restore the clip before rendering the line canvas and adornment subviews
  95. // because they may draw outside the viewport.
  96. SetClip (originalClip);
  97. originalClip = AddFrameToClip ();
  98. DoRenderLineCanvas ();
  99. // ------------------------------------
  100. // Re-draw the border and padding subviews
  101. // HACK: This is a hack to ensure that the border and padding subviews are drawn after the line canvas.
  102. DoDrawAdornmentsSubViews ();
  103. // ------------------------------------
  104. // Advance the diagnostics draw indicator
  105. Border?.AdvanceDrawIndicator ();
  106. ClearNeedsDraw ();
  107. if (this is not Adornment && SuperView is not Adornment)
  108. {
  109. // Parent
  110. Debug.Assert (Margin!.Parent == this);
  111. Debug.Assert (Border!.Parent == this);
  112. Debug.Assert (Padding!.Parent == this);
  113. // SubViewNeedsDraw is set to false by ClearNeedsDraw.
  114. Debug.Assert (SubViewNeedsDraw == false);
  115. Debug.Assert (Margin!.SubViewNeedsDraw == false);
  116. Debug.Assert (Border!.SubViewNeedsDraw == false);
  117. Debug.Assert (Padding!.SubViewNeedsDraw == false);
  118. // NeedsDraw is set to false by ClearNeedsDraw.
  119. Debug.Assert (NeedsDraw == false);
  120. Debug.Assert (Margin!.NeedsDraw == false);
  121. Debug.Assert (Border!.NeedsDraw == false);
  122. Debug.Assert (Padding!.NeedsDraw == false);
  123. }
  124. }
  125. // ------------------------------------
  126. // This causes the Margin to be drawn in a second pass if it has a ShadowStyle
  127. Margin?.CacheClip ();
  128. // ------------------------------------
  129. // Reset the clip to what it was when we started
  130. SetClip (originalClip);
  131. // ------------------------------------
  132. // We're done drawing - The Clip is reset to what it was before we started.
  133. DoDrawComplete (context);
  134. }
  135. #region DrawAdornments
  136. private void DoDrawAdornmentsSubViews ()
  137. {
  138. // NOTE: We do not support subviews of Margin?
  139. if (Border?.SubViews is { } && Border.Thickness != Thickness.Empty && Border.NeedsDraw)
  140. {
  141. // PERFORMANCE: Get the check for DrawIndicator out of this somehow.
  142. foreach (View subview in Border.SubViews.Where (v => v.Visible || v.Id == "DrawIndicator"))
  143. {
  144. if (subview.Id != "DrawIndicator")
  145. {
  146. subview.SetNeedsDraw ();
  147. }
  148. LineCanvas.Exclude (new (subview.FrameToScreen ()));
  149. }
  150. Region? saved = Border?.AddFrameToClip ();
  151. Border?.DoDrawSubViews ();
  152. SetClip (saved);
  153. }
  154. if (Padding?.SubViews is { } && Padding.Thickness != Thickness.Empty && Padding.NeedsDraw)
  155. {
  156. foreach (View subview in Padding.SubViews)
  157. {
  158. subview.SetNeedsDraw ();
  159. }
  160. Region? saved = Padding?.AddFrameToClip ();
  161. Padding?.DoDrawSubViews ();
  162. SetClip (saved);
  163. }
  164. }
  165. internal void DoDrawAdornments (Region? originalClip)
  166. {
  167. if (this is Adornment)
  168. {
  169. AddFrameToClip ();
  170. }
  171. else
  172. {
  173. // Set the clip to be just the thicknesses of the adornments
  174. // TODO: Put this union logic in a method on View?
  175. Region? clipAdornments = Margin!.Thickness.AsRegion (Margin!.FrameToScreen ());
  176. clipAdornments.Combine (Border!.Thickness.AsRegion (Border!.FrameToScreen ()), RegionOp.Union);
  177. clipAdornments.Combine (Padding!.Thickness.AsRegion (Padding!.FrameToScreen ()), RegionOp.Union);
  178. clipAdornments.Combine (originalClip, RegionOp.Intersect);
  179. SetClip (clipAdornments);
  180. }
  181. if (Margin?.NeedsLayout == true)
  182. {
  183. Margin.NeedsLayout = false;
  184. Margin?.Thickness.Draw (Driver, FrameToScreen ());
  185. Margin?.Parent?.SetSubViewNeedsDraw ();
  186. }
  187. if (SubViewNeedsDraw)
  188. {
  189. // A SubView may add to the LineCanvas. This ensures any Adornment LineCanvas updates happen.
  190. Border?.SetNeedsDraw ();
  191. Padding?.SetNeedsDraw ();
  192. Margin?.SetNeedsDraw ();
  193. }
  194. if (OnDrawingAdornments ())
  195. {
  196. return;
  197. }
  198. // TODO: add event.
  199. DrawAdornments ();
  200. }
  201. /// <summary>
  202. /// Causes <see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/> to be drawn.
  203. /// </summary>
  204. /// <remarks>
  205. /// <para>
  206. /// <see cref="Margin"/> is drawn in a separate pass if <see cref="ShadowStyle"/> is set.
  207. /// </para>
  208. /// </remarks>
  209. public void DrawAdornments ()
  210. {
  211. // We do not attempt to draw Margin. It is drawn in a separate pass.
  212. // Each of these renders lines to this View's LineCanvas
  213. // Those lines will be finally rendered in OnRenderLineCanvas
  214. if (Border is { } && Border.Thickness != Thickness.Empty)
  215. {
  216. Border?.Draw ();
  217. }
  218. if (Padding is { } && Padding.Thickness != Thickness.Empty)
  219. {
  220. Padding?.Draw ();
  221. }
  222. if (Margin is { } && Margin.Thickness != Thickness.Empty/* && Margin.ShadowStyle == ShadowStyle.None*/)
  223. {
  224. //Margin?.Draw ();
  225. }
  226. }
  227. private void ClearFrame ()
  228. {
  229. if (Driver is null)
  230. {
  231. return;
  232. }
  233. // Get screen-relative coords
  234. Rectangle toClear = FrameToScreen ();
  235. Attribute prev = SetAttribute (GetAttributeForRole (VisualRole.Normal));
  236. Driver.FillRect (toClear);
  237. SetAttribute (prev);
  238. SetNeedsDraw ();
  239. }
  240. /// <summary>
  241. /// Called when the View's Adornments are to be drawn. Prepares <see cref="View.LineCanvas"/>. If
  242. /// <see cref="SuperViewRendersLineCanvas"/> is true, only the
  243. /// <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
  244. /// false (the default), this method will cause the <see cref="LineCanvas"/> be prepared to be rendered.
  245. /// </summary>
  246. /// <returns><see langword="true"/> to stop further drawing of the Adornments.</returns>
  247. protected virtual bool OnDrawingAdornments () { return false; }
  248. #endregion DrawAdornments
  249. #region ClearViewport
  250. internal void DoClearViewport (DrawContext? context = null)
  251. {
  252. if (!NeedsDraw || ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent) || OnClearingViewport ())
  253. {
  254. return;
  255. }
  256. var dev = new DrawEventArgs (Viewport, Rectangle.Empty, context);
  257. ClearingViewport?.Invoke (this, dev);
  258. if (dev.Cancel)
  259. {
  260. // BUGBUG: We should add the Viewport to context.DrawRegion here?
  261. SetNeedsDraw ();
  262. return;
  263. }
  264. if (!ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
  265. {
  266. ClearViewport (context);
  267. OnClearedViewport ();
  268. ClearedViewport?.Invoke (this, new (Viewport, Viewport, null));
  269. }
  270. }
  271. /// <summary>
  272. /// Called when the <see cref="Viewport"/> is to be cleared.
  273. /// </summary>
  274. /// <returns><see langword="true"/> to stop further clearing.</returns>
  275. protected virtual bool OnClearingViewport () { return false; }
  276. /// <summary>Event invoked when the <see cref="Viewport"/> is to be cleared.</summary>
  277. /// <remarks>
  278. /// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
  279. /// <para>
  280. /// Rect provides the view-relative rectangle describing the currently visible viewport into the
  281. /// <see cref="View"/> .
  282. /// </para>
  283. /// </remarks>
  284. public event EventHandler<DrawEventArgs>? ClearingViewport;
  285. /// <summary>
  286. /// Called when the <see cref="Viewport"/> has been cleared
  287. /// </summary>
  288. protected virtual void OnClearedViewport () { }
  289. /// <summary>Event invoked when the <see cref="Viewport"/> has been cleared.</summary>
  290. public event EventHandler<DrawEventArgs>? ClearedViewport;
  291. /// <summary>Clears <see cref="Viewport"/> with the normal background.</summary>
  292. /// <remarks>
  293. /// <para>
  294. /// If <see cref="ViewportSettings"/> has <see cref="ViewBase.ViewportSettingsFlags.ClearContentOnly"/> only
  295. /// the portion of the content
  296. /// area that is visible within the <see cref="View.Viewport"/> will be cleared. This is useful for views that have
  297. /// a
  298. /// content area larger than the Viewport (e.g. when <see cref="ViewportSettingsFlags.AllowNegativeLocation"/> is
  299. /// enabled) and want
  300. /// the area outside the content to be visually distinct.
  301. /// </para>
  302. /// </remarks>
  303. public void ClearViewport (DrawContext? context = null)
  304. {
  305. if (Driver is null)
  306. {
  307. return;
  308. }
  309. // Get screen-relative coords
  310. Rectangle toClear = ViewportToScreen (Viewport with { Location = new (0, 0) });
  311. if (ViewportSettings.HasFlag (ViewportSettingsFlags.ClearContentOnly))
  312. {
  313. Rectangle visibleContent = ViewportToScreen (new Rectangle (new (-Viewport.X, -Viewport.Y), GetContentSize ()));
  314. toClear = Rectangle.Intersect (toClear, visibleContent);
  315. }
  316. Driver.FillRect (toClear);
  317. // context.AddDrawnRectangle (toClear);
  318. SetNeedsDraw ();
  319. }
  320. #endregion ClearViewport
  321. #region DrawText
  322. private void DoDrawText (DrawContext? context = null)
  323. {
  324. if (!NeedsDraw)
  325. {
  326. return;
  327. }
  328. if (!string.IsNullOrEmpty (TextFormatter.Text))
  329. {
  330. TextFormatter.NeedsFormat = true;
  331. }
  332. if (OnDrawingText (context))
  333. {
  334. return;
  335. }
  336. // TODO: Get rid of this vf in lieu of the one above
  337. if (OnDrawingText ())
  338. {
  339. return;
  340. }
  341. var dev = new DrawEventArgs (Viewport, Rectangle.Empty, context);
  342. DrawingText?.Invoke (this, dev);
  343. if (dev.Cancel)
  344. {
  345. return;
  346. }
  347. DrawText (context);
  348. OnDrewText ();
  349. DrewText?.Invoke (this, EventArgs.Empty);
  350. }
  351. /// <summary>
  352. /// Called when the <see cref="Text"/> of the View is to be drawn.
  353. /// </summary>
  354. /// <param name="context">The draw context to report drawn areas to.</param>
  355. /// <returns><see langword="true"/> to stop further drawing of <see cref="Text"/>.</returns>
  356. protected virtual bool OnDrawingText (DrawContext? context) { return false; }
  357. /// <summary>
  358. /// Called when the <see cref="Text"/> of the View is to be drawn.
  359. /// </summary>
  360. /// <returns><see langword="true"/> to stop further drawing of <see cref="Text"/>.</returns>
  361. protected virtual bool OnDrawingText () { return false; }
  362. /// <summary>Raised when the <see cref="Text"/> of the View is to be drawn.</summary>
  363. /// <returns>
  364. /// Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/> to stop further drawing of
  365. /// <see cref="Text"/>.
  366. /// </returns>
  367. public event EventHandler<DrawEventArgs>? DrawingText;
  368. /// <summary>
  369. /// Draws the <see cref="Text"/> of the View using the <see cref="TextFormatter"/>.
  370. /// </summary>
  371. /// <param name="context">The draw context to report drawn areas to.</param>
  372. public void DrawText (DrawContext? context = null)
  373. {
  374. var drawRect = new Rectangle (ContentToScreen (Point.Empty), GetContentSize ());
  375. // Use GetDrawRegion to get precise drawn areas
  376. Region textRegion = TextFormatter.GetDrawRegion (drawRect);
  377. // Report the drawn area to the context
  378. context?.AddDrawnRegion (textRegion);
  379. if (Driver is { })
  380. {
  381. TextFormatter.Draw (
  382. Driver,
  383. drawRect,
  384. HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal),
  385. HasFocus ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal),
  386. Rectangle.Empty);
  387. }
  388. // We assume that the text has been drawn over the entire area; ensure that the subviews are redrawn.
  389. SetSubViewNeedsDraw ();
  390. }
  391. /// <summary>
  392. /// Called when the <see cref="Text"/> of the View has been drawn.
  393. /// </summary>
  394. protected virtual void OnDrewText () { }
  395. /// <summary>Raised when the <see cref="Text"/> of the View has been drawn.</summary>
  396. public event EventHandler? DrewText;
  397. #endregion DrawText
  398. #region DrawContent
  399. private void DoDrawContent (DrawContext? context = null)
  400. {
  401. if (!NeedsDraw || OnDrawingContent (context))
  402. {
  403. return;
  404. }
  405. // TODO: Upgrade all overrides of OnDrawingContent to use DrawContext and remove this override
  406. if (OnDrawingContent ())
  407. {
  408. return;
  409. }
  410. var dev = new DrawEventArgs (Viewport, Rectangle.Empty, context);
  411. DrawingContent?.Invoke (this, dev);
  412. if (dev.Cancel)
  413. {
  414. return;
  415. }
  416. // No default drawing; let event handlers or overrides handle it
  417. }
  418. /// <summary>
  419. /// Called when the View's content is to be drawn. The default implementation does nothing.
  420. /// </summary>
  421. /// <param name="context">The draw context to report drawn areas to.</param>
  422. /// <returns><see langword="true"/> to stop further drawing content.</returns>
  423. protected virtual bool OnDrawingContent (DrawContext? context) { return false; }
  424. /// <summary>
  425. /// Called when the View's content is to be drawn. The default implementation does nothing.
  426. /// </summary>
  427. /// <returns><see langword="true"/> to stop further drawing content.</returns>
  428. protected virtual bool OnDrawingContent () { return false; }
  429. /// <summary>Raised when the View's content is to be drawn.</summary>
  430. /// <remarks>
  431. /// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
  432. /// <para>
  433. /// Rect provides the view-relative rectangle describing the currently visible viewport into the
  434. /// <see cref="View"/> .
  435. /// </para>
  436. /// </remarks>
  437. public event EventHandler<DrawEventArgs>? DrawingContent;
  438. #endregion DrawContent
  439. #region DrawSubViews
  440. private void DoDrawSubViews (DrawContext? context = null)
  441. {
  442. if (!NeedsDraw || OnDrawingSubViews (context))
  443. {
  444. return;
  445. }
  446. // TODO: Get rid of this vf in lieu of the one above
  447. if (OnDrawingSubViews ())
  448. {
  449. return;
  450. }
  451. var dev = new DrawEventArgs (Viewport, Rectangle.Empty, context);
  452. DrawingSubViews?.Invoke (this, dev);
  453. if (dev.Cancel)
  454. {
  455. return;
  456. }
  457. if (!SubViewNeedsDraw)
  458. {
  459. return;
  460. }
  461. DrawSubViews (context);
  462. }
  463. /// <summary>
  464. /// Called when the <see cref="SubViews"/> are to be drawn.
  465. /// </summary>
  466. /// <param name="context">The draw context to report drawn areas to, or null if not tracking.</param>
  467. /// <returns><see langword="true"/> to stop further drawing of <see cref="SubViews"/>.</returns>
  468. protected virtual bool OnDrawingSubViews (DrawContext? context) { return false; }
  469. /// <summary>
  470. /// Called when the <see cref="SubViews"/> are to be drawn.
  471. /// </summary>
  472. /// <returns><see langword="true"/> to stop further drawing of <see cref="SubViews"/>.</returns>
  473. protected virtual bool OnDrawingSubViews () { return false; }
  474. /// <summary>Raised when the <see cref="SubViews"/> are to be drawn.</summary>
  475. /// <remarks>
  476. /// </remarks>
  477. /// <returns>
  478. /// Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/> to stop further drawing of
  479. /// <see cref="SubViews"/>.
  480. /// </returns>
  481. public event EventHandler<DrawEventArgs>? DrawingSubViews;
  482. /// <summary>
  483. /// Draws the <see cref="SubViews"/>.
  484. /// </summary>
  485. /// <param name="context">The draw context to report drawn areas to, or null if not tracking.</param>
  486. public void DrawSubViews (DrawContext? context = null)
  487. {
  488. if (InternalSubViews.Count == 0)
  489. {
  490. return;
  491. }
  492. // Draw the subviews in reverse order to leverage clipping.
  493. foreach (View view in InternalSubViews.Snapshot ().Where (v => v.Visible).Reverse ())
  494. {
  495. // TODO: HACK - This forcing of SetNeedsDraw with SuperViewRendersLineCanvas enables auto line join to work, but is brute force.
  496. if (view.SuperViewRendersLineCanvas || view.ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
  497. {
  498. //view.SetNeedsDraw ();
  499. }
  500. view.Draw (context);
  501. if (view.SuperViewRendersLineCanvas)
  502. {
  503. LineCanvas.Merge (view.LineCanvas);
  504. view.LineCanvas.Clear ();
  505. }
  506. }
  507. }
  508. #endregion DrawSubViews
  509. #region DrawLineCanvas
  510. private void DoRenderLineCanvas ()
  511. {
  512. if (!NeedsDraw || OnRenderingLineCanvas ())
  513. {
  514. return;
  515. }
  516. // TODO: Add event
  517. RenderLineCanvas ();
  518. }
  519. /// <summary>
  520. /// Called when the <see cref="View.LineCanvas"/> is to be rendered. See <see cref="RenderLineCanvas"/>.
  521. /// </summary>
  522. /// <returns><see langword="true"/> to stop further drawing of <see cref="LineCanvas"/>.</returns>
  523. protected virtual bool OnRenderingLineCanvas () { return false; }
  524. /// <summary>The canvas that any line drawing that is to be shared by subviews of this view should add lines to.</summary>
  525. /// <remarks><see cref="Border"/> adds border lines to this LineCanvas.</remarks>
  526. public LineCanvas LineCanvas { get; } = new ();
  527. /// <summary>
  528. /// Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any
  529. /// lines. If <see langword="true"/> the rendering of any borders drawn by this Frame will be done by its parent's
  530. /// SuperView. If <see langword="false"/> (the default) this View's <see cref="OnDrawingAdornments"/> method will
  531. /// be
  532. /// called to render the borders.
  533. /// </summary>
  534. public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
  535. /// <summary>
  536. /// Causes the contents of <see cref="LineCanvas"/> to be drawn.
  537. /// If <see cref="SuperViewRendersLineCanvas"/> is true, only the
  538. /// <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
  539. /// false (the default), this method will cause the <see cref="LineCanvas"/> to be rendered.
  540. /// </summary>
  541. public void RenderLineCanvas ()
  542. {
  543. if (Driver is null)
  544. {
  545. return;
  546. }
  547. if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rectangle.Empty)
  548. {
  549. foreach (KeyValuePair<Point, Cell?> p in LineCanvas.GetCellMap ())
  550. {
  551. // Get the entire map
  552. if (p.Value is { })
  553. {
  554. SetAttribute (p.Value.Value.Attribute ?? GetAttributeForRole (VisualRole.Normal));
  555. Driver.Move (p.Key.X, p.Key.Y);
  556. // TODO: #2616 - Support combining sequences that don't normalize
  557. AddStr (p.Value.Value.Grapheme);
  558. }
  559. }
  560. LineCanvas.Clear ();
  561. }
  562. }
  563. #endregion DrawLineCanvas
  564. #region DrawComplete
  565. private void DoDrawComplete (DrawContext? context)
  566. {
  567. OnDrawComplete (context);
  568. DrawComplete?.Invoke (this, new (Viewport, Viewport, context));
  569. // Now, update the clip to exclude this view (not including Margin)
  570. if (this is not Adornment)
  571. {
  572. if (ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent))
  573. {
  574. // context!.DrawnRegion is the region that was drawn by this view. It may include regions outside
  575. // the Viewport. We need to clip it to the Viewport.
  576. context!.ClipDrawnRegion (ViewportToScreen (Viewport));
  577. // Exclude the drawn region from the clip
  578. ExcludeFromClip (context.GetDrawnRegion ());
  579. // Exclude the Border and Padding from the clip
  580. ExcludeFromClip (Border?.Thickness.AsRegion (Border.FrameToScreen ()));
  581. ExcludeFromClip (Padding?.Thickness.AsRegion (Padding.FrameToScreen ()));
  582. // QUESTION: This makes it so that no nesting of transparent views is possible, but is more correct?
  583. context = new DrawContext ();
  584. }
  585. else
  586. {
  587. // Exclude this view (not including Margin) from the Clip
  588. Rectangle borderFrame = FrameToScreen ();
  589. if (Border is { })
  590. {
  591. borderFrame = Border.FrameToScreen ();
  592. }
  593. // In the non-transparent (typical case), we want to exclude the entire view area (borderFrame) from the clip
  594. ExcludeFromClip (borderFrame);
  595. // Update context.DrawnRegion to include the entire view (borderFrame), but clipped to our SuperView's viewport
  596. // This enables the SuperView to know what was drawn by this view.
  597. context?.AddDrawnRectangle (borderFrame);
  598. }
  599. }
  600. // TODO: Determine if we need another event that conveys the FINAL DrawContext
  601. }
  602. /// <summary>
  603. /// Called when the View is completed drawing.
  604. /// </summary>
  605. /// <remarks>
  606. /// The <paramref name="context"/> parameter provides the drawn region of the View.
  607. /// </remarks>
  608. protected virtual void OnDrawComplete (DrawContext? context) { }
  609. /// <summary>Raised when the View is completed drawing.</summary>
  610. /// <remarks>
  611. /// </remarks>
  612. public event EventHandler<DrawEventArgs>? DrawComplete;
  613. #endregion DrawComplete
  614. }