#nullable enable using Terminal.Gui; using Attribute = Terminal.Gui.Attribute; /// /// Adornments are a special form of that appear outside the : /// , , and . They are defined using the /// class, which specifies the thickness of the sides of a rectangle. /// /// /// /// Each of , , and has slightly different /// behavior relative to , , keyboard input, and /// mouse input. Each can be customized by manipulating their Subviews. /// /// public class Adornment : View { /// 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) { CanFocus = true; Parent = parent; } /// 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; } #region Thickness private Thickness _thickness = Thickness.Empty; /// Defines the rectangle that the will use to draw its content. public Thickness Thickness { get => _thickness; set { Thickness current = _thickness; _thickness = value; if (current != _thickness) { if (Parent?.IsInitialized == false) { // When initialized Parent.LayoutSubViews will cause a LayoutAdornments Parent?.LayoutAdornments (); } else { Parent?.SetNeedsLayout (); Parent?.LayoutSubviews (); } OnThicknessChanged (); } } } /// Fired whenever the property changes. [CanBeNull] public event EventHandler? ThicknessChanged; /// Called whenever the property changes. public void OnThicknessChanged () { ThicknessChanged?.Invoke (this, EventArgs.Empty); } #endregion Thickness #region View Overrides /// /// Adornments cannot be used as sub-views (see ); setting this property will throw /// . /// public override View SuperView { get => null!; set => throw new InvalidOperationException (@"Adornments can not be Subviews or have SuperViews. Use Parent instead."); } //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 */ } /// /// Gets the rectangle that describes the area of the Adornment. The Location is always (0,0). /// The size is the size of the . /// /// /// The Viewport of an Adornment cannot be modified. Attempting to set this property will throw an /// . /// public override Rectangle Viewport { get => Frame with { Location = Point.Empty }; set => throw new InvalidOperationException (@"The Viewport of an Adornment cannot be modified."); } /// public override Rectangle FrameToScreen () { if (Parent is null) { return Frame; } // Adornments are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work. // To get the screen-relative coordinates of an Adornment, we need get the parent's Frame // in screen coords, ... Rectangle parentScreen = Parent.FrameToScreen (); // ...and add our Frame location to it. return new (new (parentScreen.X + Frame.X, parentScreen.Y + Frame.Y), Frame.Size); } /// public override Point ScreenToFrame (in Point location) { return Parent!.ScreenToFrame (new (location.X - Frame.X, location.Y - Frame.Y)); } /// Does nothing for Adornment /// public override bool OnDrawAdornments () { return false; } /// Redraws the Adornments that comprise the . public override void OnDrawContent (Rectangle viewport) { if (Thickness == Thickness.Empty) { return; } Rectangle prevClip = SetClip (); Rectangle screen = ViewportToScreen (viewport); Attribute normalAttr = GetNormalColor (); Driver.SetAttribute (normalAttr); // This just draws/clears the thickness, not the insides. Thickness.Draw (screen, ToString ()); if (!string.IsNullOrEmpty (TextFormatter.Text)) { if (TextFormatter is { }) { TextFormatter.ConstrainToSize = Frame.Size; TextFormatter.NeedsFormat = true; } } TextFormatter?.Draw (screen, normalAttr, normalAttr, Rectangle.Empty); if (Subviews.Count > 0) { base.OnDrawContent (viewport); } if (Driver is { }) { Driver.Clip = prevClip; } ClearLayoutNeeded (); ClearNeedsDisplay (); } /// Does nothing for Adornment /// public override bool OnRenderLineCanvas () { return false; } /// /// Adornments only render to their 's or Parent's SuperView's LineCanvas, so setting this /// property throws an . /// public override bool SuperViewRendersLineCanvas { get => false; set => throw new InvalidOperationException (@"Adornment can only render to their Parent or Parent's Superview."); } #endregion View Overrides #region Mouse Support /// /// Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness. /// /// /// The is relative to the PARENT's SuperView. /// /// /// if the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness. public override bool Contains (in Point location) { if (Parent is null) { return false; } Rectangle frame = Frame; frame.Offset (Parent.Frame.Location); return Thickness.Contains (frame, location); } /// protected internal override bool? OnMouseEnter (MouseEvent mouseEvent) { // Invert Normal if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null) { var cs = new ColorScheme (ColorScheme) { Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground) }; ColorScheme = cs; } return base.OnMouseEnter (mouseEvent); } /// protected internal override bool OnMouseLeave (MouseEvent mouseEvent) { // Invert Normal if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null) { var cs = new ColorScheme (ColorScheme) { Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground) }; ColorScheme = cs; } return base.OnMouseLeave (mouseEvent); } #endregion Mouse Support }