View.Drawing.cs 26 KB

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