Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
cd9c985
Much more efficient array insertion
dodexahedron Dec 29, 2023
669f4c6
Avoid allocating an iterator
dodexahedron Dec 29, 2023
2df4ebf
Add attribute for static analysis
dodexahedron Dec 29, 2023
9d54a66
Only allocate one dictionary and use it
dodexahedron Dec 29, 2023
7355375
This can't be null
dodexahedron Dec 29, 2023
f48c3cc
Collection expression
dodexahedron Dec 29, 2023
064dfd4
Eliminate more allocations
dodexahedron Dec 29, 2023
dc6918a
Deconstruct and make prettier
dodexahedron Dec 29, 2023
611ff02
This can now be private
dodexahedron Dec 29, 2023
0f9a62c
XmlDoc for this (mostly copied from the old method
dodexahedron Dec 29, 2023
5a57c56
Null propagation removes an indentation level here 🥳
dodexahedron Dec 29, 2023
634c51b
Simplify with collection expression
dodexahedron Dec 29, 2023
bdedcc4
OK Dependabot! We hear you!
dodexahedron Dec 30, 2023
52af593
Add an unregister method
dodexahedron Dec 31, 2023
890b1d1
Convert and improve this big test.
dodexahedron Dec 31, 2023
0bcab6a
Use using to dispose of this
dodexahedron Dec 31, 2023
3546422
First usage of the new unregister method
dodexahedron Dec 31, 2023
6959ee7
Sorted members by design guides ONLY
dodexahedron Dec 31, 2023
8a62886
Annotate this test method
dodexahedron Dec 31, 2023
ac7a087
A few pre-conditions
dodexahedron Dec 31, 2023
76f8145
Pre-condition has guaranteed this can't be null
dodexahedron Dec 31, 2023
7831e52
This is guaranteed by the pre-conditions
dodexahedron Dec 31, 2023
bf607b4
Test the operation itself is ok
dodexahedron Dec 31, 2023
d85a835
Convert and simplify conditions
dodexahedron Dec 31, 2023
0a4bd19
Track UndoStack and RedoStack
dodexahedron Dec 31, 2023
a576013
Finish up with the rest of the test
dodexahedron Dec 31, 2023
461943c
Annotate tested type
dodexahedron Dec 31, 2023
bb635df
Add disposal for this MenuBar and add a note for later
dodexahedron Dec 31, 2023
fea5fd8
Refactoring this guy to be clearer (WIP)
dodexahedron Dec 31, 2023
050a454
More refactoring to make this clearer
dodexahedron Dec 31, 2023
e3176a5
There we go
dodexahedron Dec 31, 2023
7ad50b8
And update the usages! (done)
dodexahedron Dec 31, 2023
81c45d2
These are actually pre-conditions
dodexahedron Dec 31, 2023
323298b
Be sure these don't throw
dodexahedron Dec 31, 2023
25e4eef
Finish converting and improving this test
dodexahedron Dec 31, 2023
a40b92d
Introduce a type so the menubars can be auto-disposed by usings
dodexahedron Dec 31, 2023
5a4e168
Convert these and change to pre-conditions
dodexahedron Dec 31, 2023
b3e06d8
Finish converting and improving this test
dodexahedron Dec 31, 2023
ee5d1f2
Convert MoveMenuItemLeft_CannotMoveRootItems
dodexahedron Dec 31, 2023
f0be03a
Convert these to pre-conditions
dodexahedron Dec 31, 2023
4fdd437
Make sure nothing throws and IsImpossible is false
dodexahedron Dec 31, 2023
df72088
Convert and add a reference check
dodexahedron Dec 31, 2023
4899df8
Convert and simplify
dodexahedron Dec 31, 2023
81047fa
Convert and simplify the rest of this test
dodexahedron Dec 31, 2023
3478184
Convert and upgrade this test.
dodexahedron Dec 31, 2023
a8bab3a
Convert this test
dodexahedron Dec 31, 2023
c9461c3
Add tests to ensure the helpers are valid and to reduce redundancy
dodexahedron Dec 31, 2023
7dc56f7
Remove code that is now redundant
dodexahedron Dec 31, 2023
38b2596
Some more code that can go away with the new tests
dodexahedron Dec 31, 2023
d9d9d0e
Convert the last test method
dodexahedron Dec 31, 2023
eb00a74
Silence warnings from static analysis about possible null dereferences
dodexahedron Dec 31, 2023
2ed8882
These can all be static lambdas
dodexahedron Dec 31, 2023
0832f9b
Re-ordering code after member name changes and new additions (just mo…
dodexahedron Dec 31, 2023
be599a6
One more rename/sort and make the record private sealed
dodexahedron Dec 31, 2023
2bcccd2
Collection expressions
dodexahedron Dec 31, 2023
2b37e12
Move this to namespace to match TGD
dodexahedron Dec 31, 2023
37d1c81
Annotate fixture
dodexahedron Jan 3, 2024
9bc95c6
(BUGS) Convert and improve this test
dodexahedron Jan 3, 2024
69bd63f
Convert this test
dodexahedron Jan 3, 2024
b3d9ac2
Convert another one
dodexahedron Jan 3, 2024
07821fb
Convert this one and make it test the cases combinatorially
dodexahedron Jan 3, 2024
88ed977
Convert the rest
dodexahedron Jan 3, 2024
2fa91d6
Sorted members
dodexahedron Jan 3, 2024
6bffb3e
Wrap some Assert.Multiples and a couple of renames
dodexahedron Jan 3, 2024
68fc504
Re-sort after name changes
dodexahedron Jan 3, 2024
ecebc97
Convert to a TestCaseSource like the rest
dodexahedron Jan 3, 2024
9e2ec20
Wrap a bunch of things in Assert.Multiple
dodexahedron Jan 3, 2024
80cbb79
Let's clean up after ourselves
dodexahedron Jan 3, 2024
12fb453
Can't put assumptions in assert.multiple
dodexahedron Jan 3, 2024
18bde8d
Collection expressions FTW
dodexahedron Jan 3, 2024
f6a7ced
Clean up usings
dodexahedron Jan 3, 2024
b06c161
Renames
dodexahedron Jan 3, 2024
8cdacfc
These lambdas can be static
dodexahedron Jan 3, 2024
b463a74
Use the types in preparation for test updates
dodexahedron Jan 3, 2024
6b9c51b
Sort
dodexahedron Jan 3, 2024
7e33505
Convert to constraint model
dodexahedron Jan 3, 2024
2a17ddb
Move helper to another class
dodexahedron Jan 3, 2024
df8f201
Move this by the other helper types
dodexahedron Jan 3, 2024
999b46e
Make it clear this isn't the standard dotnet ConfigurationManager
dodexahedron Jan 3, 2024
bf00422
Clean up after ourselves
dodexahedron Jan 3, 2024
fe4d921
String interpolation
dodexahedron Jan 3, 2024
2f14bc5
This one was hard-coded. Make it like the others
dodexahedron Jan 3, 2024
8508e72
Some extra (minor) checks and additional cases
dodexahedron Jan 3, 2024
224ce89
Parameterize to add a few more simple cases
dodexahedron Jan 3, 2024
13f9c41
Hoist the expected values to the top for scalar valued tests
dodexahedron Jan 3, 2024
6f96fc7
Merge branch '20-convert-propertytests-to-constraint-model' into conv…
dodexahedron Jan 3, 2024
6e73ea2
Clean up unnecessary usings and let a lambda be static
dodexahedron Jan 3, 2024
37f93aa
Use collection expressions for these
dodexahedron Jan 3, 2024
829e336
Remove unneeded usings
dodexahedron Jan 3, 2024
7cfb4db
Add nullability adornment
dodexahedron Jan 3, 2024
1d3cf3b
Remove two redundant tests
dodexahedron Jan 3, 2024
6e24544
Might as well let this test all combinations of the old inputs
dodexahedron Jan 3, 2024
741e842
usings for disposables
dodexahedron Jan 3, 2024
abfc994
Use nameof on this
dodexahedron Jan 3, 2024
b64d69b
Wrap these in assertions that they don't throw
dodexahedron Jan 3, 2024
edef839
Turn that into a switch
dodexahedron Jan 3, 2024
d423b20
Add Heya to the dictionary so it stops getting reported as an error
dodexahedron Jan 3, 2024
17c2652
Merge branch '19-convert-postests-to-constraint-model' into convert-u…
dodexahedron Jan 3, 2024
99c6b27
Annotate the fixture
dodexahedron Jan 3, 2024
8dbaef8
Statics and collection expressions
dodexahedron Jan 3, 2024
ba5aa8d
Convert these
dodexahedron Jan 3, 2024
2154b55
Merge branch '36-convert-radiogrouptests-to-constraint-model' into co…
dodexahedron Jan 3, 2024
09200cd
Ain't nobody got time for that
dodexahedron Jan 3, 2024
aadf758
fixture annotations
dodexahedron Jan 3, 2024
0e5d169
Convert the assertions
dodexahedron Jan 3, 2024
b8c445f
Minor cleanup and modernizing
dodexahedron Jan 3, 2024
0eee156
Merge branch '37-convert-scrollviewtests-to-constraint-model' into co…
dodexahedron Jan 3, 2024
8652252
Clean usings and switch to file-scoped namespace
dodexahedron Jan 3, 2024
f4ceb10
Annotations
dodexahedron Jan 3, 2024
732ac0a
Convert to constraints
dodexahedron Jan 3, 2024
d19f425
Dispose it before the last assertion
dodexahedron Jan 3, 2024
d74879d
Make these assumptions
dodexahedron Jan 3, 2024
fba852b
Use the standard style for this
dodexahedron Jan 3, 2024
e3305cf
Clean up after ourselves
dodexahedron Jan 3, 2024
55259a8
Merge branch '38-convert-spinnerviewtests-to-constraint-model' into c…
dodexahedron Jan 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 54 additions & 31 deletions src/MenuTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ public void Register(MenuBar mb)
this.bars.Add(mb);
}

/// <summary>
/// Unregisters listeners for <paramref name="mb"/>.
/// </summary>
/// <param name="mb"><see cref="MenuBar"/> to stop tracking.</param>
public void UnregisterMenuBar( MenuBar? mb )
{
if ( !bars.TryTake( out mb ) )
{
return;
}

mb.MenuAllClosed -= MenuAllClosed;
mb.MenuOpened -= MenuOpened;
mb.MenuClosing -= MenuClosing;
}

/// <summary>
/// <para>
/// Searches child items of all MenuBars tracked by this class
Expand All @@ -63,7 +79,7 @@ public void Register(MenuBar mb)
/// <returns>The immediate parent of <paramref name="item"/>.</returns>
/// <remarks>Result may be a top level menu (e.g. File, View)
/// or a sub-menu parent (e.g. View=>Windows).</remarks>
public MenuBarItem? GetParent( MenuItem item, out MenuBar? hostBar )
private MenuBarItem? GetParent( MenuItem item, out MenuBar? hostBar )
{
foreach (var bar in this.bars)
{
Expand All @@ -83,7 +99,23 @@ public void Register(MenuBar mb)
return null;
}

public bool TryGetParent(MenuItem item, [NotNullWhen(true)]out MenuBar? hostBar, [NotNullWhen(true)] out MenuBarItem? parentItem)
/// <summary>
/// Searches child items of all MenuBars tracked by this class to try and find the parent of the item passed.
/// </summary>
/// <param name="item">The item whose parent you want to find.</param>
/// <param name="hostBar">
/// When this method returns true, the <see cref="MenuBar" /> that owns <paramref name="item" />.<br /> Otherwise, <see langword="null" /> if
/// not found or parent not registered (see <see cref="Register(MenuBar)" />).
/// </param>
/// <param name="parentItem">
/// When this method returns <see langword="true" />, the immediate parent of <paramref name="item" />.<br /> Otherwise,
/// <see langword="null" />
/// </param>
/// <remarks>
/// Search is recursive and dips into sub-menus.<br /> For sub-menus it is the immediate parent that is returned.
/// </remarks>
/// <returns>A <see langword="bool" /> indicating if the search was successful or not.</returns>
public bool TryGetParent( MenuItem item, [NotNullWhen( true )] out MenuBar? hostBar, [NotNullWhen( true )] out MenuBarItem? parentItem )
{
var parentCandidate = GetParent( item, out hostBar );
if ( parentCandidate is null )
Expand All @@ -106,22 +138,21 @@ public bool TryGetParent(MenuItem item, [NotNullWhen(true)]out MenuBar? hostBar,
/// the substitution object (<see cref="MenuItem"/>). See
/// <see cref="ConvertMenuBarItemToRegularItemIfEmpty(MenuBarItem, out MenuItem?)"/>
/// for more information.</returns>
public Dictionary<MenuBarItem, MenuItem> ConvertEmptyMenus()
public Dictionary<MenuBarItem, MenuItem> ConvertEmptyMenus( )
{
var toReturn = new Dictionary<MenuBarItem, MenuItem>();

Dictionary<MenuBarItem, MenuItem> dictionary = [];
foreach (var b in this.bars)
{
foreach (var bi in b.Menus)
{
foreach (var converted in this.ConvertEmptyMenus(b, bi))
foreach ( ( MenuBarItem? convertedMenuBarItem, MenuItem? convertedMenuItem ) in this.ConvertEmptyMenus( dictionary, b, bi ) )
{
toReturn.Add(converted.Key, converted.Value);
dictionary.TryAdd( convertedMenuBarItem, convertedMenuItem );
}
}
}

return toReturn;
return dictionary;
}

/// <summary>
Expand All @@ -137,12 +168,12 @@ public Dictionary<MenuBarItem, MenuItem> ConvertEmptyMenus()
/// <param name="added">The result of the conversion (same text, same index etc but
/// <see cref="MenuItem"/> instead of <see cref="MenuBarItem"/>).</param>
/// <returns><see langword="true"/> if conversion was possible (menu was empty and belonged to tracked menu).</returns>
internal static bool ConvertMenuBarItemToRegularItemIfEmpty(MenuBarItem bar, out MenuItem? added)
internal static bool ConvertMenuBarItemToRegularItemIfEmpty( MenuBarItem bar, [NotNullWhen( true )] out MenuItem? added )
{
added = null;

// bar still has more children so don't convert
if (bar.Children.Any())
if ( bar.Children.Length != 0 )
{
return false;
}
Expand All @@ -152,48 +183,40 @@ internal static bool ConvertMenuBarItemToRegularItemIfEmpty(MenuBarItem bar, out
return false;
}

var children = parent.Children.ToList<MenuItem>();
var idx = children.IndexOf(bar);
int idx = Array.IndexOf( parent.Children, bar );

if (idx < 0)
{
return false;
}

// bar has no children so convert to MenuItem
added = new MenuItem { Title = bar.Title };
added.Data = bar.Data;
added.Shortcut = bar.Shortcut;

children.RemoveAt(idx);
children.Insert(idx, added);

parent.Children = children.ToArray();
parent.Children[ idx ] = added = new( )
{
Title = bar.Title,
Data = bar.Data,
Shortcut = bar.Shortcut
};

return true;
}

/// <inheritdoc cref="ConvertEmptyMenus()"/>
private Dictionary<MenuBarItem, MenuItem> ConvertEmptyMenus(MenuBar bar, MenuBarItem mbi)
private Dictionary<MenuBarItem, MenuItem> ConvertEmptyMenus(Dictionary<MenuBarItem,MenuItem> dictionary, MenuBar bar, MenuBarItem mbi)
{
var toReturn = new Dictionary<MenuBarItem, MenuItem>();

foreach (var c in mbi.Children.OfType<MenuBarItem>())
{
this.ConvertEmptyMenus(bar, c);
if ( ConvertMenuBarItemToRegularItemIfEmpty( c, out var added))
this.ConvertEmptyMenus(dictionary,bar, c);
if ( ConvertMenuBarItemToRegularItemIfEmpty( c, out MenuItem? added))
{
if (added != null)
{
toReturn.Add(c, added);
}
dictionary.TryAdd( c, added );

bar.CloseMenu();
bar.OpenMenu();
}
}

return toReturn;
return dictionary;
}

private void MenuClosing(object? sender, MenuClosingEventArgs obj)
Expand All @@ -204,7 +227,7 @@ private void MenuClosing(object? sender, MenuClosingEventArgs obj)
private void MenuOpened(object? sender, MenuOpenedEventArgs obj)
{
this.CurrentlyOpenMenuItem = obj.MenuItem;
this.ConvertEmptyMenus();
this.ConvertEmptyMenus( );
}

private void MenuAllClosed(object? sender, EventArgs e)
Expand Down
55 changes: 27 additions & 28 deletions src/Operations/MenuOperations/RemoveMenuItemOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ public override void Undo()
return;
}

var children = this.Parent.Children.ToList<MenuItem>();

children.Insert(this.removedAtIdx, this.OperateOn);
this.Parent.Children = children.ToArray();
this.Parent.Children =
[
.. Parent.Children[ .. removedAtIdx ],
this.OperateOn,
.. Parent.Children[ removedAtIdx .. ]
];
this.Bar?.SetNeedsDisplay();

// if any MenuBarItem were converted to vanilla MenuItem
Expand Down Expand Up @@ -123,12 +125,12 @@ protected override bool DoImpl()
return false;
}

var children = this.Parent.Children.ToList<MenuItem>();

this.removedAtIdx = Math.Max(0, children.IndexOf(this.OperateOn));

children.Remove(this.OperateOn);
this.Parent.Children = children.ToArray();
this.removedAtIdx = Math.Max( 0, Array.IndexOf( Parent.Children, OperateOn ) );
this.Parent.Children =
[
.. Parent.Children[ ..removedAtIdx ],
.. Parent.Children[ ( removedAtIdx + 1 ).. ]
];
this.Bar?.SetNeedsDisplay();

if (this.Bar != null)
Expand All @@ -137,27 +139,24 @@ protected override bool DoImpl()
}

// if a top level menu now has no children
if (this.Bar != null)
var empty = this.Bar?.Menus.Where(bi => bi.Children.Length == 0).ToArray();
if (empty?.Any() == true)
{
var empty = this.Bar.Menus.Where(bi => bi.Children.Length == 0).ToArray();
if (empty.Any())
{
// remember where they were
this.prunedEmptyTopLevelMenus = empty.ToDictionary(e => Array.IndexOf(this.Bar.Menus, e), v => v);
// remember where they were
this.prunedEmptyTopLevelMenus = empty.ToDictionary(e => Array.IndexOf(this.Bar.Menus, e), v => v);

// and remove them
this.Bar.Menus = this.Bar.Menus.Except(this.prunedEmptyTopLevelMenus.Values).ToArray();
}
// and remove them
this.Bar.Menus = this.Bar.Menus.Except(this.prunedEmptyTopLevelMenus.Values).ToArray();
}

// if we just removed the last menu header
// leaving a completely blank menu bar
if (this.Bar.Menus.Length == 0 && this.Bar.SuperView != null)
{
// remove the bar completely
this.Bar.CloseMenu();
this.barRemovedFrom = this.Bar.SuperView;
this.barRemovedFrom.Remove(this.Bar);
}
// if we just removed the last menu header
// leaving a completely blank menu bar
if (this.Bar?.Menus.Length == 0 && this.Bar.SuperView != null)
{
// remove the bar completely
this.Bar.CloseMenu();
this.barRemovedFrom = this.Bar.SuperView;
this.barRemovedFrom.Remove(this.Bar);
}

return true;
Expand Down
4 changes: 2 additions & 2 deletions src/TerminalGuiDesigner.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Include=".\logo.png">
<Pack>True</Pack>
Expand Down Expand Up @@ -145,8 +145,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
<PackageReference Include="nlog" Version="5.2.7" />
<PackageReference Include="Terminal.Gui" Version="2.0.0-pre.250" />
<PackageReference Include="nlog" Version="5.2.7" />
<PackageReference Include="Basic.Reference.Assemblies.Net80" Version="1.4.5" />
<PackageReference Include="System.CodeDom" Version="8.0.0" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/TerminalGuiDesigner.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heya/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
15 changes: 7 additions & 8 deletions src/ViewFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class ViewFactory
internal const string DefaultMenuItemText = "Edit Me";

internal static readonly Type[] KnownUnsupportedTypes =
{
[
typeof( Toplevel ),
typeof( Dialog ),
typeof( FileDialog ),
Expand All @@ -42,7 +42,7 @@ public static class ViewFactory
// BUG These seem to cause stack overflows in CreateSubControlDesigns (see TestAddView_RoundTrip)
typeof( Wizard ),
typeof( WizardStep )
};
];

/// <summary>
/// Gets a new instance of a default <see cref="MenuBarItem" />[], to include as the default initial
Expand All @@ -57,12 +57,11 @@ internal static MenuBarItem[] DefaultMenuBarItems
{
get
{
return new[]
{
new MenuBarItem(
"_File (F9)",
new[] { new MenuItem( DefaultMenuItemText, string.Empty, ( ) => { } ) } )
};
return
[
new( "_File (F9)",
[ new MenuItem( DefaultMenuItemText, string.Empty, static ( ) => { } ) ] )
];
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/ListViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void TestIListSourceProperty_Rhs( )

Assert.That( lv.Source, Has.Count.EqualTo( 2 ) );

var code = PropertyTests.ExpressionToCode( prop.GetRhs( ) );
var code = Helpers.ExpressionToCode( prop.GetRhs( ) );

Assert.That(
code, Is.EqualTo( "new Terminal.Gui.ListWrapper(new string[] {\n \"hi there\",\n \"my friend\"})".Replace( "\n", Environment.NewLine ) ) );
Expand Down
Loading