소스 검색

Fixing unit tests 4

Tig 9 달 전
부모
커밋
5dc832b6e3
35개의 변경된 파일861개의 추가작업 그리고 480개의 파일을 삭제
  1. 17 12
      Terminal.Gui/Application/Application.Run.cs
  2. 5 7
      Terminal.Gui/Terminal.Gui.csproj
  3. 0 1
      Terminal.Gui/View/Adornment/Adornment.cs
  4. 8 0
      Terminal.Gui/View/Layout/Dim.cs
  5. 2 2
      Terminal.Gui/View/Layout/PosAlign.cs
  6. 21 14
      Terminal.Gui/View/View.Adornments.cs
  7. 53 44
      Terminal.Gui/View/View.Drawing.cs
  8. 2 1
      Terminal.Gui/View/View.Hierarchy.cs
  9. 35 2
      Terminal.Gui/View/View.Layout.cs
  10. 8 3
      Terminal.Gui/View/View.Text.cs
  11. 0 1
      Terminal.Gui/View/View.cs
  12. 2 0
      Terminal.Gui/Views/ComboBox.cs
  13. 9 0
      Terminal.Gui/Views/Menu/Menu.cs
  14. 1 1
      Terminal.Gui/Views/Menu/MenuBar.cs
  15. 49 41
      UnitTests/Dialogs/DialogTests.cs
  16. 8 5
      UnitTests/Drawing/RulerTests.cs
  17. 9 5
      UnitTests/Drawing/ThicknessTests.cs
  18. 21 11
      UnitTests/Text/AutocompleteTests.cs
  19. 12 0
      UnitTests/View/Adornment/AdornmentTests.cs
  20. 2 145
      UnitTests/View/Draw/DrawTests.cs
  21. 289 0
      UnitTests/View/Draw/NeedsDisplayTests.cs
  22. 28 0
      UnitTests/View/Layout/Pos.AlignTests.cs
  23. 16 15
      UnitTests/View/Layout/Pos.AnchorEndTests.cs
  24. 1 2
      UnitTests/View/Layout/Pos.CombineTests.cs
  25. 184 36
      UnitTests/View/Layout/SetLayoutTests.cs
  26. 2 2
      UnitTests/View/Layout/ToScreenTests.cs
  27. 0 51
      UnitTests/View/NeedsDisplayTests.cs
  28. 29 31
      UnitTests/View/TextTests.cs
  29. 5 10
      UnitTests/View/ViewTests.cs
  30. 1 0
      UnitTests/Views/ButtonTests.cs
  31. 9 1
      UnitTests/Views/CheckBoxTests.cs
  32. 16 17
      UnitTests/Views/ContextMenuTests.cs
  33. 14 18
      UnitTests/Views/ScrollBarViewTests.cs
  34. 2 1
      UnitTests/Views/StatusBarTests.cs
  35. 1 1
      UnitTests/Views/TextValidateFieldTests.cs

+ 17 - 12
Terminal.Gui/Application/Application.Run.cs

@@ -181,10 +181,11 @@ public static partial class Application // Run (Begin, Run, End, Stop)
         if (!toplevel.IsInitialized)
         {
             toplevel.BeginInit ();
-            toplevel.EndInit ();
+            toplevel.EndInit (); // Calls Layout
 
-            // Force a layout - normally this is done each iteration of the main loop but we prime it here.
-            toplevel.Layout (Screen.Size);
+            //// Force a layout - normally this is done each iteration of the main loop but we prime it here.
+            //toplevel.SetLayoutNeeded ();
+            //toplevel.Layout (Screen.Size);
         }
 
         // Try to set initial focus to any TabStop
@@ -196,7 +197,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
         // DEBATE: Should Begin call Refresh (or Draw) here? It previously did.
         //   FOR: the screen has something on it after Begin is called.
         //   AGAINST: the screen is cleared and then redrawn in RunLoop. We don't want to draw twice.
-        Refresh();
+        Refresh ();
 
         toplevel.OnLoaded ();
 
@@ -229,7 +230,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
         if (mostFocused is null || !mostFocused.Visible || !mostFocused.Enabled)
         {
             CursorVisibility current = CursorVisibility.Invisible;
-            Driver?.GetCursorVisibility (out  current);
+            Driver?.GetCursorVisibility (out current);
 
             if (current != CursorVisibility.Invisible)
             {
@@ -490,8 +491,12 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// <summary>Wakes up the running application that might be waiting on input.</summary>
     public static void Wakeup () { MainLoop?.Wakeup (); }
 
-    /// <summary>Triggers a refresh of the entire display.</summary>
-    public static void Refresh ()
+    /// <summary>
+    /// Refreshes layout and the display. Only Views that need to be laid out (see <see cref="View.IsLayoutNeeded()"/>) will be laid out.
+    /// Only Views that need to be drawn (see <see cref="View.NeedsDisplay"/>) will be drawn.
+    /// </summary>
+    /// <param name="forceRedraw">If <see langword="true"/> the entire View hierarchy will be redrawn. The default is <see langword="false"/> and should only be overriden for testing.</param>
+    public static void Refresh (bool forceRedraw = false)
     {
         bool clear = false;
         foreach (Toplevel tl in TopLevels.Reverse ())
@@ -503,21 +508,21 @@ public static partial class Application // Run (Begin, Run, End, Stop)
             }
         }
 
-        if (clear)
+        if (clear || forceRedraw)
         {
-            Driver!.ClearContents ();
+            Driver?.ClearContents ();
         }
 
         foreach (Toplevel tl in TopLevels.Reverse ())
         {
-            if (clear)
+            if (clear || forceRedraw)
             {
-                tl.SetNeedsDisplay(Screen);
+                tl.SetNeedsDisplay ();
             }
             tl.Draw ();
         }
 
-        Driver!.Refresh ();
+        Driver?.Refresh ();
     }
 
     /// <summary>This event is raised on each iteration of the main loop.</summary>

+ 5 - 7
Terminal.Gui/Terminal.Gui.csproj

@@ -109,6 +109,9 @@
       <LastGenOutput>Strings.Designer.cs</LastGenOutput>
     </EmbeddedResource>
   </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Views\Scroll\" />
+  </ItemGroup>
   <!-- =================================================================== -->
   <!-- Nuget  -->
   <!-- =================================================================== -->
@@ -143,9 +146,7 @@
   </PropertyGroup>
   <ProjectExtensions><VisualStudio><UserProperties resources_4config_1json__JsonSchema="../../docfx/schemas/tui-config-schema.json" /></VisualStudio></ProjectExtensions>
 
-  <Target Name="CopyNuGetPackagesToLocalPackagesFolder"
-          AfterTargets="Pack"
-          Condition="'$(Configuration)' == 'Release'">
+  <Target Name="CopyNuGetPackagesToLocalPackagesFolder" AfterTargets="Pack" Condition="'$(Configuration)' == 'Release'">
       <PropertyGroup>
           <!-- Define the path for local_packages relative to the project directory -->
           <LocalPackagesPath>$(MSBuildThisFileDirectory)..\local_packages\</LocalPackagesPath>
@@ -166,10 +167,7 @@
       <Message Text="Found packages: @(NuGetPackages)" Importance="high" />
 
       <!-- Copy files only if found -->
-      <Copy SourceFiles="@(NuGetPackages)"
-            DestinationFolder="$(LocalPackagesPath)"
-            SkipUnchangedFiles="false"
-            Condition="@(NuGetPackages) != ''" />
+      <Copy SourceFiles="@(NuGetPackages)" DestinationFolder="$(LocalPackagesPath)" SkipUnchangedFiles="false" Condition="@(NuGetPackages) != ''" />
 
       <!-- Log success -->
       <Message Text="Copy completed successfully." Importance="high" />

+ 0 - 1
Terminal.Gui/View/Adornment/Adornment.cs

@@ -57,7 +57,6 @@ public class Adornment : View
             {
                 Parent?.SetAdornmentFrames ();
                 Parent?.SetLayoutNeeded ();
-                //Parent?.LayoutSubviews ();
 
                 OnThicknessChanged ();
             }

+ 8 - 0
Terminal.Gui/View/Layout/Dim.cs

@@ -22,6 +22,14 @@ using System.Numerics;
 ///             </listheader>
 ///             <item>
 ///                 <term>
+///                     <see cref="Dim.Absolute"/>
+///                 </term>
+///                 <description>
+///                     Creates a <see cref="Dim"/> that is a fixed size.
+///                 </description>
+///             </item>
+///             <item>
+///                 <term>
 ///                     <see cref="Dim.Auto"/>
 ///                 </term>
 ///                 <description>

+ 2 - 2
Terminal.Gui/View/Layout/PosAlign.cs

@@ -110,12 +110,12 @@ public record PosAlign : Pos
 
     internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
     {
-        if (_cachedLocation.HasValue && Aligner.ContainerSize == superviewDimension)
+        if (_cachedLocation.HasValue && Aligner.ContainerSize == superviewDimension && !us.IsLayoutNeeded())
         {
             return _cachedLocation.Value;
         }
 
-        if (us?.SuperView is null)
+        if (us.SuperView is null)
         {
             return 0;
         }

+ 21 - 14
Terminal.Gui/View/View.Adornments.cs

@@ -134,9 +134,23 @@ public partial class View // Adornments
         get => Border?.LineStyle ?? LineStyle.Single;
         set
         {
+            if (Border is null)
+            {
+                return;
+            }
+
             LineStyle old = Border?.LineStyle ?? LineStyle.None;
             CancelEventArgs<LineStyle> e = new (ref old, ref value);
-            OnBorderStyleChanging (e);
+
+            if (OnBorderStyleChanging (e)|| e.Cancel)
+            {
+                return;
+            }
+
+            SetBorderStyle (e.NewValue);
+            SetAdornmentFrames ();
+            SetLayoutNeeded ();
+
         }
     }
 
@@ -148,23 +162,16 @@ public partial class View // Adornments
     ///     Override <see cref="SetBorderStyle"/> to prevent the <see cref="BorderStyle"/> from changing.
     /// </remarks>
     /// <param name="e"></param>
-    protected void OnBorderStyleChanging (CancelEventArgs<LineStyle> e)
+    protected virtual bool OnBorderStyleChanging (CancelEventArgs<LineStyle> e)
     {
         if (Border is null)
         {
-            return;
+            return false;
         }
 
         BorderStyleChanging?.Invoke (this, e);
 
-        if (e.Cancel)
-        {
-            return;
-        }
-
-        SetBorderStyle (e.NewValue);
-        SetAdornmentFrames ();
-        SetLayoutNeeded ();
+        return e.Cancel;
     }
 
     /// <summary>
@@ -257,8 +264,8 @@ public partial class View // Adornments
             return; // CreateAdornments () has not been called yet
         }
 
-        Margin.SetFrame (Rectangle.Empty with { Size = Frame.Size });
-        Border.SetFrame (Margin.Thickness.GetInside (Margin.Frame));
-        Padding.SetFrame (Border.Thickness.GetInside (Border.Frame));
+        Margin.Frame = Rectangle.Empty with { Size = Frame.Size };
+        Border.Frame = Margin.Thickness.GetInside (Margin.Frame);
+        Padding.Frame = Border.Thickness.GetInside (Border.Frame);
     }
 }

