namespace Terminal.Gui;
// TODO: v2 - Missing 3D effect - 3D effects will be drawn by a mechanism separate from Adornments
// TODO: v2 - If a Adornment has focus, navigation keys (e.g Command.NextView) should cycle through SubViews of the Adornments
// QUESTION: How does a user navigate out of an Adornment to another Adornment, or back into the Parent's SubViews?
///
/// Adornments are a special form of that appear outside of the :
/// , , and . They are defined using the
/// class, which specifies the thickness of the sides of a rectangle.
///
///
///
/// There is no prevision for creating additional subclasses of Adornment. It is not abstract to enable unit
/// testing.
///
/// Each of , , and can be customized.
///
public class Adornment : View
{
private Thickness _thickness = Thickness.Empty;
///
public Adornment ()
{
/* Do nothing; A parameter-less constructor is required to support all views unit tests. */
}
/// Constructs a new adornment for the view specified by .
///
public Adornment (View parent) { Parent = parent; }
/// Gets the rectangle that describes the inner area of the Adornment. The Location is always (0,0).
public override Rectangle Bounds
{
get => Thickness?.GetInside (new Rectangle (Point.Empty, Frame.Size)) ?? new Rectangle (Point.Empty, Frame.Size);
set => throw new InvalidOperationException ("It makes no sense to set Bounds of a Thickness.");
}
/// The Parent of this Adornment (the View this Adornment surrounds).
///
/// Adornments are distinguished from typical View classes in that they are not sub-views, but have a parent/child
/// relationship with their containing View.
///
public View Parent { get; set; }
///
/// Adornments cannot be used as sub-views (see ); this method always throws an
/// . TODO: Are we sure?
///
public override View SuperView
{
get => null;
set => throw new NotImplementedException ();
}
///
/// Adornments only render to their 's or Parent's SuperView's LineCanvas, so setting this
/// property throws an .
///
public override bool SuperViewRendersLineCanvas
{
get => false; // throw new NotImplementedException ();
set => throw new NotImplementedException ();
}
/// Defines the rectangle that the will use to draw its content.
public Thickness Thickness
{
get => _thickness;
set
{
Thickness prev = _thickness;
_thickness = value;
if (prev != _thickness)
{
Parent?.LayoutAdornments ();
OnThicknessChanged (prev);
}
}
}
///
public override void BoundsToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
{
// Adornments are *Children* of a View, not SubViews. Thus View.BoundsToScreen will not work.
// To get the screen-relative coordinates of a Adornment, we need to know who
// the Parent is
Rectangle parentFrame = Parent?.Frame ?? Frame;
rrow = row + parentFrame.Y;
rcol = col + parentFrame.X;
// We now have rcol/rrow in coordinates relative to our View's SuperView. If our View's SuperView has
// a SuperView, keep going...
Parent?.SuperView?.BoundsToScreen (rcol, rrow, out rcol, out rrow, clipped);
}
///
public override Rectangle FrameToScreen ()
{
// Adornments are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work.
// To get the screen-relative coordinates of a Adornment, we need to know who
// the Parent is
Rectangle ret = Parent?.Frame ?? Frame;
ret.Size = Frame.Size;
ret.Location = Parent?.FrameToScreen ().Location ?? ret.Location;
// We now have coordinates relative to our View. If our View's SuperView has
// a SuperView, keep going...
return ret;
}
/// Does nothing for Adornment
///
public override bool OnDrawAdornments () { return false; }
/// Redraws the Adornments that comprise the .
public override void OnDrawContent (Rectangle contentArea)
{
if (Thickness == Thickness.Empty)
{
return;
}
Rectangle screenBounds = BoundsToScreen (Frame);
Attribute normalAttr = GetNormalColor ();
// This just draws/clears the thickness, not the insides.
Driver.SetAttribute (normalAttr);
Thickness.Draw (screenBounds, (string)(Data ?? string.Empty));
if (!string.IsNullOrEmpty (TextFormatter.Text))
{
if (TextFormatter is { })
{
TextFormatter.Size = Frame.Size;
TextFormatter.NeedsFormat = true;
}
}
TextFormatter?.Draw (screenBounds, normalAttr, normalAttr, Rectangle.Empty);
//base.OnDrawContent (contentArea);
}
/// Does nothing for Adornment
///
public override bool OnRenderLineCanvas () { return false; }
/// Called whenever the property changes.
public virtual void OnThicknessChanged (Thickness previousThickness)
{
ThicknessChanged?.Invoke (
this,
new ThicknessEventArgs { Thickness = Thickness, PreviousThickness = previousThickness }
);
}
/// Fired whenever the property changes.
public event EventHandler ThicknessChanged;
internal override Adornment CreateAdornment (Type adornmentType)
{
/* Do nothing - Adornments do not have Adornments */
return null;
}
internal override void LayoutAdornments ()
{
/* Do nothing - Adornments do not have Adornments */
}
}