|
@@ -1,52 +1,40 @@
|
|
#nullable enable
|
|
#nullable enable
|
|
using System.Diagnostics;
|
|
using System.Diagnostics;
|
|
-using System.Reflection.Metadata;
|
|
|
|
|
|
|
|
namespace Terminal.Gui;
|
|
namespace Terminal.Gui;
|
|
|
|
|
|
public partial class View // Drawing APIs
|
|
public partial class View // Drawing APIs
|
|
{
|
|
{
|
|
- private ColorScheme? _colorScheme;
|
|
|
|
|
|
+ #region Drawing Primitives
|
|
|
|
|
|
- /// <summary>The color scheme for this view, if it is not defined, it returns the <see cref="SuperView"/>'s color scheme.</summary>
|
|
|
|
- public virtual ColorScheme? ColorScheme
|
|
|
|
|
|
+ /// <summary>Moves the drawing cursor to the specified <see cref="Viewport"/>-relative location in the view.</summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// <para>
|
|
|
|
+ /// If the provided coordinates are outside the visible content area, this method does nothing.
|
|
|
|
+ /// </para>
|
|
|
|
+ /// <para>
|
|
|
|
+ /// The top-left corner of the visible content area is <c>ViewPort.Location</c>.
|
|
|
|
+ /// </para>
|
|
|
|
+ /// </remarks>
|
|
|
|
+ /// <param name="col">Column (viewport-relative).</param>
|
|
|
|
+ /// <param name="row">Row (viewport-relative).</param>
|
|
|
|
+ public bool Move (int col, int row)
|
|
{
|
|
{
|
|
- get
|
|
|
|
|
|
+ if (Driver is null || Driver?.Rows == 0)
|
|
{
|
|
{
|
|
- if (_colorScheme is null)
|
|
|
|
- {
|
|
|
|
- return SuperView?.ColorScheme;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return _colorScheme;
|
|
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
- set
|
|
|
|
- {
|
|
|
|
- if (_colorScheme != value)
|
|
|
|
- {
|
|
|
|
- _colorScheme = value;
|
|
|
|
|
|
|
|
- if (Border is { } && Border.LineStyle != LineStyle.None && Border.ColorScheme is { })
|
|
|
|
- {
|
|
|
|
- Border.ColorScheme = _colorScheme;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- SetNeedsDisplay ();
|
|
|
|
- }
|
|
|
|
|
|
+ if (col < 0 || row < 0 || col >= Viewport.Width || row >= Viewport.Height)
|
|
|
|
+ {
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- /// <summary>The canvas that any line drawing that is to be shared by subviews of this view should add lines to.</summary>
|
|
|
|
- /// <remarks><see cref="Border"/> adds border lines to this LineCanvas.</remarks>
|
|
|
|
- public LineCanvas LineCanvas { get; } = new ();
|
|
|
|
|
|
+ Point screen = ViewportToScreen (new Point (col, row));
|
|
|
|
+ Driver?.Move (screen.X, screen.Y);
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any
|
|
|
|
- /// lines. If <see langword="true"/> the rendering of any borders drawn by this Frame will be done by its parent's
|
|
|
|
- /// SuperView. If <see langword="false"/> (the default) this View's <see cref="OnDrawAdornments"/> method will be
|
|
|
|
- /// called to render the borders.
|
|
|
|
- /// </summary>
|
|
|
|
- public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
|
|
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
|
|
/// <summary>Draws the specified character in the specified viewport-relative column and row of the View.</summary>
|
|
/// <summary>Draws the specified character in the specified viewport-relative column and row of the View.</summary>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -66,42 +54,59 @@ public partial class View // Drawing APIs
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /// <summary>Clears <see cref="Viewport"/> with the normal background.</summary>
|
|
|
|
|
|
+ /// <summary>Utility function to draw strings that contain a hotkey.</summary>
|
|
|
|
+ /// <param name="text">String to display, the hotkey specifier before a letter flags the next letter as the hotkey.</param>
|
|
|
|
+ /// <param name="hotColor">Hot color.</param>
|
|
|
|
+ /// <param name="normalColor">Normal color.</param>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
- /// If <see cref="ViewportSettings"/> has <see cref="Gui.ViewportSettings.ClearContentOnly"/> only
|
|
|
|
- /// the portion of the content
|
|
|
|
- /// area that is visible within the <see cref="View.Viewport"/> will be cleared. This is useful for views that have
|
|
|
|
- /// a
|
|
|
|
- /// content area larger than the Viewport (e.g. when <see cref="ViewportSettings.AllowNegativeLocation"/> is
|
|
|
|
- /// enabled) and want
|
|
|
|
- /// the area outside the content to be visually distinct.
|
|
|
|
|
|
+ /// The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by
|
|
|
|
+ /// default.
|
|
/// </para>
|
|
/// </para>
|
|
|
|
+ /// <para>The hotkey specifier can be changed via <see cref="HotKeySpecifier"/></para>
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- public void Clear ()
|
|
|
|
|
|
+ public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
|
|
{
|
|
{
|
|
- if (Driver is null)
|
|
|
|
|
|
+ Rune hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
|
|
|
|
+ Application.Driver?.SetAttribute (normalColor);
|
|
|
|
+
|
|
|
|
+ foreach (Rune rune in text.EnumerateRunes ())
|
|
{
|
|
{
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ if (rune == new Rune (hotkeySpec.Value))
|
|
|
|
+ {
|
|
|
|
+ Application.Driver?.SetAttribute (hotColor);
|
|
|
|
|
|
- // Get screen-relative coords
|
|
|
|
- Rectangle toClear = ViewportToScreen (Viewport with { Location = new (0, 0) });
|
|
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
- Rectangle prevClip = Driver.Clip;
|
|
|
|
|
|
+ Application.Driver?.AddRune (rune);
|
|
|
|
+ Application.Driver?.SetAttribute (normalColor);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- if (ViewportSettings.HasFlag (ViewportSettings.ClearContentOnly))
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Utility function to draw strings that contains a hotkey using a <see cref="ColorScheme"/> and the "focused"
|
|
|
|
+ /// state.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="text">String to display, the underscore before a letter flags the next letter as the hotkey.</param>
|
|
|
|
+ /// <param name="focused">
|
|
|
|
+ /// If set to <see langword="true"/> this uses the focused colors from the color scheme, otherwise
|
|
|
|
+ /// the regular ones.
|
|
|
|
+ /// </param>
|
|
|
|
+ public void DrawHotString (string text, bool focused)
|
|
|
|
+ {
|
|
|
|
+ if (focused)
|
|
{
|
|
{
|
|
- Rectangle visibleContent = ViewportToScreen (new Rectangle (new (-Viewport.X, -Viewport.Y), GetContentSize ()));
|
|
|
|
- toClear = Rectangle.Intersect (toClear, visibleContent);
|
|
|
|
|
|
+ DrawHotString (text, GetHotFocusColor (), GetFocusColor ());
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ DrawHotString (
|
|
|
|
+ text,
|
|
|
|
+ Enabled ? GetHotNormalColor () : ColorScheme!.Disabled,
|
|
|
|
+ Enabled ? GetNormalColor () : ColorScheme!.Disabled
|
|
|
|
+ );
|
|
}
|
|
}
|
|
-
|
|
|
|
- Attribute prev = Driver.SetAttribute (GetNormalColor ());
|
|
|
|
- Driver.FillRect (toClear);
|
|
|
|
- Driver.SetAttribute (prev);
|
|
|
|
-
|
|
|
|
- Driver.Clip = prevClip;
|
|
|
|
- SetNeedsDisplay ();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>Fills the specified <see cref="Viewport"/>-relative rectangle with the specified color.</summary>
|
|
/// <summary>Fills the specified <see cref="Viewport"/>-relative rectangle with the specified color.</summary>
|
|
@@ -128,6 +133,10 @@ public partial class View // Drawing APIs
|
|
Driver.Clip = prevClip;
|
|
Driver.Clip = prevClip;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #endregion Drawing Primitives
|
|
|
|
+
|
|
|
|
+ #region Clipping
|
|
|
|
+
|
|
/// <summary>Sets the <see cref="ConsoleDriver"/>'s clip region to <see cref="Viewport"/>.</summary>
|
|
/// <summary>Sets the <see cref="ConsoleDriver"/>'s clip region to <see cref="Viewport"/>.</summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -168,29 +177,21 @@ public partial class View // Drawing APIs
|
|
return previous;
|
|
return previous;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #endregion Clipping
|
|
|
|
+
|
|
|
|
+ #region Drawing Engine
|
|
|
|
+
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Draws the view if it needs to be drawn. Causes the following virtual methods to be called (along with their related
|
|
|
|
- /// events):
|
|
|
|
- /// <see cref="OnDrawContent"/>, <see cref="OnDrawContentComplete"/>.
|
|
|
|
|
|
+ /// Draws the view if it needs to be drawn.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
/// The view will only be drawn if it is visible, and has any of <see cref="NeedsDisplay"/>,
|
|
/// The view will only be drawn if it is visible, and has any of <see cref="NeedsDisplay"/>,
|
|
/// <see cref="SubViewNeedsDisplay"/>,
|
|
/// <see cref="SubViewNeedsDisplay"/>,
|
|
- /// or <see cref="IsLayoutNeeded"/> set.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// Always use <see cref="Viewport"/> (view-relative) when calling <see cref="OnDrawContent(Rectangle)"/>, NOT
|
|
|
|
- /// <see cref="Frame"/> (superview-relative).
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// Views should set the color that they want to use on entry, as otherwise this will inherit the last color that
|
|
|
|
- /// was set globally on the driver.
|
|
|
|
|
|
+ /// or <see cref="NeedsLayout"/> set.
|
|
/// </para>
|
|
/// </para>
|
|
/// <para>
|
|
/// <para>
|
|
- /// Overrides of <see cref="OnDrawContent(Rectangle)"/> must ensure they do not set <c>Driver.Clip</c> to a clip
|
|
|
|
- /// region larger than the <ref name="Viewport"/> property, as this will cause the driver to clip the entire
|
|
|
|
- /// region.
|
|
|
|
|
|
+ /// // TODO: Add docs for the drawing process.
|
|
/// </para>
|
|
/// </para>
|
|
/// </remarks>
|
|
/// </remarks>
|
|
public void Draw ()
|
|
public void Draw ()
|
|
@@ -244,6 +245,8 @@ public partial class View // Drawing APIs
|
|
DoDrawComplete (Viewport);
|
|
DoDrawComplete (Viewport);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #region DrawAdornments
|
|
|
|
+
|
|
private void DoDrawAdornmentSubViews ()
|
|
private void DoDrawAdornmentSubViews ()
|
|
{
|
|
{
|
|
if (Margin?.Subviews is { })
|
|
if (Margin?.Subviews is { })
|
|
@@ -252,75 +255,89 @@ public partial class View // Drawing APIs
|
|
{
|
|
{
|
|
subview.SetNeedsDisplay ();
|
|
subview.SetNeedsDisplay ();
|
|
}
|
|
}
|
|
|
|
+
|
|
Margin?.DoDrawSubviews (Margin.Viewport);
|
|
Margin?.DoDrawSubviews (Margin.Viewport);
|
|
}
|
|
}
|
|
|
|
+
|
|
if (Border?.Subviews is { })
|
|
if (Border?.Subviews is { })
|
|
{
|
|
{
|
|
foreach (View subview in Border.Subviews)
|
|
foreach (View subview in Border.Subviews)
|
|
{
|
|
{
|
|
subview.SetNeedsDisplay ();
|
|
subview.SetNeedsDisplay ();
|
|
}
|
|
}
|
|
|
|
+
|
|
Border?.DoDrawSubviews (Border.Viewport);
|
|
Border?.DoDrawSubviews (Border.Viewport);
|
|
}
|
|
}
|
|
|
|
+
|
|
if (Padding?.Subviews is { })
|
|
if (Padding?.Subviews is { })
|
|
{
|
|
{
|
|
foreach (View subview in Padding.Subviews)
|
|
foreach (View subview in Padding.Subviews)
|
|
{
|
|
{
|
|
subview.SetNeedsDisplay ();
|
|
subview.SetNeedsDisplay ();
|
|
}
|
|
}
|
|
|
|
+
|
|
Padding?.DoDrawSubviews (Padding.Viewport);
|
|
Padding?.DoDrawSubviews (Padding.Viewport);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // TODO: Make private and change menuBar and Tab to not use
|
|
internal void DoDrawAdornments ()
|
|
internal void DoDrawAdornments ()
|
|
{
|
|
{
|
|
- if (OnDrawAdornments ())
|
|
|
|
|
|
+ if (OnDrawingAdornments ())
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// TODO: add event.
|
|
// TODO: add event.
|
|
|
|
|
|
|
|
+ // TODO: add a DrawAdornments method
|
|
|
|
+
|
|
// Each of these renders lines to either this View's LineCanvas
|
|
// Each of these renders lines to either this View's LineCanvas
|
|
// Those lines will be finally rendered in OnRenderLineCanvas
|
|
// Those lines will be finally rendered in OnRenderLineCanvas
|
|
- Margin?.Draw (); //OnDrawContent (Margin.Viewport);
|
|
|
|
|
|
+ Margin?.Draw ();
|
|
Border?.Draw ();
|
|
Border?.Draw ();
|
|
- Padding?.Draw (); //OnDrawContent (Padding.Viewport);
|
|
|
|
-
|
|
|
|
|
|
+ Padding?.Draw ();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Called when the View's adornments are to be drawn. Prepares <see cref="View.LineCanvas"/>. If <see cref="SuperViewRendersLineCanvas"/> is true, only the
|
|
|
|
|
|
+ /// Called when the View's Adornments are to be drawn. Prepares <see cref="View.LineCanvas"/>. If
|
|
|
|
+ /// <see cref="SuperViewRendersLineCanvas"/> is true, only the
|
|
/// <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
|
|
/// <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
|
|
/// false (the default), this method will cause the <see cref="LineCanvas"/> be prepared to be rendered.
|
|
/// false (the default), this method will cause the <see cref="LineCanvas"/> be prepared to be rendered.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <returns></returns>
|
|
|
|
- protected virtual bool OnDrawAdornments ()
|
|
|
|
- {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ /// <returns><see langword="true"/> to stop further drawing of the Adornments.</returns>
|
|
|
|
+ protected virtual bool OnDrawingAdornments () { return false; }
|
|
|
|
+
|
|
|
|
+ #endregion DrawAdornments
|
|
|
|
+
|
|
#region ClearViewport
|
|
#region ClearViewport
|
|
|
|
|
|
private void DoClearViewport (Rectangle viewport)
|
|
private void DoClearViewport (Rectangle viewport)
|
|
{
|
|
{
|
|
Debug.Assert (viewport == Viewport);
|
|
Debug.Assert (viewport == Viewport);
|
|
|
|
|
|
- if (OnClearViewport (Viewport))
|
|
|
|
|
|
+ if (OnClearingViewport (Viewport))
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
- ClearViewport?.Invoke (this, dev);
|
|
|
|
|
|
+ ClearingViewport?.Invoke (this, dev);
|
|
|
|
|
|
- // BUGBUG: this clears way too frequently. Need to optimize this.
|
|
|
|
- if (NeedsDisplay /* || Arrangement.HasFlag (ViewArrangement.Overlapped)*/)
|
|
|
|
|
|
+ if (dev.Cancel)
|
|
{
|
|
{
|
|
- Clear ();
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ ClearViewport ();
|
|
}
|
|
}
|
|
|
|
|
|
- protected virtual bool OnClearViewport (Rectangle viewport) { return false; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Called when the <see cref="Viewport"/> is to be cleared.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="viewport"></param>
|
|
|
|
+ /// <returns><see langword="true"/> to stop further clearing.</returns>
|
|
|
|
+ protected virtual bool OnClearingViewport (Rectangle viewport) { return false; }
|
|
|
|
|
|
/// <summary>Event invoked when the content area of the View is to be drawn.</summary>
|
|
/// <summary>Event invoked when the content area of the View is to be drawn.</summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
@@ -330,7 +347,45 @@ public partial class View // Drawing APIs
|
|
/// <see cref="View"/> .
|
|
/// <see cref="View"/> .
|
|
/// </para>
|
|
/// </para>
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- public event EventHandler<DrawEventArgs>? ClearViewport;
|
|
|
|
|
|
+ public event EventHandler<DrawEventArgs>? ClearingViewport;
|
|
|
|
+
|
|
|
|
+ /// <summary>Clears <see cref="Viewport"/> with the normal background.</summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// <para>
|
|
|
|
+ /// If <see cref="ViewportSettings"/> has <see cref="Gui.ViewportSettings.ClearContentOnly"/> only
|
|
|
|
+ /// the portion of the content
|
|
|
|
+ /// area that is visible within the <see cref="View.Viewport"/> will be cleared. This is useful for views that have
|
|
|
|
+ /// a
|
|
|
|
+ /// content area larger than the Viewport (e.g. when <see cref="ViewportSettings.AllowNegativeLocation"/> is
|
|
|
|
+ /// enabled) and want
|
|
|
|
+ /// the area outside the content to be visually distinct.
|
|
|
|
+ /// </para>
|
|
|
|
+ /// </remarks>
|
|
|
|
+ public void ClearViewport ()
|
|
|
|
+ {
|
|
|
|
+ if (Driver is null)
|
|
|
|
+ {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Get screen-relative coords
|
|
|
|
+ Rectangle toClear = ViewportToScreen (Viewport with { Location = new (0, 0) });
|
|
|
|
+
|
|
|
|
+ Rectangle prevClip = Driver.Clip;
|
|
|
|
+
|
|
|
|
+ if (ViewportSettings.HasFlag (ViewportSettings.ClearContentOnly))
|
|
|
|
+ {
|
|
|
|
+ Rectangle visibleContent = ViewportToScreen (new Rectangle (new (-Viewport.X, -Viewport.Y), GetContentSize ()));
|
|
|
|
+ toClear = Rectangle.Intersect (toClear, visibleContent);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Attribute prev = Driver.SetAttribute (GetNormalColor ());
|
|
|
|
+ Driver.FillRect (toClear);
|
|
|
|
+ Driver.SetAttribute (prev);
|
|
|
|
+
|
|
|
|
+ Driver.Clip = prevClip;
|
|
|
|
+ SetNeedsDisplay ();
|
|
|
|
+ }
|
|
|
|
|
|
#endregion ClearViewport
|
|
#endregion ClearViewport
|
|
|
|
|
|
@@ -338,17 +393,43 @@ public partial class View // Drawing APIs
|
|
|
|
|
|
private void DoDrawText (Rectangle viewport)
|
|
private void DoDrawText (Rectangle viewport)
|
|
{
|
|
{
|
|
- Debug.Assert(viewport == Viewport);
|
|
|
|
-
|
|
|
|
|
|
+ Debug.Assert (viewport == Viewport);
|
|
|
|
|
|
- if (OnDrawText (Viewport))
|
|
|
|
|
|
+ if (OnDrawingText (Viewport))
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
- DrawText?.Invoke (this, dev);
|
|
|
|
|
|
+ DrawingText?.Invoke (this, dev);
|
|
|
|
|
|
|
|
+ if (dev.Cancel)
|
|
|
|
+ {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DrawText ();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Called when the <see cref="Text"/> of the View is to be drawn.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="viewport"></param>
|
|
|
|
+ /// <returns><see langword="true"/> to stop further drawing of <see cref="Text"/>.</returns>
|
|
|
|
+ protected virtual bool OnDrawingText (Rectangle viewport) { return false; }
|
|
|
|
+
|
|
|
|
+ /// <summary>Raised when the <see cref="Text"/> of the View is to be drawn.</summary>
|
|
|
|
+ /// <returns>
|
|
|
|
+ /// Set <see cref="DrawEventArgs.Cancel"/> to <see langword="true"/> to stop further drawing of
|
|
|
|
+ /// <see cref="Text"/>.
|
|
|
|
+ /// </returns>
|
|
|
|
+ public event EventHandler<DrawEventArgs>? DrawingText;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Draws the <see cref="Text"/> of the View using the <see cref="TextFormatter"/>.
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void DrawText ()
|
|
|
|
+ {
|
|
if (!string.IsNullOrEmpty (TextFormatter.Text))
|
|
if (!string.IsNullOrEmpty (TextFormatter.Text))
|
|
{
|
|
{
|
|
TextFormatter.NeedsFormat = true;
|
|
TextFormatter.NeedsFormat = true;
|
|
@@ -358,31 +439,17 @@ public partial class View // Drawing APIs
|
|
// TODO: If the output is not in the Viewport, do nothing
|
|
// TODO: If the output is not in the Viewport, do nothing
|
|
var drawRect = new Rectangle (ContentToScreen (Point.Empty), GetContentSize ());
|
|
var drawRect = new Rectangle (ContentToScreen (Point.Empty), GetContentSize ());
|
|
|
|
|
|
- if (Id == "ScrollingDemoView")
|
|
|
|
- {
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
TextFormatter?.Draw (
|
|
TextFormatter?.Draw (
|
|
drawRect,
|
|
drawRect,
|
|
HasFocus ? GetFocusColor () : GetNormalColor (),
|
|
HasFocus ? GetFocusColor () : GetNormalColor (),
|
|
HasFocus ? GetHotFocusColor () : GetHotNormalColor (),
|
|
HasFocus ? GetHotFocusColor () : GetHotNormalColor (),
|
|
Rectangle.Empty
|
|
Rectangle.Empty
|
|
);
|
|
);
|
|
|
|
+
|
|
|
|
+ // We assume that the text has been drawn over the entire area; ensure that the subviews are redrawn.
|
|
SetSubViewNeedsDisplay ();
|
|
SetSubViewNeedsDisplay ();
|
|
}
|
|
}
|
|
|
|
|
|
- protected virtual bool OnDrawText (Rectangle viewport) { return false; }
|
|
|
|
-
|
|
|
|
- /// <summary>Event invoked when the content area of the View is to be drawn.</summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
|
|
|
|
- /// <para>
|
|
|
|
- /// Rect provides the view-relative rectangle describing the currently visible viewport into the
|
|
|
|
- /// <see cref="View"/> .
|
|
|
|
- /// </para>
|
|
|
|
- /// </remarks>
|
|
|
|
- public event EventHandler<DrawEventArgs>? DrawText;
|
|
|
|
-
|
|
|
|
#endregion DrawText
|
|
#endregion DrawText
|
|
|
|
|
|
#region DrawContent
|
|
#region DrawContent
|
|
@@ -390,51 +457,30 @@ public partial class View // Drawing APIs
|
|
private void DoDrawContent (Rectangle viewport)
|
|
private void DoDrawContent (Rectangle viewport)
|
|
{
|
|
{
|
|
Debug.Assert (viewport == Viewport);
|
|
Debug.Assert (viewport == Viewport);
|
|
- if (OnDrawContent (Viewport))
|
|
|
|
|
|
+
|
|
|
|
+ if (OnDrawingContent (Viewport))
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
- DrawContent?.Invoke (this, dev);
|
|
|
|
|
|
+ DrawingContent?.Invoke (this, dev);
|
|
|
|
+
|
|
|
|
+ if (dev.Cancel)
|
|
|
|
+ { }
|
|
|
|
+
|
|
|
|
+ // Do nothing.
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Called when the View's content is to be drawn.
|
|
|
|
|
|
+ /// Called when the View's content is to be drawn. The default implementation does nothing.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
- /// <para>
|
|
|
|
- /// The <paramref name="viewport"/> parameter is provided as a convenience; it has the same values as the
|
|
|
|
- /// <see cref="Viewport"/> property.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// The <see cref="Viewport"/> Location and Size indicate what part of the View's content, defined
|
|
|
|
- /// by <see cref="GetContentSize ()"/>, is visible and should be drawn. The coordinates taken by <see cref="Move"/>
|
|
|
|
- /// and
|
|
|
|
- /// <see cref="AddRune"/> are relative to <see cref="Viewport"/>, thus if <c>ViewPort.Location.Y</c> is <c>5</c>
|
|
|
|
- /// the 6th row of the content should be drawn using <c>MoveTo (x, 5)</c>.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// If <see cref="GetContentSize ()"/> is larger than <c>ViewPort.Size</c> drawing code should use
|
|
|
|
- /// <see cref="Viewport"/>
|
|
|
|
- /// to constrain drawing for better performance.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// The <see cref="ConsoleDriver.Clip"/> may define smaller area than <see cref="Viewport"/>; complex drawing code
|
|
|
|
- /// can be more
|
|
|
|
- /// efficient by using <see cref="ConsoleDriver.Clip"/> to constrain drawing for better performance.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// Overrides should loop through the subviews and call <see cref="Draw"/>.
|
|
|
|
- /// </para>
|
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- /// <param name="viewport">
|
|
|
|
- /// The rectangle describing the currently visible viewport into the <see cref="View"/>; has the same value as
|
|
|
|
- /// <see cref="Viewport"/>.
|
|
|
|
- /// </param>
|
|
|
|
- protected virtual bool OnDrawContent (Rectangle viewport) { return false; }
|
|
|
|
|
|
+ /// <returns><see langword="true"/> to stop further drawing content.</returns>
|
|
|
|
+ protected virtual bool OnDrawingContent (Rectangle viewport) { return false; }
|
|
|
|
|
|
- /// <summary>Event invoked when the content area of the View is to be drawn.</summary>
|
|
|
|
|
|
+ /// <summary>Raised when the View's content is to be drawn.</summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
|
|
/// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -442,279 +488,109 @@ public partial class View // Drawing APIs
|
|
/// <see cref="View"/> .
|
|
/// <see cref="View"/> .
|
|
/// </para>
|
|
/// </para>
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- public event EventHandler<DrawEventArgs>? DrawContent;
|
|
|
|
|
|
+ public event EventHandler<DrawEventArgs>? DrawingContent;
|
|
|
|
|
|
#endregion DrawContent
|
|
#endregion DrawContent
|
|
|
|
|
|
#region DrawSubviews
|
|
#region DrawSubviews
|
|
|
|
|
|
- internal void DoDrawSubviews (Rectangle viewport)
|
|
|
|
|
|
+ private void DoDrawSubviews (Rectangle viewport)
|
|
{
|
|
{
|
|
Debug.Assert (viewport == Viewport);
|
|
Debug.Assert (viewport == Viewport);
|
|
- if (OnDrawSubviews (Viewport))
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
|
|
- DrawSubviews?.Invoke (this, dev);
|
|
|
|
|
|
|
|
- // TODO: Move drawing of subviews to a separate OnDrawText virtual method
|
|
|
|
- // Draw subviews
|
|
|
|
- // TODO: Implement OnDrawText (cancelable);
|
|
|
|
- if (_subviews is null || !SubViewNeedsDisplay)
|
|
|
|
|
|
+ if (OnDrawingSubviews (Viewport))
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- IEnumerable<View> subviewsNeedingDraw = _subviews.Where (
|
|
|
|
- view => view.Visible
|
|
|
|
- && (view.NeedsDisplay
|
|
|
|
- || view.SubViewNeedsDisplay
|
|
|
|
-
|
|
|
|
- // || view.Arrangement.HasFlag (ViewArrangement.Overlapped)
|
|
|
|
- ));
|
|
|
|
-
|
|
|
|
- foreach (View view in subviewsNeedingDraw)
|
|
|
|
- {
|
|
|
|
- if (view.NeedsLayout)
|
|
|
|
- {
|
|
|
|
- //Debug.WriteLine ($"Layout should be de-coupled from drawing: {view}");
|
|
|
|
- //view.LayoutSubviews ();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
|
|
|
|
- // TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
|
|
|
|
- if (view.Arrangement.HasFlag (ViewArrangement.Overlapped))
|
|
|
|
- {
|
|
|
|
- // view.SetNeedsDisplay ();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- view.Draw ();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- protected virtual bool OnDrawSubviews (Rectangle viewport) { return false; }
|
|
|
|
-
|
|
|
|
- /// <summary>Event invoked when the content area of the View is to be drawn.</summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// <para>Will be invoked before any subviews added with <see cref="Add(View)"/> have been drawn.</para>
|
|
|
|
- /// <para>
|
|
|
|
- /// Rect provides the view-relative rectangle describing the currently visible viewport into the
|
|
|
|
- /// <see cref="View"/> .
|
|
|
|
- /// </para>
|
|
|
|
- /// </remarks>
|
|
|
|
- public event EventHandler<DrawEventArgs>? DrawSubviews;
|
|
|
|
-
|
|
|
|
- #endregion DrawSubviews
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- #region DrawComplete
|
|
|
|
|
|
+ var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
|
|
+ DrawingSubviews?.Invoke (this, dev);
|
|
|
|
|
|
- private void DoDrawComplete (Rectangle viewport)
|
|
|
|
- {
|
|
|
|
- Debug.Assert (viewport == Viewport);
|
|
|
|
- if (OnDrawComplete (Viewport))
|
|
|
|
|
|
+ if (dev.Cancel)
|
|
{
|
|
{
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
|
|
- DrawComplete?.Invoke (this, dev);
|
|
|
|
|
|
+ DrawSubviews ();
|
|
}
|
|
}
|
|
|
|
|
|
- protected virtual bool OnDrawComplete (Rectangle viewport) { return false; }
|
|
|
|
-
|
|
|
|
- /// <summary>Event invoked when the View is completed drawing.</summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// <para>
|
|
|
|
- /// Rect provides the view-relative rectangle describing the currently visible viewport into the
|
|
|
|
- /// <see cref="View"/> .
|
|
|
|
- /// </para>
|
|
|
|
- /// </remarks>
|
|
|
|
- public event EventHandler<DrawEventArgs>? DrawComplete;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- #endregion DrawComplete
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Called when the <see cref="Subviews"/> are to be drawn.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="viewport"></param>
|
|
|
|
+ /// <returns><see langword="true"/> to stop further drawing of <see cref="Subviews"/>.</returns>
|
|
|
|
+ protected virtual bool OnDrawingSubviews (Rectangle viewport) { return false; }
|
|
|
|
|
|
- /// <summary>Utility function to draw strings that contain a hotkey.</summary>
|
|
|
|
- /// <param name="text">String to display, the hotkey specifier before a letter flags the next letter as the hotkey.</param>
|
|
|
|
- /// <param name="hotColor">Hot color.</param>
|
|
|
|
- /// <param name="normalColor">Normal color.</param>
|
|
|
|
|
|
+ /// <summary>Raised when the <see cref="Subviews"/> are to be drawn.</summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
- /// <para>
|
|
|
|
- /// The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by
|
|
|
|
- /// default.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>The hotkey specifier can be changed via <see cref="HotKeySpecifier"/></para>
|
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
|
|
|
|
- {
|
|
|
|
- Rune hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
|
|
|
|
- Application.Driver?.SetAttribute (normalColor);
|
|
|
|
-
|
|
|
|
- foreach (Rune rune in text.EnumerateRunes ())
|
|
|
|
- {
|
|
|
|
- if (rune == new Rune (hotkeySpec.Value))
|
|
|
|
- {
|
|
|
|
- Application.Driver?.SetAttribute (hotColor);
|
|
|
|
-
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Application.Driver?.AddRune (rune);
|
|
|
|
- Application.Driver?.SetAttribute (normalColor);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ /// <returns>
|
|
|
|
+ /// Set <see cref="DrawEventArgs.Cancel"/> to <see langword="true"/> to stop further drawing of
|
|
|
|
+ /// <see cref="Subviews"/>.
|
|
|
|
+ /// </returns>
|
|
|
|
+ public event EventHandler<DrawEventArgs>? DrawingSubviews;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Utility function to draw strings that contains a hotkey using a <see cref="ColorScheme"/> and the "focused"
|
|
|
|
- /// state.
|
|
|
|
|
|
+ /// Draws the <see cref="Subviews"/>.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <param name="text">String to display, the underscore before a letter flags the next letter as the hotkey.</param>
|
|
|
|
- /// <param name="focused">
|
|
|
|
- /// If set to <see langword="true"/> this uses the focused colors from the color scheme, otherwise
|
|
|
|
- /// the regular ones.
|
|
|
|
- /// </param>
|
|
|
|
- public void DrawHotString (string text, bool focused)
|
|
|
|
|
|
+ public void DrawSubviews ()
|
|
{
|
|
{
|
|
- if (focused)
|
|
|
|
- {
|
|
|
|
- DrawHotString (text, GetHotFocusColor (), GetFocusColor ());
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
|
|
+ if (_subviews is null || !SubViewNeedsDisplay)
|
|
{
|
|
{
|
|
- DrawHotString (
|
|
|
|
- text,
|
|
|
|
- Enabled ? GetHotNormalColor () : ColorScheme!.Disabled,
|
|
|
|
- Enabled ? GetNormalColor () : ColorScheme!.Disabled
|
|
|
|
- );
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
|
|
|
|
- /// <returns>
|
|
|
|
- /// <see cref="ColorScheme.Focus"/> if <see cref="Enabled"/> is <see langword="true"/> or
|
|
|
|
- /// <see cref="ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>. If it's
|
|
|
|
- /// overridden can return other values.
|
|
|
|
- /// </returns>
|
|
|
|
- public virtual Attribute GetFocusColor ()
|
|
|
|
- {
|
|
|
|
- ColorScheme? cs = ColorScheme;
|
|
|
|
|
|
+ IEnumerable<View> subviewsNeedingDraw = _subviews.Where (view => view.Visible && (view.NeedsDisplay || view.SubViewNeedsDisplay));
|
|
|
|
|
|
- if (cs is null)
|
|
|
|
|
|
+ foreach (View view in subviewsNeedingDraw)
|
|
{
|
|
{
|
|
- cs = new ();
|
|
|
|
|
|
+ view.Draw ();
|
|
}
|
|
}
|
|
-
|
|
|
|
- return Enabled ? GetColor (cs.Focus) : cs.Disabled;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
|
|
|
|
- /// <returns>
|
|
|
|
- /// <see cref="ColorScheme.Focus"/> if <see cref="Enabled"/> is <see langword="true"/> or
|
|
|
|
- /// <see cref="ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>. If it's
|
|
|
|
- /// overridden can return other values.
|
|
|
|
- /// </returns>
|
|
|
|
- public virtual Attribute GetHotFocusColor ()
|
|
|
|
- {
|
|
|
|
- ColorScheme? cs = ColorScheme ?? new ();
|
|
|
|
-
|
|
|
|
- return Enabled ? GetColor (cs.HotFocus) : cs.Disabled;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
|
|
|
|
- /// <returns>
|
|
|
|
- /// <see cref="Terminal.Gui.ColorScheme.HotNormal"/> if <see cref="Enabled"/> is <see langword="true"/> or
|
|
|
|
- /// <see cref="Terminal.Gui.ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>. If it's
|
|
|
|
- /// overridden can return other values.
|
|
|
|
- /// </returns>
|
|
|
|
- public virtual Attribute GetHotNormalColor ()
|
|
|
|
- {
|
|
|
|
- ColorScheme? cs = ColorScheme;
|
|
|
|
-
|
|
|
|
- if (cs is null)
|
|
|
|
- {
|
|
|
|
- cs = new ();
|
|
|
|
- }
|
|
|
|
|
|
+ #endregion DrawSubviews
|
|
|
|
|
|
- return Enabled ? GetColor (cs.HotNormal) : cs.Disabled;
|
|
|
|
- }
|
|
|
|
|
|
+ #region DrawLineCanvas
|
|
|
|
|
|
- /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
|
|
|
|
- /// <returns>
|
|
|
|
- /// <see cref="Terminal.Gui.ColorScheme.Normal"/> if <see cref="Enabled"/> is <see langword="true"/> or
|
|
|
|
- /// <see cref="Terminal.Gui.ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>. If it's
|
|
|
|
- /// overridden can return other values.
|
|
|
|
- /// </returns>
|
|
|
|
- public virtual Attribute GetNormalColor ()
|
|
|
|
|
|
+ internal void DoRenderLineCanvas ()
|
|
{
|
|
{
|
|
- ColorScheme? cs = ColorScheme;
|
|
|
|
-
|
|
|
|
- if (cs is null)
|
|
|
|
|
|
+ if (OnRenderingLineCanvas ())
|
|
{
|
|
{
|
|
- cs = new ();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Attribute disabled = new (cs.Disabled.Foreground, cs.Disabled.Background);
|
|
|
|
-
|
|
|
|
- if (Diagnostics.HasFlag (ViewDiagnosticFlags.Hover) && _hovering)
|
|
|
|
- {
|
|
|
|
- disabled = new (disabled.Foreground.GetDarkerColor (), disabled.Background.GetDarkerColor ());
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
- return Enabled ? GetColor (cs.Normal) : disabled;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private Attribute GetColor (Attribute inputAttribute)
|
|
|
|
- {
|
|
|
|
- Attribute attr = inputAttribute;
|
|
|
|
-
|
|
|
|
- if (Diagnostics.HasFlag (ViewDiagnosticFlags.Hover) && _hovering)
|
|
|
|
- {
|
|
|
|
- attr = new (attr.Foreground.GetDarkerColor (), attr.Background.GetDarkerColor ());
|
|
|
|
- }
|
|
|
|
|
|
+ // TODO: Add event
|
|
|
|
|
|
- return attr;
|
|
|
|
|
|
+ RenderLineCanvas ();
|
|
}
|
|
}
|
|
|
|
|
|
- /// <summary>Moves the drawing cursor to the specified <see cref="Viewport"/>-relative location in the view.</summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// <para>
|
|
|
|
- /// If the provided coordinates are outside the visible content area, this method does nothing.
|
|
|
|
- /// </para>
|
|
|
|
- /// <para>
|
|
|
|
- /// The top-left corner of the visible content area is <c>ViewPort.Location</c>.
|
|
|
|
- /// </para>
|
|
|
|
- /// </remarks>
|
|
|
|
- /// <param name="col">Column (viewport-relative).</param>
|
|
|
|
- /// <param name="row">Row (viewport-relative).</param>
|
|
|
|
- public bool Move (int col, int row)
|
|
|
|
- {
|
|
|
|
- if (Driver is null || Driver?.Rows == 0)
|
|
|
|
- {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (col < 0 || row < 0 || col >= Viewport.Width || row >= Viewport.Height)
|
|
|
|
- {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Called when the <see cref="View.LineCanvas"/> is to be rendered. See <see cref="RenderLineCanvas"/>.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <returns><see langword="true"/> to stop further drawing of <see cref="LineCanvas"/>.</returns>
|
|
|
|
+ protected virtual bool OnRenderingLineCanvas () { return false; }
|
|
|
|
|
|
- Point screen = ViewportToScreen (new Point (col, row));
|
|
|
|
- Driver?.Move (screen.X, screen.Y);
|
|
|
|
|
|
+ /// <summary>The canvas that any line drawing that is to be shared by subviews of this view should add lines to.</summary>
|
|
|
|
+ /// <remarks><see cref="Border"/> adds border lines to this LineCanvas.</remarks>
|
|
|
|
+ public LineCanvas LineCanvas { get; } = new ();
|
|
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any
|
|
|
|
+ /// lines. If <see langword="true"/> the rendering of any borders drawn by this Frame will be done by its parent's
|
|
|
|
+ /// SuperView. If <see langword="false"/> (the default) this View's <see cref="OnDrawingAdornments"/> method will be
|
|
|
|
+ /// called to render the borders.
|
|
|
|
+ /// </summary>
|
|
|
|
+ public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
|
|
|
|
|
|
- internal void DoRenderLineCanvas ()
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Causes the contents of <see cref="LineCanvas"/> to be drawn.
|
|
|
|
+ /// If <see cref="SuperViewRendersLineCanvas"/> is true, only the
|
|
|
|
+ /// <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
|
|
|
|
+ /// false (the default), this method will cause the <see cref="LineCanvas"/> to be rendered.
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void RenderLineCanvas ()
|
|
{
|
|
{
|
|
- if (OnRenderLineCanvas ())
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // TODO: Add event
|
|
|
|
|
|
+ // TODO: This is super confusing and needs to be refactored.
|
|
|
|
|
|
if (Driver is null)
|
|
if (Driver is null)
|
|
{
|
|
{
|
|
@@ -765,18 +641,41 @@ public partial class View // Drawing APIs
|
|
LineCanvas.Clear ();
|
|
LineCanvas.Clear ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ #endregion DrawLineCanvas
|
|
|
|
|
|
- /// <summary>
|
|
|
|
- /// Renders <see cref="View.LineCanvas"/>. If <see cref="SuperViewRendersLineCanvas"/> is true, only the
|
|
|
|
- /// <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
|
|
|
|
- /// false (the default), this method will cause the <see cref="LineCanvas"/> to be rendered.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <returns></returns>
|
|
|
|
- protected virtual bool OnRenderLineCanvas ()
|
|
|
|
|
|
+ #region DrawComplete
|
|
|
|
+
|
|
|
|
+ private void DoDrawComplete (Rectangle viewport)
|
|
{
|
|
{
|
|
- return false;
|
|
|
|
|
|
+ Debug.Assert (viewport == Viewport);
|
|
|
|
+
|
|
|
|
+ if (OnDrawComplete (Viewport))
|
|
|
|
+ {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
|
|
|
|
+ DrawComplete?.Invoke (this, dev);
|
|
|
|
+
|
|
|
|
+ if (dev.Cancel)
|
|
|
|
+ { }
|
|
|
|
+
|
|
|
|
+ // Default implementation does nothing.
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Called when the View is completed drawing.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="viewport"></param>
|
|
|
|
+ protected virtual bool OnDrawComplete (Rectangle viewport) { return false; }
|
|
|
|
+
|
|
|
|
+ /// <summary>Raised when the View is completed drawing.</summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// </remarks>
|
|
|
|
+ public event EventHandler<DrawEventArgs>? DrawComplete;
|
|
|
|
+
|
|
|
|
+ #endregion DrawComplete
|
|
|
|
+
|
|
#region NeedsDisplay
|
|
#region NeedsDisplay
|
|
|
|
|
|
// TODO: Make _needsDisplayRect nullable instead of relying on Empty
|
|
// TODO: Make _needsDisplayRect nullable instead of relying on Empty
|
|
@@ -789,7 +688,7 @@ public partial class View // Drawing APIs
|
|
/// <summary>Gets or sets whether the view needs to be redrawn.</summary>
|
|
/// <summary>Gets or sets whether the view needs to be redrawn.</summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
- /// Will be <see langword="true"/> if the <see cref="NeedsLayout"/> property is <see langword="true"/> or if
|
|
|
|
|
|
+ /// Will be <see langword="true"/> if the <see cref="NeedsLayout"/> property is <see langword="true"/> or if
|
|
/// any part of the view's <see cref="Viewport"/> needs to be redrawn.
|
|
/// any part of the view's <see cref="Viewport"/> needs to be redrawn.
|
|
/// </para>
|
|
/// </para>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -916,4 +815,6 @@ public partial class View // Drawing APIs
|
|
}
|
|
}
|
|
|
|
|
|
#endregion NeedsDisplay
|
|
#endregion NeedsDisplay
|
|
|
|
+
|
|
|
|
+ #endregion Drawing Engine
|
|
}
|
|
}
|