Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
135 changes: 30 additions & 105 deletions Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@

namespace UICatalog.Scenarios;

[ScenarioMetadata (
"Collection Navigator",
"Demonstrates keyboard navigation in ListView & TreeView (CollectionNavigator)."
)]
[ScenarioMetadata ("Collection Navigator", "Demonstrates keyboard navigation in ListView & TreeView (CollectionNavigator).")]
[ScenarioCategory ("Controls")]
[ScenarioCategory ("ListView")]
[ScenarioCategory ("TreeView")]
[ScenarioCategory ("Text and Formatting")]
[ScenarioCategory ("Mouse and Keyboard")]
public class CollectionNavigatorTester : Scenario
{
private ObservableCollection<string> _items = new (
[
private ObservableCollection<string> _items = new ([
"a",
"b",
"bb",
Expand Down Expand Up @@ -70,8 +66,7 @@ public class CollectionNavigatorTester : Scenario
"q",
"quit",
"quitter"
]
);
]);

private Window? _top;
private ListView? _listView;
Expand All @@ -85,102 +80,46 @@ public override void Main ()
using IApplication app = Application.Create ();
app.Init ();

using Window top = new ()
{
SchemeName = "Base"
};
using Window top = new ();
top.SchemeName = "Base";
_top = top;

// MenuBar
MenuBar menu = new ();

_allowMarkingCheckBox = new ()
{
Title = "Allow _Marking"
};
_allowMarkingCheckBox = new CheckBox { Title = "Allow _Marking" };

_allowMarkingCheckBox.ValueChanged += (_, _) =>
{
if (_listView is not null)
{
_listView.ShowMarks = _allowMarkingCheckBox.Value == CheckState.Checked;
}

if (_allowMultiSelectionCheckBox is not null)
{
_allowMultiSelectionCheckBox.Enabled = _allowMarkingCheckBox.Value == CheckState.Checked;
}
};

_allowMultiSelectionCheckBox = new ()
{
Title = "Allow Multi _Selection",
Enabled = false
};
{
_listView?.ShowMarks = _allowMarkingCheckBox.Value == CheckState.Checked;

_allowMultiSelectionCheckBox.ValueChanged += (_, _) =>
{
if (_listView is not null)
{
_listView.MarkMultiple =
_allowMultiSelectionCheckBox.Value == CheckState.Checked;
}
};

menu.Add (
new MenuBarItem (
"_Configure",
[
new MenuItem
{
CommandView = _allowMarkingCheckBox
},
new MenuItem
{
CommandView = _allowMultiSelectionCheckBox
},
new MenuItem
{
Title = Strings.cmdQuit,
Key = Application.GetDefaultKey (Command.Quit),
Action = Quit
}
]
)
);

menu.Add (
new MenuBarItem (
Strings.cmdQuit,
_allowMultiSelectionCheckBox?.Enabled = _allowMarkingCheckBox.Value == CheckState.Checked;
};

_allowMultiSelectionCheckBox = new CheckBox { Title = "Allow Multi _Selection", Enabled = false };

_allowMultiSelectionCheckBox.ValueChanged += (_, _) => { _listView?.MarkMultiple = _allowMultiSelectionCheckBox.Value == CheckState.Checked; };

menu.Add (new MenuBarItem ("_Configure",
[
new MenuItem
{
Title = Strings.cmdQuit,
Key = Application.GetDefaultKey (Command.Quit),
Action = Quit
}
]
)
);
new MenuItem { CommandView = _allowMarkingCheckBox },
new MenuItem { CommandView = _allowMultiSelectionCheckBox },
new MenuItem { Title = Strings.cmdQuit, Key = Application.GetDefaultKey (Command.Quit), Action = Quit }
]));

menu.Add (new MenuBarItem (Strings.cmdQuit, [new MenuItem { Title = Strings.cmdQuit, Key = Application.GetDefaultKey (Command.Quit), Action = Quit }]));

top.Add (menu);

_items = new (_items.OrderBy (i => i, StringComparer.OrdinalIgnoreCase));
_items = new ObservableCollection<string> (_items.OrderBy (i => i, StringComparer.OrdinalIgnoreCase));

CreateListView ();

Line vsep = new ()
{
Orientation = Orientation.Vertical,
X = Pos.Right (_listView!),
Y = 1,
Height = Dim.Fill ()
};
Line vsep = new () { Orientation = Orientation.Vertical, X = Pos.Right (_listView!), Y = 1, Height = Dim.Fill () };
top.Add (vsep);
CreateTreeView ();

app.Run (top);
top.Dispose ();
}