+ 53 - 44
Terminal.Gui/View/View.Drawing.cs

@@ -38,29 +38,6 @@ public partial class View // Drawing APIs
     /// <remarks><see cref="Border"/> adds border lines to this LineCanvas.</remarks>
     public LineCanvas LineCanvas { get; } = new ();
 
-    // The view-relative region that needs to be redrawn. Marked internal for unit tests.
-    internal Rectangle _needsDisplayRect = Rectangle.Empty;
-
-    /// <summary>Gets or sets whether the view needs to be redrawn.</summary>
-    public bool NeedsDisplay
-    {
-        get => _needsDisplayRect != Rectangle.Empty;
-        set
-        {
-            if (value)
-            {
-                SetNeedsDisplay ();
-            }
-            else
-            {
-                ClearNeedsDisplay ();
-            }
-        }
-    }
-
-    /// <summary>Gets whether any Subviews need to be redrawn.</summary>
-    public bool SubViewNeedsDisplay { get; private set; }
-
     /// <summary>
     ///     Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any
     ///     lines. If <see langword="true"/> the rendering of any borders drawn by this Frame will be done by its parent's
@@ -476,11 +453,6 @@ public partial class View // Drawing APIs
     /// <returns></returns>
     public virtual bool OnDrawAdornments ()
     {
-        if (!IsInitialized)
-        {
-            return false;
-        }
-
         // Each of these renders lines to either this View's LineCanvas 
         // Those lines will be finally rendered in OnRenderLineCanvas
         // QUESTION: Why are we not calling Draw here?
@@ -606,7 +578,7 @@ public partial class View // Drawing APIs
     /// <returns></returns>
     public virtual bool OnRenderLineCanvas ()
     {
-        if (!IsInitialized || Driver is null)
+        if (Driver is null)
         {
             return false;
         }
@@ -658,37 +630,72 @@ public partial class View // Drawing APIs
         return true;
     }
 
-    /// <summary>Sets the area of this view needing to be redrawn to <see cref="Viewport"/>.</summary>
+    #region NeedsDisplay
+
+    // The viewport-relative region that needs to be redrawn. Marked internal for unit tests.
+    internal Rectangle _needsDisplayRect = Rectangle.Empty;
+
+    /// <summary>Gets or sets whether the view needs to be redrawn.</summary>
+    public bool NeedsDisplay
+    {
+        get => _needsDisplayRect != Rectangle.Empty || IsLayoutNeeded ();
+        set
+        {
+            if (value)
+            {
+                SetNeedsDisplay ();
+            }
+            else
+            {
+                ClearNeedsDisplay ();
+            }
+        }
+    }
+
+    /// <summary>Gets whether any Subviews need to be redrawn.</summary>
+    public bool SubViewNeedsDisplay { get; private set; }
+
+    /// <summary>Sets that the <see cref="Viewport"/> of this View needs to be redrawn.</summary>
     /// <remarks>
     ///     If the view has not been initialized (<see cref="IsInitialized"/> is <see langword="false"/>), this method
     ///     does nothing.
     /// </remarks>
-    public void SetNeedsDisplay () { SetNeedsDisplay (Viewport); }
+    public void SetNeedsDisplay ()
+    {
+        Rectangle viewport = Viewport;
 
-    /// <summary>Expands the area of this view needing to be redrawn to include <paramref name="region"/>.</summary>
+        if (_needsDisplayRect != Rectangle.Empty && viewport.IsEmpty)
+        {
+            // This handles the case where the view has not been initialized yet
+            return;
+        }
+
+        SetNeedsDisplay (viewport);
+    }
+
+    /// <summary>Expands the area of this view needing to be redrawn to include <paramref name="viewPortRelativeRegion"/>.</summary>
     /// <remarks>
     ///     <para>
-    ///         The location of <paramref name="region"/> is relative to the View's content, bound by <c>Size.Empty</c> and
-    ///         <see cref="GetContentSize ()"/>.
+    ///         The location of <paramref name="viewPortRelativeRegion"/> is relative to the View's <see cref="Viewport"/>.
     ///     </para>
     ///     <para>
     ///         If the view has not been initialized (<see cref="IsInitialized"/> is <see langword="false"/>), the area to be
-    ///         redrawn will be the <paramref name="region"/>.
+    ///         redrawn will be the <paramref name="viewPortRelativeRegion"/>.
     ///     </para>
     /// </remarks>
-    /// <param name="region">The content-relative region that needs to be redrawn.</param>
-    public void SetNeedsDisplay (Rectangle region)
+    /// <param name="viewPortRelativeRegion">The <see cref="Viewport"/>relative region that needs to be redrawn.</param>
+    public void SetNeedsDisplay (Rectangle viewPortRelativeRegion)
     {
         if (_needsDisplayRect.IsEmpty)
         {
-            _needsDisplayRect = region;
+            _needsDisplayRect = viewPortRelativeRegion;
         }
         else
         {
-            int x = Math.Min (_needsDisplayRect.X, region.X);
-            int y = Math.Min (_needsDisplayRect.Y, region.Y);
-            int w = Math.Max (_needsDisplayRect.Width, region.Width);
-            int h = Math.Max (_needsDisplayRect.Height, region.Height);
+            int x = Math.Min (Viewport.X, viewPortRelativeRegion.X);
+            int y = Math.Min (Viewport.Y, viewPortRelativeRegion.Y);
+            int w = Math.Max (Viewport.Width, viewPortRelativeRegion.Width);
+            int h = Math.Max (Viewport.Height, viewPortRelativeRegion.Height);
             _needsDisplayRect = new (x, y, w, h);
         }
 
@@ -705,9 +712,9 @@ public partial class View // Drawing APIs
 
         foreach (View subview in Subviews)
         {
-            if (subview.Frame.IntersectsWith (region))
+            if (subview.Frame.IntersectsWith (viewPortRelativeRegion))
             {
-                Rectangle subviewRegion = Rectangle.Intersect (subview.Frame, region);
+                Rectangle subviewRegion = Rectangle.Intersect (subview.Frame, viewPortRelativeRegion);
                 subviewRegion.X -= subview.Frame.X;
                 subviewRegion.Y -= subview.Frame.Y;
                 subview.SetNeedsDisplay (subviewRegion);
@@ -746,4 +753,6 @@ public partial class View // Drawing APIs
             subview.ClearNeedsDisplay ();
         }
     }
+    #endregion NeedsDisplay
+
 }

+ 2 - 1
Terminal.Gui/View/View.Hierarchy.cs

@@ -85,7 +85,8 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
             view.EndInit ();
         }
 
-        SetLayoutNeeded();
+        SetNeedsDisplay ();
+        SetLayoutNeeded ();
 
         return view;
     }

+ 35 - 2
Terminal.Gui/View/View.Layout.cs

@@ -214,6 +214,9 @@ public partial class View // Layout APIs
                 _y = _frame.Y;
                 _width = _frame.Width;
                 _height = _frame.Height;
+
+                // Implicit layout is ok here because we are setting the Frame directly.
+                Layout ();
             }
         }
     }
@@ -242,6 +245,7 @@ public partial class View // Layout APIs
 
         SetAdornmentFrames ();
 
+        SetNeedsDisplay ();
         SetLayoutNeeded ();
 
         // BUGBUG: When SetFrame is called from Frame_set, this event gets raised BEFORE OnResizeNeeded. Is that OK?
@@ -347,9 +351,20 @@ public partial class View // Layout APIs
             _x = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (X)} cannot be null");
 
             SetLayoutNeeded ();
+
+            if (IsAbsoluteLayout())
+            {
+                // Implicit layout is ok here because all Pos/Dim are Absolute values.
+                Layout ();
+            }
         }
     }
 
+    private bool IsAbsoluteLayout ()
+    {
+        return _x is PosAbsolute && _y is PosAbsolute && _width is DimAbsolute && _height is DimAbsolute;
+    }
+
     private Pos _y = Pos.Absolute (0);
 
     /// <summary>Gets or sets the Y position for the view (the row).</summary>
@@ -390,6 +405,12 @@ public partial class View // Layout APIs
             _y = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Y)} cannot be null");
 
             SetLayoutNeeded ();
+
+            if (IsAbsoluteLayout ())
+            {
+                // Implicit layout is ok here because all Pos/Dim are Absolute values.
+                Layout ();
+            }
         }
     }
 
@@ -443,6 +464,12 @@ public partial class View // Layout APIs
             TextFormatter.ConstrainToHeight = null;
 
             SetLayoutNeeded ();
+
+            if (IsAbsoluteLayout ())
+            {
+                // Implicit layout is ok here because all Pos/Dim are Absolute values.
+                Layout ();
+            }
         }
     }
 
@@ -496,6 +523,12 @@ public partial class View // Layout APIs
             TextFormatter.ConstrainToWidth = null;
 
             SetLayoutNeeded ();
+
+            if (IsAbsoluteLayout ())
+            {
+                // Implicit layout is ok here because all Pos/Dim are Absolute values.
+                Layout ();
+            }
         }
     }
 
@@ -743,7 +776,7 @@ public partial class View // Layout APIs
     /// <summary>
     ///     Performs layout of the view and its subviews using the content size of either the <see cref="SuperView"/> or <see cref="Application.Screen"/>.
     /// </summary>
-    /// <returns><see langword="false"/>If the view could not be laid out (typically because a dependencies was not ready). </returns>
+    /// <returns><see langword="false"/>If the view could not be laid out (typically because dependency was not ready). </returns>
     public bool Layout ()
     {
         return Layout (GetBestGuessSuperViewContentSize ());
@@ -763,7 +796,7 @@ public partial class View // Layout APIs
 
 
     // We expose no setter for this to ensure that the ONLY place it's changed is in SetNeedsLayout
-    private bool _layoutNeeded = true;
+    private bool _layoutNeeded = false;
 
     /// <summary>
     ///     Indicates the View's Frame or the layout of the View's subviews (including Adornments) have

+ 8 - 3
Terminal.Gui/View/View.Text.cs

@@ -4,21 +4,20 @@ namespace Terminal.Gui;
 
 public partial class View // Text Property APIs
 {
-    private string _text = null!;
+    private string _text = string.Empty;
 
     /// <summary>
     ///     Called when the <see cref="Text"/> has changed. Fires the <see cref="TextChanged"/> event.
     /// </summary>
     public void OnTextChanged () { TextChanged?.Invoke (this, EventArgs.Empty); }
 
-    // TODO: Make this non-virtual. Nobody overrides it.
     /// <summary>
     ///     Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
     ///     or not when <see cref="TextFormatter.WordWrap"/> is enabled.
     ///     If <see langword="true"/> trailing spaces at the end of wrapped lines will be removed when
     ///     <see cref="Text"/> is formatted for display. The default is <see langword="false"/>.
     /// </summary>
-    public virtual bool PreserveTrailingSpaces
+    public bool PreserveTrailingSpaces
     {
         get => TextFormatter.PreserveTrailingSpaces;
         set
@@ -27,6 +26,7 @@ public partial class View // Text Property APIs
             {
                 TextFormatter.PreserveTrailingSpaces = value;
                 TextFormatter.NeedsFormat = true;
+                SetLayoutNeeded ();
             }
         }
     }
@@ -58,6 +58,11 @@ public partial class View // Text Property APIs
         get => _text;
         set
         {
+            if (_text == value)
+            {
+                return;
+            }
+
             string old = _text;
             _text = value;
 

+ 0 - 1
Terminal.Gui/View/View.cs

@@ -144,7 +144,6 @@ public partial class View : Responder, ISupportInitializeNotification
         //SetupMouse ();
 
         SetupText ();
-
     }
 
     /// <summary>

+ 2 - 0
Terminal.Gui/Views/ComboBox.cs

@@ -507,6 +507,8 @@ public class ComboBox : View, IDesignable
         _listview.Clear ();
         _listview.TabStop = TabBehavior.NoStop;
         SuperView?.MoveSubviewToStart (this);
+
+        // BUGBUG: SetNeedsDisplay takes Viewport relative coordinates, not Screen
         Rectangle rect = _listview.ViewportToScreen (_listview.IsInitialized ? _listview.Viewport : Rectangle.Empty);
         SuperView?.SetNeedsDisplay (rect);
         OnCollapsed ();

+ 9 - 0
Terminal.Gui/Views/Menu/Menu.cs

@@ -71,6 +71,15 @@ internal sealed class Menu : View
         base.BeginInit ();
 
         var frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent);
