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 */ } }