private void CreateListView ()
Expand All @@ -201,7 +140,7 @@ private void CreateListView ()
};
_top.Add (label);

_listView = new ()
_listView = new ListView
{
X = 0,
Y = Pos.Bottom (label),
Expand All @@ -214,7 +153,7 @@ private void CreateListView ()

_listView.SetSource (_items);

_listView.KeystrokeNavigator.SearchStringChanged += (_, e) => { label.Text = $"ListView: {e.SearchString}"; };
_listView.KeystrokeNavigator?.SearchStringChanged += (_, e) => { label.Text = $"ListView: {e.SearchString}"; };
}

private void CreateTreeView ()
Expand All @@ -235,35 +174,21 @@ private void CreateTreeView ()
};
_top.Add (label);

_treeView = new TreeView
{
X = Pos.Right (_listView) + 1,
Y = Pos.Bottom (label),
Width = Dim.Fill (),
Height = Dim.Fill ()
};
_treeView = new TreeView { X = Pos.Right (_listView) + 1, Y = Pos.Bottom (label), Width = Dim.Fill (), Height = Dim.Fill () };
_treeView.Style.HighlightModelTextOnly = true;
_top.Add (_treeView);

TreeNode root = new () { Text = "IsLetterOrDigit examples" };
TreeNode root = new () { Text = "IsLetterOrDigit examples", Children = _items.Where (i => char.IsLetterOrDigit (i [0])).Select (i => new TreeNode { Text = i }).Cast<ITreeNode> ().ToList () };

root.Children = _items.Where (i => char.IsLetterOrDigit (i [0]))
.Select (i => new TreeNode () { Text = i })
.Cast<ITreeNode> ()
.ToList ();
_treeView.AddObject (root);
root = new () { Text = "Non-IsLetterOrDigit examples" };
root = new TreeNode { Text = "Non-IsLetterOrDigit examples", Children = _items.Where (i => !char.IsLetterOrDigit (i [0])).Select (i => new TreeNode { Text = i }).Cast<ITreeNode> ().ToList () };

root.Children = _items.Where (i => !char.IsLetterOrDigit (i [0]))
.Select (i => new TreeNode () { Text = i })
.Cast<ITreeNode> ()
.ToList ();
_treeView.AddObject (root);
_treeView.ExpandAll ();
_treeView.GoToFirst ();

_treeView.KeystrokeNavigator.SearchStringChanged += (_, e) => { label.Text = $"TreeView: {e.SearchString}"; };
}

private void Quit () { _top?.RequestStop (); }
private void Quit () => _top?.RequestStop ();
}
16 changes: 12 additions & 4 deletions Examples/UICatalog/UICatalogRunnable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,19 @@ View [] CreateThemeMenuItems ()
{
List<View> menuItems = [];

_force16ColorsMenuItemCb = new CheckBox { Title = "Force _16 Colors", Value = Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked };
_force16ColorsMenuItemCb = new CheckBox { Title = "Force _16 Colors", Value = Driver is { Force16Colors: true } ? CheckState.Checked : CheckState.UnChecked };

menuItems.Add (new MenuItem
{
CommandView = _force16ColorsMenuItemCb,
Action = () =>
{
if (Driver is null)
{
return;
}
Driver.Force16Colors = !Driver.Force16Colors;
_force16ColorsShortcutCb?.Value = Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
_force16ColorsShortcutCb?.Value = Driver is { Force16Colors: true } ? CheckState.Checked : CheckState.UnChecked;
SetNeedsDraw ();
}
});
Expand Down Expand Up @@ -818,7 +822,7 @@ private StatusBar CreateStatusBar ()

_force16ColorsShortcutCb = new CheckBox
{
Title = "16 color mode", Value = Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked, CanFocus = true
Title = "16 color mode", Value = Driver is { Force16Colors: true } ? CheckState.Checked : CheckState.UnChecked, CanFocus = true
};

Shortcut force16ColorsShortcut = new ()
Expand All @@ -830,6 +834,10 @@ private StatusBar CreateStatusBar ()
Key = GetFirstUnboundFKey ([statusBarShortcut.Key]),
Action = () =>
{
if (Driver is null)
{
return;
}
Driver.Force16Colors = !Driver.Force16Colors;
_force16ColorsMenuItemCb?.Value = Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
SetNeedsDraw ();
Expand Down Expand Up @@ -877,7 +885,7 @@ private void ConfigApplied ()
_shQuit?.Key = Application.GetDefaultKey (Command.Quit);

_disableMouseCb?.Value = App?.Mouse.IsMouseDisabled == true ? CheckState.Checked : CheckState.UnChecked;
_force16ColorsShortcutCb?.Value = Driver.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
_force16ColorsShortcutCb?.Value = Driver is { Force16Colors: true } ? CheckState.Checked : CheckState.UnChecked;

App?.TopRunnableView?.SetNeedsDraw ();
}
Expand Down
67 changes: 44 additions & 23 deletions Terminal.Gui/Views/ListView/ListView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ namespace Terminal.Gui.Views;
/// <term>Home / End</term> <description>Moves to the first or last item.</description>
/// </item>
/// <item>
/// <term>Shift+&lt;movement&gt;</term> <description>Extends the selection in the given direction.</description>
/// <term>Shift+&lt;movement&gt;</term>
/// <description>Extends the selection in the given direction.</description>
/// </item>
/// <item>
/// <term>Ctrl+A</term> <description>Selects all items.</description>
Expand All @@ -76,7 +77,8 @@ namespace Terminal.Gui.Views;
/// <term>Click</term> <description>Activates (selects) the clicked item.</description>
/// </item>
/// <item>
/// <term>Double-Click</term> <description>Accepts the clicked item (<see cref="Command.Accept"/>).</description>
/// <term>Double-Click</term>
/// <description>Accepts the clicked item (<see cref="Command.Accept"/>).</description>
/// </item>
/// <item>
/// <term>Wheel Up / Down</term> <description>Scrolls the list.</description>
Expand All @@ -96,6 +98,22 @@ public ListView ()
SetupBindingsAndCommands ();
}