+
+        if (Frame.X != frame.X)
+        {
+            X = frame.X;
+        }
+        if (Frame.Y != frame.Y)
+        {
+            Y = frame.Y;
+        }
         Width = frame.Width;
         Height = frame.Height;
 

+ 1 - 1
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -381,7 +381,7 @@ public class MenuBar : View, IDesignable
                 mi = parent?.Children?.Length > 0 ? parent.Children [_openMenu!._currentChild] : null;
             }
         }
-
+        
         MenuOpened?.Invoke (this, new (parent, mi));
     }
 

+ 49 - 41
UnitTests/Dialogs/DialogTests.cs

@@ -51,7 +51,7 @@ public class DialogTests
         // Now add a second button
         buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}";
         dlg.AddButton (new () { Text = btn2Text });
-        
+
         RunIteration (ref runstate);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
         End (runstate);
@@ -160,7 +160,7 @@ public class DialogTests
 
 
         // Default - Center
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -177,7 +177,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1}  {btn2}  {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                       title,
                                                       width,
                                                       Alignment.Fill,
@@ -194,7 +194,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}  {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                       title,
                                                       width,
                                                       Alignment.End,
@@ -211,7 +211,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}  {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                       title,
                                                       width,
                                                       Alignment.Start,
@@ -255,7 +255,7 @@ public class DialogTests
         buttonRow =
             $"{CM.Glyphs.VLine} yes {CM.Glyphs.RightBracket}{btn2}{btn3}{CM.Glyphs.LeftBracket} never{CM.Glyphs.VLine}";
 
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                       title,
                                                       width,
                                                       Alignment.Center,
@@ -273,7 +273,7 @@ public class DialogTests
         buttonRow =
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} yes {CM.Glyphs.LeftBracket} no {CM.Glyphs.LeftBracket} maybe {CM.Glyphs.LeftBracket} never {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -290,7 +290,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}es {CM.Glyphs.RightBracket}{btn2}{btn3}{btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -306,7 +306,7 @@ public class DialogTests
         // Left
         buttonRow = $"{CM.Glyphs.VLine}{btn1}{btn2}{btn3}{CM.Glyphs.LeftBracket} neve{CM.Glyphs.VLine}";
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -349,7 +349,7 @@ public class DialogTests
         d.SetBufferSize (buttonRow.Length, 1);
 
         // Default - Center
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -366,7 +366,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1}     {btn2}     {btn3}     {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -383,7 +383,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}            {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -400,7 +400,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}            {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -445,7 +445,7 @@ public class DialogTests
         d.SetBufferSize (width, 3);
 
         // Default - Center
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -462,7 +462,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1}     {btn2}     {btn3}     {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.GetColumns ());
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -479,7 +479,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}            {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.GetColumns ());
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -496,7 +496,7 @@ public class DialogTests
         buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}            {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.GetColumns ());
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -530,7 +530,7 @@ public class DialogTests
 
         d.SetBufferSize (width, 1);
 
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -547,7 +547,7 @@ public class DialogTests
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}    {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -562,7 +562,7 @@ public class DialogTests
             $"{CM.Glyphs.VLine}    {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -577,7 +577,7 @@ public class DialogTests
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}    {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -594,7 +594,7 @@ public class DialogTests
 
         d.SetBufferSize (width, 1);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -609,7 +609,7 @@ public class DialogTests
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}      {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -624,7 +624,7 @@ public class DialogTests
             $"{CM.Glyphs.VLine}      {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -639,7 +639,7 @@ public class DialogTests
             $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}      {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -675,7 +675,7 @@ public class DialogTests
 
         d.SetBufferSize (buttonRow.Length, 3);
 
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -691,7 +691,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}{btn1}  {btn2}  {btn3}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -707,7 +707,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}  {btn1} {btn2} {btn3}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -723,7 +723,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}{btn1} {btn2} {btn3}  {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -759,7 +759,7 @@ public class DialogTests
 
         d.SetBufferSize (buttonRow.Length, 3);
 
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -774,7 +774,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}{btn1}   {btn2}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Fill,
@@ -789,7 +789,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}  {btn1} {btn2}{CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.End,
@@ -804,7 +804,7 @@ public class DialogTests
         buttonRow = $@"{CM.Glyphs.VLine}{btn1} {btn2}  {CM.Glyphs.VLine}";
         Assert.Equal (width, buttonRow.Length);
 
-        (runstate, dlg) = RunButtonTestDialog (
+        (runstate, dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Start,
@@ -846,7 +846,7 @@ public class DialogTests
         // Default (Center)
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.Center, button1, button2);
+        (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.Center, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
         buttonRow = $@"{CM.Glyphs.VLine}         {btn2} {CM.Glyphs.VLine}";
@@ -858,7 +858,7 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.Fill, button1, button2);
+        (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.Fill, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
         buttonRow = $@"{CM.Glyphs.VLine}          {btn2}{CM.Glyphs.VLine}";
@@ -870,7 +870,7 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.End, button1, button2);
+        (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.End, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -881,7 +881,7 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.Start, button1, button2);
+        (runstate, dlg) = BeginButtonTestDialog (title, width, Alignment.Start, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, firstIteration);
         buttonRow = $@"{CM.Glyphs.VLine}        {btn2}  {CM.Glyphs.VLine}";
@@ -1323,7 +1323,7 @@ public class DialogTests
         int width = buttonRow.Length;
         d.SetBufferSize (buttonRow.Length, 10);
 
-        (runstate, Dialog dlg) = RunButtonTestDialog (
+        (runstate, Dialog dlg) = BeginButtonTestDialog (
                                                     title,
                                                     width,
                                                     Alignment.Center,
@@ -1382,14 +1382,15 @@ public class DialogTests
         int width = buttonRow.Length;
         d.SetBufferSize (buttonRow.Length, 3);
 
-        (runstate, Dialog dlg) = RunButtonTestDialog (title, width, Alignment.Center, null);
+        (runstate, Dialog dlg) = BeginButtonTestDialog (title, width, Alignment.Center, null);
+
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
 
         End (runstate);
         dlg.Dispose ();
     }
 
-    private (RunState, Dialog) RunButtonTestDialog (
+    private (RunState, Dialog) BeginButtonTestDialog (
         string title,
         int width,
         Alignment align,
@@ -1416,7 +1417,14 @@ public class DialogTests
         // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
         dlg.Border.Thickness = new (1, 0, 1, 0);
 
-        return (Begin (dlg), dlg);
+        RunState runState = Begin (dlg);
+
+        dlg.SetNeedsDisplay();
+        dlg.SetLayoutNeeded ();
+        dlg.Layout ();
+        dlg.Draw ();
+
+        return (runState, dlg);
     }
 
     [Fact]

+ 8 - 5
UnitTests/Drawing/RulerTests.cs

@@ -67,6 +67,7 @@ public class RulerTests
                                                      );
 
         // Postive offset
+        top.SetNeedsDisplay ();
         Application.Refresh ();
         r.Draw (new (1, 1));
 
@@ -81,6 +82,7 @@ public class RulerTests
                                                      );
 
         // Negative offset
+        top.SetNeedsDisplay ();
         Application.Refresh ();
         r.Draw (new (-1, 1));
 
@@ -95,6 +97,7 @@ public class RulerTests
                                                      );
 
         // Clip
+        top.SetNeedsDisplay ();
         Application.Refresh ();
         r.Draw (new (10, 1));
 
@@ -140,7 +143,7 @@ public class RulerTests
                                                       _output
                                                      );
 
-        Application.Refresh ();
+        Application.Refresh (true);
         r.Length = len;
         r.Draw (new (1, 0), 1);
 
@@ -202,7 +205,7 @@ public class RulerTests
                                                      );
 
         // Postive offset
-        Application.Refresh ();
+        Application.Refresh (true);
         r.Draw (new (1, 1));
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -231,7 +234,7 @@ public class RulerTests
                                                      );
 
         // Negative offset
-        Application.Refresh ();
+        Application.Refresh (true);
         r.Draw (new (1, -1));
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -260,7 +263,7 @@ public class RulerTests
                                                      );
 
         // Clip
-        Application.Refresh ();
+        Application.Refresh (true);
         r.Draw (new (1, 10));
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -335,7 +338,7 @@ public class RulerTests
                                                       _output
                                                      );
 
-        Application.Refresh ();
+        Application.Refresh (true);
         r.Length = len;
         r.Draw (new (0, 1), 1);
 

+ 9 - 5
UnitTests/Drawing/ThicknessTests.cs

@@ -172,12 +172,13 @@ public class ThicknessTests (ITestOutputHelper output)
 
         var top = new Toplevel ();
         top.Add (f);
-        Application.Begin (top);
+        RunState rs = Application.Begin (top);
 
         ((FakeDriver)Application.Driver!).SetBufferSize (45, 20);
         var t = new Thickness (0, 0, 0, 0);
         var r = new Rectangle (2, 2, 40, 15);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
+
         View.Diagnostics |= ViewDiagnosticFlags.Ruler;
         t.Draw (r, "Test");
         View.Diagnostics = ViewDiagnosticFlags.Off;
@@ -209,7 +210,8 @@ public class ThicknessTests (ITestOutputHelper output)
 
         t = new Thickness (1, 1, 1, 1);
         r = new Rectangle (1, 1, 40, 15);
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
         View.Diagnostics |= ViewDiagnosticFlags.Ruler;
         t.Draw (r, "Test");
         View.Diagnostics = ViewDiagnosticFlags.Off;
@@ -241,7 +243,8 @@ public class ThicknessTests (ITestOutputHelper output)
 
         t = new Thickness (1, 2, 3, 4);
         r = new Rectangle (2, 2, 40, 15);
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
         View.Diagnostics |= ViewDiagnosticFlags.Ruler;
         t.Draw (r, "Test");
         View.Diagnostics = ViewDiagnosticFlags.Off;
