general messing around

This commit is contained in:
Tig
2025-04-02 14:05:10 -06:00
parent a3e823dfbb
commit ba7fb2181e
14 changed files with 528 additions and 205 deletions

View File

@@ -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,
/// </remarks>
public IReadOnlyCollection<View> SubViews => InternalSubViews?.AsReadOnly () ?? _empty;
/// <summary>
/// Gets the list of SubViews of a specific type.
/// </summary>
/// <typeparam name="T">The type of subviews to retrieve.</typeparam>
/// <returns>An IReadOnlyCollection of subviews of the specified type.</returns>
public IReadOnlyCollection<T> GetSubViews<T> () where T : View
{
return InternalSubViews.OfType<T> ().ToList ().AsReadOnly ();
}
private View? _superView;
/// <summary>
@@ -312,7 +303,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
public event EventHandler<SuperViewChangedEventArgs>? SubViewRemoved;
/// <summary>
/// Removes all SubView (children) added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
/// Removes all SubViews added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
/// </summary>
/// <remarks>
/// <para>
@@ -322,12 +313,47 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
/// added.
/// </para>
/// </remarks>
public virtual void RemoveAll ()
/// <returns>
/// A list of removed Views.
/// </returns>
public virtual IReadOnlyCollection<View> RemoveAll ()
{
List<View> removedList = new List<View> ();
while (InternalSubViews.Count > 0)
{
Remove (InternalSubViews [0]);
View? removed = Remove (InternalSubViews [0]);
if (removed is { })
{
removedList.Add (removed);
}
}
return removedList.AsReadOnly ();
}
/// <summary>
/// Removes all SubViews of a type added via <see cref="Add(View)"/> or <see cref="Add(View[])"/> from this View.
/// </summary>
/// <remarks>
/// <para>
/// 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 <see cref="Dispose()"/> on any Views that were
/// added.
/// </para>
/// </remarks>
/// <returns>
/// A list of removed Views.
/// </returns>
public virtual IReadOnlyCollection<TView> RemoveAll<TView> () where TView : View
{
List<TView> removedList = new List<TView> ();
foreach (TView view in InternalSubViews.OfType<TView> ().ToList ())
{
Remove (view);
removedList.Add (view);
}
return removedList.AsReadOnly ();
}
#pragma warning disable CS0067 // The event is never used

View File

@@ -235,7 +235,7 @@ public class Bar : View, IOrientation, IDesignable
var minKeyWidth = 0;
List<Shortcut> shortcuts = GetSubViews<Shortcut> ().Where (s => s.Visible).ToList ();
List<Shortcut> shortcuts = SubViews.OfType<Shortcut> ().Where (s => s.Visible).ToList ();
foreach (Shortcut shortcut in shortcuts)
{

View File

@@ -1,12 +1,11 @@
#nullable enable
namespace Terminal.Gui;
/// <summary>
/// Provides a user interface for displaying and selecting flags.
/// Flags can be set from a dictionary or directly from an enum type.
/// </summary>
public class FlagSelector : View, IDesignable, IOrientation
public class FlagSelector : View, IOrientation, IDesignable
{
/// <summary>
/// Initializes a new instance of the <see cref="FlagSelector"/> 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;
/// <summary>
/// Gets or sets the value of the selected flags.
/// </summary>
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<uint> (Value.Value));
}
}
/// <summary>
@@ -107,12 +109,13 @@ public class FlagSelector : View, IDesignable, IOrientation
/// Set the flags and flag names.
/// </summary>
/// <param name="flags"></param>
public void SetFlags (IReadOnlyDictionary<uint, string> flags)
public virtual void SetFlags (IReadOnlyDictionary<uint, string> flags)
{
Flags = flags;
CreateSubViews ();
}
/// <summary>
/// Set the flags and flag names from an enum type.
/// </summary>
@@ -168,7 +171,7 @@ public class FlagSelector : View, IDesignable, IOrientation
}
/// <summary>
/// Gets the flags.
/// Gets the flag values and names.
/// </summary>
public IReadOnlyDictionary<uint, string>? 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<CheckBox> ())
{
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)
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="flag"></param>
/// <returns></returns>
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<CheckBox> ().Where (sv => sv.Title != "None"))
foreach (CheckBox cb in SubViews.OfType<CheckBox> ().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<CheckBox> ().Where (sv => sv.Title != "None"))
foreach (CheckBox cb in SubViews.OfType<CheckBox> ().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<CheckBox> ())
foreach (CheckBox cb in SubViews.OfType<CheckBox> ())
{
var flag = (uint)(cb.Data ?? throw new InvalidOperationException ("ComboBox.Data must be set"));
@@ -317,8 +320,6 @@ public class FlagSelector : View, IDesignable, IOrientation
}
}
/// <inheritdoc/>
protected override void OnSubViewAdded (View view) { }
#region IOrientation
@@ -342,8 +343,6 @@ public class FlagSelector : View, IDesignable, IOrientation
public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
#pragma warning restore CS0067 // The event is never used
#pragma warning restore CS0067
/// <summary>Called when <see cref="Orientation"/> has changed.</summary>
/// <param name="newOrientation"></param>
public void OnOrientationChanged (Orientation newOrientation) { SetLayout (); }
@@ -353,12 +352,14 @@ public class FlagSelector : View, IDesignable, IOrientation
/// <inheritdoc/>
public bool EnableForDesign ()
{
Styles = FlagSelectorStyles.All;
SetFlags<FlagSelectorStyles> (
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 ()
});

