mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-02 01:03:29 +01:00
Merge pull request #2 from tznind/listview_keyboard_search_tv
Add SearchCollectionNavigator to TreeView
This commit is contained in:
@@ -129,5 +129,17 @@ namespace Terminal.Gui {
|
||||
lastKeystroke = DateTime.MinValue;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if <paramref name="kb"/> is a searchable key
|
||||
/// (e.g. letters, numbers etc) that is valid to pass to to this
|
||||
/// class for search filtering
|
||||
/// </summary>
|
||||
/// <param name="kb"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsCompatibleKey (KeyEvent kb)
|
||||
{
|
||||
return !kb.IsAlt && !kb.IsCapslock && !kb.IsCtrl && !kb.IsScrolllock && !kb.IsNumlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
// Enable user to find & select an item by typing text
|
||||
if (!kb.IsAlt && !kb.IsCapslock && !kb.IsCtrl && !kb.IsScrolllock && !kb.IsNumlock) {
|
||||
if (SearchCollectionNavigator.IsCompatibleKey(kb)) {
|
||||
if (navigator == null) {
|
||||
navigator = new SearchCollectionNavigator (source.ToList ().Cast<object> ());
|
||||
}
|
||||
|
||||
@@ -140,12 +140,12 @@ namespace Terminal.Gui {
|
||||
/// <value></value>
|
||||
public MouseFlags? ObjectActivationButton { get; set; } = MouseFlags.Button1DoubleClicked;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for multi colored tree views. Return the <see cref="ColorScheme"/> to use
|
||||
/// for each passed object or null to use the default.
|
||||
/// </summary>
|
||||
public Func<T,ColorScheme> ColorGetter {get;set;}
|
||||
public Func<T, ColorScheme> ColorGetter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secondary selected regions of tree when <see cref="MultiSelect"/> is true
|
||||
@@ -220,6 +220,7 @@ namespace Terminal.Gui {
|
||||
public AspectGetterDelegate<T> AspectGetter { get; set; } = (o) => o.ToString () ?? "";
|
||||
|
||||
CursorVisibility desiredCursorVisibility = CursorVisibility.Invisible;
|
||||
private SearchCollectionNavigator searchCollectionNavigator;
|
||||
|
||||
/// <summary>
|
||||
/// Get / Set the wished cursor when the tree is focused.
|
||||
@@ -227,7 +228,7 @@ namespace Terminal.Gui {
|
||||
/// Defaults to <see cref="CursorVisibility.Invisible"/>
|
||||
/// </summary>
|
||||
public CursorVisibility DesiredCursorVisibility {
|
||||
get {
|
||||
get {
|
||||
return MultiSelect ? desiredCursorVisibility : CursorVisibility.Invisible;
|
||||
}
|
||||
set {
|
||||
@@ -576,19 +577,44 @@ namespace Terminal.Gui {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if it is a single character pressed without any control keys
|
||||
if (keyEvent.KeyValue > 0 && keyEvent.KeyValue < 0xFFFF) {
|
||||
try {
|
||||
|
||||
// First of all deal with any registered keybindings
|
||||
var result = InvokeKeybindings (keyEvent);
|
||||
if (result != null) {
|
||||
return (bool)result;
|
||||
}
|
||||
|
||||
// If not a keybinding, is the key a searchable key press?
|
||||
if (SearchCollectionNavigator.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) {
|
||||
|
||||
IReadOnlyCollection<Branch<T>> map;
|
||||
|
||||
// If there has been a call to InvalidateMap since the last time we allocated a
|
||||
// SearchCollectionNavigator then we need a new one to reflect the new exposed
|
||||
// tree state
|
||||
if (cachedLineMap == null || searchCollectionNavigator == null) {
|
||||
map = BuildLineMap ();
|
||||
searchCollectionNavigator = new SearchCollectionNavigator (map.Select (b => AspectGetter (b.Model)).ToArray ());
|
||||
}
|
||||
else {
|
||||
// we still need the map, handily its the cached one which means super fast access
|
||||
map = BuildLineMap ();
|
||||
}
|
||||
|
||||
// Find the current selected object within the tree
|
||||
var current = map.IndexOf (b => b.Model == SelectedObject);
|
||||
var newIndex = searchCollectionNavigator.CalculateNewIndex (current, (char)keyEvent.KeyValue);
|
||||
|
||||
if (newIndex != -1) {
|
||||
SelectedObject = map.ElementAt (newIndex).Model;
|
||||
EnsureVisible (selectedObject);
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
|
||||
if (char.IsLetterOrDigit ((char)keyEvent.KeyValue) && AllowLetterBasedNavigation && !keyEvent.IsShift && !keyEvent.IsAlt && !keyEvent.IsCtrl) {
|
||||
AdjustSelectionToNextItemBeginningWith ((char)keyEvent.KeyValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var result = InvokeKeybindings (keyEvent);
|
||||
if (result != null)
|
||||
return (bool)result;
|
||||
} finally {
|
||||
|
||||
PositionCursor ();
|
||||
@@ -626,7 +652,7 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
/// <param name="toFind"></param>
|
||||
/// <returns></returns>
|
||||
public int? GetObjectRow(T toFind)
|
||||
public int? GetObjectRow (T toFind)
|
||||
{
|
||||
var idx = BuildLineMap ().IndexOf (o => o.Model.Equals (toFind));
|
||||
|
||||
|
||||
@@ -3,25 +3,41 @@ using Xunit;
|
||||
|
||||
namespace Terminal.Gui.Core {
|
||||
public class SearchCollectionNavigatorTests {
|
||||
static string [] simpleStrings = new string []{
|
||||
"appricot", // 0
|
||||
"arm", // 1
|
||||
"bat", // 2
|
||||
"batman", // 3
|
||||
"candle" // 4
|
||||
};
|
||||
[Fact]
|
||||
public void TestSearchCollectionNavigator_ShouldAcceptNegativeOne ()
|
||||
{
|
||||
var n = new SearchCollectionNavigator (simpleStrings);
|
||||
|
||||
// Expect that index of -1 (i.e. no selection) should work correctly
|
||||
// and select the first entry of the letter 'b'
|
||||
Assert.Equal (2, n.CalculateNewIndex (-1, 'b'));
|
||||
}
|
||||
[Fact]
|
||||
public void TestSearchCollectionNavigator_OutOfBoundsShouldBeIgnored()
|
||||
{
|
||||
var n = new SearchCollectionNavigator (simpleStrings);
|
||||
|
||||
// Expect saying that index 500 is the current selection should not cause
|
||||
// error and just be ignored (treated as no selection)
|
||||
Assert.Equal (2, n.CalculateNewIndex (500, 'b'));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestSearchCollectionNavigator_Cycling ()
|
||||
{
|
||||
var strings = new string []{
|
||||
"appricot",
|
||||
"arm",
|
||||
"bat",
|
||||
"batman",
|
||||
"candle"
|
||||
};
|
||||
|
||||
var n = new SearchCollectionNavigator (strings);
|
||||
var n = new SearchCollectionNavigator (simpleStrings);
|
||||
Assert.Equal (2, n.CalculateNewIndex ( 0, 'b'));
|
||||
Assert.Equal (3, n.CalculateNewIndex ( 2, 'b'));
|
||||
|
||||
// if 4 (candle) is selected it should loop back to bat
|
||||
Assert.Equal (2, n.CalculateNewIndex ( 4, 'b'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user