@@ -273,7 +276,8 @@ public class ThicknessTests (ITestOutputHelper output)
 
         t = new Thickness (-1, 1, 1, 1);
         r = new Rectangle (5, 5, 40, 15);
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
         View.Diagnostics |= ViewDiagnosticFlags.Ruler;
         t.Draw (r, "Test");
         View.Diagnostics = ViewDiagnosticFlags.Off;

+ 21 - 11
UnitTests/Text/AutocompleteTests.cs

@@ -19,12 +19,13 @@ public class AutocompleteTests (ITestOutputHelper output)
                                 .ToList ();
         Toplevel top = new ();
         top.Add (tv);
-        Application.Begin (top);
+        RunState rs = Application.Begin (top);
 
         for (var i = 0; i < 7; i++)
         {
             Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
-            Application.Refresh ();
+            top.SetNeedsDisplay();
+            Application.RunIteration (ref rs);
 
             if (i < 4 || i > 5)
             {
@@ -51,7 +52,8 @@ This a long line and against TextView.
                                        new() { Position = new (6, 0), Flags = MouseFlags.Button1Pressed }
                                       )
                     );
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -62,7 +64,8 @@ This a long line and against TextView.
                                                      );
 
         Assert.True (tv.NewKeyDownEvent (Key.G));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -72,7 +75,8 @@ This ag long line and against TextView.
                                                      );
 
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -82,7 +86,8 @@ This ag long line and against TextView.
                                                      );
 
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -92,7 +97,8 @@ This ag long line and against TextView.
                                                      );
 
         Assert.True (tv.NewKeyDownEvent (Key.CursorLeft));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -103,7 +109,8 @@ This ag long line and against TextView.",
         for (var i = 0; i < 3; i++)
         {
             Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
-            Application.Refresh ();
+            top.SetNeedsDisplay ();
+            Application.RunIteration (ref rs);
 
             TestHelpers.AssertDriverContentsWithFrameAre (
                                                           @"
@@ -114,7 +121,8 @@ This ag long line and against TextView.
         }
 
         Assert.True (tv.NewKeyDownEvent (Key.Backspace));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -125,7 +133,8 @@ This a long line and against TextView.
                                                      );
 
         Assert.True (tv.NewKeyDownEvent (Key.N));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -135,7 +144,8 @@ This an long line and against TextView.
                                                      );
 
         Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
-        Application.Refresh ();
+        top.SetNeedsDisplay ();
+        Application.RunIteration (ref rs);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"

+ 12 - 0
UnitTests/View/Adornment/AdornmentTests.cs

@@ -109,6 +109,18 @@ public class AdornmentTests (ITestOutputHelper output)
         Assert.Equal (new Rectangle (2, 4, 5, 5), boundsAsScreen);
     }
 