/// <inheritdoc/>
public bool EnableForDesign ()
{
ListWrapper<string> source = new (["List Item 1", "List Item two", "List Item 3", "List Item Quattro", "Last List Item"]);
Source = source;

return true;
}

/// <inheritdoc/>
protected override void Dispose (bool disposing)
{
Source?.Dispose ();
base.Dispose (disposing);
}

#region IListDataSource

/// <summary>
Expand Down Expand Up @@ -166,7 +184,7 @@ public IListDataSource? Source
{
field.CollectionChanged += SourceOnCollectionChanged;
SetContentSize (new Size (EffectiveMaxItemLength, field?.Count ?? Viewport.Height));
KeystrokeNavigator.Collection = field?.ToList ();
KeystrokeNavigator?.Collection = field?.ToList ();
}

SelectedItem = null;
Expand Down Expand Up @@ -240,11 +258,30 @@ protected virtual void OnSourceChanged () { }

#region Keystroke Navigation

private IListCollectionNavigator? _keystrokeNavigator = new CollectionNavigator ();

/// <summary>
/// Gets the <see cref="CollectionNavigator"/> that searches the <see cref="ListView.Source"/> collection as the
/// user types.
/// Gets or sets the <see cref="CollectionNavigator"/> that searches the <see cref="ListView.Source"/> collection as
/// the user types. The default implementation is a <see cref="CollectionNavigator"/> that uses the string
/// representation of the items in the collection. Set to <see langword="null"/> to disable keystroke navigation.
/// </summary>
public IListCollectionNavigator KeystrokeNavigator { get; } = new CollectionNavigator ();
/// <remarks>
/// When a new navigator is assigned, its <see cref="IListCollectionNavigator.Collection"/> is automatically
/// synchronized with the current <see cref="Source"/>.
/// </remarks>
public IListCollectionNavigator? KeystrokeNavigator
{
get => _keystrokeNavigator;
set
{
_keystrokeNavigator = value;

if (_keystrokeNavigator is { } && Source is { })
{
_keystrokeNavigator.Collection = Source.ToList ();
}
}
}

/// <inheritdoc/>
protected override bool OnKeyDown (Key key)
Expand All @@ -257,7 +294,7 @@ protected override bool OnKeyDown (Key key)
}

// Enable user to find & select an item by typing text
if (!KeystrokeNavigator.Matcher.IsCompatibleKey (key))
if (KeystrokeNavigator is null || !KeystrokeNavigator.Matcher.IsCompatibleKey (key))
{
return false;
}
Expand All @@ -277,20 +314,4 @@ protected override bool OnKeyDown (Key key)
}

#endregion Keystroke Navigation

/// <inheritdoc/>
public bool EnableForDesign ()
{
ListWrapper<string> source = new (["List Item 1", "List Item two", "List Item 3", "List Item Quattro", "Last List Item"]);
Source = source;

return true;
}

/// <inheritdoc/>
protected override void Dispose (bool disposing)
{
Source?.Dispose ();
base.Dispose (disposing);
}
}
Loading
Loading