diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs index adb9b63c0..40c4b5702 100644 --- a/Terminal.Gui/Application/Application.Run.cs +++ b/Terminal.Gui/Application/Application.Run.cs @@ -537,14 +537,23 @@ public static partial class Application // Run (Begin, Run, End, Stop) // TODO: Rename this to DrawRunnables in https://github.com/gui-cs/Terminal.Gui/issues/2491 private static void DrawToplevels (bool forceDraw) { - foreach (Toplevel tl in TopLevels.Reverse ()) + // Reset the clip region to the entire screen + if (Driver is { }) + { + Driver.Clip = new (Screen); + } + + foreach (Toplevel tl in TopLevels) { if (forceDraw) { tl.SetNeedsDraw (); } + //Region? saved = Driver?.Clip.Clone (); tl.Draw (); + //Driver.Clip = saved; + Driver?.Clip.Exclude (tl.FrameToScreen ()); } } diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index d86a5f147..a778bc8d1 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -392,6 +392,10 @@ public abstract class ConsoleDriver { for (int c = rect.X; c < rect.X + rect.Width; c++) { + if (!IsValidLocation (c, r)) + { + continue; + } Contents [r, c] = new Cell { Rune = (rune != default ? rune : (Rune)' '), diff --git a/Terminal.Gui/Drawing/Region.cs b/Terminal.Gui/Drawing/Region.cs index a1f6c889f..528f2bc72 100644 --- a/Terminal.Gui/Drawing/Region.cs +++ b/Terminal.Gui/Drawing/Region.cs @@ -15,7 +15,7 @@ public class Region : IDisposable /// Initializes a new instance of the class with the specified rectangle. /// /// The initial rectangle for the region. - public Region (Rectangle rectangle) { _rectangles = new() { rectangle }; } + public Region (Rectangle rectangle) { _rectangles = new () { rectangle }; } /// /// Adds the specified rectangle to the region. @@ -169,6 +169,20 @@ public class Region : IDisposable /// An array of objects that make up the region. public Rectangle [] GetRegionScans () { return _rectangles.ToArray (); } + /// + /// Offsets all rectangles in the region by the specified amounts. + /// + /// The amount to offset along the x-axis. + /// The amount to offset along the y-axis. + public void Offset (int offsetX, int offsetY) + { + for (int i = 0; i < _rectangles.Count; i++) + { + var rect = _rectangles [i]; + _rectangles [i] = new Rectangle (rect.Left + offsetX, rect.Top + offsetY, rect.Width, rect.Height); + } + } + /// /// Merges overlapping rectangles into a minimal set of non-overlapping rectangles. /// diff --git a/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs b/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs index 84923fd98..2abe2091d 100644 --- a/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs +++ b/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs @@ -15,7 +15,7 @@ public abstract partial class PopupAutocomplete private readonly PopupAutocomplete _autoComplete; - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (!_autoComplete.LastPopupPos.HasValue) { diff --git a/Terminal.Gui/View/Adornment/Adornment.cs b/Terminal.Gui/View/Adornment/Adornment.cs index ec79986a1..2f3b5fdd4 100644 --- a/Terminal.Gui/View/Adornment/Adornment.cs +++ b/Terminal.Gui/View/Adornment/Adornment.cs @@ -162,7 +162,7 @@ public class Adornment : View, IDesignable /// /// /// to stop further clearing. - protected override bool OnClearingViewport (Rectangle viewport) + protected override bool OnClearingViewport () { if (Thickness == Thickness.Empty) { @@ -173,18 +173,21 @@ public class Adornment : View, IDesignable SetAttribute (normalAttr); // This just draws/clears the thickness, not the insides. - Thickness.Draw (ViewportToScreen (viewport), Diagnostics, ToString ()); + Thickness.Draw (ViewportToScreen (Viewport), Diagnostics, ToString ()); + + //NeedsDraw = true; return true; } /// - protected override bool OnDrawingText (Rectangle viewport) { return Thickness == Thickness.Empty; } + protected override bool OnDrawingText () { return Thickness == Thickness.Empty; } /// - protected override bool OnDrawingSubviews (Rectangle viewport) { return Thickness == Thickness.Empty; } + protected override bool OnDrawingSubviews () { return Thickness == Thickness.Empty; } /// Does nothing for Adornment + /// /// protected override bool OnRenderingLineCanvas () { return true; } diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs index af38114d6..5db617015 100644 --- a/Terminal.Gui/View/Adornment/Border.cs +++ b/Terminal.Gui/View/Adornment/Border.cs @@ -107,13 +107,13 @@ public class Border : Adornment #endif if (View.Diagnostics.HasFlag (ViewDiagnosticFlags.DrawIndicator)) { - _drawIndicator = new SpinnerView () + DrawIndicator = new SpinnerView () { X = 1, Style = new SpinnerStyle.Dots2 (), SpinDelay = 0, }; - Add (_drawIndicator); + Add (DrawIndicator); } } @@ -613,14 +613,14 @@ public class Border : Adornment #endregion Mouse Support /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (Thickness == Thickness.Empty) { return true; } - Rectangle screenBounds = ViewportToScreen (viewport); + Rectangle screenBounds = ViewportToScreen (Viewport); // TODO: v2 - this will eventually be two controls: "BorderView" and "Label" (for the title) @@ -914,17 +914,19 @@ public class Border : Adornment } } - return true; + return true; ; } - private SpinnerView? _drawIndicator = null; + public SpinnerView? DrawIndicator { get; private set; } = null; + + /// /// protected override bool OnRenderingLineCanvas () { - if (_drawIndicator is { }) + if (DrawIndicator is { }) { - _drawIndicator.AdvanceAnimation (false); - _drawIndicator.DrawText(); + //DrawIndicator.AdvanceAnimation (false); + //DrawIndicator.DrawText(); } RenderLineCanvas (); diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs index 053f555b7..c325462f0 100644 --- a/Terminal.Gui/View/Adornment/Margin.cs +++ b/Terminal.Gui/View/Adornment/Margin.cs @@ -68,14 +68,14 @@ public class Margin : Adornment } /// - protected override bool OnClearingViewport (Rectangle viewport) + protected override bool OnClearingViewport () { if (Thickness == Thickness.Empty) { return true; } - Rectangle screen = ViewportToScreen (viewport); + Rectangle screen = ViewportToScreen (Viewport); if (ShadowStyle != ShadowStyle.None) { diff --git a/Terminal.Gui/View/Adornment/ShadowView.cs b/Terminal.Gui/View/Adornment/ShadowView.cs index f789c5b92..c92bed497 100644 --- a/Terminal.Gui/View/Adornment/ShadowView.cs +++ b/Terminal.Gui/View/Adornment/ShadowView.cs @@ -37,25 +37,25 @@ internal class ShadowView : View } /// - protected override bool OnClearingViewport (Rectangle viewport) + protected override bool OnClearingViewport () { // Prevent clearing (so we can have transparency) return true; } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { switch (ShadowStyle) { case ShadowStyle.Opaque: if (Orientation == Orientation.Vertical) { - DrawVerticalShadowOpaque (viewport); + DrawVerticalShadowOpaque (Viewport); } else { - DrawHorizontalShadowOpaque (viewport); + DrawHorizontalShadowOpaque (Viewport); } break; @@ -67,11 +67,11 @@ internal class ShadowView : View if (Orientation == Orientation.Vertical) { - DrawVerticalShadowTransparent (viewport); + DrawVerticalShadowTransparent (Viewport); } else { - DrawHorizontalShadowTransparent (viewport); + DrawHorizontalShadowTransparent (Viewport); } //SetAttribute (prevAttr); @@ -114,10 +114,9 @@ internal class ShadowView : View private void DrawHorizontalShadowTransparent (Rectangle viewport) { - Rectangle screen = ViewportToScreen (viewport); + Rectangle screen = ViewportToScreen (Viewport); - // Fill the rest of the rectangle - note we skip the last since vertical will draw it - for (int i = Math.Max(0, screen.X + 1); i < screen.X + screen.Width - 1; i++) + for (int i = Math.Max(0, screen.X + 1); i < screen.X + screen.Width; i++) { Driver?.Move (i, screen.Y); @@ -134,7 +133,7 @@ internal class ShadowView : View AddRune (0, 0, Glyphs.ShadowVerticalStart); // Fill the rest of the rectangle with the glyph - for (var i = 1; i < viewport.Height; i++) + for (var i = 1; i < viewport.Height - 1; i++) { AddRune (0, i, Glyphs.ShadowVertical); } @@ -142,7 +141,7 @@ internal class ShadowView : View private void DrawVerticalShadowTransparent (Rectangle viewport) { - Rectangle screen = ViewportToScreen (viewport); + Rectangle screen = ViewportToScreen (Viewport); // Fill the rest of the rectangle for (int i = Math.Max (0, screen.Y); i < screen.Y + viewport.Height; i++) diff --git a/Terminal.Gui/View/View.Drawing.Clipping.cs b/Terminal.Gui/View/View.Drawing.Clipping.cs index cb9c4c54b..76ef8260f 100644 --- a/Terminal.Gui/View/View.Drawing.Clipping.cs +++ b/Terminal.Gui/View/View.Drawing.Clipping.cs @@ -3,6 +3,32 @@ namespace Terminal.Gui; public partial class View { + internal Region? SetClipToFrame () + { + if (Driver is null) + { + return null; + } + + Region previous = Driver.Clip ?? new (Application.Screen); + + Region frameRegion = Driver.Clip!.Clone (); + // Translate viewportRegion to screen-relative coords + Rectangle screenRect = FrameToScreen (); + frameRegion.Intersect (screenRect); + + if (this is Adornment adornment && adornment.Thickness != Thickness.Empty) + { + // Ensure adornments can't draw outside thier thickness + frameRegion.Exclude (adornment.Thickness.GetInside (Frame)); + } + + Driver.Clip = frameRegion; + + return previous; + + } + /// Sets the 's clip region to . /// /// @@ -19,7 +45,7 @@ public partial class View /// The current screen-relative clip region, which can be then re-applied by setting /// . /// - public Region? SetClip () + public Region? SetClipToViewport () { if (Driver is null) { @@ -28,19 +54,50 @@ public partial class View Region previous = Driver.Clip ?? new (Application.Screen); - // Clamp the Clip to the entire visible area - Rectangle clip = Rectangle.Intersect (ViewportToScreen (Viewport with { Location = Point.Empty }), previous.GetBounds()); + Region viewportRegion = Driver.Clip!.Clone (); + + Rectangle viewport = ViewportToScreen (new Rectangle (Point.Empty, Viewport.Size)); + viewportRegion?.Intersect (viewport); if (ViewportSettings.HasFlag (ViewportSettings.ClipContentOnly)) { // Clamp the Clip to the just content area that is within the viewport Rectangle visibleContent = ViewportToScreen (new Rectangle (new (-Viewport.X, -Viewport.Y), GetContentSize ())); - clip = Rectangle.Intersect (clip, visibleContent); + viewportRegion?.Intersect (visibleContent); } - Driver.Clip = new (clip);// !.Complement(clip); + if (this is Adornment adornment && adornment.Thickness != Thickness.Empty) + { + // Ensure adornments can't draw outside their thickness + viewportRegion?.Exclude (adornment.Thickness.GetInside (viewport)); + } + + Driver.Clip = viewportRegion; return previous; } + /// Gets the view-relative clip region. + public Region? GetClip () + { + // get just the portion of the application clip that is within this view's Viewport + if (Driver is null) + { + return null; + } + + // Get our Viewport in screen coordinates + Rectangle screen = ViewportToScreen (Viewport with { Location = Point.Empty }); + + // Get the clip region in screen coordinates + Region? clip = Driver.Clip; + if (clip is null) + { + return null; + } + Region? previous = Driver.Clip; + clip = clip.Clone (); + clip.Intersect (screen); + return clip; + } } diff --git a/Terminal.Gui/View/View.Drawing.Primitives.cs b/Terminal.Gui/View/View.Drawing.Primitives.cs index 4f8ed4c45..497fd5489 100644 --- a/Terminal.Gui/View/View.Drawing.Primitives.cs +++ b/Terminal.Gui/View/View.Drawing.Primitives.cs @@ -116,7 +116,7 @@ public partial class View return; } - Region prevClip = SetClip (); + Region prevClip = SetClipToViewport (); Rectangle toClear = ViewportToScreen (rect); Attribute prev = SetAttribute (new (color ?? GetNormalColor ().Background)); Driver.FillRect (toClear); diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs index 4ce67fcf3..121c0372f 100644 --- a/Terminal.Gui/View/View.Drawing.cs +++ b/Terminal.Gui/View/View.Drawing.cs @@ -1,5 +1,5 @@ #nullable enable -#define HACK_DRAW_OVERLAPPED +//#define HACK_DRAW_OVERLAPPED using System.ComponentModel; using System.Diagnostics; @@ -24,36 +24,71 @@ public partial class View // Drawing APIs { if (!CanBeVisible (this) || (!NeedsDraw && !SubViewNeedsDraw)) { + if (this is not Adornment) + { + Driver?.Clip.Exclude (FrameToScreen ()); + } + return; } + if (Border is { Diagnostics: ViewDiagnosticFlags.DrawIndicator }) + { + if (Border.DrawIndicator is { }) + { + Border.DrawIndicator.AdvanceAnimation (false); + Border.DrawIndicator.DrawText (); + + } + } + + // Frame/View-relative relative, thus the bounds location should be 0,0 + //Debug.Assert(clipRegion.GetBounds().X == 0 && clipRegion.GetBounds ().Y == 0); + + Region? saved = SetClipToFrame (); DoDrawAdornments (); DoSetAttribute (); - + if (saved is { }) + { + Driver!.Clip = saved; + } // By default, we clip to the viewport preventing drawing outside the viewport // We also clip to the content, but if a developer wants to draw outside the viewport, they can do // so via settings. SetClip honors the ViewportSettings.DisableVisibleContentClipping flag. - Region prevClip = SetClip (); + // Get our Viewport in screen coordinates - DoClearViewport (Viewport); - DoDrawText (Viewport); - DoDrawContent (Viewport); + saved = SetClipToViewport (); - DoDrawSubviews (Viewport); + DoClearViewport (); + DoDrawText (); + DoDrawContent (); + + DoDrawSubviews (); // Restore the clip before rendering the line canvas and adornment subviews // because they may draw outside the viewport. - if (Driver is { }) + if (saved is { }) { - Driver.Clip = prevClip; + Driver!.Clip = saved; } + saved = SetClipToFrame (); DoRenderLineCanvas (); DoDrawAdornmentSubViews (); ClearNeedsDraw (); // We're done DoDrawComplete (); + if (saved is { }) + { + Driver!.Clip = saved; + } + + if (this is not Adornment) + { + Driver?.Clip.Exclude (FrameToScreen ()); + } + } #region DrawAdornments @@ -62,34 +97,45 @@ public partial class View // Drawing APIs { // This causes the Adornment's subviews to be REDRAWN // TODO: Figure out how to make this more efficient - if (Margin?.Subviews is { }) + if (Margin?.Subviews is { } && Margin.Thickness != Thickness.Empty) { foreach (View subview in Margin.Subviews) { subview.SetNeedsDraw (); } - Margin?.DoDrawSubviews (Margin.Viewport); + Region? saved = Margin?.SetClipToFrame (); + Margin?.DoDrawSubviews (); + if (saved is { }) + { + Driver!.Clip = saved; + } } - if (Border?.Subviews is { }) + if (Border?.Subviews is { } && Border.Thickness != Thickness.Empty) { foreach (View subview in Border.Subviews) { subview.SetNeedsDraw (); } - Border?.DoDrawSubviews (Border.Viewport); + Border?.DoDrawSubviews (); } - if (Padding?.Subviews is { }) + if (Padding?.Subviews is { } && Padding.Thickness != Thickness.Empty) { foreach (View subview in Padding.Subviews) { subview.SetNeedsDraw (); } - Padding?.DoDrawSubviews (Padding.Viewport); + Region? saved = Padding?.SetClipToFrame (); + Padding?.DoDrawSubviews (); + if (saved is { }) + { + Driver!.Clip = saved; + } + } } @@ -102,12 +148,6 @@ public partial class View // Drawing APIs // TODO: add event. - // Subviews of Adornments count as subviews - if (!NeedsDraw && !SubViewNeedsDraw) - { - return; - } - DrawAdornments (); } @@ -118,9 +158,20 @@ public partial class View // Drawing APIs { // Each of these renders lines to either this View's LineCanvas // Those lines will be finally rendered in OnRenderLineCanvas - Margin?.Draw (); - Border?.Draw (); - Padding?.Draw (); + if (Margin is { } && Margin.Thickness != Thickness.Empty) + { + Margin?.Draw (); + } + + if (Border is { } && Border.Thickness != Thickness.Empty) + { + Border?.Draw (); + } + + if (Padding is { } && Padding.Thickness != Thickness.Empty) + { + Padding?.Draw (); + } } /// @@ -129,6 +180,7 @@ public partial class View // Drawing APIs /// of this view's subviews will be rendered. If is /// false (the default), this method will cause the be prepared to be rendered. /// + /// /// to stop further drawing of the Adornments. protected virtual bool OnDrawingAdornments () { return false; } @@ -151,11 +203,6 @@ public partial class View // Drawing APIs return; } - if (!NeedsDraw && !SubViewNeedsDraw) - { - return; - } - SetNormalAttribute (); } @@ -187,11 +234,10 @@ public partial class View // Drawing APIs #endregion #region ClearViewport - private void DoClearViewport (Rectangle viewport) + private void DoClearViewport () { - Debug.Assert (viewport == Viewport); - if (OnClearingViewport (Viewport)) + if (OnClearingViewport ()) { return; } @@ -215,9 +261,8 @@ public partial class View // Drawing APIs /// /// Called when the is to be cleared. /// - /// /// to stop further clearing. - protected virtual bool OnClearingViewport (Rectangle viewport) { return false; } + protected virtual bool OnClearingViewport () { return false; } /// Event invoked when the content area of the View is to be drawn. /// @@ -267,11 +312,10 @@ public partial class View // Drawing APIs #region DrawText - private void DoDrawText (Rectangle viewport) + private void DoDrawText () { - Debug.Assert (viewport == Viewport); - if (OnDrawingText (Viewport)) + if (OnDrawingText ()) { return; } @@ -295,9 +339,8 @@ public partial class View // Drawing APIs /// /// Called when the of the View is to be drawn. /// - /// /// to stop further drawing of . - protected virtual bool OnDrawingText (Rectangle viewport) { return false; } + protected virtual bool OnDrawingText () { return false; } /// Raised when the of the View is to be drawn. /// @@ -335,11 +378,9 @@ public partial class View // Drawing APIs #region DrawContent - private void DoDrawContent (Rectangle viewport) + private void DoDrawContent () { - Debug.Assert (viewport == Viewport); - - if (OnDrawingContent (Viewport)) + if (OnDrawingContent ()) { return; } @@ -359,7 +400,7 @@ public partial class View // Drawing APIs /// /// /// to stop further drawing content. - protected virtual bool OnDrawingContent (Rectangle viewport) { return false; } + protected virtual bool OnDrawingContent () { return false; } /// Raised when the View's content is to be drawn. /// @@ -375,11 +416,9 @@ public partial class View // Drawing APIs #region DrawSubviews - private void DoDrawSubviews (Rectangle viewport) + private void DoDrawSubviews () { - Debug.Assert (viewport == Viewport); - - if (OnDrawingSubviews (Viewport)) + if (OnDrawingSubviews ()) { return; } @@ -403,9 +442,8 @@ public partial class View // Drawing APIs /// /// Called when the are to be drawn. /// - /// /// to stop further drawing of . - protected virtual bool OnDrawingSubviews (Rectangle viewport) { return false; } + protected virtual bool OnDrawingSubviews () { return false; } /// Raised when the are to be drawn. /// @@ -421,7 +459,7 @@ public partial class View // Drawing APIs /// public void DrawSubviews () { - if (_subviews is null || !SubViewNeedsDraw) + if (_subviews is null) { return; } @@ -430,10 +468,10 @@ public partial class View // Drawing APIs IEnumerable subviewsNeedingDraw = _subviews.Where (view => (view.Visible && (view.NeedsDraw || view.SubViewNeedsDraw)) || view.Arrangement.HasFlag (ViewArrangement.Overlapped)); #else - IEnumerable subviewsNeedingDraw = _subviews.Where (view => (view.Visible && (view.NeedsDraw || view.SubViewNeedsDraw))); + IEnumerable subviewsNeedingDraw = _subviews.Where (view => (view.Visible)); #endif - foreach (View view in subviewsNeedingDraw) + foreach (View view in subviewsNeedingDraw.Reverse()) { #if HACK_DRAW_OVERLAPPED if (view.Arrangement.HasFlag (ViewArrangement.Overlapped)) @@ -443,8 +481,8 @@ public partial class View // Drawing APIs } #endif view.Draw (); - } + } #endregion DrawSubviews @@ -466,6 +504,7 @@ public partial class View // Drawing APIs /// /// Called when the is to be rendered. See . /// + /// /// to stop further drawing of . protected virtual bool OnRenderingLineCanvas () { return false; } @@ -548,7 +587,7 @@ public partial class View // Drawing APIs { OnDrawComplete (); - DrawComplete?.Invoke (this, EventArgs.Empty); + DrawComplete?.Invoke (this, new (Viewport, Viewport)); // Default implementation does nothing. } @@ -561,7 +600,7 @@ public partial class View // Drawing APIs /// Raised when the View is completed drawing. /// /// - public event EventHandler? DrawComplete; + public event EventHandler? DrawComplete; #endregion DrawComplete @@ -648,9 +687,20 @@ public partial class View // Drawing APIs _needsDrawRect = new (x, y, w, h); } - Margin?.SetNeedsDraw (); - Border?.SetNeedsDraw (); - Padding?.SetNeedsDraw (); + if (Margin is { } && Margin.Thickness != Thickness.Empty) + { + Margin?.SetNeedsDraw (); + } + + if (Border is { } && Border.Thickness != Thickness.Empty) + { + Border?.SetNeedsDraw (); + } + + if (Padding is { } && Padding.Thickness != Thickness.Empty) + { + Padding?.SetNeedsDraw (); + } SuperView?.SetSubViewNeedsDraw (); @@ -693,10 +743,21 @@ public partial class View // Drawing APIs _needsDrawRect = Rectangle.Empty; SubViewNeedsDraw = false; - Margin?.ClearNeedsDraw (); - Border?.ClearNeedsDraw (); - Padding?.ClearNeedsDraw (); + if (Margin is { } && Margin.Thickness != Thickness.Empty) + { + Margin?.ClearNeedsDraw (); + } + + if (Border is { } && Border.Thickness != Thickness.Empty) + { + Border?.ClearNeedsDraw (); + } + + if (Padding is { } && Padding.Thickness != Thickness.Empty) + { + Padding?.ClearNeedsDraw (); + } foreach (View subview in Subviews) { subview.ClearNeedsDraw (); diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs index 0f5cb0239..c39c0a21f 100644 --- a/Terminal.Gui/View/View.Layout.cs +++ b/Terminal.Gui/View/View.Layout.cs @@ -118,7 +118,7 @@ public partial class View // Layout APIs // Now add our Frame location parentScreen.Offset (screen.X, screen.Y); - return parentScreen; + return parentScreen with { Size = Frame.Size }; } Point viewportOffset = current.GetViewportOffsetFromFrame (); @@ -732,6 +732,14 @@ public partial class View // Layout APIs SuperView?.SetNeedsLayout (); } + if (SuperView is null) + { + foreach (var tl in Application.TopLevels) + { + tl.SetNeedsDraw (); + } + } + if (this is not Adornment adornment) { return; diff --git a/Terminal.Gui/Views/ColorBar.cs b/Terminal.Gui/Views/ColorBar.cs index 14d8b25b3..824b49958 100644 --- a/Terminal.Gui/Views/ColorBar.cs +++ b/Terminal.Gui/Views/ColorBar.cs @@ -82,7 +82,7 @@ internal abstract class ColorBar : View, IColorBar } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { var xOffset = 0; @@ -96,7 +96,7 @@ internal abstract class ColorBar : View, IColorBar xOffset = Text.EnumerateRunes ().Sum (c => c.GetColumns ()); } - _barWidth = viewport.Width - xOffset; + _barWidth = Viewport.Width - xOffset; _barStartsAt = xOffset; DrawBar (xOffset, 0, _barWidth); diff --git a/Terminal.Gui/Views/ColorPicker.16.cs b/Terminal.Gui/Views/ColorPicker.16.cs index 91d447578..ab85ceedb 100644 --- a/Terminal.Gui/Views/ColorPicker.16.cs +++ b/Terminal.Gui/Views/ColorPicker.16.cs @@ -131,16 +131,14 @@ public class ColorPicker16 : View } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { - base.OnDrawingContent (viewport); - SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ()); var colorIndex = 0; - for (var y = 0; y < Math.Max (2, viewport.Height / BoxHeight); y++) + for (var y = 0; y < Math.Max (2, Viewport.Height / BoxHeight); y++) { - for (var x = 0; x < Math.Max (8, viewport.Width / BoxWidth); x++) + for (var x = 0; x < Math.Max (8, Viewport.Width / BoxWidth); x++) { int foregroundColorIndex = y == 0 ? colorIndex + _cols : colorIndex - _cols; diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs index 4a256fc0e..415fc1acd 100644 --- a/Terminal.Gui/Views/ColorPicker.cs +++ b/Terminal.Gui/Views/ColorPicker.cs @@ -99,7 +99,7 @@ public partial class ColorPicker : View public event EventHandler? ColorChanged; /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { Attribute normal = GetNormalColor (); SetAttribute (new (SelectedColor, normal.Background)); diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index f0a6bfcc7..ca3a76406 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -294,7 +294,7 @@ public class ComboBox : View, IDesignable public virtual void OnCollapsed () { Collapsed?.Invoke (this, EventArgs.Empty); } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (!_autoHide) @@ -889,7 +889,7 @@ public class ComboBox : View, IDesignable return res; } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { Attribute current = ColorScheme?.Focus ?? Attribute.Default; SetAttribute (current); diff --git a/Terminal.Gui/Views/DatePicker.cs b/Terminal.Gui/Views/DatePicker.cs index cc1be2e77..2962464ec 100644 --- a/Terminal.Gui/Views/DatePicker.cs +++ b/Terminal.Gui/Views/DatePicker.cs @@ -288,7 +288,7 @@ public class DatePicker : View } /// - protected override bool OnDrawingText (Rectangle viewport) { return true; } + protected override bool OnDrawingText () { return true; } private static string StandardizeDateFormat (string format) { diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index 019d74a04..b80dcacab 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialog.cs @@ -382,7 +382,7 @@ public class FileDialog : Dialog, IDesignable } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (!string.IsNullOrWhiteSpace (_feedback)) { diff --git a/Terminal.Gui/Views/GraphView/Annotations.cs b/Terminal.Gui/Views/GraphView/Annotations.cs index 3f1256fa7..adfa602a7 100644 --- a/Terminal.Gui/Views/GraphView/Annotations.cs +++ b/Terminal.Gui/Views/GraphView/Annotations.cs @@ -130,12 +130,12 @@ public class LegendAnnotation : View, IAnnotation // BUGBUG: Legend annotations are subviews. But for some reason the are rendered directly in OnDrawContent // BUGBUG: instead of just being normal subviews. They get rendered as blank rects and thus we disable subview drawing. /// - protected override bool OnDrawingText (Rectangle viewport) { return true; } + protected override bool OnDrawingText () { return true; } // BUGBUG: Legend annotations are subviews. But for some reason the are rendered directly in OnDrawContent // BUGBUG: instead of just being normal subviews. They get rendered as blank rects and thus we disable subview drawing. /// - protected override bool OnClearingViewport (Rectangle viewport) { return true; } + protected override bool OnClearingViewport () { return true; } /// Draws the Legend and all entries into the area within diff --git a/Terminal.Gui/Views/GraphView/GraphView.cs b/Terminal.Gui/Views/GraphView/GraphView.cs index 0254d8e86..93dcc1992 100644 --- a/Terminal.Gui/Views/GraphView/GraphView.cs +++ b/Terminal.Gui/Views/GraphView/GraphView.cs @@ -197,7 +197,7 @@ public class GraphView : View, IDesignable } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (CellSize.X == 0 || CellSize.Y == 0) { diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index c2fa42186..cc0ba4df5 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -421,7 +421,7 @@ public class HexView : View, IDesignable } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (Source is null) { @@ -434,16 +434,16 @@ public class HexView : View, IDesignable Move (0, 0); int nBlocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN; - var data = new byte [nBlocks * NUM_BYTES_PER_HEX_COLUMN * viewport.Height]; + var data = new byte [nBlocks * NUM_BYTES_PER_HEX_COLUMN * Viewport.Height]; Source.Position = _displayStart; int n = _source!.Read (data, 0, data.Length); Attribute selectedAttribute = GetHotNormalColor (); Attribute editedAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background); Attribute editingAttribute = new Attribute (GetFocusColor ().Background, GetFocusColor ().Foreground); - for (var line = 0; line < viewport.Height; line++) + for (var line = 0; line < Viewport.Height; line++) { - Rectangle lineRect = new (0, line, viewport.Width, 1); + Rectangle lineRect = new (0, line, Viewport.Width, 1); if (!Viewport.Contains (lineRect)) { diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs index 4e71b5c23..82b7499e1 100644 --- a/Terminal.Gui/Views/Line.cs +++ b/Terminal.Gui/Views/Line.cs @@ -64,7 +64,7 @@ public class Line : View, IOrientation } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { LineCanvas lc = LineCanvas; @@ -78,7 +78,7 @@ public class Line : View, IOrientation lc = adornment.Parent?.LineCanvas; } - Point pos = ViewportToScreen (viewport).Location; + Point pos = ViewportToScreen (Viewport).Location; int length = Orientation == Orientation.Horizontal ? Frame.Width : Frame.Height; if (SuperView is {} && SuperViewRendersLineCanvas && Orientation == Orientation.Horizontal) diff --git a/Terminal.Gui/Views/LineView.cs b/Terminal.Gui/Views/LineView.cs index ab9a600b7..ccb65d3fd 100644 --- a/Terminal.Gui/Views/LineView.cs +++ b/Terminal.Gui/Views/LineView.cs @@ -54,7 +54,7 @@ public class LineView : View public Rune? StartingAnchor { get; set; } /// Draws the line including any starting/ending anchors - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { Move (0, 0); SetAttribute (GetNormalColor ()); diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index ab4e98115..01661c27b 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -769,7 +769,7 @@ public class ListView : View, IDesignable } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { Attribute current = ColorScheme?.Focus ?? Attribute.Default; SetAttribute (current); diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index 5b7887a75..611404358 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -394,7 +394,7 @@ internal sealed class Menu : View } // By doing this we draw last, over everything else. - private void Top_DrawComplete (object? sender, EventArgs e) + private void Top_DrawComplete (object? sender, DrawEventArgs e) { if (!Visible) { diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs index 269f2a18a..d66d2a573 100644 --- a/Terminal.Gui/Views/Menu/MenuBar.cs +++ b/Terminal.Gui/Views/Menu/MenuBar.cs @@ -297,7 +297,7 @@ public class MenuBar : View, IDesignable public event EventHandler? MenuOpening; /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { var pos = 0; diff --git a/Terminal.Gui/Views/NumericUpDown.cs b/Terminal.Gui/Views/NumericUpDown.cs index cc32300c9..5d3d602ce 100644 --- a/Terminal.Gui/Views/NumericUpDown.cs +++ b/Terminal.Gui/Views/NumericUpDown.cs @@ -264,7 +264,7 @@ public class NumericUpDown : View where T : notnull // Prevent the drawing of Text /// - protected override bool OnDrawingText (Rectangle viewport) { return true; } + protected override bool OnDrawingText () { return true; } } /// diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs index 9abb55478..957139f17 100644 --- a/Terminal.Gui/Views/ProgressBar.cs +++ b/Terminal.Gui/Views/ProgressBar.cs @@ -139,7 +139,7 @@ public class ProgressBar : View, IDesignable } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { SetAttribute (GetHotNormalColor ()); diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index a110a7d54..ac2867a3a 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -360,7 +360,7 @@ public class RadioGroup : View, IDesignable, IOrientation } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { SetAttribute (GetNormalColor ()); diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs index 02fc2b355..8873e5ba7 100644 --- a/Terminal.Gui/Views/ScrollBarView.cs +++ b/Terminal.Gui/Views/ScrollBarView.cs @@ -446,7 +446,7 @@ public class ScrollBarView : View public virtual void OnChangedPosition () { ChangedPosition?.Invoke (this, EventArgs.Empty); } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (ColorScheme is null || ((!ShowScrollIndicator || Size == 0) && AutoHideScrollBars && Visible)) { diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 5e34fce5f..3d3a4ddbc 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -372,7 +372,7 @@ public class ScrollView : View } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { SetViewsNeedsDraw (); diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index daba5466c..fbd08e0b9 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -775,7 +775,7 @@ public class Slider : View, IOrientation #region Drawing /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { // TODO: make this more surgical to reduce repaint diff --git a/Terminal.Gui/Views/SpinnerView/SpinnerView.cs b/Terminal.Gui/Views/SpinnerView/SpinnerView.cs index 1122fb8a6..e80db3e00 100644 --- a/Terminal.Gui/Views/SpinnerView/SpinnerView.cs +++ b/Terminal.Gui/Views/SpinnerView/SpinnerView.cs @@ -183,10 +183,10 @@ public class SpinnerView : View, IDesignable } /// - protected override bool OnClearingViewport (Rectangle viewport) { return true; } + protected override bool OnClearingViewport () { return true; } /// - protected override bool OnDrawingText (Rectangle viewport) + protected override bool OnDrawingText () { if (Sequence is { Length: > 0 } && _currentIdx < Sequence.Length) { diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index f98e82ebf..8fe189914 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -58,7 +58,7 @@ public class TabView : View () => { TabScrollOffset = Tabs.Count - 1; - SelectedTab = Tabs.LastOrDefault()!; + SelectedTab = Tabs.LastOrDefault ()!; return true; } @@ -306,19 +306,19 @@ public class TabView : View } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (Tabs.Any ()) { - Region savedClip = SetClip (); + // Region savedClip = SetClip (); _tabsBar.Draw (); _contentView.SetNeedsDraw (); _contentView.Draw (); - if (Driver is { }) - { - Driver.Clip = savedClip; - } + //if (Driver is { }) + //{ + // Driver.Clip = savedClip; + //} } return true; @@ -656,7 +656,7 @@ public class TabView : View } /// - protected override bool OnClearingViewport (Rectangle viewport) + protected override bool OnClearingViewport () { // clear any old text ClearViewport (); @@ -664,7 +664,7 @@ public class TabView : View return true; } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { _host._tabLocations = _host.CalculateViewport (Viewport).ToArray (); @@ -675,7 +675,7 @@ public class TabView : View } /// - protected override bool OnDrawingSubviews (Rectangle viewport) + protected override bool OnDrawingSubviews () { RenderTabLine (); diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index c31ebe738..b18c9087d 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -911,7 +911,7 @@ public class TableView : View, IDesignable } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { Move (0, 0); diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index d3082d449..5cf5f1b61 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -931,7 +931,7 @@ public class TextField : View } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { _isDrawing = true; diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs index c561197ab..e1560c077 100644 --- a/Terminal.Gui/Views/TextValidateField.cs +++ b/Terminal.Gui/Views/TextValidateField.cs @@ -553,7 +553,7 @@ namespace Terminal.Gui } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (_provider is null) { diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 7e409b439..9880bd39c 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3550,7 +3550,7 @@ public class TextView : View } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { _isDrawing = true; @@ -3616,7 +3616,7 @@ public class TextView : View cols = Math.Max (cols, 1); } - if (!TextModel.SetCol (ref col, viewport.Right, cols)) + if (!TextModel.SetCol (ref col, Viewport.Right, cols)) { break; } @@ -3639,7 +3639,7 @@ public class TextView : View if (row < bottom) { SetNormalColor (); - ClearRegion (viewport.Left, row, right, bottom); + ClearRegion (Viewport.Left, row, right, bottom); } //PositionCursor (); diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index f7179a586..9a6acb72a 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -174,9 +174,11 @@ public class TileView : View // BUG: v2 fix this hack // QUESTION: Does this need to be fixed before events are refactored? /// Overridden so no Frames get drawn + /// /// protected override bool OnDrawingAdornments () { return true; } + /// /// protected override bool OnRenderingLineCanvas () { return false; } @@ -982,9 +984,9 @@ public class TileView : View } /// - protected override bool OnClearingViewport (Rectangle viewport) { return true; } + protected override bool OnClearingViewport () { return true; } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { DrawSplitterSymbol (); diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 59351c27f..728a411bd 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -1146,7 +1146,7 @@ public class TreeView : View, ITreeView where T : class public event EventHandler> ObjectActivated; /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (roots is null) { diff --git a/UICatalog/BenchmarkResults.cs b/UICatalog/BenchmarkResults.cs index 146318548..050fcf5c4 100644 --- a/UICatalog/BenchmarkResults.cs +++ b/UICatalog/BenchmarkResults.cs @@ -21,6 +21,7 @@ public class BenchmarkResults public int UpdatedCount { get; set; } = 0; [JsonInclude] public int DrawCompleteCount { get; set; } = 0; + [JsonInclude] public int LaidOutCount { get; set; } = 0; } diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json index e1bc9cdfc..7ed43499a 100644 --- a/UICatalog/Properties/launchSettings.json +++ b/UICatalog/Properties/launchSettings.json @@ -46,7 +46,7 @@ }, "Charmap": { "commandName": "Project", - "commandLineArgs": "\"Character Map\" -b" + "commandLineArgs": "Bars -b" } } } \ No newline at end of file diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs index a28fd4eaf..1d62c4ad7 100644 --- a/UICatalog/Scenario.cs +++ b/UICatalog/Scenario.cs @@ -226,18 +226,6 @@ public class Scenario : IDisposable private void OnApplicationNotifyNewRunState (object? sender, RunStateEventArgs e) { - // Get a list of all subviews under Application.Top (and their subviews, etc.) - // and subscribe to their DrawComplete event - void SubscribeAllSubviews (View view) - { - view.DrawComplete += (s, a) => BenchmarkResults.DrawCompleteCount++; - view.SubviewsLaidOut += (s, a) => BenchmarkResults.LaidOutCount++; - foreach (View subview in view.Subviews) - { - SubscribeAllSubviews (subview); - } - } - SubscribeAllSubviews (Application.Top!); _currentDemoKey = 0; @@ -257,6 +245,19 @@ public class Scenario : IDisposable return true; }); + return; + + // Get a list of all subviews under Application.Top (and their subviews, etc.) + // and subscribe to their DrawComplete event + void SubscribeAllSubviews (View view) + { + view.DrawComplete += (s, a) => BenchmarkResults.DrawCompleteCount++; + view.SubviewsLaidOut += (s, a) => BenchmarkResults.LaidOutCount++; + foreach (View subview in view.Subviews) + { + SubscribeAllSubviews (subview); + } + } } // If the scenario doesn't close within the abort time, this will force it to quit diff --git a/UICatalog/Scenarios/Adornments.cs b/UICatalog/Scenarios/Adornments.cs index 1ecbb196a..a3dab0f2b 100644 --- a/UICatalog/Scenarios/Adornments.cs +++ b/UICatalog/Scenarios/Adornments.cs @@ -13,7 +13,8 @@ public class Adornments : Scenario Window app = new () { - Title = GetQuitKeyAndName () + Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None }; var editor = new AdornmentsEditor @@ -79,17 +80,21 @@ public class Adornments : Scenario Width = 40, Height = Dim.Percent (20), Text = "Label\nY=AnchorEnd(),Height=Dim.Percent(10)", - ColorScheme = Colors.ColorSchemes ["Error"] + ColorScheme = Colors.ColorSchemes ["Dialog"] }; window.Margin.Data = "Margin"; - window.Margin.Thickness = new (3); + window.Margin.Text = "Margin Text"; + window.Margin.Thickness = new (0); window.Border.Data = "Border"; - window.Border.Thickness = new (3); + window.Border.Text = "Border Text"; + window.Border.Thickness = new (0); window.Padding.Data = "Padding"; + window.Padding.Text = "Padding Text line 1\nPadding Text line 3\nPadding Text line 3\nPadding Text line 4\nPadding Text line 5"; window.Padding.Thickness = new (3); + window.Padding.ColorScheme = Colors.ColorSchemes ["Error"]; window.Padding.CanFocus = true; var longLabel = new Label @@ -99,20 +104,21 @@ public class Adornments : Scenario longLabel.TextFormatter.WordWrap = true; window.Add (tf1, color, button, label, btnButtonInWindow, labelAnchorEnd, longLabel); + window.ClearingViewport += (s, e) => e.Cancel = true; window.Initialized += (s, e) => { editor.ViewToEdit = window; editor.ShowViewIdentifier = true; - var labelInPadding = new Label { X = 1, Y = 0, Title = "_Text:" }; + var labelInPadding = new Label { X = 0, Y = 1, Title = "_Text:" }; window.Padding.Add (labelInPadding); var textFieldInPadding = new TextField { X = Pos.Right (labelInPadding) + 1, - Y = Pos.Top (labelInPadding), Width = 15, - Text = "some text", + Y = Pos.Top (labelInPadding), Width = 10, + Text = "text (Y = 1)", CanFocus = true }; textFieldInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok"); @@ -121,9 +127,10 @@ public class Adornments : Scenario var btnButtonInPadding = new Button { X = Pos.Center (), - Y = 0, - Text = "_Button in Padding", - CanFocus = true + Y = 1, + Text = "_Button in Padding Y = 1", + CanFocus = true, + HighlightStyle = HighlightStyle.None, }; btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok"); btnButtonInPadding.BorderStyle = LineStyle.Dashed; diff --git a/UICatalog/Scenarios/AdvancedClipping.cs b/UICatalog/Scenarios/AdvancedClipping.cs new file mode 100644 index 000000000..734eedfaf --- /dev/null +++ b/UICatalog/Scenarios/AdvancedClipping.cs @@ -0,0 +1,151 @@ +using System.Text; +using System.Timers; +using Terminal.Gui; + +namespace UICatalog.Scenarios; + +[ScenarioMetadata ("AdvancedClipping", "AdvancedClipping Tester")] +[ScenarioCategory ("AdvancedClipping")] +public class AdvancedClipping : Scenario +{ + private int _hotkeyCount; + + public override void Main () + { + Application.Init (); + + Window app = new () + { + Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None + }; + + //var arrangementEditor = new ArrangementEditor() + //{ + // X = Pos.AnchorEnd (), + // Y = 0, + // AutoSelectViewToEdit = true, + //}; + //app.Add (arrangementEditor); + + View tiledView1 = CreateTiledView (1, 0, 0); + + + ProgressBar tiledProgressBar = new () + { + X = 0, + Y = 1, + Width = Dim.Fill (), + Id = "tiledProgressBar", + // BorderStyle = LineStyle.Rounded + }; + tiledView1.Add (tiledProgressBar); + + View tiledView2 = CreateTiledView (2, 2, 2); + + app.Add (tiledView1); + app.Add (tiledView2); + + //View tiledView3 = CreateTiledView (3, 6, 6); + //app.Add (tiledView3); + + //using View overlappedView1 = CreateOverlappedView (1, 30, 2); + + //ProgressBar progressBar = new () + //{ + // X = Pos.AnchorEnd (), + // Y = Pos.AnchorEnd (), + // Width = Dim.Fill (), + // Id = "progressBar", + // BorderStyle = LineStyle.Rounded + //}; + //overlappedView1.Add (progressBar); + + + //View overlappedView2 = CreateOverlappedView (2, 32, 4); + //View overlappedView3 = CreateOverlappedView (3, 34, 6); + + //app.Add (overlappedView1); + //app.Add (overlappedView2); + //app.Add (overlappedView3); + + Timer progressTimer = new Timer (250) + { + AutoReset = true + }; + + progressTimer.Elapsed += (s, e) => + { + + if (tiledProgressBar.Fraction == 1.0) + { + tiledProgressBar.Fraction = 0; + } + + Application.Wakeup (); + + tiledProgressBar.Fraction += 0.1f; + // tiledProgressBar.SetNeedsDraw (); + }; + + progressTimer.Start (); + Application.Run (app); + progressTimer.Stop (); + app.Dispose (); + Application.Shutdown (); + + return; + } + + private View CreateOverlappedView (int id, Pos x, Pos y) + { + var overlapped = new View + { + X = x, + Y = y, + Height = Dim.Auto (minimumContentDim: 4), + Width = Dim.Auto (minimumContentDim: 14), + Title = $"Overlapped{id} _{GetNextHotKey ()}", + ColorScheme = Colors.ColorSchemes ["Toplevel"], + Id = $"Overlapped{id}", + ShadowStyle = ShadowStyle.Transparent, + BorderStyle = LineStyle.Double, + CanFocus = true, // Can't drag without this? BUGBUG + TabStop = TabBehavior.TabGroup, + Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped | ViewArrangement.Resizable + }; + return overlapped; + } + + private View CreateTiledView (int id, Pos x, Pos y) + { + var tiled = new View + { + X = x, + Y = y, + Height = Dim.Auto (minimumContentDim: 4), + Width = Dim.Auto (minimumContentDim: 14), + Title = $"Tiled{id} _{GetNextHotKey ()}", + Id = $"Tiled{id}", + Text = $"Tiled{id}", + BorderStyle = LineStyle.Single, + CanFocus = true, // Can't drag without this? BUGBUG + TabStop = TabBehavior.TabStop, + Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable + }; + tiled.Padding.Thickness = new (1); + tiled.Padding.Diagnostics = ViewDiagnosticFlags.Thickness; + + FrameView fv = new () + { + Title = "FrameView", + Width = 15, + Height = 1, + }; + tiled.Add (fv); + + return tiled; + } + + private char GetNextHotKey () { return (char)('A' + _hotkeyCount++); } +} diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index 34d852c05..5c2d57870 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -171,7 +171,7 @@ public class AllViewsTester : Scenario _settingsPane.Add (label, _demoTextView); - _eventLog = new() + _eventLog = new () { // X = Pos.Right(_layoutEditor) }; @@ -254,7 +254,7 @@ public class AllViewsTester : Scenario // Instantiate view var view = (View)Activator.CreateInstance (type)!; - _eventLog!.ViewToLog = _curView; + _eventLog!.ViewToLog = view; if (view is IDesignable designable) { diff --git a/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs b/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs index 00fcf9ffa..9024e502f 100644 --- a/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs +++ b/UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs @@ -179,7 +179,7 @@ public class AnimationScenario : Scenario private Rectangle oldSize = Rectangle.Empty; public void NextFrame () { currentFrame = (currentFrame + 1) % frameCount; } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { if (frameCount == 0) { diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 55f6dac1f..ea92fe677 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -681,9 +681,9 @@ internal class CharMap : View, IDesignable private static int RowWidth => RowLabelWidth + COLUMN_WIDTH * 16; public event EventHandler Hover; - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { - if (viewport.Height == 0 || viewport.Width == 0) + if (Viewport.Height == 0 || Viewport.Width == 0) { return true; } diff --git a/UICatalog/Scenarios/Editors/EventLog.cs b/UICatalog/Scenarios/Editors/EventLog.cs index 41c9ebb41..fec969ae0 100644 --- a/UICatalog/Scenarios/Editors/EventLog.cs +++ b/UICatalog/Scenarios/Editors/EventLog.cs @@ -22,7 +22,7 @@ public class EventLog : ListView X = Pos.AnchorEnd (); Y = 0; - Width = Dim.Func (() => Math.Min (SuperView!.Viewport.Width / 2, MaxLength + GetAdornmentsThickness ().Horizontal)); + Width = Dim.Func (() => Math.Min (SuperView!.Viewport.Width / 3, MaxLength + GetAdornmentsThickness ().Horizontal)); Height = Dim.Fill (); ExpandButton = new () diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs index 07c972773..b8e3eb37c 100644 --- a/UICatalog/Scenarios/Images.cs +++ b/UICatalog/Scenarios/Images.cs @@ -624,7 +624,7 @@ public class Images : Scenario public Image FullResImage; private Image _matchSize; - protected override bool OnDrawingContent (Rectangle bounds) + protected override bool OnDrawingContent () { if (FullResImage == null) { @@ -632,15 +632,15 @@ public class Images : Scenario } // if we have not got a cached resized image of this size - if (_matchSize == null || bounds.Width != _matchSize.Width || bounds.Height != _matchSize.Height) + if (_matchSize == null || Viewport.Width != _matchSize.Width || Viewport.Height != _matchSize.Height) { // generate one - _matchSize = FullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); + _matchSize = FullResImage.Clone (x => x.Resize (Viewport.Width, Viewport.Height)); } - for (var y = 0; y < bounds.Height; y++) + for (var y = 0; y < Viewport.Height; y++) { - for (var x = 0; x < bounds.Width; x++) + for (var x = 0; x < Viewport.Width; x++) { Rgba32 rgb = _matchSize [x, y]; @@ -682,8 +682,8 @@ public class Images : Scenario private (int columns, int rows) CalculateGridSize (Rectangle bounds) { // Characters are twice as wide as they are tall, so use 2:1 width-to-height ratio - int availableWidth = bounds.Width / 2; // Each color block is 2 character wide - int availableHeight = bounds.Height; + int availableWidth = Viewport.Width / 2; // Each color block is 2 character wide + int availableHeight = Viewport.Height; int numColors = _palette.Count; @@ -701,7 +701,7 @@ public class Images : Scenario return (columns, rows); } - protected override bool OnDrawingContent (Rectangle bounds) + protected override bool OnDrawingContent () { if (_palette == null || _palette.Count == 0) { @@ -709,7 +709,7 @@ public class Images : Scenario } // Calculate the grid size based on the bounds - (int columns, int rows) = CalculateGridSize (bounds); + (int columns, int rows) = CalculateGridSize (Viewport); // Draw the colors in the palette for (var i = 0; i < _palette.Count && i < columns * rows; i++) diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs index 30259ef62..82d4758aa 100644 --- a/UICatalog/Scenarios/LineDrawing.cs +++ b/UICatalog/Scenarios/LineDrawing.cs @@ -268,7 +268,7 @@ public class DrawingArea : View public ITool CurrentTool { get; set; } = new DrawLineTool (); public DrawingArea () { AddLayer (); } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { foreach (LineCanvas canvas in Layers) { @@ -375,7 +375,7 @@ public class AttributeView : View } /// - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { Color fg = Value.Foreground; Color bg = Value.Background; diff --git a/UICatalog/Scenarios/ShadowStyles.cs b/UICatalog/Scenarios/ShadowStyles.cs index 9f17d7d68..75ee30b3c 100644 --- a/UICatalog/Scenarios/ShadowStyles.cs +++ b/UICatalog/Scenarios/ShadowStyles.cs @@ -40,9 +40,15 @@ public class ShadowStyles : Scenario Title = "Shadow Window", Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped, BorderStyle = LineStyle.Double, - ShadowStyle = ShadowStyle.Transparent + ShadowStyle = ShadowStyle.Transparent, }; + app.DrawingText += (s, e) => + { + Application.Driver?.FillRect (app.ViewportToScreen (app.Viewport), '*'); + e.Cancel = true; + }; + var buttonInWin = new Button { X = Pos.Center (), diff --git a/UICatalog/Scenarios/SimpleDialog.cs b/UICatalog/Scenarios/SimpleDialog.cs index 2ab9dd07f..f326870ba 100644 --- a/UICatalog/Scenarios/SimpleDialog.cs +++ b/UICatalog/Scenarios/SimpleDialog.cs @@ -15,17 +15,30 @@ public sealed class SimpleDialog : Scenario Window appWindow = new () { Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None }; + + appWindow.DrawingText += (s, e) => + { + Application.Driver?.FillRect (appWindow.ViewportToScreen (appWindow.Viewport), '*'); + e.Cancel = true; + }; + Dialog dialog = new () { Id = "dialog", Width = 20, Height = 4, Title = "Dialog" }; dialog.Arrangement |= ViewArrangement.Resizable; var button = new Button { - Id = "button", X = Pos.Center (), Y = 1, Text = "_Press me!", - WantContinuousButtonPressed = false, + Id = "button", + X = 0, + Y = 0, + NoDecorations = true, + NoPadding = true, + Text = "A", + //WantContinuousButtonPressed = false, HighlightStyle = HighlightStyle.None, - ShadowStyle = ShadowStyle.None, + ShadowStyle = ShadowStyle.Transparent, }; button.Accepting += (s, e) => @@ -34,7 +47,7 @@ public sealed class SimpleDialog : Scenario e.Cancel = true; }; appWindow.Add (button); - + // Run - Start the application. Application.Run (appWindow); dialog.Dispose (); diff --git a/UICatalog/Scenarios/Snake.cs b/UICatalog/Scenarios/Snake.cs index 5bae51e11..9bcd8a198 100644 --- a/UICatalog/Scenarios/Snake.cs +++ b/UICatalog/Scenarios/Snake.cs @@ -314,7 +314,7 @@ public class Snake : Scenario public SnakeState State { get; } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { SetAttribute (white); ClearViewport (); diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index a8482945e..1318a570d 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -132,9 +132,9 @@ internal class GradientsView : View private const int LABEL_HEIGHT = 1; private const int GRADIENT_WITH_LABEL_HEIGHT = GRADIENT_HEIGHT + LABEL_HEIGHT + 1; // +1 for spacing - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { - DrawTopLineGradient (viewport); + DrawTopLineGradient (Viewport); var x = 2; var y = 3; @@ -149,7 +149,7 @@ internal class GradientsView : View foreach ((string label, GradientDirection direction) in gradients) { - if (x + GRADIENT_WIDTH > viewport.Width) + if (x + GRADIENT_WIDTH > Viewport.Width) { x = 2; // Reset to left margin y += GRADIENT_WITH_LABEL_HEIGHT; // Move down to next row diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index efcfd6216..f6fc56d2d 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -137,13 +137,12 @@ public class ScenarioTests : TestsAllViews } } - /// /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run and measuring the perf of each. /// [Theory] [MemberData (nameof (AllScenarioTypes))] - public void All_Scenarios_Time (Type scenarioType) + public void All_Scenarios_Benchmark (Type scenarioType) { Assert.Null (_timeoutLock); _timeoutLock = new (); @@ -167,6 +166,7 @@ public class ScenarioTests : TestsAllViews int updatedCount = 0; int drawCompleteCount = 0; + int addedCount = 0; int laidOutCount = 0; _output.WriteLine ($"Running Scenario '{scenarioType}'"); @@ -201,6 +201,7 @@ public class ScenarioTests : TestsAllViews _output.WriteLine ($" called Driver.Refresh {refreshedCount} times."); _output.WriteLine ($" which updated the screen {updatedCount} times."); _output.WriteLine ($" called View.Draw {drawCompleteCount} times."); + _output.WriteLine ($" added {addedCount} views."); _output.WriteLine ($" called View.LayoutComplete {laidOutCount} times."); // Restore the configuration locations @@ -264,6 +265,7 @@ public class ScenarioTests : TestsAllViews { view.DrawComplete += (s, a) => drawCompleteCount++; view.SubviewsLaidOut += (s, a) => laidOutCount++; + view.Added += (s, a) => addedCount++; foreach (View subview in view.Subviews) { SubscribeAllSubviews (subview); diff --git a/UnitTests/View/Draw/DrawTests.cs b/UnitTests/View/Draw/DrawTests.cs index 2dabd9d9c..c2aed2041 100644 --- a/UnitTests/View/Draw/DrawTests.cs +++ b/UnitTests/View/Draw/DrawTests.cs @@ -947,7 +947,7 @@ public class DrawTests (ITestOutputHelper _output) Assert.Equal (view.Frame, Application.Driver?.Clip?.GetBounds ()); // Act - view.SetClip (); + view.SetClipToViewport (); // Assert Assert.Equal (expectedClip, Application.Driver?.Clip?.GetBounds()); @@ -981,7 +981,7 @@ public class DrawTests (ITestOutputHelper _output) view.Viewport = view.Viewport with { X = 1, Y = 1 }; // Act - view.SetClip (); + view.SetClipToViewport (); // Assert Assert.Equal (expectedClip, Application.Driver?.Clip.GetBounds ()); diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 63f76d83c..5fa7b9281 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -1102,7 +1102,7 @@ At 0,0 public bool IsKeyUp { get; set; } public override string Text { get; set; } - protected override bool OnDrawingContent (Rectangle viewport) + protected override bool OnDrawingContent () { var idx = 0; diff --git a/docfx/docs/drawing.md b/docfx/docs/drawing.md index f7f3ab375..fd21cb0e5 100644 --- a/docfx/docs/drawing.md +++ b/docfx/docs/drawing.md @@ -41,6 +41,12 @@ Each of these steps can be overridden by developers using the standard [Terminal If a View need to redraw because something changed within it's Content Area it can call @Terminal.Gui.View.SetNeedsDraw. If a View needs to be redrawn because something has changed the size of the Viewport, it can call @Terminal.Gui.View.SetNeedsLayout. +### Clipping + +Clipping enables better performance by ensuring on regions of the terminal that need to be drawn actually get drawn by the @Terminal.Gui.ConsoleDriver. Terminal.Gui supports non-rectangular clip regions with @Terminal.Gui.Region. @Terminal.Gui.ConsoleDriver.Clip is the application managed clip region and is managed by @Terminal.Gui.Application. Developers cannot change this directly. + +The View drawing APIs, e.g. @Terminal.Gui.View.OnDrawingSubviews, are passed the clip region currently enforced in Viewport-relative coordinates. + ## Coordinate System for Drawing The @Terminal.Gui.View draw APIs all take coordinates specified in *Viewport-Relative* coordinates. That is, `0, 0` is the top-left cell visible to the user. diff --git a/local_packages/Terminal.Gui.2.0.0.nupkg b/local_packages/Terminal.Gui.2.0.0.nupkg index c0ee67dd0..009ca8827 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.nupkg and b/local_packages/Terminal.Gui.2.0.0.nupkg differ diff --git a/local_packages/Terminal.Gui.2.0.0.snupkg b/local_packages/Terminal.Gui.2.0.0.snupkg index 301976b83..62039da43 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.snupkg and b/local_packages/Terminal.Gui.2.0.0.snupkg differ