+    [Fact]
+    public void SetAdornmentFrames_Sets_Frames_Correctly ()
+    {
+        var parent = new View { X = 1, Y = 2, Width = 10, Height = 20 };
+        parent.SetAdornmentFrames();
+
+        Assert.Equal (new Rectangle (1, 2, 10, 20), parent.Frame);
+        Assert.Equal (new Rectangle (0, 0, 10, 20), parent.Viewport);
+        Assert.Equal (new Rectangle (0, 0, 10, 20), parent.Margin.Frame);
+        Assert.Equal (new Rectangle (0, 0, 10, 20), parent.Margin.Viewport);
+    }
+
     [Fact]
     public void Frames_are_Parent_SuperView_Relative ()
     {

+ 2 - 145
UnitTests/View/DrawTests.cs → UnitTests/View/Draw/DrawTests.cs

@@ -7,138 +7,6 @@ namespace Terminal.Gui.ViewTests;
 [Trait ("Category", "Output")]
 public class DrawTests (ITestOutputHelper _output)
 {
-    [Fact]
-    public void NeedsDisplay_True_After_Constructor ()
-    {
-        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
-        Assert.True (view.NeedsDisplay);
-    }
-
-    [Fact]
-    public void NeedsDisplay_False_After_BeginInit ()
-    {
-        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
-        Assert.True (view.NeedsDisplay);
-
-        view.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.NeedsDisplay = false;
-
-        view.BeginInit ();
-        Assert.False (view.NeedsDisplay);
-    }
-
-    [Fact]
-    public void NeedsDisplay_False_After_EndInit ()
-    {
-        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
-        Assert.True (view.NeedsDisplay);
-
-        view.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.EndInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
-        view.BeginInit ();
-        view.NeedsDisplay = false;
-        view.EndInit ();
-        Assert.False (view.NeedsDisplay);
-    }
-
-    [Fact]
-    public void NeedsDisplay_False_After_SetRelativeLayout ()
-    {
-        var view = new View { Width = 2, Height = 2 };
-        Assert.True (view.NeedsDisplay);
-
-        view.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.EndInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.SetRelativeLayout (Application.Screen.Size);
-        Assert.True (view.NeedsDisplay);
-
-        view.NeedsDisplay = false;
-        view.SetRelativeLayout (new (10, 10));
-        Assert.False (view.NeedsDisplay);
-
-        view = new View { Width = Dim.Percent(50), Height = Dim.Percent(50) };
-        View superView = new ()
-        {
-            Id = "superView",
-            Width = Dim.Fill(),
-            Height = Dim.Fill()
-        };
-        superView.Add (view);
-
-        superView.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-        Assert.True (superView.NeedsDisplay);
-
-        superView.EndInit ();
-        Assert.True (view.NeedsDisplay);
-        Assert.True (superView.NeedsDisplay);
-
-        superView.SetRelativeLayout (Application.Screen.Size);
-        Assert.True (view.NeedsDisplay);
-        Assert.True (superView.NeedsDisplay);
-
-        superView.NeedsDisplay = false;
-        superView.SetRelativeLayout (new (10, 10));
-        Assert.False (superView.NeedsDisplay);
-        Assert.False (view.NeedsDisplay);
-
-        view.SetRelativeLayout (new (11, 11));
-        Assert.True (superView.NeedsDisplay);
-        Assert.True (view.NeedsDisplay);
-
-    }
-
-    [Fact]
-    public void NeedsDisplay_True_After_LayoutSubviews ()
-    {
-        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
-        Assert.True (view.NeedsDisplay);
-
-        view.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.EndInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.SetRelativeLayout (Application.Screen.Size);
-        Assert.True (view.NeedsDisplay);
-
-        view.LayoutSubviews ();
-        Assert.True (view.NeedsDisplay);
-    }
-
-    [Fact]
-    public void NeedsDisplay_False_After_Draw ()
-    {
-        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
-        Assert.True (view.NeedsDisplay);
-
-        view.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.EndInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.SetRelativeLayout (Application.Screen.Size);
-        Assert.True (view.NeedsDisplay);
-
-        view.LayoutSubviews ();
-        Assert.True (view.NeedsDisplay);
-
-        view.Draw ();
-        Assert.False (view.NeedsDisplay);
-    }
 
     [Fact]
     [SetupFakeDriver]
@@ -537,25 +405,14 @@ public class DrawTests (ITestOutputHelper _output)
     public void Draw_Minimum_Full_Border_With_Empty_Viewport ()
     {
         var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        Assert.True (view.IsLayoutNeeded());
         Assert.True (view.NeedsDisplay);
-
-        view.BeginInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.EndInit ();
-        Assert.True (view.NeedsDisplay);
-
-        view.SetRelativeLayout (Application.Screen.Size);
-        Assert.True (view.NeedsDisplay);
-
-        view.LayoutSubviews ();
-        Assert.True (view.NeedsDisplay);
+        view.Layout ();
 
         Assert.Equal (new (0, 0, 2, 2), view.Frame);
         Assert.Equal (Rectangle.Empty, view.Viewport);
 
         Assert.True (view.NeedsDisplay);
-
         view.Draw ();
 
         TestHelpers.AssertDriverContentsWithFrameAre (

+ 289 - 0
UnitTests/View/Draw/NeedsDisplayTests.cs

@@ -0,0 +1,289 @@
+#nullable enable
+using System.Text;
+using Microsoft.VisualStudio.TestPlatform.Utilities;
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+[Trait ("Category", "Output")]
+public class NeedsDisplayTests ()
+{
+    [Fact]
+    public void NeedsDisplay_False_If_Width_Height_Zero ()
+    {
+        View view = new () { Width = 0, Height = 0 };
+        view.BeginInit ();
+        view.EndInit ();
+        Assert.False (view.NeedsDisplay);
+        //Assert.False (view.SubViewNeedsDisplay);
+    }
+
+
+    [Fact]
+    public void NeedsDisplay_True_Initially_If_Width_Height_Not_Zero ()
+    {
+        View superView = new () { Width = 1, Height = 1 };
+        View view1 = new () { Width = 1, Height = 1 };
+        View view2 = new () { Width = 1, Height = 1 };
+
+        superView.Add (view1, view2);
+        superView.BeginInit ();
+        superView.EndInit ();
+
+        Assert.True (superView.NeedsDisplay);
+        Assert.True (superView.SubViewNeedsDisplay);
+        Assert.True (view1.NeedsDisplay);
+        Assert.True (view2.NeedsDisplay);
+
+        superView.Draw ();
+
+        Assert.False (superView.NeedsDisplay);
+        Assert.False (superView.SubViewNeedsDisplay);
+        Assert.False (view1.NeedsDisplay);
+        Assert.False (view2.NeedsDisplay);
+
+        superView.SetNeedsDisplay ();
+
+        Assert.True (superView.NeedsDisplay);
+        Assert.True (superView.SubViewNeedsDisplay);
+        Assert.True (view1.NeedsDisplay);
+        Assert.True (view2.NeedsDisplay);
+    }
+
+
+    [Fact]
+    public void NeedsDisplay_True_After_Constructor ()
+    {
+        var view = new View { Width = 2, Height = 2 };
+        Assert.True (view.NeedsDisplay);
+
+        view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        Assert.True (view.NeedsDisplay);
+    }
+
+    [Fact]
+    public void NeedsDisplay_True_After_BeginInit ()
+    {
+        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        Assert.True (view.NeedsDisplay);
+
+        view.BeginInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view.NeedsDisplay = false;
+
+        view.BeginInit ();
+        Assert.True (view.NeedsDisplay); // Because layout is still needed
+
+        view.Layout ();
+        Assert.False (view.NeedsDisplay);
+    }
+
+    [Fact]
+    public void NeedsDisplay_False_After_EndInit ()
+    {
+        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        Assert.True (view.NeedsDisplay);
+
+        view.BeginInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view.EndInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        view.BeginInit ();
+        view.NeedsDisplay = false;
+        view.EndInit ();
+        Assert.False (view.NeedsDisplay);
+    }
+
+
+    [Fact]
+    public void NeedsDisplay_After_SetLayoutNeeded ()
+    {
+        var view = new View { Width = 2, Height = 2 };
+        Assert.True (view.NeedsDisplay);
+        Assert.False (view.IsLayoutNeeded ());
+
+        view.Draw ();
+        Assert.False (view.NeedsDisplay);
+        Assert.False (view.IsLayoutNeeded ());
+
+        view.SetLayoutNeeded ();
+        Assert.True (view.NeedsDisplay);
+        Assert.True (view.IsLayoutNeeded ());
+    }
+
+    [Fact]
+    public void NeedsDisplay_False_After_SetRelativeLayout ()
+    {
+        var view = new View { Width = 2, Height = 2 };
+        Assert.True (view.NeedsDisplay);
+        Assert.False (view.IsLayoutNeeded ());
+
+        view.Draw ();
+        Assert.False (view.NeedsDisplay);
+        Assert.False (view.IsLayoutNeeded ());
+
+        // SRL won't change anything since the view is Absolute
+        view.SetRelativeLayout (Application.Screen.Size);
+        Assert.False (view.NeedsDisplay);
+
+        view.SetLayoutNeeded ();
+        // SRL won't change anything since the view is Absolute
+        view.SetRelativeLayout (Application.Screen.Size);
+        Assert.True (view.NeedsDisplay);
+
+        view.NeedsDisplay = false;
+        // SRL won't change anything since the view is Absolute. However, Layout has not been called
+        view.SetRelativeLayout (new (10, 10));
+        Assert.True (view.NeedsDisplay);
+
+        view = new View { Width = Dim.Percent (50), Height = Dim.Percent (50) };
+        View superView = new ()
+        {
+            Id = "superView",
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        };
+        Assert.True (superView.NeedsDisplay);
+
+        superView.Add (view);
+        Assert.True (view.NeedsDisplay);
+        Assert.True (superView.NeedsDisplay);
+
+        superView.BeginInit ();
+        Assert.True (view.NeedsDisplay);
+        Assert.True (superView.NeedsDisplay);
+
+        superView.EndInit ();
+        Assert.True (view.NeedsDisplay);
+        Assert.True (superView.NeedsDisplay);
+
+        superView.SetRelativeLayout (Application.Screen.Size);
+        Assert.True (view.NeedsDisplay);
+        Assert.True (superView.NeedsDisplay);
+
+        superView.NeedsDisplay = false;
+        superView.SetRelativeLayout (new (10, 10));
+        Assert.True (superView.NeedsDisplay);
+        Assert.True (view.NeedsDisplay);
+
+        superView.Layout ();
+
+        view.SetRelativeLayout (new (11, 11));
+        Assert.True (superView.NeedsDisplay);
+        Assert.True (view.NeedsDisplay);
+
+    }
+
+    [Fact]
+    public void NeedsDisplay_True_After_LayoutSubviews ()
+    {
+        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        Assert.True (view.NeedsDisplay);
+
+        view.BeginInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view.EndInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view.SetRelativeLayout (Application.Screen.Size);
+        Assert.True (view.NeedsDisplay);
+
+        view.LayoutSubviews ();
+        Assert.True (view.NeedsDisplay);
+    }
+
+    [Fact]
+    public void NeedsDisplay_False_After_Draw ()
+    {
+        var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
+        Assert.True (view.NeedsDisplay);
+
+        view.BeginInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view.EndInit ();
+        Assert.True (view.NeedsDisplay);
+
+        view.SetRelativeLayout (Application.Screen.Size);
+        Assert.True (view.NeedsDisplay);
+
+        view.LayoutSubviews ();
+        Assert.True (view.NeedsDisplay);
+
+        view.Draw ();
+        Assert.False (view.NeedsDisplay);
+    }
+
+    [Fact]
+    public void NeedsDisplayRect_Is_Viewport_Relative ()
+    {
+        View superView = new ()
+        {
+            Id = "superView",
+            Width = 10,
+            Height = 10
+        };
+        Assert.Equal (new (0, 0, 10, 10), superView.Frame);
+        Assert.Equal (new (0, 0, 10, 10), superView.Viewport);
+        Assert.Equal (new (0, 0, 10, 10), superView._needsDisplayRect);
+
+        var view = new View
+        {
+            Id = "view",
+        };
+
+        view.Frame = new (0, 1, 2, 3);
+        Assert.Equal (new (0, 1, 2, 3), view.Frame);
+        Assert.Equal (new (0, 0, 2, 3), view.Viewport);
+        Assert.Equal (new (0, 0, 2, 3), view._needsDisplayRect);
+
+        superView.Add (view);
+        Assert.Equal (new (0, 0, 10, 10), superView.Frame);
+        Assert.Equal (new (0, 0, 10, 10), superView.Viewport);
+        Assert.Equal (new (0, 0, 10, 10), superView._needsDisplayRect);
+        Assert.Equal (new (0, 1, 2, 3), view.Frame);
+        Assert.Equal (new (0, 0, 2, 3), view.Viewport);
+        Assert.Equal (new (0, 0, 2, 3), view._needsDisplayRect);
+
+        view.Frame = new (3, 3, 5, 5);
+        Assert.Equal (new (3, 3, 5, 5), view.Frame);
+        Assert.Equal (new (0, 0, 5, 5), view.Viewport);
+        Assert.Equal (new (0, 0, 5, 5), view._needsDisplayRect);
+
+        view.Frame = new (3, 3, 6, 6); // Grow right/bottom 1
+        Assert.Equal (new (3, 3, 6, 6), view.Frame);
+        Assert.Equal (new (0, 0, 6, 6), view.Viewport);
+        Assert.Equal (new (0, 0, 6, 6), view._needsDisplayRect); 
+
+        view.Frame = new (3, 3, 5, 5); // Shrink right/bottom 1
+        Assert.Equal (new (3, 3, 5, 5), view.Frame);
+        Assert.Equal (new (0, 0, 5, 5), view.Viewport);
+        Assert.Equal (new (0, 0, 5, 5), view._needsDisplayRect);
+
+        view.SetContentSize (new (10, 10));
+        Assert.Equal (new (3, 3, 5, 5), view.Frame);
+        Assert.Equal (new (0, 0, 5, 5), view.Viewport);
+        Assert.Equal (new (0, 0, 5, 5), view._needsDisplayRect);
+
+        view.Viewport = new (1, 1, 5, 5); // Scroll up/left 1
+        Assert.Equal (new (3, 3, 5, 5), view.Frame);
+        Assert.Equal (new (1, 1, 5, 5), view.Viewport);
+        Assert.Equal (new (0, 0, 5, 5), view._needsDisplayRect);
+
+        view.Frame = new (3, 3, 6, 6); // Grow right/bottom 1
+        Assert.Equal (new (3, 3, 6, 6), view.Frame);
+        Assert.Equal (new (1, 1, 6, 6), view.Viewport);
+        Assert.Equal (new (1, 1, 6, 6), view._needsDisplayRect); 
+
+        view.Frame = new (3, 3, 5, 5);
+        Assert.Equal (new (3, 3, 5, 5), view.Frame);
+        Assert.Equal (new (1, 1, 5, 5), view.Viewport);
+        Assert.Equal (new (1, 1, 5, 5), view._needsDisplayRect);
+
+    }
+}

+ 28 - 0
UnitTests/View/Layout/Pos.AlignTests.cs

@@ -106,6 +106,34 @@ public class PosAlignTests
         Assert.IsType<PosAlign> (pos);
     }
 
+    [Fact]
+    public void PosAlign_Set_View_X ()
+    {
+        Pos posAlign = Pos.Align (Alignment.Center);
+
+        var superView = new View ()
+        {
+            Id = "superView",
+            Width = 10,
+            Height = 1
+        };
+        var view = new View ()
+        {
+            Id = "view",
+            Width = 1,
+            Height = 1
+        };
+        superView.Add (view);
+        view.X = posAlign;
+        superView.Layout ();
+        Assert.Equal (4, view.Frame.X);
+
+        posAlign = Pos.Align (Alignment.End);
+        view.X = posAlign;
+        superView.Layout ();
+        Assert.Equal (9, view.Frame.X);
+    }
+
     // TODO: Test scenarios where views with matching GroupId's are added/removed from a Superview
 
     // TODO: Make AlignAndUpdateGroup internal and write low-level unit tests for it

+ 16 - 15
UnitTests/View/Layout/Pos.AnchorEndTests.cs

@@ -47,14 +47,14 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     }
 
     [Fact]
-    public void  PosAnchorEnd_CreatesCorrectInstance ()
+    public void PosAnchorEnd_CreatesCorrectInstance ()
     {
         var pos = Pos.AnchorEnd (10);
         Assert.IsType<PosAnchorEnd> (pos);
     }
 
     [Fact]
-    public void  PosAnchorEnd_Negative_Throws ()
+    public void PosAnchorEnd_Negative_Throws ()
     {
         Pos pos;
         int n = -1;
@@ -64,7 +64,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     [Theory]
     [InlineData (0)]
     [InlineData (1)]
-    public void  PosAnchorEnd_SetsValue_GetAnchor_Is_Negative (int offset)
+    public void PosAnchorEnd_SetsValue_GetAnchor_Is_Negative (int offset)
     {
         Pos pos = Pos.AnchorEnd (offset);
         Assert.Equal (offset, -pos.GetAnchor (0));
@@ -78,7 +78,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     [InlineData (20, 10, 5)]
     [InlineData (25, 10, 0)]
     [InlineData (26, 10, -1)]
-    public void  PosAnchorEnd_With_Offset_PositionsViewOffsetFromRight (int offset, int width, int expectedXPosition)
+    public void PosAnchorEnd_With_Offset_PositionsViewOffsetFromRight (int offset, int width, int expectedXPosition)
     {
         // Arrange
         var superView = new View { Width = 25, Height = 25 };
@@ -102,7 +102,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     // UseDimForOffset tests
 
     [Fact]
-    public void  PosAnchorEnd_UseDimForOffset_CreatesCorrectInstance ()
+    public void PosAnchorEnd_UseDimForOffset_CreatesCorrectInstance ()
     {
         var pos = Pos.AnchorEnd ();
         Assert.IsType<PosAnchorEnd> (pos);
@@ -110,7 +110,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     }
 
     [Fact]
-    public void  PosAnchorEnd_UseDimForOffset_SetsValue_GetAnchor_Is_Negative ()
+    public void PosAnchorEnd_UseDimForOffset_SetsValue_GetAnchor_Is_Negative ()
     {
         Pos pos = Pos.AnchorEnd ();
         Assert.Equal (-10, -pos.GetAnchor (10));
@@ -123,7 +123,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     [InlineData (11, 14)]
     [InlineData (25, 0)]
     [InlineData (26, -1)]
-    public void  PosAnchorEnd_UseDimForOffset_PositionsViewOffsetByDim (int dim, int expectedXPosition)
+    public void PosAnchorEnd_UseDimForOffset_PositionsViewOffsetByDim (int dim, int expectedXPosition)
     {
         // Arrange
         var superView = new View { Width = 25, Height = 25 };
@@ -149,14 +149,14 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     [InlineData (10, 23)]
     [InlineData (50, 13)]
     [InlineData (100, 0)]
-    public void  PosAnchorEnd_UseDimForOffset_DimPercent_PositionsViewOffsetByDim (int percent, int expectedXPosition)
+    public void PosAnchorEnd_UseDimForOffset_DimPercent_PositionsViewOffsetByDim (int percent, int expectedXPosition)
     {
         // Arrange
         var superView = new View { Width = 25, Height = 25 };
         var view = new View
         {
             X = Pos.AnchorEnd (),
-            Width = Dim.Percent ( percent),
+            Width = Dim.Percent (percent),
             Height = 1
         };
         superView.Add (view);
@@ -173,13 +173,13 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     // This test used to be Dialog_In_Window_With_TextField_And_Button_AnchorEnd in DialogTests.
     [Fact]
     [SetupFakeDriver]
-    public void  PosAnchorEnd_View_And_Button ()
+    public void PosAnchorEnd_View_And_Button ()
     {
         ((FakeDriver)Application.Driver!).SetBufferSize (20, 5);
 
         // Override CM
         Button.DefaultShadow = ShadowStyle.None;
-        
+
         var b = $"{CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket}";
 
         var frame = new FrameView { Width = 18, Height = 3 };
@@ -203,9 +203,9 @@ public class PosAnchorEndTests (ITestOutputHelper output)
         };
 
         frame.Add (btn, view);
-        frame.BeginInit ();
-        frame.EndInit ();
-        frame.Draw ();
+        frame.BeginInit(); // Needed to enable Border
+        frame.EndInit();
+        frame.Layout ();
 
         Assert.Equal (6, btn.Viewport.Width);
         Assert.Equal (10, btn.Frame.X); // frame.Viewport.Width (16) - btn.Frame.Width (6) = 10
@@ -219,6 +219,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
         Assert.Equal (9, view.Frame.Width);
         Assert.Equal (1, view.Frame.Height);
 
+        frame.Draw ();
         var expected = $@"
 ┌────────────────┐
 │012345678 {b}│
@@ -232,7 +233,7 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     // TODO: A new test that calls SetRelativeLayout directly is needed.
     [Fact]
     [AutoInitShutdown]
-    public void  PosAnchorEnd_Equal_Inside_Window ()
+    public void PosAnchorEnd_Equal_Inside_Window ()
     {
         var viewWidth = 10;
         var viewHeight = 1;

+ 1 - 2
UnitTests/View/Layout/Pos.CombineTests.cs

@@ -86,8 +86,7 @@ public class PosCombineTests (ITestOutputHelper output)
         win2.Add (view2);
         win1.Add (view1, win2);
         Application.Top.Add (win1);
-        Application.Top.BeginInit ();
-        Application.Top.EndInit ();
+        Application.Top.Layout ();
 
         Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Top.Frame);
         Assert.Equal (new Rectangle (0, 0, 5, 1), view1.Frame);

+ 184 - 36
UnitTests/View/Layout/SetLayoutTests.cs

@@ -117,6 +117,7 @@ public class SetLayoutTests (ITestOutputHelper output)
         superView.LayoutStarted += (sender, e) => layoutStartedRaised++;
         superView.LayoutComplete += (sender, e) => layoutCompleteRaised++;
 
+        superView.SetLayoutNeeded ();
         superView.LayoutSubviews ();
         Assert.Equal (1, layoutStartedRaised);
         Assert.Equal (1, layoutCompleteRaised);
@@ -290,96 +291,243 @@ public class SetLayoutTests (ITestOutputHelper output)
 
         superView.Dispose ();
     }
+    
+    [Fact]
+    public void Set_X_PosAbsolute_Layout_Is_Implicit ()
+    {
+        var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.X);
+
+        v.X = 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (1, v.Frame.X);
+
+        v.X = 2;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (2, v.Frame.X);
+
+        v.X = Pos.Absolute (3);
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.X);
+
+        v.X = Pos.Absolute (3) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (4, v.Frame.X);
+
+        v.X = 1 + Pos.Absolute (1) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.X);
+
+    }
 
     [Fact]
-    public void Set_X_Does_Not_Change_Frame_Until_Layout ()
+    public void Set_X_Non_PosAbsolute_Explicit_Layout_Required ()
     {
         var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.X);
 
-        v.Layout ();
+        v.X = Pos.Center ();
+        Assert.True (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.X);
 
-        v.X = 1;
+        v.X = Pos.Percent (50);
+        Assert.True (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.X);
 
-        v.Layout ();
-        Assert.Equal (1, v.Frame.X);
+        v.X = Pos.Align (Alignment.Center);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.X);
 
-        v.X = 2;
-        Assert.Equal (1, v.Frame.X);
+        v.X = Pos.Func (() => 10);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.X);
 
-        v.Layout ();
-        Assert.Equal (2, v.Frame.X);
+        v.X = Pos.AnchorEnd ();
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.X);
+
+        v.X = Pos.Top (new View ());
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.X);
     }
 
 
     [Fact]
