namespace Terminal.Gui.ViewBase;
public partial class View
{
// TODO: Change NeedsDraw to use a Region instead of Rectangle
// TODO: Make _needsDrawRect nullable instead of relying on Empty
// TODO: If null, it means ?
// TODO: If Empty, it means no need to redraw
// TODO: If not Empty, it means the region that needs to be redrawn
///
/// The viewport-relative region that needs to be redrawn. Marked internal for unit tests.
///
internal Rectangle NeedsDrawRect { get; set; } = Rectangle.Empty;
/// Gets or sets whether the view needs to be redrawn.
///
///
/// Will be if the property is or if
/// any part of the view's needs to be redrawn.
///
///
/// Setting has no effect on .
///
///
public bool NeedsDraw
{
get => Visible && (NeedsDrawRect != Rectangle.Empty || Margin?.NeedsDraw == true || Border?.NeedsDraw == true || Padding?.NeedsDraw == true);
set
{
if (value)
{
SetNeedsDraw ();
}
else
{
ClearNeedsDraw ();
}
}
}
/// Gets whether any SubViews need to be redrawn.
public bool SubViewNeedsDraw { get; private set; }
/// Sets that the of this View needs to be redrawn.
///
/// If the view has not been initialized ( is ), this method
/// does nothing.
///
public void SetNeedsDraw ()
{
Rectangle viewport = Viewport;
if (!Visible || (NeedsDrawRect != Rectangle.Empty && viewport.IsEmpty))
{
// This handles the case where the view has not been initialized yet
return;
}
SetNeedsDraw (viewport);
}
/// Expands the area of this view needing to be redrawn to include .
///
///
/// The location of is relative to the View's .
///
///
/// If the view has not been initialized ( is ), the area to be
/// redrawn will be the .
///
///
/// The relative region that needs to be redrawn.
public void SetNeedsDraw (Rectangle viewPortRelativeRegion)
{
if (!Visible)
{
return;
}
if (NeedsDrawRect.IsEmpty)
{
NeedsDrawRect = viewPortRelativeRegion;
}
else
{
int x = Math.Min (Viewport.X, viewPortRelativeRegion.X);
int y = Math.Min (Viewport.Y, viewPortRelativeRegion.Y);
int w = Math.Max (Viewport.Width, viewPortRelativeRegion.Width);
int h = Math.Max (Viewport.Height, viewPortRelativeRegion.Height);
NeedsDrawRect = new (x, y, w, h);
}
// Do not set on Margin - it will be drawn in a separate pass.
if (Border is { } && Border.Thickness != Thickness.Empty)
{
Border?.SetNeedsDraw ();
}
if (Padding is { } && Padding.Thickness != Thickness.Empty)
{
Padding?.SetNeedsDraw ();
}
SuperView?.SetSubViewNeedsDraw ();
if (this is Adornment adornment)
{
adornment.Parent?.SetSubViewNeedsDraw ();
}
// There was multiple enumeration error here, so calling new snapshot collection - probably a stop gap
foreach (View subview in InternalSubViews.Snapshot ())
{
if (subview.Frame.IntersectsWith (viewPortRelativeRegion))
{
Rectangle subviewRegion = Rectangle.Intersect (subview.Frame, viewPortRelativeRegion);
subviewRegion.X -= subview.Frame.X;
subviewRegion.Y -= subview.Frame.Y;
subview.SetNeedsDraw (subviewRegion);
}
}
}
/// Sets to for this View and all Superviews.
public void SetSubViewNeedsDraw ()
{
if (!Visible)
{
return;
}
SubViewNeedsDraw = true;
if (this is Adornment adornment)
{
adornment.Parent?.SetSubViewNeedsDraw ();
}
if (SuperView is { SubViewNeedsDraw: false })
{
SuperView.SetSubViewNeedsDraw ();
}
}
/// Clears and .
protected void ClearNeedsDraw ()
{
NeedsDrawRect = Rectangle.Empty;
Margin?.ClearNeedsDraw ();
Border?.ClearNeedsDraw ();
Padding?.ClearNeedsDraw ();
// There was multiple enumeration error here, so calling new snapshot collection - probably a stop gap
foreach (View subview in InternalSubViews.Snapshot ())
{
subview.ClearNeedsDraw ();
}
SubViewNeedsDraw = false;
if (SuperView is { })
{
SuperView.SubViewNeedsDraw = false;
}
// This ensures LineCanvas' get redrawn
if (!SuperViewRendersLineCanvas)
{
LineCanvas.Clear ();
}
}
}