View File

@@ -0,0 +1,99 @@
#nullable enable
namespace Terminal.Gui;
/// <summary>
/// Provides a user interface for displaying and selecting flags.
/// Flags can be set from a dictionary or directly from an enum type.
/// </summary>
public class FlagSelector<TEnum> : FlagSelector where TEnum : struct, Enum
{
/// <summary>
/// Initializes a new instance of the <see cref="FlagSelector{TEnum}"/> class.
/// </summary>
public FlagSelector ()
{
SetFlags ();
}
/// <summary>
/// Gets or sets the value of the selected flags.
/// </summary>
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;
}
/// <summary>
/// Set the display names for the flags.
/// </summary>
/// <param name="nameSelector">A function that converts enum values to display names</param>
/// <remarks>
/// This method allows changing the display names of the flags while keeping the flag values hard-defined by the enum type.
/// </remarks>
/// <example>
/// <code>
/// // 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()
/// });
/// </code>
/// </example>
public void SetFlagNames (Func<TEnum, string> nameSelector)
{
Dictionary<uint, string> flagsDictionary = Enum.GetValues<TEnum> ()
.ToDictionary (f => Convert.ToUInt32 (f), nameSelector);
base.SetFlags (flagsDictionary);
}
private void SetFlags ()
{
Dictionary<uint, string> flagsDictionary = Enum.GetValues<TEnum> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f.ToString ());
base.SetFlags (flagsDictionary);
}
/// <summary>
/// Prevents calling the base SetFlags method with arbitrary flag values.
/// </summary>
/// <param name="flags"></param>
public override void SetFlags (IReadOnlyDictionary<uint, string> flags)
{
throw new InvalidOperationException ("Setting flag values directly is not allowed. Use SetFlagNames to change display names.");
}
/// <inheritdoc />
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;
}
}

View File

