From 8c7982f9c03eb4f34aa2aceb96b6df3dce763a6e Mon Sep 17 00:00:00 2001 From: Tig Date: Thu, 17 Oct 2024 10:39:56 -0600 Subject: [PATCH] Tons of Layout refactoring. LayoutSubviews is now internal. --- Terminal.Gui/Application/Application.Run.cs | 37 +- .../Application/Application.Screen.cs | 22 +- Terminal.Gui/View/Adornment/Adornment.cs | 9 +- Terminal.Gui/View/Adornment/Border.cs | 30 +- Terminal.Gui/View/Adornment/Margin.cs | 8 +- Terminal.Gui/View/Layout/PosAlign.cs | 2 +- Terminal.Gui/View/View.Adornments.cs | 10 +- Terminal.Gui/View/View.Content.cs | 2 +- Terminal.Gui/View/View.Layout.cs | 135 +++--- Terminal.Gui/View/View.cs | 8 +- Terminal.Gui/Views/ColorPicker.cs | 7 +- Terminal.Gui/Views/FileDialog.cs | 5 +- Terminal.Gui/Views/ScrollBarView.cs | 4 +- Terminal.Gui/Views/TabView.cs | 47 +- Terminal.Gui/Views/TileView.cs | 74 ++- Terminal.Gui/Views/Toplevel.cs | 30 +- Terminal.Gui/Views/Wizard/Wizard.cs | 3 - UICatalog/Scenarios/BorderEditor.cs | 6 +- UICatalog/Scenarios/ColorPicker.cs | 1 - UICatalog/Scenarios/ComputedLayout.cs | 10 - UICatalog/Scenarios/ConfigurationEditor.cs | 2 - UICatalog/Scenarios/Dialogs.cs | 1 - UICatalog/Scenarios/Editor.cs | 3 - UICatalog/Scenarios/LineDrawing.cs | 7 +- UICatalog/Scenarios/PosAlignDemo.cs | 2 - UICatalog/Scenarios/Progress.cs | 2 - UICatalog/Scenarios/Scrolling.cs | 4 +- UICatalog/Scenarios/Sliders.cs | 8 - UICatalog/Scenarios/TileViewNesting.cs | 13 +- UICatalog/Scenarios/Wizards.cs | 3 - UnitTests/Application/ApplicationTests.cs | 2 +- UnitTests/Dialogs/DialogTests.cs | 23 +- UnitTests/Dialogs/WizardTests.cs | 10 +- UnitTests/View/Adornment/BorderTests.cs | 17 +- UnitTests/View/DrawTests.cs | 313 +++++++++--- UnitTests/View/Layout/AbsoluteLayoutTests.cs | 236 --------- UnitTests/View/Layout/Dim.AutoTests.cs | 24 +- UnitTests/View/Layout/LayoutTests.cs | 459 ++++++++++++++++-- UnitTests/View/Layout/Pos.CenterTests.cs | 4 +- UnitTests/View/Layout/ToScreenTests.cs | 3 +- UnitTests/View/Mouse/MouseTests.cs | 5 +- UnitTests/View/ViewTests.cs | 2 +- UnitTests/Views/ContextMenuTests.cs | 16 +- UnitTests/Views/MenuBarTests.cs | 20 +- UnitTests/Views/TextFieldTests.cs | 3 +- UnitTests/Views/TextViewTests.cs | 3 +- UnitTests/Views/ToplevelTests.cs | 4 +- 47 files changed, 933 insertions(+), 706 deletions(-) delete mode 100644 UnitTests/View/Layout/AbsoluteLayoutTests.cs diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs index ee980a37f..fa35743f8 100644 --- a/Terminal.Gui/Application/Application.Run.cs +++ b/Terminal.Gui/Application/Application.Run.cs @@ -183,12 +183,8 @@ public static partial class Application // Run (Begin, Run, End, Stop) toplevel.BeginInit (); toplevel.EndInit (); - if (toplevel.SetRelativeLayout (Driver!.Screen.Size)) - { - toplevel.LayoutSubviews (); - } - - // toplevel.SetLayoutNeeded(); + // Force a layout - normally this is done each iteration of the main loop but we prime it here. + toplevel.Layout (Screen.Size); } // Try to set initial focus to any TabStop @@ -197,6 +193,11 @@ public static partial class Application // Run (Begin, Run, End, Stop) toplevel.SetFocus (); } + // 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(); + toplevel.OnLoaded (); if (PositionCursor ()) @@ -227,11 +228,12 @@ public static partial class Application // Run (Begin, Run, End, Stop) // If the view is not visible or enabled, don't position the cursor if (mostFocused is null || !mostFocused.Visible || !mostFocused.Enabled) { - Driver!.GetCursorVisibility (out CursorVisibility current); + CursorVisibility current = CursorVisibility.Invisible; + Driver?.GetCursorVisibility (out current); if (current != CursorVisibility.Invisible) { - Driver.SetCursorVisibility (CursorVisibility.Invisible); + Driver?.SetCursorVisibility (CursorVisibility.Invisible); } return false; @@ -497,11 +499,7 @@ public static partial class Application // Run (Begin, Run, End, Stop) if (tl.IsLayoutNeeded ()) { clear = true; - - if (tl.SetRelativeLayout (Screen.Size)) - { - tl.LayoutSubviews (); - } + tl.Layout (Screen.Size); } } @@ -554,23 +552,23 @@ public static partial class Application // Run (Begin, Run, End, Stop) return; } - RunIteration (ref state, ref firstIteration); + firstIteration = RunIteration (ref state, firstIteration); } MainLoop!.Running = false; // Run one last iteration to consume any outstanding input events from Driver // This is important for remaining OnKeyUp events. - RunIteration (ref state, ref firstIteration); + RunIteration (ref state, firstIteration); } /// Run one application iteration. /// The state returned by . /// - /// Set to if this is the first run loop iteration. Upon return, it - /// will be set to if at least one iteration happened. + /// Set to if this is the first run loop iteration. /// - public static void RunIteration (ref RunState state, ref bool firstIteration) + /// if at least one iteration happened. + public static bool RunIteration (ref RunState state, bool firstIteration = false) { if (MainLoop!.Running && MainLoop.EventsPending ()) { @@ -588,7 +586,7 @@ public static partial class Application // Run (Begin, Run, End, Stop) if (Top is null) { - return; + return firstIteration; } Refresh (); @@ -598,6 +596,7 @@ public static partial class Application // Run (Begin, Run, End, Stop) Driver!.UpdateCursor (); } + return firstIteration; } /// Stops the provided , causing or the if provided. diff --git a/Terminal.Gui/Application/Application.Screen.cs b/Terminal.Gui/Application/Application.Screen.cs index 940b1b22c..bc5a3c597 100644 --- a/Terminal.Gui/Application/Application.Screen.cs +++ b/Terminal.Gui/Application/Application.Screen.cs @@ -3,13 +3,28 @@ namespace Terminal.Gui; public static partial class Application // Screen related stuff { + private static Size _screenSize = new (2048, 2048); + + /// + /// INTERNAL API for Unit Tests. Only works if there's no driver. + /// + /// + internal static void SetScreenSize (Size size) + { + if (Driver is { }) + { + throw new InvalidOperationException ("Cannot set the screen size when the ConsoleDriver is already initialized."); + } + _screenSize = size; + } + /// /// Gets the size of the screen. This is the size of the screen as reported by the . /// /// /// If the has not been initialized, this will return a default size of 2048x2048; useful for unit tests. /// - public static Rectangle Screen => Driver?.Screen ?? new (0, 0, 2048, 2048); + public static Rectangle Screen => Driver?.Screen ?? new (new (0, 0), _screenSize); /// Invoked when the terminal's size changed. The new size of the terminal is provided. /// @@ -35,11 +50,8 @@ public static partial class Application // Screen related stuff foreach (Toplevel t in TopLevels) { - if (t.SetRelativeLayout (args.Size.Value)) - { - t.LayoutSubviews (); - } t.OnSizeChanging (new (args.Size)); + t.SetLayoutNeeded (); } Refresh (); diff --git a/Terminal.Gui/View/Adornment/Adornment.cs b/Terminal.Gui/View/Adornment/Adornment.cs index 9d3559f63..7a01a27f4 100644 --- a/Terminal.Gui/View/Adornment/Adornment.cs +++ b/Terminal.Gui/View/Adornment/Adornment.cs @@ -152,18 +152,15 @@ public class Adornment : View Rectangle screen = ViewportToScreen (viewport); Attribute normalAttr = GetNormalColor (); - Driver.SetAttribute (normalAttr); + Driver?.SetAttribute (normalAttr); // This just draws/clears the thickness, not the insides. Thickness.Draw (screen, ToString ()); if (!string.IsNullOrEmpty (TextFormatter.Text)) { - if (TextFormatter is { }) - { - TextFormatter.ConstrainToSize = Frame.Size; - TextFormatter.NeedsFormat = true; - } + TextFormatter.ConstrainToSize = Frame.Size; + TextFormatter.NeedsFormat = true; } TextFormatter?.Draw (screen, normalAttr, normalAttr, Rectangle.Empty); diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs index 29eb50cbd..9a36b583e 100644 --- a/Terminal.Gui/View/Adornment/Border.cs +++ b/Terminal.Gui/View/Adornment/Border.cs @@ -703,15 +703,15 @@ public class Border : Adornment bool drawBottom = Thickness.Bottom > 0 && Frame.Width > 1 && Frame.Height > 1; bool drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0); - Attribute prevAttr = Driver.GetAttribute (); + Attribute prevAttr = Driver?.GetAttribute () ?? Attribute.Default; if (ColorScheme is { }) { - Driver.SetAttribute (GetNormalColor ()); + Driver?.SetAttribute (GetNormalColor ()); } else { - Driver.SetAttribute (Parent!.GetNormalColor ()); + Driver?.SetAttribute (Parent!.GetNormalColor ()); } if (drawTop) @@ -726,7 +726,7 @@ public class Border : Adornment borderBounds.Width, Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } else @@ -741,7 +741,7 @@ public class Border : Adornment Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } @@ -755,7 +755,7 @@ public class Border : Adornment Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); lc?.AddLine ( @@ -763,7 +763,7 @@ public class Border : Adornment Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } @@ -774,7 +774,7 @@ public class Border : Adornment 2, Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); // Add a vert line for ╔╡ @@ -783,7 +783,7 @@ public class Border : Adornment titleBarsLength, Orientation.Vertical, LineStyle.Single, - Driver.GetAttribute () + Driver?.GetAttribute () ); // Add a vert line for ╞ @@ -798,7 +798,7 @@ public class Border : Adornment titleBarsLength, Orientation.Vertical, LineStyle.Single, - Driver.GetAttribute () + Driver?.GetAttribute () ); // Add the right hand line for ╞═════╗ @@ -813,7 +813,7 @@ public class Border : Adornment borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } } @@ -827,7 +827,7 @@ public class Border : Adornment sideLineLength, Orientation.Vertical, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } #endif @@ -839,7 +839,7 @@ public class Border : Adornment borderBounds.Width, Orientation.Horizontal, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } @@ -850,11 +850,11 @@ public class Border : Adornment sideLineLength, Orientation.Vertical, lineStyle, - Driver.GetAttribute () + Driver?.GetAttribute () ); } - Driver.SetAttribute (prevAttr); + Driver?.SetAttribute (prevAttr); // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler if (Diagnostics.HasFlag (ViewDiagnosticFlags.Ruler)) diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs index 46acbddbb..eff603e29 100644 --- a/Terminal.Gui/View/Adornment/Margin.cs +++ b/Terminal.Gui/View/Adornment/Margin.cs @@ -117,16 +117,12 @@ public class Margin : Adornment { IEnumerable subviewsNeedingDraw = Subviews.Where ( view => view.Visible - && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.IsLayoutNeeded ()) + && (view.NeedsDisplay || view.SubViewNeedsDisplay) ); foreach (View view in subviewsNeedingDraw) { - if (view.IsLayoutNeeded ()) - { - view.LayoutSubviews (); - } - + //view.Layout (); view.Draw (); } } diff --git a/Terminal.Gui/View/Layout/PosAlign.cs b/Terminal.Gui/View/Layout/PosAlign.cs index 197621cf0..6d2360b1f 100644 --- a/Terminal.Gui/View/Layout/PosAlign.cs +++ b/Terminal.Gui/View/Layout/PosAlign.cs @@ -10,7 +10,7 @@ namespace Terminal.Gui; /// /// /// Updating the properties of is supported, but will not automatically cause re-layout to -/// happen. +/// happen. /// must be called on the SuperView. /// /// diff --git a/Terminal.Gui/View/View.Adornments.cs b/Terminal.Gui/View/View.Adornments.cs index 54f110d2a..c03e832d4 100644 --- a/Terminal.Gui/View/View.Adornments.cs +++ b/Terminal.Gui/View/View.Adornments.cs @@ -54,7 +54,7 @@ public partial class View // Adornments /// /// /// Changing the size of an adornment (, , or ) will - /// change the size of and trigger to update the layout of the + /// change the size of which will call to update the layout of the /// and its . /// /// @@ -109,8 +109,8 @@ public partial class View // Adornments /// View's content and are not clipped by the View's Clip Area. /// /// - /// Changing the size of a frame (, , or ) will - /// change the size of the and trigger to update the layout of the + /// Changing the size of an adornment (, , or ) will + /// change the size of which will call to update the layout of the /// and its . /// /// @@ -217,8 +217,8 @@ public partial class View // Adornments /// View's content and are not clipped by the View's Clip Area. /// /// - /// Changing the size of a frame (, , or ) will - /// change the size of the and trigger to update the layout of the + /// Changing the size of an adornment (, , or ) will + /// change the size of which will call to update the layout of the /// and its . /// /// diff --git a/Terminal.Gui/View/View.Content.cs b/Terminal.Gui/View/View.Content.cs index ba441f56f..749c4725b 100644 --- a/Terminal.Gui/View/View.Content.cs +++ b/Terminal.Gui/View/View.Content.cs @@ -265,7 +265,7 @@ public partial class View /// /// /// Altering the Viewport Size will eventually (when the view is next laid out) cause the - /// and methods to be called. + /// and methods to be called. /// /// public virtual Rectangle Viewport diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs index d143cf10b..af6dad65e 100644 --- a/Terminal.Gui/View/View.Layout.cs +++ b/Terminal.Gui/View/View.Layout.cs @@ -190,7 +190,7 @@ public partial class View // Layout APIs /// /// /// Altering the Frame will eventually (when the view hierarchy is next laid out via see - /// cref="LayoutSubviews"/>) cause and + /// cref="LayoutSubviews"/>) cause and /// /// methods to be called. /// @@ -207,21 +207,15 @@ public partial class View // Layout APIs } set { - if (_frame == value) - { - return; - } - // This will set _frame, call SetsNeedsLayout, and raise OnViewportChanged/ViewportChanged - SetFrame (value with { Width = Math.Max (value.Width, 0), Height = Math.Max (value.Height, 0) }); - - // If Frame gets set, set all Pos/Dim to Absolute values. - _x = _frame.X; - _y = _frame.Y; - _width = _frame.Width; - _height = _frame.Height; - - SetLayoutNeeded (); + if (SetFrame (value with { Width = Math.Max (value.Width, 0), Height = Math.Max (value.Height, 0) })) + { + // If Frame gets set, set all Pos/Dim to Absolute values. + _x = _frame.X; + _y = _frame.Y; + _width = _frame.Width; + _height = _frame.Height; + } } } @@ -229,11 +223,12 @@ public partial class View // Layout APIs /// INTERNAL API - Sets _frame, calls SetsNeedsLayout, and raises OnViewportChanged/ViewportChanged /// /// - private void SetFrame (in Rectangle frame) + /// if the frame was changed. + private bool SetFrame (in Rectangle frame) { if (_frame == frame) { - return; + return false; } var oldViewport = Rectangle.Empty; @@ -252,6 +247,8 @@ public partial class View // Layout APIs // BUGBUG: When SetFrame is called from Frame_set, this event gets raised BEFORE OnResizeNeeded. Is that OK? OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport)); + + return true; } /// Gets the with a screen-relative location. @@ -331,7 +328,7 @@ public partial class View // Layout APIs /// /// /// Changing this property will eventually (when the view is next drawn) cause the - /// and methods to be called. + /// and methods to be called. /// /// /// Changing this property will cause to be updated. @@ -350,12 +347,6 @@ public partial class View // Layout APIs _x = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (X)} cannot be null"); - if (!IsInitialized) - { - // Supports Unit Tests that don't call Begin/EndInit - //SetRelativeLayout (GetBestGuessSuperViewContentSize ()); - } - SetLayoutNeeded (); } } @@ -380,7 +371,7 @@ public partial class View // Layout APIs /// /// /// Changing this property will eventually (when the view is next drawn) cause the - /// and methods to be called. + /// and methods to be called. /// /// /// Changing this property will cause to be updated. @@ -399,12 +390,6 @@ public partial class View // Layout APIs _y = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Y)} cannot be null"); - if (!IsInitialized) - { - // Supports Unit Tests that don't call Begin/EndInit - //SetRelativeLayout (GetBestGuessSuperViewContentSize ()); - } - SetLayoutNeeded (); } } @@ -430,7 +415,7 @@ public partial class View // Layout APIs /// /// /// Changing this property will eventually (when the view is next drawn) cause the - /// and methods to be called. + /// and methods to be called. /// /// /// Changing this property will cause to be updated. @@ -458,12 +443,6 @@ public partial class View // Layout APIs // Reset TextFormatter - Will be recalculated in SetTextFormatterSize TextFormatter.ConstrainToHeight = null; - if (!IsInitialized) - { - // Supports Unit Tests that don't call Begin/EndInit - //SetRelativeLayout (GetBestGuessSuperViewContentSize ()); - } - SetLayoutNeeded (); } } @@ -489,7 +468,7 @@ public partial class View // Layout APIs /// /// /// Changing this property will eventually (when the view is next drawn) cause the - /// and methods to be called. + /// and methods to be called. /// /// /// Changing this property will cause to be updated. @@ -517,12 +496,6 @@ public partial class View // Layout APIs // Reset TextFormatter - Will be recalculated in SetTextFormatterSize TextFormatter.ConstrainToWidth = null; - if (!IsInitialized) - { - // Supports Unit Tests that don't call Begin/EndInit - //SetRelativeLayout (GetBestGuessSuperViewContentSize ()); - } - SetLayoutNeeded (); } } @@ -559,7 +532,7 @@ public partial class View // Layout APIs /// /// /// If any of the view's subviews have a position or dimension dependent on either or - /// other subviews, on + /// other subviews, on /// will be called for that subview. /// /// @@ -657,7 +630,6 @@ public partial class View // Layout APIs SetTitleTextFormatterSize (); } - //SetNeedsDisplay (); SuperView?.SetNeedsDisplay (); } @@ -674,10 +646,8 @@ public partial class View // Layout APIs return true; } - // TODO: remove virtual from this /// - /// Invoked when the dimensions of the view have changed, for example in response to the container view or terminal - /// resizing. + /// INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes the view's relative layout has been set via . /// /// /// @@ -690,7 +660,7 @@ public partial class View // Layout APIs /// /// Raises the event before it returns. /// - public virtual void LayoutSubviews () + internal void LayoutSubviews () { if (!IsInitialized) { @@ -707,6 +677,7 @@ public partial class View // Layout APIs Size contentSize = GetContentSize (); OnLayoutStarted (new (contentSize)); + // The Adornments already have their Frame's set by SetRelativeLayout so we call LayoutSubViews vs. Layout here. Margin?.LayoutSubviews (); Border?.LayoutSubviews (); Padding?.LayoutSubviews (); @@ -720,7 +691,7 @@ public partial class View // Layout APIs List redo = new (); foreach (View v in ordered) { - if (!LayoutSubview (v, contentSize)) + if (!v.Layout (contentSize)) { redo.Add (v); } @@ -729,7 +700,7 @@ public partial class View // Layout APIs bool layoutStillNeeded = false; foreach (View v in redo) { - if (!LayoutSubview (v, contentSize)) + if (!v.Layout (contentSize)) { layoutStillNeeded = true; } @@ -742,7 +713,7 @@ public partial class View // Layout APIs foreach ((View from, View to) in edges) { Debug.Fail ("This is dead code?"); - LayoutSubview (to, from.GetContentSize ()); + to.Layout (from.GetContentSize ()); } } @@ -752,17 +723,16 @@ public partial class View // Layout APIs } /// - /// Lays out . + /// Performs layout of the view and its subviews within the specified content size. /// - /// /// /// If the view could not be laid out (typically because a dependencies was not ready). - private bool LayoutSubview (View subView, Size contentSize) + public bool Layout (Size contentSize) { // Note, SetRelativeLayout calls SetTextFormatterSize - if (subView.SetRelativeLayout (contentSize)) + if (SetRelativeLayout (contentSize)) { - subView.LayoutSubviews (); + LayoutSubviews (); return true; } @@ -770,6 +740,15 @@ public partial class View // Layout APIs return false; } + /// + /// Performs layout of the view and its subviews using the content size of either the or . + /// + /// If the view could not be laid out (typically because a dependencies was not ready). + public bool Layout () + { + return Layout (GetBestGuessSuperViewContentSize ()); + } + /// /// Raises the event. Called from before all sub-views /// have been laid out. @@ -787,17 +766,26 @@ public partial class View // Layout APIs private bool _layoutNeeded = true; /// - /// Indicates the View's Frame or the relative layout of the View's subviews (including Adornments) have - /// changed since the last time the View was laid out. Used to prevent from needlessly computing - /// layout. + /// Indicates the View's Frame or the layout of the View's subviews (including Adornments) have + /// changed since the last time the View was laid out. /// - /// - public bool IsLayoutNeeded () => _layoutNeeded; + /// + /// Used to prevent from needlessly computing + /// layout. + /// + /// + /// if layout is needed. + public bool IsLayoutNeeded () { return _layoutNeeded; } /// - /// INTERNAL API - Sets for this View and all of it's subviews and it's SuperView. - /// The main loop will call SetRelativeLayout and LayoutSubviews for any view with set. + /// Sets to return , indicating this View and all of it's subviews (including adornments) need to be laid out in the next Application iteration. /// + /// + /// + /// The will cause to be called on the next so there is normally no reason to call see . + /// + /// + public void SetLayoutNeeded () { if (IsLayoutNeeded ()) @@ -880,7 +868,7 @@ public partial class View // Layout APIs } /// - /// Collects dimension (where Width or Height is `DimView`) dependencies for a given view. + /// INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view. /// /// The dimension (width or height) to collect dependencies for. /// The view for which to collect dimension dependencies. @@ -891,7 +879,7 @@ public partial class View // Layout APIs /// internal void CollectDim (Dim? dim, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) { - if (dim!.Has (out var dv)) + if (dim!.Has (out DimView dv)) { if (dv.Target != this) { @@ -899,21 +887,15 @@ public partial class View // Layout APIs } } - if (dim!.Has (out var dc)) + if (dim!.Has (out DimCombine dc)) { CollectDim (dc.Left, from, ref nNodes, ref nEdges); CollectDim (dc.Right, from, ref nNodes, ref nEdges); } - - if (dim!.Has (out var df)) - { - - } - } /// - /// Collects position (where X or Y is `PosView`) dependencies for a given view. + /// INTERNAL API - Collects position (where X or Y is `PosView`) dependencies for a given view. /// /// The position (X or Y) to collect dependencies for. /// The view for which to collect position dependencies. @@ -924,6 +906,7 @@ public partial class View // Layout APIs /// internal void CollectPos (Pos pos, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) { + // TODO: Use Pos.Has instead. switch (pos) { case PosView pv: diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs index ef0ecbe21..12387317e 100644 --- a/Terminal.Gui/View/View.cs +++ b/Terminal.Gui/View/View.cs @@ -74,7 +74,9 @@ namespace Terminal.Gui; /// To flag the entire view for redraw call . /// /// -/// The method is invoked when the size or layout of a view has changed. +/// The method is called when the size or layout of a view has changed. The will +/// cause to be called on the next so there is normally no reason to direclty call +/// see . /// /// /// Views have a property that defines the default colors that subviews should use for @@ -243,8 +245,8 @@ public partial class View : Responder, ISupportInitializeNotification } } - // TOOD: Figure out how to move this out of here and just depend on IsLayoutNeeded in Mainloop - LayoutSubviews(); + // TODO: Figure out how to move this out of here and just depend on IsLayoutNeeded in Mainloop + Layout (); Initialized?.Invoke (this, EventArgs.Empty); } diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs index a4209ffe4..5acb61f10 100644 --- a/Terminal.Gui/Views/ColorPicker.cs +++ b/Terminal.Gui/Views/ColorPicker.cs @@ -90,10 +90,7 @@ public partial class ColorPicker : View CreateTextField (); SelectedColor = oldValue; - if (IsInitialized) - { - LayoutSubviews (); - } + SetLayoutNeeded (); } /// @@ -275,6 +272,8 @@ public partial class ColorPicker : View { _tfHex.Text = colorHex; } + + SetLayoutNeeded (); } private void UpdateSingleBarValueFromTextField (object? sender, HasFocusEventArgs e) diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index 2ded19418..2818afa07 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialog.cs @@ -189,7 +189,7 @@ public class FileDialog : Dialog bool newState = !tile.ContentView.Visible; tile.ContentView.Visible = newState; _btnToggleSplitterCollapse.Text = GetToggleSplitterText (newState); - LayoutSubviews (); + SetLayoutNeeded(); }; _tbFind = new TextField @@ -493,7 +493,8 @@ public class FileDialog : Dialog _btnOk.X = Pos.Right (_btnCancel) + 1; MoveSubviewTowardsStart (_btnCancel); } - LayoutSubviews (); + + SetLayoutNeeded(); } /// diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs index 24dc68ef9..072447c9e 100644 --- a/Terminal.Gui/Views/ScrollBarView.cs +++ b/Terminal.Gui/Views/ScrollBarView.cs @@ -5,6 +5,8 @@ // Miguel de Icaza (miguel@gnome.org) // +using System.Diagnostics; + namespace Terminal.Gui; /// ScrollBarViews are views that display a 1-character scrollbar, either horizontal or vertical @@ -259,7 +261,7 @@ public class ScrollBarView : View { SetRelativeLayout (SuperView?.Frame.Size ?? Host.Frame.Size); ShowHideScrollBars (false); - SetNeedsDisplay (); + SetLayoutNeeded (); } } } diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index d605b6fa0..8c6364451 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -147,12 +147,27 @@ public class TabView : View OnSelectedTabChanged (old, value); } + SetLayoutNeeded (); } } + private TabStyle _style = new (); + /// Render choices for how to display tabs. After making changes, call . /// - public TabStyle Style { get; set; } = new (); + public TabStyle Style + { + get => _style; + set + { + if (_style == value) + { + return; + } + _style = value; + SetLayoutNeeded(); + } + } /// All tabs currently hosted by the control. /// @@ -163,7 +178,11 @@ public class TabView : View public int TabScrollOffset { get => _tabScrollOffset; - set => _tabScrollOffset = EnsureValidScrollOffsets (value); + set + { + _tabScrollOffset = EnsureValidScrollOffsets (value); + SetLayoutNeeded (); + } } /// Adds the given to . @@ -188,7 +207,7 @@ public class TabView : View tab.View?.SetFocus (); } - SetNeedsDisplay (); + SetLayoutNeeded (); } /// @@ -245,12 +264,7 @@ public class TabView : View // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0 } - if (IsInitialized) - { - LayoutSubviews (); - } - - SetNeedsDisplay (); + SetLayoutNeeded(); } /// Updates to ensure that is visible. @@ -327,7 +341,7 @@ public class TabView : View } EnsureSelectedTabIsVisible (); - SetNeedsDisplay (); + SetLayoutNeeded (); } /// Event for when changes. @@ -349,7 +363,6 @@ public class TabView : View if (Tabs.Count == 1 || SelectedTab is null) { SelectedTab = Tabs.ElementAt (0); - SetNeedsDisplay (); return SelectedTab is { }; } @@ -359,9 +372,7 @@ public class TabView : View // Currently selected tab has vanished! if (currentIdx == -1) { - SelectedTab = Tabs.ElementAt (0); - SetNeedsDisplay (); - + SelectedTab = Tabs.ElementAt (0); return true; } @@ -373,7 +384,6 @@ public class TabView : View } SelectedTab = _tabs [newIdx]; - SetNeedsDisplay (); EnsureSelectedTabIsVisible (); @@ -611,7 +621,7 @@ public class TabView : View { _host.SwitchTabBy (scrollIndicatorHit); - SetNeedsDisplay (); + SetLayoutNeeded (); return true; } @@ -619,7 +629,7 @@ public class TabView : View if (hit is { }) { _host.SelectedTab = hit; - SetNeedsDisplay (); + SetLayoutNeeded (); return true; } @@ -1256,7 +1266,8 @@ public class TabView : View tab.Text = toRender.TextToRender; - LayoutSubviews (); + // BUGBUG: Layout should only be called from Mainloop iteration! + Layout (); tab.OnDrawAdornments (); diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index fdf2c2951..72640e16f 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -23,6 +23,23 @@ public class TileView : View { CanFocus = true; RebuildForTileCount (tiles); + + LayoutStarted += (_, _) => + { + Rectangle viewport = Viewport; + + if (HasBorder ()) + { + viewport = new ( + viewport.X + 1, + viewport.Y + 1, + Math.Max (0, viewport.Width - 2), + Math.Max (0, viewport.Height - 2) + ); + } + + Setup (viewport); + }; } /// The line style to use when drawing the splitter lines. @@ -34,12 +51,14 @@ public class TileView : View get => _orientation; set { + if (_orientation == value) + { + return; + } + _orientation = value; - if (IsInitialized) - { - LayoutSubviews (); - } + SetLayoutNeeded (); } } @@ -131,13 +150,7 @@ public class TileView : View } } - SetNeedsDisplay (); - - if (IsInitialized) - { - LayoutSubviews (); - } - + SetLayoutNeeded (); return toReturn; } @@ -155,31 +168,6 @@ public class TileView : View /// public bool IsRootTileView () { return _parentTileView == null; } - // TODO: Use OnLayoutStarted instead - /// - public override void LayoutSubviews () - { - if (!IsInitialized) - { - return; - } - - Rectangle viewport = Viewport; - - if (HasBorder ()) - { - viewport = new ( - viewport.X + 1, - viewport.Y + 1, - Math.Max (0, viewport.Width - 2), - Math.Max (0, viewport.Height - 2) - ); - } - - Setup (viewport); - base.LayoutSubviews (); - } - // BUG: v2 fix this hack // QUESTION: Does this need to be fixed before events are refactored? /// Overridden so no Frames get drawn @@ -364,12 +352,8 @@ public class TileView : View _tiles.Add (tile); tile.ContentView.Id = $"Tile.ContentView {i}"; Add (tile.ContentView); - tile.TitleChanged += (s, e) => SetNeedsDisplay (); - } - - if (IsInitialized) - { - LayoutSubviews (); + // BUGBUG: This should not be needed: + tile.TitleChanged += (s, e) => SetLayoutNeeded (); } } @@ -407,9 +391,6 @@ public class TileView : View Add (_tiles [i].ContentView); } - SetNeedsDisplay (); - LayoutSubviews (); - return removed; } @@ -441,7 +422,6 @@ public class TileView : View } _splitterDistances [idx] = value; - GetRootTileView ().LayoutSubviews (); OnSplitterMoved (idx); return true; @@ -957,7 +937,7 @@ public class TileView : View moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Viewport.Height - 2, mouseEvent.Position.Y))); } - Parent.SetNeedsDisplay (); + Parent.SetLayoutNeeded (); return true; } diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 291fc955e..7861fcfd0 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -235,7 +235,7 @@ public partial class Toplevel : View return; } - var layoutSubviews = false; + //var layoutSubviews = false; var maxWidth = 0; if (superView.Margin is { } && superView == top.SuperView) @@ -251,36 +251,26 @@ public partial class Toplevel : View if (top?.X is null or PosAbsolute && top?.Frame.X != nx) { top!.X = nx; - layoutSubviews = true; + //layoutSubviews = true; } if (top?.Y is null or PosAbsolute && top?.Frame.Y != ny) { top!.Y = ny; - layoutSubviews = true; + //layoutSubviews = true; } } - //// TODO: v2 - This is a hack to get the StatusBar to be positioned correctly. - //if (sb != null - // && !top!.Subviews.Contains (sb) - // && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0) - // && top.Height is DimFill - // && -top.Height.GetAnchor (0) < 1) + + //if (superView.IsLayoutNeeded () || layoutSubviews) //{ - // top.Height = Dim.Fill (sb.Visible ? 1 : 0); - // layoutSubviews = true; + // superView.LayoutSubviews (); //} - if (superView.IsLayoutNeeded () || layoutSubviews) - { - superView.LayoutSubviews (); - } - - if (IsLayoutNeeded ()) - { - LayoutSubviews (); - } + //if (IsLayoutNeeded ()) + //{ + // LayoutSubviews (); + //} } /// Invoked when the terminal has been resized. The new of the terminal is provided. diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index bb1903034..2f88ce2e1 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -546,9 +546,6 @@ public class Wizard : Dialog SizeStep (CurrentStep); SetLayoutNeeded (); - LayoutSubviews (); - - //Draw (); } private void Wizard_Closing (object sender, ToplevelClosingEventArgs obj) diff --git a/UICatalog/Scenarios/BorderEditor.cs b/UICatalog/Scenarios/BorderEditor.cs index ce5df6dce..375ff2c5a 100644 --- a/UICatalog/Scenarios/BorderEditor.cs +++ b/UICatalog/Scenarios/BorderEditor.cs @@ -29,7 +29,7 @@ public class BorderEditor : AdornmentEditor { List borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast ().ToList (); - _rbBorderStyle = new() + _rbBorderStyle = new () { X = 0, @@ -46,7 +46,7 @@ public class BorderEditor : AdornmentEditor _rbBorderStyle.SelectedItemChanged += OnRbBorderStyleOnSelectedItemChanged; - _ckbTitle = new() + _ckbTitle = new () { X = 0, Y = Pos.Bottom (_rbBorderStyle), @@ -91,7 +91,7 @@ public class BorderEditor : AdornmentEditor } ((Border)AdornmentToEdit).SetNeedsDisplay (); - LayoutSubviews (); + SetLayoutNeeded (); } void OnCkbTitleOnToggle (object sender, CancelEventArgs args) diff --git a/UICatalog/Scenarios/ColorPicker.cs b/UICatalog/Scenarios/ColorPicker.cs index c05931912..8c2682ef3 100644 --- a/UICatalog/Scenarios/ColorPicker.cs +++ b/UICatalog/Scenarios/ColorPicker.cs @@ -218,7 +218,6 @@ public class ColorPickers : Scenario // Set default colors. foregroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Foreground.GetClosestNamedColor16 (); backgroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Background.GetClosestNamedColor16 (); - app.Initialized += (s, e) => app.LayoutSubviews (); Application.Run (app); app.Dispose (); diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index bdf10337c..12696543f 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -370,7 +370,6 @@ public class ComputedLayout : Scenario // The call to app.LayoutSubviews causes the Computed layout to // get updated. anchorButton.Text += "!"; - app.LayoutSubviews (); }; app.Add (anchorButton); @@ -415,10 +414,7 @@ public class ComputedLayout : Scenario { // This demonstrates how to have a dynamically sized button // Each time the button is clicked the button's text gets longer - // The call to app.LayoutSubviews causes the Computed layout to - // get updated. leftButton.Text += "!"; - app.LayoutSubviews (); }; // show positioning vertically using Pos.AnchorEnd @@ -433,10 +429,7 @@ public class ComputedLayout : Scenario { // This demonstrates how to have a dynamically sized button // Each time the button is clicked the button's text gets longer - // The call to app.LayoutSubviews causes the Computed layout to - // get updated. centerButton.Text += "!"; - app.LayoutSubviews (); }; // show positioning vertically using another window and Pos.Bottom @@ -451,10 +444,7 @@ public class ComputedLayout : Scenario { // This demonstrates how to have a dynamically sized button // Each time the button is clicked the button's text gets longer - // The call to app.LayoutSubviews causes the Computed layout to - // get updated. rightButton.Text += "!"; - app.LayoutSubviews (); }; View [] buttons = { leftButton, centerButton, rightButton }; diff --git a/UICatalog/Scenarios/ConfigurationEditor.cs b/UICatalog/Scenarios/ConfigurationEditor.cs index 2c898ac8b..5f40f51dd 100644 --- a/UICatalog/Scenarios/ConfigurationEditor.cs +++ b/UICatalog/Scenarios/ConfigurationEditor.cs @@ -154,8 +154,6 @@ public class ConfigurationEditor : Scenario { _tileView.Tiles.ToArray () [1].ContentView.SetFocus (); } - - Application.Top.LayoutSubviews (); } private void Quit () diff --git a/UICatalog/Scenarios/Dialogs.cs b/UICatalog/Scenarios/Dialogs.cs index 9a4156a39..8cddafcce 100644 --- a/UICatalog/Scenarios/Dialogs.cs +++ b/UICatalog/Scenarios/Dialogs.cs @@ -340,7 +340,6 @@ public class Dialogs : Scenario button.Text += char.ConvertFromUtf32 (CODE_POINT); } - dialog.LayoutSubviews (); e.Cancel = true; }; dialog.Add (addChar); diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 13e32bf5e..dd44d2980 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -306,9 +306,6 @@ public class Editor : Scenario _scrollBar.OtherScrollBarView.Size = _textView.Maxlength; _scrollBar.OtherScrollBarView.Position = _textView.LeftColumn; } - - _scrollBar.LayoutSubviews (); - _scrollBar.Refresh (); }; diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs index 38ea3bbfb..e3a5eaf0f 100644 --- a/UICatalog/Scenarios/LineDrawing.cs +++ b/UICatalog/Scenarios/LineDrawing.cs @@ -253,12 +253,8 @@ public class ToolsView : Window private void ToolsView_Initialized (object sender, EventArgs e) { - LayoutSubviews (); - Width = Math.Max (_colors.Frame.Width, _stylePicker.Frame.Width) + GetAdornmentsThickness ().Horizontal; - Height = _colors.Frame.Height + _stylePicker.Frame.Height + _addLayerBtn.Frame.Height + GetAdornmentsThickness ().Vertical; - SuperView.LayoutSubviews (); } } @@ -289,6 +285,9 @@ public class DrawingArea : View } } } + + // TODO: This is a hack to work around overlapped views not drawing correctly. + SuperView?.SetLayoutNeeded(); } //// BUGBUG: Why is this not handled by a key binding??? diff --git a/UICatalog/Scenarios/PosAlignDemo.cs b/UICatalog/Scenarios/PosAlignDemo.cs index b5aa98b31..765a8e452 100644 --- a/UICatalog/Scenarios/PosAlignDemo.cs +++ b/UICatalog/Scenarios/PosAlignDemo.cs @@ -335,8 +335,6 @@ public sealed class PosAlignDemo : Scenario } } } - - superView.LayoutSubviews (); } /// diff --git a/UICatalog/Scenarios/Progress.cs b/UICatalog/Scenarios/Progress.cs index 174ca266c..c87fc47a3 100644 --- a/UICatalog/Scenarios/Progress.cs +++ b/UICatalog/Scenarios/Progress.cs @@ -304,7 +304,6 @@ public class Progress : Scenario { Spinner.Visible = true; ActivityProgressBar.Width = Dim.Fill () - Spinner.Width; - LayoutSubviews (); } ); } @@ -319,7 +318,6 @@ public class Progress : Scenario { Spinner.Visible = false; ActivityProgressBar.Width = Dim.Fill () - Spinner.Width; - LayoutSubviews (); } ); } diff --git a/UICatalog/Scenarios/Scrolling.cs b/UICatalog/Scenarios/Scrolling.cs index fcd4cddae..8d7cdac00 100644 --- a/UICatalog/Scenarios/Scrolling.cs +++ b/UICatalog/Scenarios/Scrolling.cs @@ -134,10 +134,8 @@ public class Scrolling : Scenario { // This demonstrates how to have a dynamically sized button // Each time the button is clicked the button's text gets longer - // The call to Win.LayoutSubviews causes the Computed layout to - // get updated. anchorButton.Text += "!"; - app.LayoutSubviews (); + }; scrollView.Add (anchorButton); diff --git a/UICatalog/Scenarios/Sliders.cs b/UICatalog/Scenarios/Sliders.cs index 393b9768f..e0bc36174 100644 --- a/UICatalog/Scenarios/Sliders.cs +++ b/UICatalog/Scenarios/Sliders.cs @@ -219,11 +219,6 @@ public class Sliders : Scenario } } } - - if (app.IsInitialized) - { - app.LayoutSubviews (); - } }; optionsSlider.SetOption (0); // Legends optionsSlider.SetOption (1); // RangeAllowSingle @@ -326,8 +321,6 @@ public class Sliders : Scenario } } } - - app.LayoutSubviews (); }; #endregion Slider Orientation Slider @@ -384,7 +377,6 @@ public class Sliders : Scenario } } - app.LayoutSubviews (); }; #endregion Legends Orientation Slider diff --git a/UICatalog/Scenarios/TileViewNesting.cs b/UICatalog/Scenarios/TileViewNesting.cs index 728fdf26f..9882865ec 100644 --- a/UICatalog/Scenarios/TileViewNesting.cs +++ b/UICatalog/Scenarios/TileViewNesting.cs @@ -35,16 +35,16 @@ public class TileViewNesting : Scenario _textField.TextChanged += (s, e) => SetupTileView (); _cbHorizontal = new() { X = Pos.Right (_textField) + 1, Text = "Horizontal" }; - _cbHorizontal.CheckedStateChanging += (s, e) => SetupTileView (); + _cbHorizontal.CheckedStateChanged += (s, e) => SetupTileView (); _cbBorder = new() { X = Pos.Right (_cbHorizontal) + 1, Text = "Border" }; - _cbBorder.CheckedStateChanging += (s, e) => SetupTileView (); + _cbBorder.CheckedStateChanged += (s, e) => SetupTileView (); _cbTitles = new() { X = Pos.Right (_cbBorder) + 1, Text = "Titles" }; - _cbTitles.CheckedStateChanging += (s, e) => SetupTileView (); + _cbTitles.CheckedStateChanged += (s, e) => SetupTileView (); _cbUseLabels = new() { X = Pos.Right (_cbTitles) + 1, Text = "Use Labels" }; - _cbUseLabels.CheckedStateChanging += (s, e) => SetupTileView (); + _cbUseLabels.CheckedStateChanged += (s, e) => SetupTileView (); _workArea = new() { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill () }; @@ -196,11 +196,6 @@ public class TileViewNesting : Scenario _viewsToCreate = numberOfViews; AddMoreViews (root); } - - if (_loaded) - { - _workArea.LayoutSubviews (); - } } private void Split (TileView to, bool left) diff --git a/UICatalog/Scenarios/Wizards.cs b/UICatalog/Scenarios/Wizards.cs index e26c717e8..245a95503 100644 --- a/UICatalog/Scenarios/Wizards.cs +++ b/UICatalog/Scenarios/Wizards.cs @@ -336,9 +336,6 @@ public class Wizards : Scenario scrollBar.OtherScrollBarView.Size = someText.Maxlength; scrollBar.OtherScrollBarView.Position = someText.LeftColumn; } - - scrollBar.LayoutSubviews (); - scrollBar.Refresh (); }; fourthStep.Add (scrollBar); diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index d373c331e..90970b3be 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -546,7 +546,7 @@ public class ApplicationTests var actionCalled = 0; Application.Invoke (() => { actionCalled++; }); Application.MainLoop.Running = true; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (1, actionCalled); top.Dispose (); Application.Shutdown (); diff --git a/UnitTests/Dialogs/DialogTests.cs b/UnitTests/Dialogs/DialogTests.cs index b5efdb5be..82c3e9f3a 100644 --- a/UnitTests/Dialogs/DialogTests.cs +++ b/UnitTests/Dialogs/DialogTests.cs @@ -51,8 +51,8 @@ public class DialogTests // Now add a second button buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}"; dlg.AddButton (new () { Text = btn2Text }); - var first = false; - RunIteration (ref runstate, ref first); + + RunIteration (ref runstate); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); dlg.Dispose (); @@ -76,8 +76,7 @@ public class DialogTests // Now add a second button buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2}{CM.Glyphs.VLine}"; dlg.AddButton (new () { Text = btn2Text }); - first = false; - RunIteration (ref runstate, ref first); + RunIteration (ref runstate); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); dlg.Dispose (); @@ -101,8 +100,8 @@ public class DialogTests // Now add a second button buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2}{CM.Glyphs.VLine}"; dlg.AddButton (new () { Text = btn2Text }); - first = false; - RunIteration (ref runstate, ref first); + + RunIteration (ref runstate); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); dlg.Dispose (); @@ -126,8 +125,8 @@ public class DialogTests // Now add a second button buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {CM.Glyphs.VLine}"; dlg.AddButton (new () { Text = btn2Text }); - first = false; - RunIteration (ref runstate, ref first); + + RunIteration (ref runstate); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); dlg.Dispose (); @@ -849,7 +848,7 @@ public class DialogTests button2 = new Button { Text = btn2Text }; (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.Center, button1, button2); button1.Visible = false; - RunIteration (ref runstate, ref firstIteration); + RunIteration (ref runstate, firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2} {CM.Glyphs.VLine}"; TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); @@ -861,7 +860,7 @@ public class DialogTests button2 = new Button { Text = btn2Text }; (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.Fill, button1, button2); button1.Visible = false; - RunIteration (ref runstate, ref firstIteration); + RunIteration (ref runstate, firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2}{CM.Glyphs.VLine}"; TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); @@ -873,7 +872,7 @@ public class DialogTests button2 = new Button { Text = btn2Text }; (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.End, button1, button2); button1.Visible = false; - RunIteration (ref runstate, ref firstIteration); + RunIteration (ref runstate, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); dlg.Dispose (); @@ -884,7 +883,7 @@ public class DialogTests button2 = new Button { Text = btn2Text }; (runstate, dlg) = RunButtonTestDialog (title, width, Alignment.Start, button1, button2); button1.Visible = false; - RunIteration (ref runstate, ref firstIteration); + RunIteration (ref runstate, firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2} {CM.Glyphs.VLine}"; TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); diff --git a/UnitTests/Dialogs/WizardTests.cs b/UnitTests/Dialogs/WizardTests.cs index c8ef785cb..accd38139 100644 --- a/UnitTests/Dialogs/WizardTests.cs +++ b/UnitTests/Dialogs/WizardTests.cs @@ -31,10 +31,10 @@ public class WizardTests () RunState runstate = Application.Begin (wizard); var firstIteration = true; - Application.RunIteration (ref runstate, ref firstIteration); + Application.RunIteration (ref runstate, firstIteration); wizard.NextFinishButton.InvokeCommand (Command.Accept); - Application.RunIteration (ref runstate, ref firstIteration); + Application.RunIteration (ref runstate, firstIteration); Application.End (runstate); Assert.True (finishedFired); Assert.True (closedFired); @@ -56,7 +56,7 @@ public class WizardTests () wizard.Closed += (s, e) => { closedFired = true; }; runstate = Application.Begin (wizard); - Application.RunIteration (ref runstate, ref firstIteration); + Application.RunIteration (ref runstate, firstIteration); Assert.Equal (step1.Title, wizard.CurrentStep.Title); wizard.NextFinishButton.InvokeCommand (Command.Accept); @@ -90,7 +90,7 @@ public class WizardTests () wizard.Closed += (s, e) => { closedFired = true; }; runstate = Application.Begin (wizard); - Application.RunIteration (ref runstate, ref firstIteration); + Application.RunIteration (ref runstate, firstIteration); Assert.Equal (step2.Title, wizard.CurrentStep.Title); Assert.Equal (wizard.GetLastStep ().Title, wizard.CurrentStep.Title); @@ -464,7 +464,7 @@ public class WizardTests () //wizard.LayoutSubviews (); var firstIteration = false; RunState runstate = Application.Begin (wizard); - Application.RunIteration (ref runstate, ref firstIteration); + Application.RunIteration (ref runstate, firstIteration); // TODO: Disabled until Dim.Auto is used in Dialog //TestHelpers.AssertDriverContentsWithFrameAre ( diff --git a/UnitTests/View/Adornment/BorderTests.cs b/UnitTests/View/Adornment/BorderTests.cs index c77d3d499..29882235f 100644 --- a/UnitTests/View/Adornment/BorderTests.cs +++ b/UnitTests/View/Adornment/BorderTests.cs @@ -96,7 +96,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (width, 5); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = string.Empty; switch (width) @@ -227,10 +227,9 @@ public class BorderTests (ITestOutputHelper output) win.Border.Thickness = win.Border.Thickness with { Top = 3 }; RunState rs = Application.Begin (win); - var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (width, 4); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, false); var expected = string.Empty; switch (width) @@ -364,7 +363,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (width, 4); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = string.Empty; switch (width) @@ -487,7 +486,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (20, height); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = string.Empty; switch (height) @@ -549,7 +548,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (width, 3); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = string.Empty; switch (width) @@ -729,7 +728,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (5, 5); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = @" ╔═══╗ @@ -757,7 +756,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (10, 4); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = @" ╔════════╗ @@ -780,7 +779,7 @@ public class BorderTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (3, 3); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = @" ┌─┐ diff --git a/UnitTests/View/DrawTests.cs b/UnitTests/View/DrawTests.cs index 2e02ecc1a..faf728884 100644 --- a/UnitTests/View/DrawTests.cs +++ b/UnitTests/View/DrawTests.cs @@ -7,6 +7,139 @@ 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] public void Move_Is_Constrained_To_Viewport () @@ -17,18 +150,18 @@ public class DrawTests (ITestOutputHelper _output) Y = 1, Width = 3, Height = 3 }; - view.Margin.Thickness = new Thickness (1); + view.Margin.Thickness = new (1); // Only valid location w/in Viewport is 0, 0 (view) - 2, 2 (screen) view.Move (0, 0); - Assert.Equal (new Point (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row)); + Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row)); view.Move (-1, -1); - Assert.Equal (new Point (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row)); + Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row)); view.Move (1, 1); - Assert.Equal (new Point (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row)); + Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row)); } [Fact] @@ -41,7 +174,7 @@ public class DrawTests (ITestOutputHelper _output) Y = 1, Width = 3, Height = 3 }; - view.Margin.Thickness = new Thickness (1); + view.Margin.Thickness = new (1); View.Diagnostics = ViewDiagnosticFlags.Padding; view.BeginInit (); view.EndInit (); @@ -84,6 +217,7 @@ public class DrawTests (ITestOutputHelper _output) superView.LayoutSubviews (); superView.Draw (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -93,6 +227,7 @@ public class DrawTests (ITestOutputHelper _output) Rectangle toFill = new (x, y, width, height); view.FillRect (toFill); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -103,6 +238,7 @@ public class DrawTests (ITestOutputHelper _output) // Now try to clear beyond Viewport (invalid; clipping should prevent) superView.SetNeedsDisplay (); superView.Draw (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -111,6 +247,7 @@ public class DrawTests (ITestOutputHelper _output) _output); toFill = new (-width, -height, width, height); view.FillRect (toFill); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -121,6 +258,7 @@ public class DrawTests (ITestOutputHelper _output) // Now try to clear beyond Viewport (valid) superView.SetNeedsDisplay (); superView.Draw (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -129,6 +267,7 @@ public class DrawTests (ITestOutputHelper _output) _output); toFill = new (-1, -1, width + 1, height + 1); view.FillRect (toFill); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -139,6 +278,7 @@ public class DrawTests (ITestOutputHelper _output) // Now clear too much size superView.SetNeedsDisplay (); superView.Draw (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -147,6 +287,7 @@ public class DrawTests (ITestOutputHelper _output) _output); toFill = new (0, 0, width * 2, height * 2); view.FillRect (toFill); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -174,6 +315,7 @@ public class DrawTests (ITestOutputHelper _output) superView.LayoutSubviews (); superView.Draw (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -182,6 +324,7 @@ public class DrawTests (ITestOutputHelper _output) _output); view.Clear (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -210,6 +353,7 @@ public class DrawTests (ITestOutputHelper _output) superView.LayoutSubviews (); superView.Draw (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -218,6 +362,7 @@ public class DrawTests (ITestOutputHelper _output) _output); view.Clear (); + TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌─┐ @@ -226,7 +371,6 @@ public class DrawTests (ITestOutputHelper _output) _output); } - [Fact] [AutoInitShutdown] [Trait ("Category", "Unicode")] @@ -243,7 +387,7 @@ public class DrawTests (ITestOutputHelper _output) Assert.Equal (2, r.GetColumns ()); var win = new Window { Title = us }; - var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill ()}; + var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () }; var tf = new TextField { Text = us, Y = 1, Width = 3 }; win.Add (view, tf); Toplevel top = new (); @@ -317,7 +461,7 @@ public class DrawTests (ITestOutputHelper _output) """; Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output); - Assert.Equal (new Rectangle (0, 0, 30, 10), pos); + Assert.Equal (new (0, 0, 30, 10), pos); Application.End (rsDiag); dg.Dispose (); @@ -352,12 +496,13 @@ public class DrawTests (ITestOutputHelper _output) Toplevel top = new (); top.Add (viewRight, viewBottom); - Application.Begin (top); + var rs = Application.Begin (top); ((FakeDriver)Application.Driver!).SetBufferSize (7, 7); + Application.RunIteration (ref rs); TestHelpers.AssertDriverContentsWithFrameAre ( """ - + Test @@ -391,13 +536,25 @@ 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.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); Assert.Equal (new (0, 0, 2, 2), view.Frame); Assert.Equal (Rectangle.Empty, view.Viewport); + Assert.True (view.NeedsDisplay); + view.Draw (); TestHelpers.AssertDriverContentsWithFrameAre ( @@ -415,7 +572,7 @@ public class DrawTests (ITestOutputHelper _output) public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Bottom () { var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single }; - view.Border.Thickness = new Thickness (1, 1, 1, 0); + view.Border.Thickness = new (1, 1, 1, 0); view.BeginInit (); view.EndInit (); view.SetRelativeLayout (Application.Screen.Size); @@ -433,7 +590,7 @@ public class DrawTests (ITestOutputHelper _output) public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Left () { var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single }; - view.Border.Thickness = new Thickness (0, 1, 1, 1); + view.Border.Thickness = new (0, 1, 1, 1); view.BeginInit (); view.EndInit (); view.SetRelativeLayout (Application.Screen.Size); @@ -458,7 +615,7 @@ public class DrawTests (ITestOutputHelper _output) public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Right () { var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single }; - view.Border.Thickness = new Thickness (1, 1, 0, 1); + view.Border.Thickness = new (1, 1, 0, 1); view.BeginInit (); view.EndInit (); view.SetRelativeLayout (Application.Screen.Size); @@ -483,7 +640,7 @@ public class DrawTests (ITestOutputHelper _output) public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Top () { var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single }; - view.Border.Thickness = new Thickness (1, 0, 1, 1); + view.Border.Thickness = new (1, 0, 1, 1); view.BeginInit (); view.EndInit (); @@ -494,7 +651,8 @@ public class DrawTests (ITestOutputHelper _output) view.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ("││", + TestHelpers.AssertDriverContentsWithFrameAre ( + "││", _output ); } @@ -510,40 +668,40 @@ public class DrawTests (ITestOutputHelper _output) Width = 1, Height = 7, Text = """ - s - u - b - V - i - e - w - """ + s + u + b + V + i + e + w + """ }; var view = new View { Id = "view", Width = 2, Height = 20, Text = """ - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - """ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + """ }; view.Add (subView); var content = new View { Id = "content", Width = 20, Height = 20 }; @@ -560,12 +718,12 @@ public class DrawTests (ITestOutputHelper _output) container.Add (content); Toplevel top = new (); top.Add (container); - Application.Driver!.Clip = container.Frame; - Application.Begin (top); + var rs = Application.Begin (top); + top.Draw (); TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 0s 1u 2b @@ -580,7 +738,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + s u b @@ -600,7 +758,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 1u 2b 3V @@ -615,7 +773,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 6w 7 8 @@ -630,7 +788,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 9 """, _output @@ -682,9 +840,10 @@ public class DrawTests (ITestOutputHelper _output) top.LayoutComplete += Top_LayoutComplete; Application.Begin (top); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 01234 subVi """, @@ -696,7 +855,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 12345 ubVie """, @@ -708,7 +867,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + ubVie """, _output @@ -766,12 +925,12 @@ public class DrawTests (ITestOutputHelper _output) container.Add (content); Toplevel top = new (); top.Add (container); - Application.Driver!.Clip = container.Frame; Application.Begin (top); + Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 0s 1u 2b @@ -786,7 +945,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + s u b @@ -806,7 +965,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 1u 2b 3V @@ -821,7 +980,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 6w 7 8 @@ -836,7 +995,7 @@ public class DrawTests (ITestOutputHelper _output) TestHelpers.AssertDriverContentsWithFrameAre ( """ - + 9 """, _output @@ -892,15 +1051,16 @@ public class DrawTests (ITestOutputHelper _output) var expected = """ - ┌┤𝔹├─────┐ - │𝔹 │ - │𝔹 │ - └────────┘ - """; + ┌┤𝔹├─────┐ + │𝔹 │ + │𝔹 │ + └────────┘ + """; TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); TestHelpers.AssertDriverContentsAre (expected, _output); top.Dispose (); + // This test has nothing to do with color - removing as it is not relevant and fragile } @@ -916,15 +1076,16 @@ public class DrawTests (ITestOutputHelper _output) // Visible content is (1, 1, 10, 10) // Expected clip is (1, 1, 10, 10) - same as visible content Rectangle expectedClip = new (1, 1, 10, 10); + // Arrange - var view = new View () + var view = new View { Width = Dim.Fill (), Height = Dim.Fill (), ViewportSettings = ViewportSettings.ClipContentOnly }; view.SetContentSize (new Size (10, 10)); - view.Border.Thickness = new Thickness (1); + view.Border.Thickness = new (1); view.BeginInit (); view.EndInit (); Assert.Equal (view.Frame, Application.Driver?.Clip); @@ -949,14 +1110,15 @@ public class DrawTests (ITestOutputHelper _output) // Visible content is (1, 1, 10, 10) // Expected clip is (1, 1, 23, 23) - same as Viewport Rectangle expectedClip = new (1, 1, 23, 23); + // Arrange - var view = new View () + var view = new View { Width = Dim.Fill (), - Height = Dim.Fill (), + Height = Dim.Fill () }; view.SetContentSize (new Size (10, 10)); - view.Border.Thickness = new Thickness (1); + view.Border.Thickness = new (1); view.BeginInit (); view.EndInit (); Assert.Equal (view.Frame, Application.Driver?.Clip); @@ -970,7 +1132,6 @@ public class DrawTests (ITestOutputHelper _output) view.Dispose (); } - [Fact] [TestRespondersDisposed] public void Draw_Throws_IndexOutOfRangeException_With_Negative_Bounds () @@ -983,11 +1144,11 @@ public class DrawTests (ITestOutputHelper _output) top.Add (view); Application.Iteration += (s, a) => - { - Assert.Equal (-2, view.X); + { + Assert.Equal (-2, view.X); - Application.RequestStop (); - }; + Application.RequestStop (); + }; try { @@ -1000,8 +1161,8 @@ public class DrawTests (ITestOutputHelper _output) } top.Dispose (); + // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); } - } diff --git a/UnitTests/View/Layout/AbsoluteLayoutTests.cs b/UnitTests/View/Layout/AbsoluteLayoutTests.cs deleted file mode 100644 index 1ed9fab03..000000000 --- a/UnitTests/View/Layout/AbsoluteLayoutTests.cs +++ /dev/null @@ -1,236 +0,0 @@ -using Xunit.Abstractions; - -namespace Terminal.Gui.LayoutTests; - -public class AbsoluteLayoutTests (ITestOutputHelper output) -{ - private readonly ITestOutputHelper _output = output; - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_Change_Height_or_Width_Absolute () - { - var frame = new Rectangle (1, 2, 3, 4); - var newFrame = new Rectangle (1, 2, 30, 40); - - var v = new View { Frame = frame }; - v.Height = newFrame.Height; - v.Width = newFrame.Width; - Assert.Equal (newFrame, v.Frame); - - Assert.Equal ( - new (0, 0, newFrame.Width, newFrame.Height), - v.Viewport - ); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (1), v.X); - Assert.Equal (Pos.Absolute (2), v.Y); - Assert.Equal ($"Absolute({newFrame.Height})", v.Height.ToString ()); - Assert.Equal ($"Absolute({newFrame.Width})", v.Width.ToString ()); - v.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_Change_Height_or_Width_MakesComputed () - { - var v = new View { Frame = Rectangle.Empty }; - v.Height = Dim.Fill (); - v.Width = Dim.Fill (); - v.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_Change_X_or_Y_Absolute () - { - var frame = new Rectangle (1, 2, 3, 4); - var newFrame = new Rectangle (10, 20, 3, 4); - - var v = new View { Frame = frame }; - v.X = newFrame.X; - v.Y = newFrame.Y; - Assert.Equal (newFrame, v.Frame); - - Assert.Equal ( - new (0, 0, newFrame.Width, newFrame.Height), - v.Viewport - ); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal ($"Absolute({newFrame.X})", v.X.ToString ()); - Assert.Equal ($"Absolute({newFrame.Y})", v.Y.ToString ()); - Assert.Equal (Dim.Absolute (3), v.Width); - Assert.Equal (Dim.Absolute (4), v.Height); - v.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_Change_X_or_Y_MakesComputed () - { - var v = new View { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_Change_X_Y_Height_Width_Absolute () - { - var v = new View { Frame = Rectangle.Empty }; - v.X = 1; - v.Y = 2; - v.Height = 3; - v.Width = 4; - v.Dispose (); - - v = new() { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Width = Dim.Fill (); - v.Height = Dim.Fill (); - v.Dispose (); - - v = new() { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Width = Dim.Fill (); - v.Height = Dim.Fill (); - - v.X = 1; - v.Dispose (); - - v = new() { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Width = Dim.Fill (); - v.Height = Dim.Fill (); - - v.Y = 2; - v.Dispose (); - - v = new() { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Width = Dim.Fill (); - v.Height = Dim.Fill (); - - v.Width = 3; - v.Dispose (); - - v = new() { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Width = Dim.Fill (); - v.Height = Dim.Fill (); - - v.Height = 3; - v.Dispose (); - - v = new() { Frame = Rectangle.Empty }; - v.X = Pos.Center (); - v.Y = Pos.Center (); - v.Width = Dim.Fill (); - v.Height = Dim.Fill (); - - v.X = 1; - v.Y = 2; - v.Height = 3; - v.Width = 4; - v.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_Constructor () - { - var v = new View (); - v.Dispose (); - - var frame = Rectangle.Empty; - v = new() { Frame = frame }; - Assert.Equal (frame, v.Frame); - - Assert.Equal ( - new (0, 0, frame.Width, frame.Height), - v.Viewport - ); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (0), v.X); - Assert.Equal (Pos.Absolute (0), v.Y); - Assert.Equal (Dim.Absolute (0), v.Width); - Assert.Equal (Dim.Absolute (0), v.Height); - v.Dispose (); - - frame = new (1, 2, 3, 4); - v = new() { Frame = frame }; - Assert.Equal (frame, v.Frame); - - Assert.Equal ( - new (0, 0, frame.Width, frame.Height), - v.Viewport - ); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (1), v.X); - Assert.Equal (Pos.Absolute (2), v.Y); - Assert.Equal (Dim.Absolute (3), v.Width); - Assert.Equal (Dim.Absolute (4), v.Height); - v.Dispose (); - - v = new() { Frame = frame, Text = "v" }; - Assert.Equal (frame, v.Frame); - - Assert.Equal ( - new (0, 0, frame.Width, frame.Height), - v.Viewport - ); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (1), v.X); - Assert.Equal (Pos.Absolute (2), v.Y); - Assert.Equal (Dim.Absolute (3), v.Width); - Assert.Equal (Dim.Absolute (4), v.Height); - v.Dispose (); - - v = new() { X = frame.X, Y = frame.Y, Text = "v" }; - - Assert.Equal (new (frame.X, frame.Y, 0, 0), v.Frame); - Assert.Equal (new (0, 0, 0, 0), v.Viewport); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (1), v.X); - Assert.Equal (Pos.Absolute (2), v.Y); - Assert.Equal (Dim.Absolute (0), v.Width); - Assert.Equal (Dim.Absolute (0), v.Height); - v.Dispose (); - - v = new (); - Assert.Equal (new (0, 0, 0, 0), v.Frame); - Assert.Equal (new (0, 0, 0, 0), v.Viewport); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (0), v.X); - Assert.Equal (Pos.Absolute (0), v.Y); - Assert.Equal (Dim.Absolute (0), v.Width); - Assert.Equal (Dim.Absolute (0), v.Height); - v.Dispose (); - - v = new() { X = frame.X, Y = frame.Y, Width = frame.Width, Height = frame.Height }; - Assert.Equal (new (frame.X, frame.Y, 3, 4), v.Frame); - Assert.Equal (new (0, 0, 3, 4), v.Viewport); // With Absolute Viewport *is* deterministic before Layout - Assert.Equal (Pos.Absolute (1), v.X); - Assert.Equal (Pos.Absolute (2), v.Y); - Assert.Equal (Dim.Absolute (3), v.Width); - Assert.Equal (Dim.Absolute (4), v.Height); - v.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void AbsoluteLayout_LayoutSubviews () - { - var superRect = new Rectangle (0, 0, 100, 100); - var super = new View { Frame = superRect, Text = "super" }; - var v1 = new View { X = 0, Y = 0, Width = 10, Height = 10 }; - - var v2 = new View { X = 10, Y = 10, Width = 10, Height = 10 }; - - super.Add (v1, v2); - - super.LayoutSubviews (); - Assert.Equal (new (0, 0, 10, 10), v1.Frame); - Assert.Equal (new (10, 10, 10, 10), v2.Frame); - super.Dispose (); - } -} diff --git a/UnitTests/View/Layout/Dim.AutoTests.cs b/UnitTests/View/Layout/Dim.AutoTests.cs index 1f11997b8..27efad835 100644 --- a/UnitTests/View/Layout/Dim.AutoTests.cs +++ b/UnitTests/View/Layout/Dim.AutoTests.cs @@ -4,12 +4,11 @@ using static Terminal.Gui.Dim; namespace Terminal.Gui.LayoutTests; -[Trait("Category", "Layout")] +[Trait ("Category", "Layout")] public partial class DimAutoTests (ITestOutputHelper output) { private readonly ITestOutputHelper _output = output; - [SetupFakeDriver] [Fact] public void Change_To_Non_Auto_Resets_ContentSize () { @@ -26,6 +25,7 @@ public partial class DimAutoTests (ITestOutputHelper output) // Change text to a longer string view.Text = "0123456789"; + view.Layout (new (100, 100)); Assert.Equal (new (0, 0, 10, 1), view.Frame); Assert.Equal (new (10, 1), view.GetContentSize ()); @@ -33,6 +33,7 @@ public partial class DimAutoTests (ITestOutputHelper output) view.Width = 5; view.Height = 1; + view.SetRelativeLayout (new (100, 100)); Assert.Equal (new (5, 1), view.GetContentSize ()); } @@ -216,7 +217,7 @@ public partial class DimAutoTests (ITestOutputHelper output) Style: DimAutoStyle.Auto ); - var c = new DimAuto( + var c = new DimAuto ( MaximumContentDim: 2, MinimumContentDim: 1, Style: DimAutoStyle.Auto @@ -966,9 +967,9 @@ public partial class DimAutoTests (ITestOutputHelper output) [Fact] public void DimAutoStyle_Content_UsesLargestSubview_WhenContentSizeNotSet () { - var view = new View (); - view.Add (new View { Frame = new (0, 0, 5, 5) }); // Smaller subview - view.Add (new View { Frame = new (0, 0, 10, 10) }); // Larger subview + var view = new View { Id = "view" }; + view.Add (new View { Id = "smaller", Frame = new (0, 0, 5, 5) }); // Smaller subview + view.Add (new View { Id = "larger", Frame = new (0, 0, 10, 10) }); // Larger subview Dim dim = Auto (DimAutoStyle.Content); @@ -990,6 +991,8 @@ public partial class DimAutoTests (ITestOutputHelper output) [Fact] public void DimAutoStyle_Content_Pos_AnchorEnd_Locates_Correctly () { + Application.SetScreenSize (new Size (10, 10)); + DimAutoTestView view = new (Auto (DimAutoStyle.Content), Auto (DimAutoStyle.Content)); View subView = new () @@ -999,24 +1002,23 @@ public partial class DimAutoTests (ITestOutputHelper output) }; view.Add (subView); - view.SetRelativeLayout (new (10, 10)); + view.Layout (); Assert.Equal (new (5, 1), view.Frame.Size); Assert.Equal (new (0, 0), view.Frame.Location); view.X = 0; - view.Y = Pos.AnchorEnd (1); - view.SetRelativeLayout (new (10, 10)); + view.Layout (); Assert.Equal (new (5, 1), view.Frame.Size); Assert.Equal (new (0, 9), view.Frame.Location); view.Y = Pos.AnchorEnd (); - view.SetRelativeLayout (new (10, 10)); + view.Layout (); Assert.Equal (new (5, 1), view.Frame.Size); Assert.Equal (new (0, 9), view.Frame.Location); view.Y = Pos.AnchorEnd () - 1; - view.SetRelativeLayout (new (10, 10)); + view.Layout (); Assert.Equal (new (5, 1), view.Frame.Size); Assert.Equal (new (0, 8), view.Frame.Location); } diff --git a/UnitTests/View/Layout/LayoutTests.cs b/UnitTests/View/Layout/LayoutTests.cs index a22c113d3..d0482654c 100644 --- a/UnitTests/View/Layout/LayoutTests.cs +++ b/UnitTests/View/Layout/LayoutTests.cs @@ -6,6 +6,56 @@ public class LayoutTests (ITestOutputHelper output) { private readonly ITestOutputHelper _output = output; + + [Fact] + [AutoInitShutdown] + public void Screen_Size_Change_Causes_Layout () + { + Application.Top = new (); + + var view = new View + { + X = 3, + Y = 2, + Width = 10, + Height = 1, + Text = "0123456789" + }; + Application.Top.Add (view); + + var rs = Application.Begin (Application.Top); + + Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows)); + Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame); + Assert.Equal (new (0, 0, 80, 25), Application.Top.Frame); + + ((FakeDriver)Application.Driver!).SetBufferSize (20, 10); + Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame); + + Assert.Equal (new (0, 0, 20, 10), Application.Top.Frame); + + Application.End (rs); + + } + + [Fact] + [TestRespondersDisposed] + public void LayoutSubviews () + { + var superRect = new Rectangle (0, 0, 100, 100); + var super = new View { Frame = superRect, Text = "super" }; + var v1 = new View { X = 0, Y = 0, Width = 10, Height = 10 }; + + var v2 = new View { X = 10, Y = 10, Width = 10, Height = 10 }; + + super.Add (v1, v2); + + super.LayoutSubviews (); + Assert.Equal (new (0, 0, 10, 10), v1.Frame); + Assert.Equal (new (10, 10, 10, 10), v2.Frame); + super.Dispose (); + } + [Fact] public void LayoutSubviews_No_SuperView () { @@ -35,7 +85,7 @@ public class LayoutTests (ITestOutputHelper output) } [Fact] - public void Add_Does_Not_Call_LayoutSubviews () + public void Add_Does_Not_Call_Layout () { var superView = new View { Id = "superView" }; var view = new View { Id = "view" }; @@ -49,10 +99,7 @@ public class LayoutTests (ITestOutputHelper output) Assert.False (layoutStartedRaised); Assert.False (layoutCompleteRaised); - superView.Remove(view); - - superView.BeginInit(); - superView.EndInit (); + superView.Remove (view); superView.Add (view); @@ -61,20 +108,6 @@ public class LayoutTests (ITestOutputHelper output) } - [Fact] - public void BeginEndInit_Do_Not_Call_LayoutSubviews () - { - var superView = new View { Id = "superView" }; - bool layoutStartedRaised = false; - bool layoutCompleteRaised = false; - superView.LayoutStarted += (sender, e) => layoutStartedRaised = true; - superView.LayoutComplete += (sender, e) => layoutCompleteRaised = true; - superView.BeginInit (); - superView.EndInit (); - Assert.False (layoutStartedRaised); - Assert.False (layoutCompleteRaised); - } - [Fact] public void LayoutSubViews_Raises_LayoutStarted_LayoutComplete () { @@ -203,38 +236,376 @@ public class LayoutTests (ITestOutputHelper output) { var superView = new View (); var view = new View (); + + var layoutStartedCount = 0; + var layoutCompleteCount = 0; + + var borderLayoutStartedCount = 0; + var borderLayoutCompleteCount = 0; + + view.LayoutStarted += (sender, e) => layoutStartedCount++; + view.LayoutComplete += (sender, e) => layoutCompleteCount++; + + view.Border.LayoutStarted += (sender, e) => borderLayoutStartedCount++; + view.Border.LayoutComplete += (sender, e) => borderLayoutCompleteCount++; + + superView.Add (view); + Assert.Equal (0, borderLayoutStartedCount); + Assert.Equal (0, borderLayoutCompleteCount); + Assert.Equal (0, layoutStartedCount); + Assert.Equal (0, layoutCompleteCount); + superView.BeginInit (); + Assert.Equal (0, borderLayoutStartedCount); + Assert.Equal (0, borderLayoutCompleteCount); + Assert.Equal (0, layoutStartedCount); + Assert.Equal (0, layoutCompleteCount); + superView.EndInit (); - - var layoutStarted = false; - var layoutComplete = false; - - var borderLayoutStarted = false; - var borderLayoutComplete = false; - - view.LayoutStarted += (sender, e) => layoutStarted = true; - view.LayoutComplete += (sender, e) => layoutComplete = true; - - view.Border.LayoutStarted += (sender, e) => - { - Assert.True (layoutStarted); - borderLayoutStarted = true; - }; - view.Border.LayoutComplete += (sender, e) => - { - Assert.True (layoutStarted); - Assert.False (layoutComplete); - borderLayoutComplete = true; - }; + Assert.Equal (1, borderLayoutStartedCount); + Assert.Equal (1, borderLayoutCompleteCount); + Assert.Equal (1, layoutStartedCount); + Assert.Equal (1, layoutCompleteCount); superView.LayoutSubviews (); + Assert.Equal (1, borderLayoutStartedCount); + Assert.Equal (1, borderLayoutCompleteCount); + Assert.Equal (1, layoutStartedCount); + Assert.Equal (1, layoutCompleteCount); - Assert.True (borderLayoutStarted); - Assert.True (borderLayoutComplete); + superView.SetLayoutNeeded (); + superView.LayoutSubviews (); + Assert.Equal (2, borderLayoutStartedCount); + Assert.Equal (2, borderLayoutCompleteCount); + Assert.Equal (2, layoutStartedCount); + Assert.Equal (2, layoutCompleteCount); - Assert.True (layoutStarted); - Assert.True (layoutComplete); superView.Dispose (); } + + [Fact] + public void LayoutSubviews__Honors_IsLayoutNeeded () + { + var superView = new View (); + var view = new View (); + + var layoutStartedCount = 0; + var layoutCompleteCount = 0; + + var borderLayoutStartedCount = 0; + var borderLayoutCompleteCount = 0; + + view.LayoutStarted += (sender, e) => layoutStartedCount++; + view.LayoutComplete += (sender, e) => layoutCompleteCount++; + + view.Border.LayoutStarted += (sender, e) => borderLayoutStartedCount++; + view.Border.LayoutComplete += (sender, e) => borderLayoutCompleteCount++; + + + superView.Add (view); + + superView.LayoutSubviews (); + Assert.Equal (1, borderLayoutStartedCount); + Assert.Equal (1, borderLayoutCompleteCount); + Assert.Equal (1, layoutStartedCount); + Assert.Equal (1, layoutCompleteCount); + + superView.LayoutSubviews (); + Assert.Equal (1, borderLayoutStartedCount); + Assert.Equal (1, borderLayoutCompleteCount); + Assert.Equal (1, layoutStartedCount); + Assert.Equal (1, layoutCompleteCount); + + superView.SetLayoutNeeded (); + superView.LayoutSubviews (); + Assert.Equal (2, borderLayoutStartedCount); + Assert.Equal (2, borderLayoutCompleteCount); + Assert.Equal (2, layoutStartedCount); + Assert.Equal (2, layoutCompleteCount); + + superView.Dispose (); + } + + [Fact] + public void Set_X_Does_Not_Change_Frame_Until_Layout () + { + var v = new View (); + Assert.Equal (0, v.Frame.X); + + v.Layout (); + Assert.Equal (0, v.Frame.X); + + v.X = 1; + Assert.Equal (0, v.Frame.X); + + v.Layout (); + Assert.Equal (1, v.Frame.X); + + v.X = 2; + Assert.Equal (1, v.Frame.X); + + v.Layout (); + Assert.Equal (2, v.Frame.X); + } + + + [Fact] + public void Set_Y_Does_Not_Change_Frame_Until_Layout () + { + var v = new View (); + Assert.Equal (0, v.Frame.Y); + + v.Layout (); + Assert.Equal (0, v.Frame.Y); + + v.Y = 1; + Assert.Equal (0, v.Frame.Y); + + v.Layout (); + Assert.Equal (1, v.Frame.Y); + + v.Y = 2; + Assert.Equal (1, v.Frame.Y); + + v.Layout (); + Assert.Equal (2, v.Frame.Y); + } + + + [Fact] + public void Set_Width_Does_Not_Change_Frame_Until_Layout () + { + var v = new View (); + Assert.Equal (0, v.Frame.Width); + + v.Layout (); + Assert.Equal (0, v.Frame.Width); + + v.Width = 1; + Assert.Equal (0, v.Frame.Width); + + v.Layout (); + Assert.Equal (1, v.Frame.Width); + + v.Width = 2; + Assert.Equal (1, v.Frame.Width); + + v.Layout (); + Assert.Equal (2, v.Frame.Width); + } + + + [Fact] + public void Set_Height_Does_Not_Change_Frame_Until_Layout () + { + var v = new View (); + Assert.Equal (0, v.Frame.Height); + + v.Layout (); + Assert.Equal (0, v.Frame.Height); + + v.Height = 1; + Assert.Equal (0, v.Frame.Height); + + v.Layout (); + Assert.Equal (1, v.Frame.Height); + + v.Height = 2; + Assert.Equal (1, v.Frame.Height); + + v.Layout (); + Assert.Equal (2, v.Frame.Height); + } + + [Fact] + [TestRespondersDisposed] + public void Change_Height_or_Width_MakesComputed () + { + var v = new View { Frame = Rectangle.Empty }; + v.Height = Dim.Fill (); + v.Width = Dim.Fill (); + v.Dispose (); + } + + [Fact] + [TestRespondersDisposed] + public void Change_X_or_Y_Absolute () + { + var frame = new Rectangle (1, 2, 3, 4); + var newFrame = new Rectangle (10, 20, 3, 4); + + var v = new View { Frame = frame }; + v.X = newFrame.X; + v.Y = newFrame.Y; + v.Layout (); + Assert.Equal (newFrame, v.Frame); + + Assert.Equal ( + new (0, 0, newFrame.Width, newFrame.Height), + v.Viewport + ); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal ($"Absolute({newFrame.X})", v.X.ToString ()); + Assert.Equal ($"Absolute({newFrame.Y})", v.Y.ToString ()); + Assert.Equal (Dim.Absolute (3), v.Width); + Assert.Equal (Dim.Absolute (4), v.Height); + v.Dispose (); + } + + [Fact] + [TestRespondersDisposed] + public void Change_X_or_Y_MakesComputed () + { + var v = new View { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Dispose (); + } + + [Fact] + [TestRespondersDisposed] + public void Change_X_Y_Height_Width_Absolute () + { + var v = new View { Frame = Rectangle.Empty }; + v.X = 1; + v.Y = 2; + v.Height = 3; + v.Width = 4; + v.Dispose (); + + v = new () { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Width = Dim.Fill (); + v.Height = Dim.Fill (); + v.Dispose (); + + v = new () { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Width = Dim.Fill (); + v.Height = Dim.Fill (); + + v.X = 1; + v.Dispose (); + + v = new () { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Width = Dim.Fill (); + v.Height = Dim.Fill (); + + v.Y = 2; + v.Dispose (); + + v = new () { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Width = Dim.Fill (); + v.Height = Dim.Fill (); + + v.Width = 3; + v.Dispose (); + + v = new () { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Width = Dim.Fill (); + v.Height = Dim.Fill (); + + v.Height = 3; + v.Dispose (); + + v = new () { Frame = Rectangle.Empty }; + v.X = Pos.Center (); + v.Y = Pos.Center (); + v.Width = Dim.Fill (); + v.Height = Dim.Fill (); + + v.X = 1; + v.Y = 2; + v.Height = 3; + v.Width = 4; + v.Dispose (); + } + + [Fact] + public void Constructor () + { + var v = new View (); + v.Dispose (); + + var frame = Rectangle.Empty; + v = new () { Frame = frame }; + v.Layout (); + Assert.Equal (frame, v.Frame); + + Assert.Equal ( + new (0, 0, frame.Width, frame.Height), + v.Viewport + ); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal (Pos.Absolute (0), v.X); + Assert.Equal (Pos.Absolute (0), v.Y); + Assert.Equal (Dim.Absolute (0), v.Width); + Assert.Equal (Dim.Absolute (0), v.Height); + v.Dispose (); + + frame = new (1, 2, 3, 4); + v = new () { Frame = frame }; + v.Layout (); + Assert.Equal (frame, v.Frame); + Assert.Equal ( + new (0, 0, frame.Width, frame.Height), + v.Viewport + ); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal (Pos.Absolute (1), v.X); + Assert.Equal (Pos.Absolute (2), v.Y); + Assert.Equal (Dim.Absolute (3), v.Width); + Assert.Equal (Dim.Absolute (4), v.Height); + v.Dispose (); + + v = new () { Frame = frame, Text = "v" }; + v.Layout (); + Assert.Equal (frame, v.Frame); + Assert.Equal ( + new (0, 0, frame.Width, frame.Height), + v.Viewport + ); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal (Pos.Absolute (1), v.X); + Assert.Equal (Pos.Absolute (2), v.Y); + Assert.Equal (Dim.Absolute (3), v.Width); + Assert.Equal (Dim.Absolute (4), v.Height); + v.Dispose (); + + v = new () { X = frame.X, Y = frame.Y, Text = "v" }; + v.Layout (); + Assert.Equal (new (frame.X, frame.Y, 0, 0), v.Frame); + Assert.Equal (new (0, 0, 0, 0), v.Viewport); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal (Pos.Absolute (1), v.X); + Assert.Equal (Pos.Absolute (2), v.Y); + Assert.Equal (Dim.Absolute (0), v.Width); + Assert.Equal (Dim.Absolute (0), v.Height); + v.Dispose (); + + v = new (); + v.Layout (); + Assert.Equal (new (0, 0, 0, 0), v.Frame); + Assert.Equal (new (0, 0, 0, 0), v.Viewport); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal (Pos.Absolute (0), v.X); + Assert.Equal (Pos.Absolute (0), v.Y); + Assert.Equal (Dim.Absolute (0), v.Width); + Assert.Equal (Dim.Absolute (0), v.Height); + v.Dispose (); + + v = new () { X = frame.X, Y = frame.Y, Width = frame.Width, Height = frame.Height }; + v.Layout (); + Assert.Equal (new (frame.X, frame.Y, 3, 4), v.Frame); + Assert.Equal (new (0, 0, 3, 4), v.Viewport); // With Absolute Viewport *is* deterministic before Layout + Assert.Equal (Pos.Absolute (1), v.X); + Assert.Equal (Pos.Absolute (2), v.Y); + Assert.Equal (Dim.Absolute (3), v.Width); + Assert.Equal (Dim.Absolute (4), v.Height); + v.Dispose (); + } + } diff --git a/UnitTests/View/Layout/Pos.CenterTests.cs b/UnitTests/View/Layout/Pos.CenterTests.cs index ccd6ba5bd..a27dc5e22 100644 --- a/UnitTests/View/Layout/Pos.CenterTests.cs +++ b/UnitTests/View/Layout/Pos.CenterTests.cs @@ -94,7 +94,7 @@ public class PosCenterTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (20, height); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = string.Empty; switch (height) @@ -241,7 +241,7 @@ public class PosCenterTests (ITestOutputHelper output) var firstIteration = false; ((FakeDriver)Application.Driver!).SetBufferSize (width, 7); - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); var expected = string.Empty; switch (width) diff --git a/UnitTests/View/Layout/ToScreenTests.cs b/UnitTests/View/Layout/ToScreenTests.cs index 17153badc..3e724f0ed 100644 --- a/UnitTests/View/Layout/ToScreenTests.cs +++ b/UnitTests/View/Layout/ToScreenTests.cs @@ -960,7 +960,7 @@ public class ToScreenTests (ITestOutputHelper output) }; Application.Top.Add (view); - Application.Begin (Application.Top); + var rs = Application.Begin (Application.Top); Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows)); Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame); @@ -970,6 +970,7 @@ public class ToScreenTests (ITestOutputHelper output) Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame); Assert.Equal (new (0, 0, 20, 10), Application.Top.Frame); + _ = TestHelpers.AssertDriverContentsWithFrameAre ( @" ┌──────────────────┐ diff --git a/UnitTests/View/Mouse/MouseTests.cs b/UnitTests/View/Mouse/MouseTests.cs index c4b10fe95..ab23aead1 100644 --- a/UnitTests/View/Mouse/MouseTests.cs +++ b/UnitTests/View/Mouse/MouseTests.cs @@ -103,12 +103,15 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews var top = new Toplevel (); top.Add (testView); - Application.Begin (top); + + var rs = Application.Begin (top); + Assert.Equal (4, testView.Frame.X); Assert.Equal (new Point (4, 4), testView.Frame.Location); Application.RaiseMouseEvent (new () { ScreenPosition = new (xy, xy), Flags = MouseFlags.Button1Pressed }); Application.RaiseMouseEvent (new () { ScreenPosition = new (xy + 1, xy + 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition }); + Application.RunIteration(ref rs, false); Assert.Equal (expectedMoved, new Point (5, 5) == testView.Frame.Location); top.Dispose (); diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index a6cd27670..3c10f3d72 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -1025,7 +1025,7 @@ At 0,0 view.Visible = false; var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" diff --git a/UnitTests/Views/ContextMenuTests.cs b/UnitTests/Views/ContextMenuTests.cs index 1bd08ed72..0b5d9a52e 100644 --- a/UnitTests/Views/ContextMenuTests.cs +++ b/UnitTests/Views/ContextMenuTests.cs @@ -151,7 +151,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (8, 2), Flags = MouseFlags.Button3Clicked }); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -234,7 +234,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (9, 3), Flags = MouseFlags.Button3Clicked }); var firstIteration = false; - Application.RunIteration (ref rsDialog, ref firstIteration); + Application.RunIteration (ref rsDialog, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -290,7 +290,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (9, 3), Flags = MouseFlags.Button3Clicked }); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -1238,7 +1238,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 13), Flags = MouseFlags.Button1Clicked }); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (new Rectangle (5, 11, 10, 5), Application.Top.Subviews [0].Frame); Assert.Equal (new Rectangle (5, 11, 15, 6), Application.Top.Subviews [1].Frame); @@ -1256,7 +1256,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 12), Flags = MouseFlags.Button1Clicked }); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (new Rectangle (5, 11, 10, 5), Application.Top.Subviews [0].Frame); TestHelpers.AssertDriverContentsWithFrameAre ( @@ -1330,7 +1330,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 13), Flags = MouseFlags.ReportMousePosition }); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (new Rectangle (5, 11, 10, 5), Application.Top.Subviews [0].Frame); TestHelpers.AssertDriverContentsWithFrameAre ( @@ -1347,7 +1347,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 14), Flags = MouseFlags.ReportMousePosition }); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (new Rectangle (5, 11, 10, 5), Application.Top.Subviews [0].Frame); TestHelpers.AssertDriverContentsWithFrameAre ( @@ -1365,7 +1365,7 @@ public class ContextMenuTests (ITestOutputHelper output) Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 13), Flags = MouseFlags.ReportMousePosition }); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (new Rectangle (5, 11, 10, 5), Application.Top.Subviews [0].Frame); TestHelpers.AssertDriverContentsWithFrameAre ( diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index 7ef68bfe2..c2ba406f7 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/UnitTests/Views/MenuBarTests.cs @@ -591,7 +591,7 @@ public class MenuBarTests (ITestOutputHelper output) Assert.Equal ("File", menu.Menus [0].Title); menu.OpenMenu (); var firstIteration = false; - Application.RunIteration (ref rsDialog, ref firstIteration); + Application.RunIteration (ref rsDialog, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -619,7 +619,7 @@ public class MenuBarTests (ITestOutputHelper output) // Need to fool MainLoop into thinking it's running Application.MainLoop.Running = true; - Application.RunIteration (ref rsDialog, ref firstIteration); + Application.RunIteration (ref rsDialog, firstIteration); Assert.Equal (items [0], menu.Menus [0].Title); TestHelpers.AssertDriverContentsWithFrameAre ( @@ -649,14 +649,14 @@ public class MenuBarTests (ITestOutputHelper output) Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked }); firstIteration = false; - Application.RunIteration (ref rsDialog, ref firstIteration); + Application.RunIteration (ref rsDialog, firstIteration); Assert.Equal (items [i], menu.Menus [0].Title); } ((FakeDriver)Application.Driver!).SetBufferSize (20, 15); menu.OpenMenu (); firstIteration = false; - Application.RunIteration (ref rsDialog, ref firstIteration); + Application.RunIteration (ref rsDialog, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -792,7 +792,7 @@ public class MenuBarTests (ITestOutputHelper output) Assert.Equal ("File", menu.Menus [0].Title); menu.OpenMenu (); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -815,7 +815,7 @@ public class MenuBarTests (ITestOutputHelper output) // Need to fool MainLoop into thinking it's running Application.MainLoop.Running = true; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (items [0], menu.Menus [0].Title); TestHelpers.AssertDriverContentsWithFrameAre ( @@ -834,14 +834,14 @@ public class MenuBarTests (ITestOutputHelper output) Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked }); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (items [i], menu.Menus [0].Title); } ((FakeDriver)Application.Driver!).SetBufferSize (20, 15); menu.OpenMenu (); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -2864,7 +2864,7 @@ Edit menu.OpenMenu (); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" @@ -2877,7 +2877,7 @@ Edit ((FakeDriver)Application.Driver!).SetBufferSize (20, 15); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ( @" diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 478e1d00b..216b10fd0 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -1081,8 +1081,7 @@ public class TextFieldTests (ITestOutputHelper output) _textField.CursorPosition = 0; _textField.NewKeyDownEvent (Key.CursorRight.WithCtrl.WithShift); - var first = true; - Application.RunIteration (ref rs, ref first); + Application.RunIteration (ref rs); Assert.Equal (4, _textField.CursorPosition); // TAB to jump between text fields. diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 7dc1f9858..3e574d843 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -6364,8 +6364,7 @@ This is the second line. _textView.NewKeyDownEvent (Key.CursorRight.WithCtrl.WithShift); - var first = true; - Application.RunIteration (ref rs, ref first); + Application.RunIteration (ref rs, true); Assert.Equal (new Point (4, 0), _textView.CursorPosition); // TAB to jump between text fields. diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index 6bf764de6..6663a4ebc 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -885,7 +885,7 @@ public partial class ToplevelTests (ITestOutputHelper output) Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Pressed }); var firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (window.Border, Application.MouseGrabView); Assert.Equal (new (0, 0, 10, 3), window.Frame); @@ -897,7 +897,7 @@ public partial class ToplevelTests (ITestOutputHelper output) }); firstIteration = false; - Application.RunIteration (ref rs, ref firstIteration); + Application.RunIteration (ref rs, firstIteration); Assert.Equal (window.Border, Application.MouseGrabView); Assert.Equal (new (1, 1, 10, 3), window.Frame);