-    public void Set_Y_Does_Not_Change_Frame_Until_Layout ()
+    public void Set_Y_PosAbsolute_Layout_Is_Implicit ()
     {
         var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Y);
 
-        v.Layout ();
+        v.Y = 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (1, v.Frame.Y);
+
+        v.Y = 2;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (2, v.Frame.Y);
+
+        v.Y = Pos.Absolute (3);
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.Y);
+
+        v.Y = Pos.Absolute (3) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (4, v.Frame.Y);
+
+        v.Y = 1 + Pos.Absolute (1) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.Y);
+
+    }
+
+    [Fact]
+    public void Set_Y_Non_PosAbsolute_Explicit_Layout_Required ()
+    {
+        var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Y);
 
-        v.Y = 1;
+        v.Y = Pos.Center ();
+        Assert.True (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Y);
 
-        v.Layout ();
-        Assert.Equal (1, v.Frame.Y);
+        v.Y = Pos.Percent (50);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Y);
 
-        v.Y = 2;
-        Assert.Equal (1, v.Frame.Y);
+        v.Y = Pos.Align (Alignment.Center);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Y);
 
-        v.Layout ();
-        Assert.Equal (2, v.Frame.Y);
+        v.Y = Pos.Func (() => 10);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Y);
+
+        v.Y = Pos.AnchorEnd ();
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Y);
+
+        v.Y = Pos.Top (new View ());
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Y);
     }
 
 
     [Fact]
-    public void Set_Width_Does_Not_Change_Frame_Until_Layout ()
+    public void Set_Width_DimAbsolute_Layout_Is_Implicit ()
     {
         var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Width);
 
-        v.Layout ();
+        v.Width = 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (1, v.Frame.Width);
+
+        v.Width = 2;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (2, v.Frame.Width);
+
+        v.Width = Dim.Absolute (3);
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.Width);
+
+        v.Width = Dim.Absolute (3) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (4, v.Frame.Width);
+
+        v.Width = 1 + Dim.Absolute (1) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.Width);
+
+    }
+
+    [Fact]
+    public void Set_Width_Non_DimAbsolute_Explicit_Layout_Required ()
+    {
+        var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Width);
 
-        v.Width = 1;
+        v.Width = Dim.Auto();
+        Assert.True (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Width);
 
-        v.Layout ();
-        Assert.Equal (1, v.Frame.Width);
+        v.Width = Dim.Percent (50);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Width);
 
-        v.Width = 2;
-        Assert.Equal (1, v.Frame.Width);
+        v.Width = Dim.Fill ();
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Width);
 
-        v.Layout ();
-        Assert.Equal (2, v.Frame.Width);
+        v.Width = Dim.Func (() => 10);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Width);
+
+        v.Width = Dim.Width(new View ());
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Width);
     }
 
+    [Fact]
+    public void Set_Height_DimAbsolute_Layout_Is_Implicit ()
+    {
+        var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Height);
+
+        v.Height = 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (1, v.Frame.Height);
+
+        v.Height = 2;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (2, v.Frame.Height);
+
+        v.Height = Dim.Absolute (3);
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.Height);
+
+        v.Height = Dim.Absolute (3) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (4, v.Frame.Height);
+
+        v.Height = 1 + Dim.Absolute (1) + 1;
+        Assert.False (v.IsLayoutNeeded ());
+        Assert.Equal (3, v.Frame.Height);
+
+    }
 
     [Fact]
-    public void Set_Height_Does_Not_Change_Frame_Until_Layout ()
+    public void Set_Height_Non_DimAbsolute_Explicit_Layout_Required ()
     {
         var v = new View ();
+        Assert.False (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Height);
 
-        v.Layout ();
+        v.Height = Dim.Auto ();
+        Assert.True (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Height);
 
-        v.Height = 1;
+        v.Height = Dim.Percent (50);
+        Assert.True (v.IsLayoutNeeded ());
         Assert.Equal (0, v.Frame.Height);
 
-        v.Layout ();
-        Assert.Equal (1, v.Frame.Height);
+        v.Height = Dim.Fill ();
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Height);
 
-        v.Height = 2;
-        Assert.Equal (1, v.Frame.Height);
+        v.Height = Dim.Func (() => 10);
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Height);
 
-        v.Layout ();
-        Assert.Equal (2, v.Frame.Height);
+        v.Height = Dim.Height (new View ());
+        Assert.True (v.IsLayoutNeeded ());
+        Assert.Equal (0, v.Frame.Height);
     }
 
     [Fact]

+ 2 - 2
UnitTests/View/Layout/ToScreenTests.cs

@@ -320,7 +320,7 @@ public class ToScreenTests (ITestOutputHelper output)
         view.Frame = frame;
 
         superView.Add (view);
-        superView.LayoutSubviews ();
+        superSuperView.Layout ();
 
         // Act
         var screen = view.FrameToScreen ();
@@ -884,7 +884,7 @@ public class ToScreenTests (ITestOutputHelper output)
         view.Frame = frame;
 
         superView.Add (view);
-        superView.LayoutSubviews ();
+        superView.Layout ();
 
         // Act
         var screen = view.ViewportToScreen (new Point (viewportX, 0));

+ 0 - 51
UnitTests/View/NeedsDisplayTests.cs

@@ -1,51 +0,0 @@
-#nullable enable
-using System.Text;
-using Xunit.Abstractions;
-
-namespace Terminal.Gui.ViewTests;
-
-[Trait("Category","Output")]
-public class NeedsDisplayTests ()
-{
-    [Fact]
-    public void NeedsDisplay_False_If_Width_Height_Zero ()
-    {
-        View view = new () { Width = 0, Height = 0};
-        view.BeginInit();
-        view.EndInit();
-        Assert.False (view.NeedsDisplay);
-        //Assert.False (view.SubViewNeedsDisplay);
-    }
-
-
-    [Fact]
-    public void NeedsDisplay_True_Initially_If_Width_Height_Not_Zero ()
-    {
-        View superView = new () { Width = 1, Height = 1};
-        View view1 = new () { Width = 1, Height = 1 };
-        View view2 = new () { Width = 1, Height = 1 };
-
-        superView.Add(view1, view2);
-        superView.BeginInit ();
-        superView.EndInit ();
-
-        Assert.True (superView.NeedsDisplay);
-        Assert.True (superView.SubViewNeedsDisplay);
-        Assert.True (view1.NeedsDisplay);
-        Assert.True (view2.NeedsDisplay);
-
-        superView.Draw ();
-
-        Assert.False (superView.NeedsDisplay);
-        Assert.False (superView.SubViewNeedsDisplay);
-        Assert.False (view1.NeedsDisplay);
-        Assert.False (view2.NeedsDisplay);
-
-        superView.SetNeedsDisplay();
-
-        Assert.True (superView.NeedsDisplay);
-        Assert.True (superView.SubViewNeedsDisplay);
-        Assert.True (view1.NeedsDisplay);
-        Assert.True (view2.NeedsDisplay);
-    }
-}

+ 29 - 31
UnitTests/View/TextTests.cs