@@ -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<MenuBarItemv2> ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first)
{
_active = true;
ShowPopover (first);
if (HideActiveItem ())
{
return true;
}
return true;
}
if (SubViews.OfType<MenuBarItemv2> ().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 <see cref="PopoverMenu"/>.
/// </summary>
/// <exception cref="NotImplementedException"></exception>
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.
/// </summary>
/// <returns></returns>
public bool IsActive ()
{
return _active;
}
public bool IsActive () { return _active; }
/// <inheritdoc />
/// <inheritdoc/>
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);
}
/// <inheritdoc />
/// <inheritdoc/>
protected override void OnMouseLeave ()
{
if (!IsOpen ())
{
CanFocus = false;
}
base.OnMouseLeave ();
}
/// <inheritdoc />
/// <inheritdoc/>
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; }
/// <summary>
/// Hides the popover menu associated with the active menu bar item and updates the focus state.
/// </summary>
/// <returns><see langword="true"/> if the popover was hidden</returns>
public bool HideActiveItem ()
{
return HideItem (GetActiveItem ());
}
public bool HideActiveItem () { return HideItem (GetActiveItem ()); }
/// <summary>
/// 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<MenuBarItemv2>())
{
if (mbi is MenuBarItemv2 { PopoverMenu: { } })
{
IEnumerable<Menuv2> 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<MenuBarItemv2> ())
{
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);
}
}
}
}
}
}

View File

@@ -163,10 +163,12 @@ public class WizardStep : View
/// <summary>Removes all <see cref="View"/>s from the <see cref="WizardStep"/>.</summary>
/// <remarks></remarks>
public override void RemoveAll ()
public override IReadOnlyCollection<View> RemoveAll ()
{
_contentView.RemoveAll ();
IReadOnlyCollection<View> removed = _contentView.RemoveAll ();
ShowHide ();
return removed;
}
/// <summary>Does the work to show and hide the contentView and helpView as appropriate</summary>

View File

@@ -171,7 +171,7 @@ public class ScenarioTests : TestsAllViews
var top = new Toplevel ();
Dictionary<string, Type> viewClasses = GetAllViewClasses().ToDictionary (t => t.Name);
Dictionary<string, Type> 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 <T> arguments
List<Type> typeArguments = new ();
// use <object>
// use <object> 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<T> to MyClass<object>
type = type.MakeGenericType (typeArguments.ToArray ());
}

View File

@@ -63,14 +63,30 @@ public class TestsAllViews
if (type is { IsGenericType: true, IsTypeDefinition: true })
{
List<Type> gTypes = new ();
List<Type> typeArguments = new ();
foreach (Type args in type.GetGenericArguments ())
// use <object> 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)!);
}

View File

@@ -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<Button> ();
// Assert
Assert.Equal (3, superView.SubViews.Count);
Assert.DoesNotContain (subView4, superView.SubViews);
Assert.Single (removedViews);
Assert.Contains (subView4, removedViews);
}
[Fact]
public void RemoveAllTView_Does_Not_Remove_Other_Types ()
{
// Arrange
var superView = new View ();
var subView1 = new View ();
var subView2 = new Button ();
var subView3 = new Label ();
superView.Add (subView1, subView2, subView3);
// Act
var removedViews = superView.RemoveAll<Button> ();
// Assert
Assert.Equal (2, superView.SubViews.Count);
Assert.Contains (subView1, superView.SubViews);
Assert.Contains (subView3, superView.SubViews);
Assert.Single (removedViews);
Assert.Contains (subView2, removedViews);
}
}

View File

