From ba7fb2181e0f663220e85e99a91908c27166f04e Mon Sep 17 00:00:00 2001 From: Tig Date: Wed, 2 Apr 2025 14:05:10 -0600 Subject: [PATCH] general messing around --- Terminal.Gui/View/View.Hierarchy.cs | 52 ++++-- Terminal.Gui/Views/Bar.cs | 2 +- Terminal.Gui/Views/FlagSelector.cs | 119 ++++++------- Terminal.Gui/Views/FlagSelectorTEnum.cs | 99 +++++++++++ Terminal.Gui/Views/Menu/MenuBarv2.cs | 130 +++++++------- Terminal.Gui/Views/Wizard/WizardStep.cs | 6 +- .../UICatalog/ScenarioTests.cs | 33 +++- Tests/UnitTests/TestsAllViews.cs | 24 ++- .../View/SubviewTests.cs | 66 +++++++ .../Views/FlagSelectorTests.cs | 164 +++++++++++++----- UICatalog/Scenarios/AllViewsTester.cs | 23 ++- UICatalog/Scenarios/ConfigurationEditor.cs | 2 +- UICatalog/Scenarios/DynamicStatusBar.cs | 6 +- UICatalog/UICatalogTop.cs | 7 +- 14 files changed, 528 insertions(+), 205 deletions(-) create mode 100644 Terminal.Gui/Views/FlagSelectorTEnum.cs diff --git a/Terminal.Gui/View/View.Hierarchy.cs b/Terminal.Gui/View/View.Hierarchy.cs index c39bca0cc..ea017ff33 100644 --- a/Terminal.Gui/View/View.Hierarchy.cs +++ b/Terminal.Gui/View/View.Hierarchy.cs @@ -1,4 +1,5 @@ #nullable enable +using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -21,16 +22,6 @@ public partial class View // SuperView/SubView hierarchy management (SuperView, /// public IReadOnlyCollection SubViews => InternalSubViews?.AsReadOnly () ?? _empty; - /// - /// Gets the list of SubViews of a specific type. - /// - /// The type of subviews to retrieve. - /// An IReadOnlyCollection of subviews of the specified type. - public IReadOnlyCollection GetSubViews () where T : View - { - return InternalSubViews.OfType ().ToList ().AsReadOnly (); - } - private View? _superView; /// @@ -312,7 +303,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView, public event EventHandler? SubViewRemoved; /// - /// Removes all SubView (children) added via or from this View. + /// Removes all SubViews added via or from this View. /// /// /// @@ -322,12 +313,47 @@ public partial class View // SuperView/SubView hierarchy management (SuperView, /// added. /// /// - public virtual void RemoveAll () + /// + /// A list of removed Views. + /// + public virtual IReadOnlyCollection RemoveAll () { + List removedList = new List (); while (InternalSubViews.Count > 0) { - Remove (InternalSubViews [0]); + View? removed = Remove (InternalSubViews [0]); + if (removed is { }) + { + removedList.Add (removed); + } } + + return removedList.AsReadOnly (); + } + + /// + /// Removes all SubViews of a type added via or from this View. + /// + /// + /// + /// Normally SubViews will be disposed when this View is disposed. Removing a SubView causes ownership of the + /// SubView's + /// lifecycle to be transferred to the caller; the caller must call on any Views that were + /// added. + /// + /// + /// + /// A list of removed Views. + /// + public virtual IReadOnlyCollection RemoveAll () where TView : View + { + List removedList = new List (); + foreach (TView view in InternalSubViews.OfType ().ToList ()) + { + Remove (view); + removedList.Add (view); + } + return removedList.AsReadOnly (); } #pragma warning disable CS0067 // The event is never used diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs index 7e1ae9c49..b4ba2bef8 100644 --- a/Terminal.Gui/Views/Bar.cs +++ b/Terminal.Gui/Views/Bar.cs @@ -235,7 +235,7 @@ public class Bar : View, IOrientation, IDesignable var minKeyWidth = 0; - List shortcuts = GetSubViews ().Where (s => s.Visible).ToList (); + List shortcuts = SubViews.OfType ().Where (s => s.Visible).ToList (); foreach (Shortcut shortcut in shortcuts) { diff --git a/Terminal.Gui/Views/FlagSelector.cs b/Terminal.Gui/Views/FlagSelector.cs index 75160e019..523565934 100644 --- a/Terminal.Gui/Views/FlagSelector.cs +++ b/Terminal.Gui/Views/FlagSelector.cs @@ -1,12 +1,11 @@ #nullable enable namespace Terminal.Gui; - /// /// Provides a user interface for displaying and selecting flags. /// Flags can be set from a dictionary or directly from an enum type. /// -public class FlagSelector : View, IDesignable, IOrientation +public class FlagSelector : View, IOrientation, IDesignable { /// /// Initializes a new instance of the class. @@ -30,12 +29,12 @@ public class FlagSelector : View, IDesignable, IOrientation private bool? HandleAcceptCommand (ICommandContext? ctx) { return RaiseAccepting (ctx); } - private uint _value; + private uint? _value; /// /// Gets or sets the value of the selected flags. /// - public uint Value + public uint? Value { get => _value; set @@ -47,19 +46,19 @@ public class FlagSelector : View, IDesignable, IOrientation _value = value; - if (_value == 0) + if (_value is null) { + UncheckNone (); UncheckAll (); } else { - UncheckNone (); UpdateChecked (); } if (ValueEdit is { }) { - ValueEdit.Text = value.ToString (); + ValueEdit.Text = _value.ToString (); } RaiseValueChanged (); @@ -69,7 +68,10 @@ public class FlagSelector : View, IDesignable, IOrientation private void RaiseValueChanged () { OnValueChanged (); - ValueChanged?.Invoke (this, new (Value)); + if (Value.HasValue) + { + ValueChanged?.Invoke (this, new EventArgs (Value.Value)); + } } /// @@ -107,12 +109,13 @@ public class FlagSelector : View, IDesignable, IOrientation /// Set the flags and flag names. /// /// - public void SetFlags (IReadOnlyDictionary flags) + public virtual void SetFlags (IReadOnlyDictionary flags) { Flags = flags; CreateSubViews (); } + /// /// Set the flags and flag names from an enum type. /// @@ -168,7 +171,7 @@ public class FlagSelector : View, IDesignable, IOrientation } /// - /// Gets the flags. + /// Gets the flag values and names. /// public IReadOnlyDictionary? Flags { get; internal set; } @@ -181,23 +184,19 @@ public class FlagSelector : View, IDesignable, IOrientation return; } - View [] subviews = SubViews.ToArray (); - - RemoveAll (); - - foreach (View v in subviews) + foreach (CheckBox cb in RemoveAll ()) { - v.Dispose (); + cb.Dispose (); } - if (Styles.HasFlag (FlagSelectorStyles.ShowNone) && !Flags.ContainsKey (0)) + if (Styles.HasFlag (FlagSelectorStyles.ShowNone) && !Flags.ContainsKey (default!)) { - Add (CreateCheckBox ("None", 0)); + Add (CreateCheckBox ("None", default!)); } for (var index = 0; index < Flags.Count; index++) { - if (!Styles.HasFlag (FlagSelectorStyles.ShowNone) && Flags.ElementAt (index).Key == 0) + if (!Styles.HasFlag (FlagSelectorStyles.ShowNone) && Flags.ElementAt (index).Key == default!) { continue; } @@ -223,48 +222,52 @@ public class FlagSelector : View, IDesignable, IOrientation return; - CheckBox CreateCheckBox (string name, uint flag) + + } + /// + /// + /// + /// + /// + /// + protected virtual CheckBox CreateCheckBox (string name, uint flag) + { + var checkbox = new CheckBox { - var checkbox = new CheckBox - { - CanFocus = false, - Title = name, - Id = name, - Data = flag, - HighlightStyle = HighlightStyle - }; + CanFocus = false, + Title = name, + Id = name, + Data = flag, + HighlightStyle = HighlightStyle + }; - checkbox.Selecting += (sender, args) => { RaiseSelecting (args.Context); }; + checkbox.Selecting += (sender, args) => { RaiseSelecting (args.Context); }; - checkbox.CheckedStateChanged += (sender, args) => + checkbox.CheckedStateChanged += (sender, args) => + { + uint? newValue = Value; + + if (checkbox.CheckedState == CheckState.Checked) { - uint newValue = Value; - - if (checkbox.CheckedState == CheckState.Checked) + if (flag == default!) { - if ((uint)checkbox.Data == 0) - { - newValue = 0; - } - else - { - newValue |= flag; - } + newValue = 0; } else { - newValue &= ~flag; + newValue = newValue | flag; } + } + else + { + newValue = newValue & ~flag; + } - Value = newValue; + Value = newValue; + }; - //UpdateChecked(); - }; - - return checkbox; - } + return checkbox; } - private void SetLayout () { foreach (View sv in SubViews) @@ -285,7 +288,7 @@ public class FlagSelector : View, IDesignable, IOrientation private void UncheckAll () { - foreach (CheckBox cb in GetSubViews ().Where (sv => sv.Title != "None")) + foreach (CheckBox cb in SubViews.OfType ().Where (sv => (uint)(sv.Data ?? default!) != default!)) { cb.CheckedState = CheckState.UnChecked; } @@ -293,7 +296,7 @@ public class FlagSelector : View, IDesignable, IOrientation private void UncheckNone () { - foreach (CheckBox cb in GetSubViews ().Where (sv => sv.Title != "None")) + foreach (CheckBox cb in SubViews.OfType ().Where (sv => sv.Title != "None")) { cb.CheckedState = CheckState.UnChecked; } @@ -301,7 +304,7 @@ public class FlagSelector : View, IDesignable, IOrientation private void UpdateChecked () { - foreach (CheckBox cb in GetSubViews ()) + foreach (CheckBox cb in SubViews.OfType ()) { var flag = (uint)(cb.Data ?? throw new InvalidOperationException ("ComboBox.Data must be set")); @@ -317,8 +320,6 @@ public class FlagSelector : View, IDesignable, IOrientation } } - /// - protected override void OnSubViewAdded (View view) { } #region IOrientation @@ -342,8 +343,6 @@ public class FlagSelector : View, IDesignable, IOrientation public event EventHandler>? OrientationChanged; #pragma warning restore CS0067 // The event is never used -#pragma warning restore CS0067 - /// Called when has changed. /// public void OnOrientationChanged (Orientation newOrientation) { SetLayout (); } @@ -353,12 +352,14 @@ public class FlagSelector : View, IDesignable, IOrientation /// public bool EnableForDesign () { + Styles = FlagSelectorStyles.All; SetFlags ( f => f switch { - FlagSelectorStyles.ShowNone => "Show _None Value", - FlagSelectorStyles.ShowValueEdit => "Show _Value Editor", - FlagSelectorStyles.All => "Show _All Flags Selector", + FlagSelectorStyles.None => "_No Style", + FlagSelectorStyles.ShowNone => "Show _None Value Style", + FlagSelectorStyles.ShowValueEdit => "Show _Value Editor Style", + FlagSelectorStyles.All => "_All Styles", _ => f.ToString () }); diff --git a/Terminal.Gui/Views/FlagSelectorTEnum.cs b/Terminal.Gui/Views/FlagSelectorTEnum.cs new file mode 100644 index 000000000..8a2b4e747 --- /dev/null +++ b/Terminal.Gui/Views/FlagSelectorTEnum.cs @@ -0,0 +1,99 @@ +#nullable enable +namespace Terminal.Gui; + +/// +/// Provides a user interface for displaying and selecting flags. +/// Flags can be set from a dictionary or directly from an enum type. +/// +public class FlagSelector : FlagSelector where TEnum : struct, Enum +{ + /// + /// Initializes a new instance of the class. + /// + public FlagSelector () + { + SetFlags (); + } + + /// + /// Gets or sets the value of the selected flags. + /// + public new TEnum? Value + { + get => base.Value.HasValue ? (TEnum)Enum.ToObject (typeof (TEnum), base.Value.Value) : (TEnum?)null; + set => base.Value = value.HasValue ? Convert.ToUInt32 (value.Value) : (uint?)null; + } + + /// + /// Set the display names for the flags. + /// + /// A function that converts enum values to display names + /// + /// This method allows changing the display names of the flags while keeping the flag values hard-defined by the enum type. + /// + /// + /// + /// // Use enum values with custom display names + /// var flagSelector = new FlagSelector(); + /// flagSelector.SetFlagNames(f => f switch { + /// FlagSelectorStyles.ShowNone => "Show None Value", + /// FlagSelectorStyles.ShowValueEdit => "Show Value Editor", + /// FlagSelectorStyles.All => "Everything", + /// _ => f.ToString() + /// }); + /// + /// + public void SetFlagNames (Func nameSelector) + { + Dictionary flagsDictionary = Enum.GetValues () + .ToDictionary (f => Convert.ToUInt32 (f), nameSelector); + base.SetFlags (flagsDictionary); + } + + private void SetFlags () + { + Dictionary flagsDictionary = Enum.GetValues () + .ToDictionary (f => Convert.ToUInt32 (f), f => f.ToString ()); + base.SetFlags (flagsDictionary); + } + + /// + /// Prevents calling the base SetFlags method with arbitrary flag values. + /// + /// + public override void SetFlags (IReadOnlyDictionary flags) + { + throw new InvalidOperationException ("Setting flag values directly is not allowed. Use SetFlagNames to change display names."); + } + + /// + protected override CheckBox CreateCheckBox (string name, uint flag) + { + var checkbox = base.CreateCheckBox (name, flag); + checkbox.CheckedStateChanged += (sender, args) => + { + TEnum? newValue = Value; + + if (checkbox.CheckedState == CheckState.Checked) + { + if (flag == default!) + { + newValue = new TEnum (); + } + else + { + newValue = (TEnum)Enum.ToObject (typeof (TEnum), Convert.ToUInt32 (newValue) | flag); + } + } + else + { + newValue = (TEnum)Enum.ToObject (typeof (TEnum), Convert.ToUInt32 (newValue) & ~flag); + } + + Value = newValue; + }; + + return checkbox; + } + +} diff --git a/Terminal.Gui/Views/Menu/MenuBarv2.cs b/Terminal.Gui/Views/Menu/MenuBarv2.cs index 834edd2d8..90e0ab9d4 100644 --- a/Terminal.Gui/Views/Menu/MenuBarv2.cs +++ b/Terminal.Gui/Views/Menu/MenuBarv2.cs @@ -1,6 +1,5 @@ #nullable enable using System.ComponentModel; -using System.Diagnostics; namespace Terminal.Gui; @@ -27,29 +26,31 @@ public class MenuBarv2 : Menuv2, IDesignable Orientation = Orientation.Horizontal; Key = DefaultKey; - AddCommand (Command.HotKey, - () => - { - if (RaiseHandlingHotKey () is true) - { - return true; - } - if (HideActiveItem ()) - { - return true; - } + AddCommand ( + Command.HotKey, + () => + { + if (RaiseHandlingHotKey () is true) + { + return true; + } - if (GetSubViews ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first) - { - _active = true; - ShowPopover (first); + if (HideActiveItem ()) + { + return true; + } - return true; - } + if (SubViews.OfType ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first) + { + _active = true; + ShowPopover (first); - return false; - }); + return true; + } + + return false; + }); HotKeyBindings.Add (Key, Command.HotKey); KeyBindings.Add (Key, Command.Quit); @@ -72,7 +73,7 @@ public class MenuBarv2 : Menuv2, IDesignable return true; } - return false;//RaiseAccepted (ctx); + return false; //RaiseAccepted (ctx); }); AddCommand (Command.Right, MoveRight); @@ -115,10 +116,12 @@ public class MenuBarv2 : Menuv2, IDesignable set { RemoveAll (); + if (value is null) { return; } + foreach (MenuBarItemv2 mbi in value) { Add (mbi); @@ -137,10 +140,7 @@ public class MenuBarv2 : Menuv2, IDesignable /// Gets whether any of the menu bar items have a visible . /// /// - public bool IsOpen () - { - return SubViews.Count (sv => sv is MenuBarItemv2 { PopoverMenu: { Visible: true } }) > 0; - } + public bool IsOpen () { return SubViews.Count (sv => sv is MenuBarItemv2 { PopoverMenu: { Visible: true } }) > 0; } private bool _active; @@ -149,12 +149,9 @@ public class MenuBarv2 : Menuv2, IDesignable /// over a menu bar item will activate it. /// /// - public bool IsActive () - { - return _active; - } + public bool IsActive () { return _active; } - /// + /// protected override bool OnMouseEnter (CancelEventArgs eventArgs) { // If the MenuBar does not have focus and the mouse enters: Enable CanFocus @@ -163,20 +160,22 @@ public class MenuBarv2 : Menuv2, IDesignable { CanFocus = true; } + return base.OnMouseEnter (eventArgs); } - /// + /// protected override void OnMouseLeave () { if (!IsOpen ()) { CanFocus = false; } + base.OnMouseLeave (); } - /// + /// protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedView) { if (!newHasFocus) @@ -298,19 +297,13 @@ public class MenuBarv2 : Menuv2, IDesignable menuBarItem.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom)); } - private MenuBarItemv2? GetActiveItem () - { - return SubViews.FirstOrDefault (sv => sv is MenuBarItemv2 { PopoverMenu: { Visible: true } }) as MenuBarItemv2; - } + private MenuBarItemv2? GetActiveItem () { return SubViews.FirstOrDefault (sv => sv is MenuBarItemv2 { PopoverMenu: { Visible: true } }) as MenuBarItemv2; } /// /// Hides the popover menu associated with the active menu bar item and updates the focus state. /// /// if the popover was hidden - public bool HideActiveItem () - { - return HideItem (GetActiveItem ()); - } + public bool HideActiveItem () { return HideItem (GetActiveItem ()); } /// /// Hides popover menu associated with the specified menu bar item and updates the focus state. @@ -323,6 +316,7 @@ public class MenuBarv2 : Menuv2, IDesignable { return false; } + _active = false; HasFocus = false; activeItem.PopoverMenu!.Visible = false; @@ -336,11 +330,12 @@ public class MenuBarv2 : Menuv2, IDesignable { // Note: This menu is used by unit tests. If you modify it, you'll likely have to update // unit tests. - CheckBox? bordersCb = new CheckBox () + var bordersCb = new CheckBox { Title = "_Borders", CheckedState = CheckState.Checked }; + Add ( new MenuBarItemv2 ( "_File", @@ -355,38 +350,17 @@ public class MenuBarv2 : Menuv2, IDesignable Title = "_Preferences", SubMenu = new ( [ - new MenuItemv2 + new() { CommandView = bordersCb, HelpText = "Toggle Menu Borders", - Action = () => - { - foreach (MenuBarItemv2 mbi in GetSubViews()) - { - if (mbi is MenuBarItemv2 { PopoverMenu: { } }) - { - IEnumerable subMenus = mbi.PopoverMenu.GetAllSubMenus(); - - foreach (Menuv2? subMenu in subMenus) - { - if (bordersCb.CheckedState == CheckState.Checked) - { - subMenu.Border!.Thickness = new (1); - } - else - { - subMenu.Border!.Thickness = new (0); - } - } - } - } - } + Action = ToggleMenuBorders }, - new MenuItemv2 + new() { Title = "_Settings...", HelpText = "More settings", - Action = () => MessageBox.Query ("Settings", "This is the Settings Dialog\n", ["_Ok", "_Cancel"]) + Action = () => MessageBox.Query ("Settings", "This is the Settings Dialog\n", "_Ok", "_Cancel") } ] ) @@ -427,6 +401,30 @@ public class MenuBarv2 : Menuv2, IDesignable ] ) ); + return true; + + void ToggleMenuBorders () + { + foreach (MenuBarItemv2 mbi in SubViews.OfType ()) + { + if (mbi is not { PopoverMenu: { } }) + { + continue; + } + + foreach (Menuv2? subMenu in mbi.PopoverMenu.GetAllSubMenus ()) + { + if (bordersCb.CheckedState == CheckState.Checked) + { + subMenu.Border!.Thickness = new (1); + } + else + { + subMenu.Border!.Thickness = new (0); + } + } + } + } } } diff --git a/Terminal.Gui/Views/Wizard/WizardStep.cs b/Terminal.Gui/Views/Wizard/WizardStep.cs index cf1b781da..617a47c19 100644 --- a/Terminal.Gui/Views/Wizard/WizardStep.cs +++ b/Terminal.Gui/Views/Wizard/WizardStep.cs @@ -163,10 +163,12 @@ public class WizardStep : View /// Removes all s from the . /// - public override void RemoveAll () + public override IReadOnlyCollection RemoveAll () { - _contentView.RemoveAll (); + IReadOnlyCollection removed = _contentView.RemoveAll (); ShowHide (); + + return removed; } /// Does the work to show and hide the contentView and helpView as appropriate diff --git a/Tests/IntegrationTests/UICatalog/ScenarioTests.cs b/Tests/IntegrationTests/UICatalog/ScenarioTests.cs index 02ce55e61..ebe927ed8 100644 --- a/Tests/IntegrationTests/UICatalog/ScenarioTests.cs +++ b/Tests/IntegrationTests/UICatalog/ScenarioTests.cs @@ -171,7 +171,7 @@ public class ScenarioTests : TestsAllViews var top = new Toplevel (); - Dictionary viewClasses = GetAllViewClasses().ToDictionary (t => t.Name); + Dictionary viewClasses = GetAllViewClasses ().ToDictionary (t => t.Name); Window leftPane = new () { @@ -277,7 +277,7 @@ public class ScenarioTests : TestsAllViews classListView.SelectedItemChanged += (s, args) => { // Remove existing class, if any - if (curView is {}) + if (curView is { }) { curView.SubViewsLaidOut -= LayoutCompleteHandler; hostPane.Remove (curView); @@ -357,10 +357,13 @@ public class ScenarioTests : TestsAllViews { classListView.MoveDown (); - Assert.Equal ( - curView!.GetType ().Name, - viewClasses.Values.ToArray () [classListView.SelectedItem].Name - ); + if (curView is { }) + { + Assert.Equal ( + curView.GetType ().Name, + viewClasses.Values.ToArray () [classListView.SelectedItem].Name + ); + } } else { @@ -507,12 +510,26 @@ public class ScenarioTests : TestsAllViews // For each of the arguments List typeArguments = new (); - // use + // use or the original type if applicable foreach (Type arg in type.GetGenericArguments ()) { - typeArguments.Add (typeof (object)); + if (arg.IsValueType && Nullable.GetUnderlyingType (arg) == null) + { + typeArguments.Add (arg); + } + else + { + typeArguments.Add (typeof (object)); + } } + // Ensure the type does not contain any generic parameters + if (type.ContainsGenericParameters) + { + Logging.Warning ($"Cannot create an instance of {type} because it contains generic parameters."); + //throw new ArgumentException ($"Cannot create an instance of {type} because it contains generic parameters."); + return null; + } // And change what type we are instantiating from MyClass to MyClass type = type.MakeGenericType (typeArguments.ToArray ()); } diff --git a/Tests/UnitTests/TestsAllViews.cs b/Tests/UnitTests/TestsAllViews.cs index 76daf0f8e..7b7a77b1a 100644 --- a/Tests/UnitTests/TestsAllViews.cs +++ b/Tests/UnitTests/TestsAllViews.cs @@ -63,14 +63,30 @@ public class TestsAllViews if (type is { IsGenericType: true, IsTypeDefinition: true }) { - List gTypes = new (); + List typeArguments = new (); - foreach (Type args in type.GetGenericArguments ()) + // use or the original type if applicable + foreach (Type arg in type.GetGenericArguments ()) { - gTypes.Add (typeof (object)); + if (arg.IsValueType && Nullable.GetUnderlyingType (arg) == null) + { + typeArguments.Add (arg); + } + else + { + typeArguments.Add (typeof (object)); + } } - type = type.MakeGenericType (gTypes.ToArray ()); + type = type.MakeGenericType (typeArguments.ToArray ()); + + // Ensure the type does not contain any generic parameters + if (type.ContainsGenericParameters) + { + Logging.Warning ($"Cannot create an instance of {type} because it contains generic parameters."); + //throw new ArgumentException ($"Cannot create an instance of {type} because it contains generic parameters."); + return null; + } Assert.IsType (type, (View)Activator.CreateInstance (type)!); } diff --git a/Tests/UnitTestsParallelizable/View/SubviewTests.cs b/Tests/UnitTestsParallelizable/View/SubviewTests.cs index e31024513..0812b7c76 100644 --- a/Tests/UnitTestsParallelizable/View/SubviewTests.cs +++ b/Tests/UnitTestsParallelizable/View/SubviewTests.cs @@ -591,4 +591,70 @@ public class SubViewTests Assert.NotEqual (superView, subView.SuperView); Assert.Empty (superView.SubViews); } + + [Fact] + public void RemoveAll_Removes_All_SubViews () + { + // Arrange + var superView = new View (); + var subView1 = new View (); + var subView2 = new View (); + var subView3 = new View (); + + superView.Add (subView1, subView2, subView3); + + // Act + var removedViews = superView.RemoveAll (); + + // Assert + Assert.Empty (superView.SubViews); + Assert.Equal (3, removedViews.Count); + Assert.Contains (subView1, removedViews); + Assert.Contains (subView2, removedViews); + Assert.Contains (subView3, removedViews); + } + + [Fact] + public void RemoveAllTView_Removes_All_SubViews_Of_Specific_Type () + { + // Arrange + var superView = new View (); + var subView1 = new View (); + var subView2 = new View (); + var subView3 = new View (); + var subView4 = new Button (); + + superView.Add (subView1, subView2, subView3, subView4); + + // Act + var removedViews = superView.RemoveAll