@@ -18,6 +18,7 @@ public class TextTests (ITestOutputHelper output)
     {
         var view = new View ();
         view.Text = text;
+        view.Layout ();
         Assert.Equal (new (expectedW, expectedH), view.TextFormatter.ConstrainToSize);
     }
 
@@ -31,6 +32,7 @@ public class TextTests (ITestOutputHelper output)
         var view = new View ();
         view.SetContentSize (new (1, 1));
         view.Text = text;
+        view.Layout ();
         Assert.Equal (new (expectedW, expectedH), view.TextFormatter.ConstrainToSize);
     }
 
@@ -45,8 +47,7 @@ public class TextTests (ITestOutputHelper output)
         var viewY = new View { Text = "Y", Y = Pos.Bottom (label), Width = 1, Height = 1 };
 
         top.Add (label, viewX, viewY);
-        top.BeginInit ();
-        top.EndInit ();
+        top.Layout ();
 
         Assert.Equal (new (0, 0, 5, 1), label.Frame);
 
@@ -94,7 +95,7 @@ Y
         RunState rs = Application.Begin (top);
 
         label.Text = "Hello";
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new (0, 0, 1, 5), label.Frame);
 
@@ -111,7 +112,7 @@ Y
 
         label.Width = 2;
         label.Height = 10;
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new (0, 0, 2, 10), label.Frame);
 
@@ -184,9 +185,7 @@ Y
         view.Text = "Hello World";
         view.Width = 11;
         view.Height = 1;
-        win.LayoutSubviews ();
-        Application.Refresh ();
-
+        Application.RunIteration (ref rs);
         Assert.Equal (new (0, 0, 11, 1), view.Frame);
         Assert.Equal ("Absolute(0)", view.X.ToString ());
         Assert.Equal ("Absolute(0)", view.Y.ToString ());
@@ -215,9 +214,8 @@ Y
 
         view.Width = Dim.Auto ();
         view.Height = Dim.Auto ();
-
         view.Text = "Hello Worlds";
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         int len = "Hello Worlds".Length;
         Assert.Equal (12, len);
         Assert.Equal (new (0, 0, len, 1), view.Frame);
@@ -243,8 +241,7 @@ Y
         pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 
         view.TextDirection = TextDirection.TopBottom_LeftRight;
-        Application.Refresh ();
-
+        Application.RunIteration (ref rs);
         Assert.Equal (new (0, 0, 1, 12), view.Frame);
         Assert.Equal (new (0, 0, 1, 12), view.Frame);
 
@@ -271,22 +268,23 @@ Y
         // Setting to false causes Width and Height to be set to the current ContentSize
         view.Width = 1;
         view.Height = 12;
-
+        Application.RunIteration (ref rs);
         Assert.Equal (new (0, 0, 1, 12), view.Frame);
 
         view.Width = 12;
         view.Height = 1;
         view.TextFormatter.ConstrainToSize = new (12, 1);
-        win.LayoutSubviews ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new (12, 1), view.TextFormatter.ConstrainToSize);
         Assert.Equal (new (0, 0, 12, 1), view.Frame);
+
         top.Clear ();
+        view.SetNeedsDisplay ();
         view.Draw ();
         expected = @" HelloWorlds";
-
         TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         // TextDirection.TopBottom_LeftRight - Height of 1 and Width of 12 means 
         // that the text will be spread "vertically" across 1 line.
@@ -312,7 +310,7 @@ Y
         pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 
         view.PreserveTrailingSpaces = true;
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new (0, 0, 12, 1), view.Frame);
 
@@ -341,7 +339,7 @@ Y
         view.Width = f.Height;
         view.Height = f.Width;
         view.TextDirection = TextDirection.TopBottom_LeftRight;
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new (0, 0, 1, 12), view.Frame);
 
@@ -368,7 +366,7 @@ Y
         view.Width = Dim.Auto ();
         view.Height = Dim.Auto ();
 
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new (0, 0, 1, 12), view.Frame);
 
@@ -414,7 +412,7 @@ Y
         win.Add (view);
         var top = new Toplevel ();
         top.Add (win);
-        Application.Begin (top);
+        RunState rs = Application.Begin (top);
         ((FakeDriver)Application.Driver!).SetBufferSize (4, 10);
 
         Assert.Equal (5, text.Length);
@@ -445,7 +443,7 @@ Y
         Assert.Equal (10, text.Length);
 
         //view.Height = Dim.Fill () - text.Length;
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new (0, 0, 1, 5), view.Frame);
         Assert.Equal (new (1, 5), view.TextFormatter.ConstrainToSize);
@@ -571,7 +569,7 @@ w ";
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 
         verticalView.Text = $"最初の行{Environment.NewLine}二行目";
-        Application.Top.Draw ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new (0, 3, 4, 4), verticalView.Frame);
 
         expected = @"
@@ -664,7 +662,7 @@ w ";
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
 
         verticalView.Text = "最初の行二行目";
-        Application.Top.Draw ();
+        Application.RunIteration (ref rs);
 
         // height was initialized with 8 and can only grow or keep initial value
         Assert.Equal (new (0, 3, 2, 7), verticalView.Frame);
@@ -707,16 +705,19 @@ w ";
         var top = new Toplevel ();
         top.Add (lbl);
         RunState rs = Application.Begin (top);
+        Assert.Equal (new (0, 0, 3, 1), lbl.Frame);
 
         Assert.Equal ("123 ", GetContents ());
 
         lbl.Text = "12";
+        lbl.Layout ();
 
         Assert.Equal (new (0, 0, 2, 1), lbl.Frame);
-        Assert.Equal (new (0, 0, 3, 1), lbl._needsDisplayRect);
-        Assert.Equal (new (0, 0, 0, 0), lbl.SuperView._needsDisplayRect);
+        Assert.Equal (new (0, 0, 2, 1), lbl._needsDisplayRect);
+        Assert.Equal (new (0, 0, 80, 25), lbl.SuperView._needsDisplayRect);
         Assert.True (lbl.SuperView.IsLayoutNeeded ());
-        Application.Refresh();
+        Application.RunIteration (ref rs);
+
         Assert.Equal ("12  ", GetContents ());
 
         string GetContents ()
@@ -1126,6 +1127,8 @@ w ";
             Width = Dim.Auto (DimAutoStyle.Text),
             Height = Dim.Auto (DimAutoStyle.Text)
         };
+        Assert.True (view.IsLayoutNeeded ());
+        view.Layout ();
         Assert.Equal (new (0, 0, 5, 1), view.Frame);
         Assert.Equal (new (0, 0, 5, 1), view.Viewport);
 
@@ -1166,12 +1169,7 @@ w ";
             Width = Dim.Auto (DimAutoStyle.Text),
             Height = Dim.Auto (DimAutoStyle.Text)
         };
-        Assert.Equal (new (0, 0, 1, 5), view.Frame);
-        Assert.Equal (new (0, 0, 1, 5), view.Viewport);
-
-        view.BeginInit ();
-        Assert.Equal (new (0, 0, 1, 5), view.Frame);
-        view.EndInit ();
+        view.Layout ();
         Assert.Equal (new (0, 0, 1, 5), view.Frame);
         Assert.Equal (new (0, 0, 1, 5), view.Viewport);
     }

+ 5 - 10
UnitTests/View/ViewTests.cs

@@ -308,7 +308,7 @@ At 0,0
         view.Height = 1;
         Assert.Equal (new (3, 3, 10, 1), view.Frame);
         Assert.Equal (new (0, 0, 10, 1), view.Viewport);
-        Assert.Equal (new (0, 0, 30, 2), view._needsDisplayRect);
+        Assert.Equal (new (0, 0, 10, 1), view._needsDisplayRect);
         top.Draw ();
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -341,8 +341,6 @@ At 0,0
         top.Add (label, view);
         RunState runState = Application.Begin (top);
 
-        top.Draw ();
-
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
 At 0,0                       
@@ -403,7 +401,7 @@ At 0,0
         view.Height = 1;
         Assert.Equal (new (1, 1, 10, 1), view.Frame);
         Assert.Equal (new (0, 0, 10, 1), view.Viewport);
-        Assert.Equal (new (0, 0, 30, 2), view._needsDisplayRect);
+        Assert.Equal (new (0, 0, 10, 1), view._needsDisplayRect);
         top.Draw ();
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -557,8 +555,6 @@ At 0,0
         top.Add (label, view);
         RunState runState = Application.Begin (top);
 
-        view.Draw ();
-
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
 At 0,0                       
@@ -620,7 +616,7 @@ At 0,0
         view.Height = 1;
         Assert.Equal (new (3, 3, 10, 1), view.Frame);
         Assert.Equal (new (0, 0, 10, 1), view.Viewport);
-        Assert.Equal (new (0, 0, 30, 2), view._needsDisplayRect);
+        Assert.Equal (new (0, 0, 10, 1), view._needsDisplayRect);
         view.Draw ();
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -653,8 +649,6 @@ At 0,0
         top.Add (label, view);
         RunState runState = Application.Begin (top);
 
-        view.Draw ();
-
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
 At 0,0                       
@@ -717,7 +711,7 @@ At 0,0
         view.Height = 1;
         Assert.Equal (new (1, 1, 10, 1), view.Frame);
         Assert.Equal (new (0, 0, 10, 1), view.Viewport);
-        Assert.Equal (new (0, 0, 30, 2), view._needsDisplayRect);
+        Assert.Equal (new (0, 0, 10, 1), view._needsDisplayRect);
         view.Draw ();
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -1010,6 +1004,7 @@ At 0,0
 
         view.Width = Dim.Auto ();
         view.Height = Dim.Auto ();
+        Application.RunIteration (ref rs);
         Assert.Equal ("Testing visibility.".Length, view.Frame.Width);
         Assert.True (view.Visible);
         ((FakeDriver)Application.Driver!).SetBufferSize (30, 5);

+ 1 - 0
UnitTests/Views/ButtonTests.cs

@@ -185,6 +185,7 @@ public class ButtonTests (ITestOutputHelper output)
         btn.Dispose ();
 
         btn = new () { Text = "_Test", IsDefault = true };
+        btn.Layout ();
         Assert.Equal (new (10, 1), btn.TextFormatter.ConstrainToSize);
 
 

+ 9 - 1
UnitTests/Views/CheckBoxTests.cs

@@ -36,6 +36,7 @@ public class CheckBoxTests (ITestOutputHelper output)
             Height = height,
             Text = text
         };
+        checkBox.Layout ();
 
         Assert.Equal (new (expectedWidth, expectedHeight), checkBox.Frame.Size);
         Assert.Equal (new (expectedWidth, expectedHeight), checkBox.Viewport.Size);
@@ -99,6 +100,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         var ckb = new CheckBox ();
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
+        ckb.Layout ();
         Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal (string.Empty, ckb.Text);