@@ -1,124 +1,208 @@
namespace Terminal.Gui.ViewsTests;
public class FlagSelectorTests
{
[Fact]
public void Initialization_ShouldSetDefaults()
public void Initialization_ShouldSetDefaults ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
Assert.True(flagSelector.CanFocus);
Assert.Equal(Dim.Auto(DimAutoStyle.Content), flagSelector.Width);
Assert.Equal(Dim.Auto(DimAutoStyle.Content), flagSelector.Height);
Assert.Equal(Orientation.Vertical, flagSelector.Orientation);
Assert.True (flagSelector.CanFocus);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Width);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Height);
Assert.Equal (Orientation.Vertical, flagSelector.Orientation);
}
[Fact]
public void SetFlags_WithDictionary_ShouldSetFlags()
public void SetFlags_WithDictionary_ShouldSetFlags ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
Assert.Equal(flags, flagSelector.Flags);
Assert.Equal (flags, flagSelector.Flags);
}
[Fact]
public void SetFlags_WithEnum_ShouldSetFlags()
public void SetFlags_WithEnum_ShouldSetFlags ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
flagSelector.SetFlags<FlagSelectorStyles>();
flagSelector.SetFlags<FlagSelectorStyles> ();
var expectedFlags = Enum.GetValues<FlagSelectorStyles>()
.ToDictionary(f => Convert.ToUInt32(f), f => f.ToString());
var expectedFlags = Enum.GetValues<FlagSelectorStyles> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f.ToString ());
Assert.Equal(expectedFlags, flagSelector.Flags);
Assert.Equal (expectedFlags, flagSelector.Flags);
}
[Fact]
public void SetFlags_WithEnumAndCustomNames_ShouldSetFlags()
public void SetFlags_WithEnumAndCustomNames_ShouldSetFlags ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
flagSelector.SetFlags<FlagSelectorStyles>(f => f switch
flagSelector.SetFlags<FlagSelectorStyles> (f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString()
_ => f.ToString ()
});
var expectedFlags = Enum.GetValues<FlagSelectorStyles>()
.ToDictionary(f => Convert.ToUInt32(f), f => f switch
var expectedFlags = Enum.GetValues<FlagSelectorStyles> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString()
_ => f.ToString ()
});
Assert.Equal(expectedFlags, flagSelector.Flags);
Assert.Equal (expectedFlags, flagSelector.Flags);
}
[Fact]
public void Value_Set_ShouldUpdateCheckedState()
public void Value_Set_ShouldUpdateCheckedState ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
flagSelector.Value = 1;
var checkBox = flagSelector.SubViews.OfType<CheckBox>().First(cb => (uint)cb.Data == 1);
Assert.Equal(CheckState.Checked, checkBox.CheckedState);
var checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == 1);
Assert.Equal (CheckState.Checked, checkBox.CheckedState);
checkBox = flagSelector.SubViews.OfType<CheckBox>().First(cb => (uint)cb.Data == 2);
Assert.Equal(CheckState.UnChecked, checkBox.CheckedState);
checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == 2);
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
}
[Fact]
public void Styles_Set_ShouldCreateSubViews()
public void Styles_Set_ShouldCreateSubViews ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
flagSelector.Styles = FlagSelectorStyles.ShowNone;
Assert.Contains(flagSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "None");
Assert.Contains (flagSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "None");
}
[Fact]
public void ValueChanged_Event_ShouldBeRaised()
public void ValueChanged_Event_ShouldBeRaised ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
bool eventRaised = false;
flagSelector.ValueChanged += (sender, args) => eventRaised = true;
flagSelector.Value = 1;
Assert.True(eventRaised);
Assert.True (eventRaised);
}
// Tests for FlagSelector<TEnum>
[Fact]
public void GenericInitialization_ShouldSetDefaults ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
Assert.True (flagSelector.CanFocus);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Width);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Height);
Assert.Equal (Orientation.Vertical, flagSelector.Orientation);
}
[Fact]
public void Generic_SetFlags_Methods_Throw ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
Assert.Throws<InvalidOperationException> (() => flagSelector.SetFlags (new Dictionary<uint, string> ()));
Assert.Throws<InvalidOperationException> (() => flagSelector.SetFlags<FlagSelectorStyles> ());
Assert.Throws<InvalidOperationException> (() => flagSelector.SetFlags<FlagSelectorStyles> (styles => null));
}
[Fact]
public void Generic_ ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
var flags = flagSelector.Flags;
}
[Fact]
public void GenericSetFlagNames_ShouldSetFlagNames ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
flagSelector.SetFlagNames (f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString ()
});
var expectedFlags = Enum.GetValues<FlagSelectorStyles> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString ()
});
Assert.Equal (expectedFlags, flagSelector.Flags);
}
[Fact]
public void GenericValue_Set_ShouldUpdateCheckedState ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
flagSelector.SetFlagNames (f => f.ToString ());
flagSelector.Value = FlagSelectorStyles.ShowNone;
var checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == Convert.ToUInt32 (FlagSelectorStyles.ShowNone));
Assert.Equal (CheckState.Checked, checkBox.CheckedState);
checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == Convert.ToUInt32 (FlagSelectorStyles.ShowValueEdit));
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
}
[Fact]
public void GenericValueChanged_Event_ShouldBeRaised ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
flagSelector.SetFlagNames (f => f.ToString ());
bool eventRaised = false;
flagSelector.ValueChanged += (sender, args) => eventRaised = true;
flagSelector.Value = FlagSelectorStyles.ShowNone;
Assert.True (eventRaised);
}
}

