diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs index 2ec2382f1..9defe9662 100644 --- a/Terminal.Gui/Application/Application.Run.cs +++ b/Terminal.Gui/Application/Application.Run.cs @@ -547,12 +547,38 @@ public static partial class Application // Run (Begin, Run, End, Stop) } tl.Draw (); - ExcludeFromClip (tl.FrameToScreen ()); } + DrawMargins (TopLevels.Cast ().ToList ()); + ClipToScreen (); } + // TODO: This is inefficent + private static bool DrawMargins (List peers) + { + if (peers.Count == 0) + { + return false; + } + foreach (View view in peers) + { + if (view.Margin is { CachedClip: { }}) + { + view.Margin.NeedsDraw = true; + Region? saved = Driver?.Clip; + Application.SetClip (view.Margin.CachedClip); + view.Margin.Draw (); + Application.SetClip (saved); + } + view.Margin.CachedClip = null; + + DrawMargins (view.Subviews.ToList ()); + view.NeedsDraw = false; + } + + return true; + } /// This event is raised on each iteration of the main loop. /// See also diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs index 44ba0a892..b3db707be 100644 --- a/Terminal.Gui/View/Adornment/Margin.cs +++ b/Terminal.Gui/View/Adornment/Margin.cs @@ -16,6 +16,11 @@ namespace Terminal.Gui; /// public class Margin : Adornment { + private const int SHADOW_WIDTH = 1; + private const int SHADOW_HEIGHT = 1; + private const int PRESS_MOVE_HORIZONTAL = 1; + private const int PRESS_MOVE_VERTICAL = 0; + /// public Margin () { /* Do nothing; A parameter-less constructor is required to support all views unit tests. */ @@ -35,6 +40,8 @@ public class Margin : Adornment CanFocus = false; } + public Region? CachedClip { get; set; } + private bool _pressed; private ShadowView? _bottomShadow; @@ -88,44 +95,12 @@ public class Margin : Adornment if (ShadowStyle != ShadowStyle.None) { // Don't clear where the shadow goes - screen = Rectangle.Inflate (screen, -1, -1); + screen = Rectangle.Inflate (screen, -SHADOW_WIDTH, -SHADOW_HEIGHT); } - // This just draws/clears the thickness, not the insides. - Thickness.Draw (screen, Diagnostics, ToString ()); - return true; } - ///// - //protected override bool OnDrawingContent () - //{ - // Rectangle screen = FrameToScreen(); - // for (int r = 0; r < screen.Height; r++) - // { - // for (int c = 0; c < screen.Width; c++) - // { - // Driver?.Move (c, r); - - // if (Driver?.Contents is { } && c < Driver.Contents.GetLength (1) && r < Driver.Contents.GetLength (0)) - // { - // Driver.AddRune (Driver.Contents [r, c].Rune); - // } - // } - // } - // return true; - //} - - ///// - ////protected override bool OnDrawSubviews (Rectangle viewport) { return true; } - - //protected override bool OnDrawComplete (Rectangle viewport) - //{ - // DoDrawSubviews (viewport); - - // return true; - //} - /// /// Sets whether the Margin includes a shadow effect. The shadow is drawn on the right and bottom sides of the /// Margin. @@ -149,22 +124,22 @@ public class Margin : Adornment if (ShadowStyle != ShadowStyle.None) { // Turn off shadow - Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right - 1, Thickness.Bottom - 1); + Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right - SHADOW_WIDTH, Thickness.Bottom - SHADOW_HEIGHT); } if (style != ShadowStyle.None) { // Turn on shadow - Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right + 1, Thickness.Bottom + 1); + Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right + SHADOW_WIDTH, Thickness.Bottom + SHADOW_HEIGHT); } if (style != ShadowStyle.None) { _rightShadow = new () { - X = Pos.AnchorEnd (1), + X = Pos.AnchorEnd (SHADOW_WIDTH), Y = 0, - Width = 1, + Width = SHADOW_WIDTH, Height = Dim.Fill (), ShadowStyle = style, Orientation = Orientation.Vertical @@ -173,11 +148,11 @@ public class Margin : Adornment _bottomShadow = new () { X = 0, - Y = Pos.AnchorEnd (1), + Y = Pos.AnchorEnd (SHADOW_HEIGHT), Width = Dim.Fill (), - Height = 1, + Height = SHADOW_HEIGHT, ShadowStyle = style, - Orientation = Orientation.Horizontal + Orientation = Orientation.Horizontal, }; Add (_rightShadow, _bottomShadow); } @@ -189,15 +164,9 @@ public class Margin : Adornment public override ShadowStyle ShadowStyle { get => base.ShadowStyle; - set - { - base.ShadowStyle = SetShadow (value); - - } + set => base.ShadowStyle = SetShadow (value); } - private const int PRESS_MOVE_HORIZONTAL = 1; - private const int PRESS_MOVE_VERTICAL = 0; private void Margin_Highlight (object? sender, CancelEventArgs e) { diff --git a/Terminal.Gui/View/Adornment/ShadowView.cs b/Terminal.Gui/View/Adornment/ShadowView.cs index c92bed497..dbe71576a 100644 --- a/Terminal.Gui/View/Adornment/ShadowView.cs +++ b/Terminal.Gui/View/Adornment/ShadowView.cs @@ -36,6 +36,13 @@ internal class ShadowView : View return base.GetNormalColor (); } + /// + /// + protected override bool OnDrawingText () + { + return true; + } + /// protected override bool OnClearingViewport () { @@ -116,13 +123,16 @@ internal class ShadowView : View { Rectangle screen = ViewportToScreen (Viewport); - for (int i = Math.Max(0, screen.X + 1); i < screen.X + screen.Width; i++) + for (int r = Math.Max (0, screen.Y); r < screen.Y + screen.Height; r++) { - Driver?.Move (i, screen.Y); - - if (i < Driver?.Contents!.GetLength (1) && screen.Y < Driver?.Contents?.GetLength (0)) + for (int c = Math.Max (0, screen.X + 1); c < screen.X + screen.Width; c++) { - Driver.AddRune (Driver.Contents [screen.Y, i].Rune); + Driver?.Move (c, r); + + if (c < Driver?.Contents!.GetLength (1) && r < Driver?.Contents?.GetLength (0)) + { + Driver.AddRune (Driver.Contents [r, c].Rune); + } } } } @@ -144,13 +154,16 @@ internal class ShadowView : View Rectangle screen = ViewportToScreen (Viewport); // Fill the rest of the rectangle - for (int i = Math.Max (0, screen.Y); i < screen.Y + viewport.Height; i++) + for (int c = Math.Max (0, screen.X); c < screen.X + screen.Width; c++) { - Driver?.Move (screen.X, i); - - if (Driver?.Contents is { } && screen.X < Driver.Contents.GetLength (1) && i < Driver.Contents.GetLength (0)) + for (int r = Math.Max (0, screen.Y); r < screen.Y + viewport.Height; r++) { - Driver.AddRune (Driver.Contents [i, screen.X].Rune); + Driver?.Move (c, r); + + if (Driver?.Contents is { } && screen.X < Driver.Contents.GetLength (1) && r < Driver.Contents.GetLength (0)) + { + Driver.AddRune (Driver.Contents [r, c].Rune); + } } } } diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs index c242b1fc2..a1f77820f 100644 --- a/Terminal.Gui/View/View.Drawing.cs +++ b/Terminal.Gui/View/View.Drawing.cs @@ -22,8 +22,13 @@ public partial class View // Drawing APIs /// public void Draw () { + if (!CanBeVisible (this)) + { + return; + } + Region? saved = Driver?.Clip; - if (CanBeVisible (this) && (NeedsDraw || SubViewNeedsDraw)) + if (NeedsDraw || SubViewNeedsDraw) { saved = SetClipToFrame (); DoDrawAdornments (); @@ -36,11 +41,16 @@ public partial class View // Drawing APIs saved = SetClipToViewport (); + // TODO: Simplify/optimize SetAttribute system. DoSetAttribute (); DoClearViewport (); - DoSetAttribute (); - DoDrawSubviews (); + if (SubViewNeedsDraw) + { + DoSetAttribute (); + + DoDrawSubviews (); + } DoSetAttribute (); DoDrawText (); @@ -69,35 +79,42 @@ public partial class View // Drawing APIs ClearNeedsDraw (); } - // We're done - DoDrawComplete (); - Application.SetClip (saved); - - if (this is not Adornment && Driver?.Clip is { }) + // This causes the Margin to be drawn in a second pass + // TODO: Figure out how to make this more efficient + if (Margin is { } && Margin?.Thickness != Thickness.Empty) { - Application.ExcludeFromClip (FrameToScreen ()); + Margin!.CachedClip = Application.Driver?.Clip?.Clone (); } + // We're done drawing + DoDrawComplete (); + // QUESTION: SHould this go before DoDrawComplete? + Application.SetClip (saved); + + // Exclude this view from the clip + if (this is not Adornment && Driver?.Clip is { }) + { + Rectangle borderFrame = FrameToScreen (); + + if (Border is { }) + { + borderFrame = Border.FrameToScreen (); + } + + Application.ExcludeFromClip (borderFrame); + } } #region DrawAdornments private void DoDrawAdornmentSubViews () { - // This causes the Adornment's subviews to be REDRAWN - // TODO: Figure out how to make this more efficient - if (Margin?.Subviews is { } && Margin.Thickness != Thickness.Empty) - { - foreach (View subview in Margin.Subviews) - { - subview.SetNeedsDraw (); - } - - Region? saved = Margin?.SetClipToFrame (); - Margin?.DoDrawSubviews (); - Application.SetClip (saved); - - } + //if (Margin?.Subviews is { } && Margin.Thickness != Thickness.Empty) + //{ + // //Region? saved = Margin?.SetClipToFrame (); + // //Margin?.DoDrawSubviews (); + // //Application.SetClip (saved); + //} if (Border?.Subviews is { } && Border.Thickness != Thickness.Empty) { @@ -145,7 +162,7 @@ public partial class View // Drawing APIs // Those lines will be finally rendered in OnRenderLineCanvas if (Margin is { } && Margin.Thickness != Thickness.Empty) { - Margin?.Draw (); + //Margin?.Draw (); } if (Border is { } && Border.Thickness != Thickness.Empty) @@ -165,7 +182,6 @@ 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; } @@ -467,7 +483,6 @@ public partial class View // Drawing APIs #endif view.Draw (); } - } #endregion DrawSubviews @@ -489,7 +504,6 @@ 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; } @@ -575,6 +589,7 @@ public partial class View // Drawing APIs DrawComplete?.Invoke (this, new (Viewport, Viewport)); // Default implementation does nothing. + } /// @@ -591,6 +606,8 @@ public partial class View // Drawing APIs #region NeedsDraw + // 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 diff --git a/Terminal.Gui/View/View.Hierarchy.cs b/Terminal.Gui/View/View.Hierarchy.cs index 8bd58a185..5f45236b3 100644 --- a/Terminal.Gui/View/View.Hierarchy.cs +++ b/Terminal.Gui/View/View.Hierarchy.cs @@ -77,7 +77,6 @@ public partial class View // SuperView/SubView hierarchy management (SuperView, if (view.Enabled && !Enabled) { - view._oldEnabled = true; view.Enabled = false; } diff --git a/Terminal.Gui/Views/SpinnerView/SpinnerStyle.cs b/Terminal.Gui/Views/SpinnerView/SpinnerStyle.cs index c4b5f8415..fac2005c9 100644 --- a/Terminal.Gui/Views/SpinnerView/SpinnerStyle.cs +++ b/Terminal.Gui/Views/SpinnerView/SpinnerStyle.cs @@ -54,7 +54,7 @@ public abstract class SpinnerStyle /// /// /// This is the maximum speed the spinner will rotate at. You still need to call - /// or to advance/start animation. + /// or to advance/start animation. /// public abstract int SpinDelay { get; } diff --git a/UICatalog/Scenarios/AdvancedClipping.cs b/UICatalog/Scenarios/AdvancedClipping.cs index 75a03437b..0cb94c833 100644 --- a/UICatalog/Scenarios/AdvancedClipping.cs +++ b/UICatalog/Scenarios/AdvancedClipping.cs @@ -20,36 +20,47 @@ public class AdvancedClipping : Scenario //BorderStyle = LineStyle.None }; - app.DrawingText += (s, e) => + app.DrawingContent += (s, e) => { Application.Driver?.FillRect (app.ViewportToScreen (app.Viewport), CM.Glyphs.Dot); - //app.SetSubViewNeedsDraw(); e.Cancel = true; }; - //var arrangementEditor = new ArrangementEditor() - //{ - // X = Pos.AnchorEnd (), - // Y = 0, - // AutoSelectViewToEdit = true, - //}; - //app.Add (arrangementEditor); + var arrangementEditor = new ArrangementEditor () + { + X = Pos.AnchorEnd (), + Y = 0, + AutoSelectViewToEdit = true, + }; + app.Add (arrangementEditor); View tiledView1 = CreateTiledView (1, 0, 0); - ProgressBar tiledProgressBar = new () + tiledView1.Width = 30; + + ProgressBar tiledProgressBar1 = new () { X = 0, - Y = Pos.AnchorEnd(), + Y = Pos.AnchorEnd (), + Width = Dim.Fill (), + Id = "tiledProgressBar", + BidirectionalMarquee = true, + }; + tiledView1.Add (tiledProgressBar1); + + View tiledView2 = CreateTiledView (2, 4, 2); + + ProgressBar tiledProgressBar2 = new () + { + X = 0, + Y = Pos.AnchorEnd (), Width = Dim.Fill (), Id = "tiledProgressBar", BidirectionalMarquee = true, ProgressBarStyle = ProgressBarStyle.MarqueeBlocks - // BorderStyle = LineStyle.Rounded + // BorderStyle = LineStyle.Rounded }; - tiledView1.Add (tiledProgressBar); - - View tiledView2 = CreateTiledView (2, 4, 2); + tiledView2.Add (tiledProgressBar2); app.Add (tiledView1); app.Add (tiledView2); @@ -84,7 +95,8 @@ public class AdvancedClipping : Scenario progressTimer.Elapsed += (s, e) => { - tiledProgressBar.Pulse(); + tiledProgressBar1.Pulse (); + tiledProgressBar2.Pulse (); Application.Wakeup (); }; @@ -131,12 +143,13 @@ public class AdvancedClipping : Scenario BorderStyle = LineStyle.Single, CanFocus = true, // Can't drag without this? BUGBUG TabStop = TabBehavior.TabStop, - Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable + Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable, + ShadowStyle = ShadowStyle.Transparent }; //tiled.Padding.Thickness = new (1); //tiled.Padding.Diagnostics = ViewDiagnosticFlags.Thickness; - tiled.Margin.Thickness = new (1); + //tiled.Margin.Thickness = new (1); FrameView fv = new () { @@ -145,7 +158,7 @@ public class AdvancedClipping : Scenario Height = 3, }; tiled.Add (fv); - + return tiled; } diff --git a/UICatalog/Scenarios/ShadowStyles.cs b/UICatalog/Scenarios/ShadowStyles.cs index 59e9f6852..f0e206c66 100644 --- a/UICatalog/Scenarios/ShadowStyles.cs +++ b/UICatalog/Scenarios/ShadowStyles.cs @@ -43,7 +43,7 @@ public class ShadowStyles : Scenario ShadowStyle = ShadowStyle.Transparent, }; - app.DrawingText += (s, e) => + app.DrawingContent += (s, e) => { Application.Driver?.FillRect (app.ViewportToScreen (app.Viewport), CM.Glyphs.Dot); e.Cancel = true;