@@ -109,6 +111,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         ckb = new () { Text = "Test", CheckedState = CheckState.Checked };
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
+        ckb.Layout ();
         Assert.Equal (CheckState.Checked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal ("Test", ckb.Text);
@@ -119,6 +122,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         ckb = new () { Text = "Test", X = 1, Y = 2 };
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
+        ckb.Layout ();
         Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal ("Test", ckb.Text);
@@ -129,6 +133,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         ckb = new () { Text = "Test", X = 3, Y = 4, CheckedState = CheckState.Checked };
         Assert.True (ckb.Width is DimAuto);
         Assert.True (ckb.Height is DimAuto);
+        ckb.Layout ();
         Assert.Equal (CheckState.Checked, ckb.CheckedState);
         Assert.False (ckb.AllowCheckStateNone);
         Assert.Equal ("Test", ckb.Text);
@@ -395,7 +400,7 @@ public class CheckBoxTests (ITestOutputHelper output)
         var top = new Toplevel ();
         top.Add (win);
 
-        Application.Begin (top);
+        RunState rs = Application.Begin (top);
         ((FakeDriver)Application.Driver!).SetBufferSize (30, 6);
 
         Assert.Equal (Alignment.Fill, checkBox1.TextAlignment);
@@ -416,9 +421,12 @@ public class CheckBoxTests (ITestOutputHelper output)
         Assert.Equal (new (0, 0, 30, 6), pos);
 
         checkBox1.CheckedState = CheckState.Checked;
+        Application.RunIteration (ref rs);
         Assert.Equal (new (1, 1, 25, 1), checkBox1.Frame);
         Assert.Equal (_size25x1, checkBox1.TextFormatter.ConstrainToSize);
+
         checkBox2.CheckedState = CheckState.Checked;
+        Application.RunIteration (ref rs);
         Assert.Equal (new (1, 2, 25, 1), checkBox2.Frame);
         Assert.Equal (_size25x1, checkBox2.TextFormatter.ConstrainToSize);
         Application.Refresh ();

+ 16 - 17
UnitTests/Views/ContextMenuTests.cs

@@ -205,11 +205,11 @@ public class ContextMenuTests (ITestOutputHelper output)
                                                      );
 
         // Don't use Dialog here as it has more layout logic. Use Window instead.
-        var dialog = new Window { X = 2, Y = 2, Width = 15, Height = 4 };
-        dialog.Add (new TextField { X = Pos.Center (), Width = 10, Text = "Test" });
-        RunState rsDialog = Application.Begin (dialog);
+        var testWindow = new Window { X = 2, Y = 2, Width = 15, Height = 4 };
+        testWindow.Add (new TextField { X = Pos.Center (), Width = 10, Text = "Test" });
+        RunState rsDialog = Application.Begin (testWindow);
 
-        Assert.Equal (new Rectangle (2, 2, 15, 4), dialog.Frame);
+        Assert.Equal (new Rectangle (2, 2, 15, 4), testWindow.Frame);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -233,8 +233,7 @@ public class ContextMenuTests (ITestOutputHelper output)
 
         Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (9, 3), Flags = MouseFlags.Button3Clicked });
 
-        var firstIteration = false;
-        Application.RunIteration (ref rsDialog, firstIteration);
+        Application.RunIteration (ref rsDialog);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -508,10 +507,10 @@ public class ContextMenuTests (ITestOutputHelper output)
         Assert.Equal (new Point (-1, -2), cm.Position);
 
         Toplevel top = new ();
-        Application.Begin (top);
+        RunState rs = Application.Begin (top);
 
         cm.Show (menuItems);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (new Point (-1, -2), cm.Position);
 
@@ -535,7 +534,7 @@ public class ContextMenuTests (ITestOutputHelper output)
                                         new MouseEventArgs { Position = new (0, 3), Flags = MouseFlags.ReportMousePosition, View = top.Subviews [0] }
                                        )
                     );
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (-1, -2), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -560,7 +559,7 @@ public class ContextMenuTests (ITestOutputHelper output)
         ((FakeDriver)Application.Driver!).SetBufferSize (40, 20);
         cm.Position = new Point (41, -2);
         cm.Show (menuItems);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (41, -2), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -583,7 +582,7 @@ public class ContextMenuTests (ITestOutputHelper output)
                                         new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = top.Subviews [0] }
                                        )
                     );
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (41, -2), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -607,7 +606,7 @@ public class ContextMenuTests (ITestOutputHelper output)
 
         cm.Position = new Point (41, 9);
         cm.Show (menuItems);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (41, 9), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -630,7 +629,7 @@ public class ContextMenuTests (ITestOutputHelper output)
                                         new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = top.Subviews [0] }
                                        )
                     );
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (41, 9), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -651,7 +650,7 @@ public class ContextMenuTests (ITestOutputHelper output)
 
         cm.Position = new Point (41, 22);
         cm.Show (menuItems);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (41, 22), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -674,7 +673,7 @@ public class ContextMenuTests (ITestOutputHelper output)
                                         new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = top.Subviews [0] }
                                        )
                     );
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (41, 22), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -695,7 +694,7 @@ public class ContextMenuTests (ITestOutputHelper output)
         ((FakeDriver)Application.Driver!).SetBufferSize (18, 8);
         cm.Position = new Point (19, 10);
         cm.Show (menuItems);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (19, 10), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (
@@ -718,7 +717,7 @@ public class ContextMenuTests (ITestOutputHelper output)
                                         new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = top.Subviews [0] }
                                        )
                     );
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
         Assert.Equal (new Point (19, 10), cm.Position);
 
         TestHelpers.AssertDriverContentsWithFrameAre (

+ 14 - 18
UnitTests/Views/ScrollBarViewTests.cs

@@ -756,7 +756,7 @@ This is a test
         RemoveHandlers ();
 
         _scrollBar = new ScrollBarView (_hostView, true);
-        Application.Begin (_hostView.SuperView as Toplevel);
+        RunState rs = Application.Begin (_hostView.SuperView as Toplevel);
 
         Assert.True (_scrollBar.IsVertical);
         Assert.False (_scrollBar.OtherScrollBarView.IsVertical);
@@ -767,8 +767,7 @@ This is a test
         Assert.NotEqual (_scrollBar.OtherScrollBarView.Size, _hostView.Cols);
 
         AddHandlers ();
-        _hostView.SuperView.LayoutSubviews ();
-        _hostView.Draw ();
+        Application.RunIteration (ref rs);
 
         Assert.Equal (_scrollBar.Position, _hostView.Top);
         Assert.Equal (_scrollBar.Size, _hostView.Lines);
@@ -827,14 +826,11 @@ This is a test
                                            scrollBar.OtherScrollBarView.Size = textView.Maxlength;
                                            scrollBar.OtherScrollBarView.Position = textView.LeftColumn;
                                        }
-
-                                       scrollBar.LayoutSubviews ();
-                                       scrollBar.Refresh ();
                                    };
         var top = new Toplevel ();
         top.Add (win);
 
-        Application.Begin (top);
+        RunState rs = Application.Begin (top);
         ((FakeDriver)Application.Driver!).SetBufferSize (45, 20);
 
         Assert.True (scrollBar.AutoHideScrollBars);
@@ -874,7 +870,7 @@ This is a test
 
         textView.WordWrap = true;
         ((FakeDriver)Application.Driver!).SetBufferSize (26, 20);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.True (textView.WordWrap);
         Assert.True (scrollBar.AutoHideScrollBars);
@@ -911,7 +907,7 @@ This is a test
         Assert.Equal (new Rectangle (0, 0, 26, 20), pos);
 
         ((FakeDriver)Application.Driver!).SetBufferSize (10, 10);
-        Application.Refresh ();
+        Application.RunIteration (ref rs);
 
         Assert.True (textView.WordWrap);
         Assert.True (scrollBar.AutoHideScrollBars);
@@ -976,6 +972,7 @@ This is a test
     public void Internal_Tests ()
     {
         Toplevel top = new ();
+        top.Layout ();
         Assert.Equal (new Rectangle (0, 0, 80, 25), top.Viewport);
         var view = new View { Width = Dim.Fill (), Height = Dim.Fill () };
         top.Add (view);
@@ -986,6 +983,7 @@ This is a test
         sbv.Position = 0;
         sbv.OtherScrollBarView.Size = 100;
         sbv.OtherScrollBarView.Position = 0;
+        top.Layout ();
 
         // Host bounds is not empty.
         Assert.True (sbv.CanScroll (10, out int max, sbv.IsVertical));
@@ -1029,11 +1027,10 @@ This is a test
     public void KeepContentAlwaysInViewport_False ()
     {
         _scrollBar = new ScrollBarView (_hostView, true);
-        Application.Begin (_hostView.SuperView as Toplevel);
+        RunState rs = Application.Begin (_hostView.SuperView as Toplevel);
 
         AddHandlers ();
-        _hostView.SuperView.LayoutSubviews ();
-        _hostView.Draw ();
+        Application.RunIteration (ref rs);
 
         _scrollBar.KeepContentAlwaysInViewport = false;
         _scrollBar.Position = 50;
@@ -1056,11 +1053,11 @@ This is a test
     public void KeepContentAlwaysInViewport_True ()
     {
         _scrollBar = new ScrollBarView (_hostView, true);
-        Application.Begin (_hostView.SuperView as Toplevel);
+        RunState rs = Application.Begin (_hostView.SuperView as Toplevel);
 
         AddHandlers ();
-        _hostView.SuperView.LayoutSubviews ();
-        _hostView.Draw ();
+        Application.RunIteration (ref rs);
+
         Assert.Equal (80, _hostView.Viewport.Width);
         Assert.Equal (25, _hostView.Viewport.Height);
         Assert.Equal (79, _scrollBar.OtherScrollBarView.Viewport.Width);
@@ -1128,11 +1125,10 @@ This is a test
     public void ShowScrollIndicator_Check ()
     {
         _scrollBar = new ScrollBarView (_hostView, true);
-        Application.Begin (_hostView.SuperView as Toplevel);
+        RunState rs = Application.Begin (_hostView.SuperView as Toplevel);
 
         AddHandlers ();
-        _hostView.SuperView.LayoutSubviews ();
-        _hostView.Draw ();
+        Application.RunIteration (ref rs);
 
         Assert.True (_scrollBar.ShowScrollIndicator);
         Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);

+ 2 - 1
UnitTests/Views/StatusBarTests.cs

@@ -129,13 +129,14 @@ public class StatusBarTests
     public void StatusBar_Constructor_Default ()
     {
         var sb = new StatusBar ();
-
+        
         Assert.Empty (sb.Subviews);
         Assert.True (sb.CanFocus);
         Assert.Equal (Colors.ColorSchemes ["Menu"], sb.ColorScheme);
         Assert.Equal (0, sb.X);
         Assert.Equal ("AnchorEnd()", sb.Y.ToString ());
         Assert.Equal (Dim.Fill (), sb.Width);
+        sb.Layout ();
         Assert.Equal (1, sb.Frame.Height);
     }
 

+ 1 - 1
UnitTests/Views/TextValidateFieldTests.cs

@@ -70,7 +70,7 @@ public class TextValidateField_NET_Provider_Tests
         // | Disable a previous shift up or shift down.
         // A-Alphanumeric, required. a-Alphanumeric, optional.
         var field = new TextValidateField { Provider = new NetMaskedTextProvider ("999 000 LLL >LLL |AAA aaa") };
-
+        field.Layout ();
         Assert.Equal (field.Viewport.Width, field.Provider.DisplayText.Length);
         Assert.NotEqual (field.Provider.DisplayText.Length, field.Provider.Text.Length);
         Assert.Equal (new string (' ', field.Text.Length), field.Provider.Text);