View File

@@ -276,16 +276,31 @@ public class AllViewsTester : Scenario
// For each of the <T> arguments
List<Type> typeArguments = new ();
// use <object>
// use <object> 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));
}
}
// And change what type we are instantiating from MyClass<T> to MyClass<object>
// And change what type we are instantiating from MyClass<T> to MyClass<object> or MyClass<T>
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;
}
// Instantiate view
var view = (View)Activator.CreateInstance (type)!;
_eventLog!.ViewToLog = view;
@@ -315,7 +330,6 @@ public class AllViewsTester : Scenario
view.Id = "_curView";
_curView = view;
_curView = view;
_hostPane!.Add (_curView);
_layoutEditor!.ViewToEdit = _curView;
@@ -324,6 +338,7 @@ public class AllViewsTester : Scenario
_curView.SetNeedsLayout ();
}
private void DisposeCurrentView ()
{
if (_curView != null)

View File

@@ -92,7 +92,7 @@ public class ConfigurationEditor : Scenario
return;
}
foreach (ConfigTextView t in _tabView.GetSubViews<ConfigTextView> ())
foreach (ConfigTextView t in _tabView.SubViews.OfType<ConfigTextView> ())
{
t.ColorScheme = EditorColorScheme;
}

View File

@@ -318,7 +318,7 @@ public class DynamicStatusBar : Scenario
if (statusItem != null)
{
Shortcut [] items = _statusBar.GetSubViews<Shortcut> ().ToArray ();
Shortcut [] items = _statusBar.SubViews.OfType<Shortcut> ().ToArray ();
if (i > 0)
{
@@ -341,7 +341,7 @@ public class DynamicStatusBar : Scenario
if (statusItem != null)
{
Shortcut [] items = _statusBar.GetSubViews<Shortcut> ().ToArray ();
Shortcut [] items = _statusBar.SubViews.OfType<Shortcut> ().ToArray ();
if (i < items.Length - 1)
{
@@ -521,7 +521,7 @@ public class DynamicStatusBar : Scenario
if (statusItem != null)
{
foreach (Shortcut si in _statusBar.GetSubViews<Shortcut> ())
foreach (Shortcut si in _statusBar.SubViews.OfType<Shortcut> ())
{
DataContext.Items.Add (new DynamicStatusItemList (si.Title, si));
}

View File

@@ -105,7 +105,7 @@ public class UICatalogTop : Toplevel
private RadioGroup? _themesRg;
private RadioGroup? _topSchemeRg;
private RadioGroup? _logLevelRg;
private FlagSelector? _diagnosticFlagsSelector;
private FlagSelector<ViewDiagnosticFlags>? _diagnosticFlagsSelector;
private CheckBox? _disableMouseCb;
private MenuBarv2 CreateMenuBar ()
@@ -238,10 +238,9 @@ public class UICatalogTop : Toplevel
{
CanFocus = false,
Styles = FlagSelectorStyles.ShowNone,
HighlightStyle = HighlightStyle.None
HighlightStyle = HighlightStyle.None,
};
_diagnosticFlagsSelector.SetFlags<ViewDiagnosticFlags> ();
_diagnosticFlagsSelector.Value = Diagnostics;
_diagnosticFlagsSelector.ValueChanged += (sender, args) =>
{
_diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value;