mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-14 16:02:50 +08:00
parent
753894de94
commit
fd69ad0b01
@ -14,6 +14,7 @@ internal sealed class ListPrompt<T>
|
|||||||
|
|
||||||
public async Task<ListPromptState<T>> Show(
|
public async Task<ListPromptState<T>> Show(
|
||||||
ListPromptTree<T> tree,
|
ListPromptTree<T> tree,
|
||||||
|
Func<T, string> converter,
|
||||||
SelectionMode selectionMode,
|
SelectionMode selectionMode,
|
||||||
bool skipUnselectableItems,
|
bool skipUnselectableItems,
|
||||||
bool searchEnabled,
|
bool searchEnabled,
|
||||||
@ -41,7 +42,7 @@ internal sealed class ListPrompt<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
var nodes = tree.Traverse().ToList();
|
var nodes = tree.Traverse().ToList();
|
||||||
var state = new ListPromptState<T>(nodes, _strategy.CalculatePageSize(_console, nodes.Count, requestedPageSize), wrapAround, selectionMode, skipUnselectableItems, searchEnabled);
|
var state = new ListPromptState<T>(nodes, converter, _strategy.CalculatePageSize(_console, nodes.Count, requestedPageSize), wrapAround, selectionMode, skipUnselectableItems, searchEnabled);
|
||||||
var hook = new ListPromptRenderHook<T>(_console, () => BuildRenderable(state));
|
var hook = new ListPromptRenderHook<T>(_console, () => BuildRenderable(state));
|
||||||
|
|
||||||
using (new RenderHookScope(_console, hook))
|
using (new RenderHookScope(_console, hook))
|
||||||
|
@ -3,6 +3,8 @@ namespace Spectre.Console;
|
|||||||
internal sealed class ListPromptState<T>
|
internal sealed class ListPromptState<T>
|
||||||
where T : notnull
|
where T : notnull
|
||||||
{
|
{
|
||||||
|
private readonly Func<T, string> _converter;
|
||||||
|
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
public int ItemCount => Items.Count;
|
public int ItemCount => Items.Count;
|
||||||
public int PageSize { get; }
|
public int PageSize { get; }
|
||||||
@ -16,8 +18,15 @@ internal sealed class ListPromptState<T>
|
|||||||
public ListPromptItem<T> Current => Items[Index];
|
public ListPromptItem<T> Current => Items[Index];
|
||||||
public string SearchText { get; private set; }
|
public string SearchText { get; private set; }
|
||||||
|
|
||||||
public ListPromptState(IReadOnlyList<ListPromptItem<T>> items, int pageSize, bool wrapAround, SelectionMode mode, bool skipUnselectableItems, bool searchEnabled)
|
public ListPromptState(
|
||||||
|
IReadOnlyList<ListPromptItem<T>> items,
|
||||||
|
Func<T, string> converter,
|
||||||
|
int pageSize, bool wrapAround,
|
||||||
|
SelectionMode mode,
|
||||||
|
bool skipUnselectableItems,
|
||||||
|
bool searchEnabled)
|
||||||
{
|
{
|
||||||
|
_converter = converter ?? throw new ArgumentNullException(nameof(converter));
|
||||||
Items = items;
|
Items = items;
|
||||||
PageSize = pageSize;
|
PageSize = pageSize;
|
||||||
WrapAround = wrapAround;
|
WrapAround = wrapAround;
|
||||||
@ -126,7 +135,11 @@ internal sealed class ListPromptState<T>
|
|||||||
if (!char.IsControl(keyInfo.KeyChar))
|
if (!char.IsControl(keyInfo.KeyChar))
|
||||||
{
|
{
|
||||||
search = SearchText + keyInfo.KeyChar;
|
search = SearchText + keyInfo.KeyChar;
|
||||||
var item = Items.FirstOrDefault(x => x.Data.ToString()?.Contains(search, StringComparison.OrdinalIgnoreCase) == true && (!x.IsGroup || Mode != SelectionMode.Leaf));
|
|
||||||
|
var item = Items.FirstOrDefault(x =>
|
||||||
|
_converter.Invoke(x.Data).Contains(search, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& (!x.IsGroup || Mode != SelectionMode.Leaf));
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
index = Items.IndexOf(item);
|
index = Items.IndexOf(item);
|
||||||
@ -140,7 +153,10 @@ internal sealed class ListPromptState<T>
|
|||||||
search = search.Substring(0, search.Length - 1);
|
search = search.Substring(0, search.Length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = Items.FirstOrDefault(x => x.Data.ToString()?.Contains(search, StringComparison.OrdinalIgnoreCase) == true && (!x.IsGroup || Mode != SelectionMode.Leaf));
|
var item = Items.FirstOrDefault(x =>
|
||||||
|
_converter.Invoke(x.Data).Contains(search, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
(!x.IsGroup || Mode != SelectionMode.Leaf));
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
index = Items.IndexOf(item);
|
index = Items.IndexOf(item);
|
||||||
|
@ -94,7 +94,8 @@ public sealed class MultiSelectionPrompt<T> : IPrompt<List<T>>, IListPromptStrat
|
|||||||
{
|
{
|
||||||
// Create the list prompt
|
// Create the list prompt
|
||||||
var prompt = new ListPrompt<T>(console, this);
|
var prompt = new ListPrompt<T>(console, this);
|
||||||
var result = await prompt.Show(Tree, Mode, false, false, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
|
var converter = Converter ?? TypeConverterHelper.ConvertToString;
|
||||||
|
var result = await prompt.Show(Tree, converter, Mode, false, false, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (Mode == SelectionMode.Leaf)
|
if (Mode == SelectionMode.Leaf)
|
||||||
{
|
{
|
||||||
|
@ -99,7 +99,8 @@ public sealed class SelectionPrompt<T> : IPrompt<T>, IListPromptStrategy<T>
|
|||||||
{
|
{
|
||||||
// Create the list prompt
|
// Create the list prompt
|
||||||
var prompt = new ListPrompt<T>(console, this);
|
var prompt = new ListPrompt<T>(console, this);
|
||||||
var result = await prompt.Show(_tree, Mode, true, SearchEnabled, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
|
var converter = Converter ?? TypeConverterHelper.ConvertToString;
|
||||||
|
var result = await prompt.Show(_tree, converter, Mode, true, SearchEnabled, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// Return the selected item
|
// Return the selected item
|
||||||
return result.Items[result.Index].Data;
|
return result.Items[result.Index].Data;
|
||||||
|
@ -3,7 +3,10 @@ namespace Spectre.Console.Tests.Unit;
|
|||||||
public sealed class ListPromptStateTests
|
public sealed class ListPromptStateTests
|
||||||
{
|
{
|
||||||
private ListPromptState<string> CreateListPromptState(int count, int pageSize, bool shouldWrap, bool searchEnabled)
|
private ListPromptState<string> CreateListPromptState(int count, int pageSize, bool shouldWrap, bool searchEnabled)
|
||||||
=> new(Enumerable.Range(0, count).Select(i => new ListPromptItem<string>(i.ToString())).ToList(), pageSize, shouldWrap, SelectionMode.Independent, true, searchEnabled);
|
=> new(
|
||||||
|
Enumerable.Range(0, count).Select(i => new ListPromptItem<string>(i.ToString())).ToList(),
|
||||||
|
text => text,
|
||||||
|
pageSize, shouldWrap, SelectionMode.Independent, true, searchEnabled);
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Have_Start_Index_Zero()
|
public void Should_Have_Start_Index_Zero()
|
||||||
|
@ -410,4 +410,45 @@ public sealed class TextPromptTests
|
|||||||
// Then
|
// Then
|
||||||
return Verifier.Verify(console.Output);
|
return Verifier.Verify(console.Output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Search_In_Remapped_Result()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new TestConsole();
|
||||||
|
console.Profile.Capabilities.Interactive = true;
|
||||||
|
console.EmitAnsiSequences();
|
||||||
|
console.Input.PushText("2");
|
||||||
|
console.Input.PushKey(ConsoleKey.Enter);
|
||||||
|
|
||||||
|
var choices = new List<CustomSelectionItem>
|
||||||
|
{
|
||||||
|
new(33, "Item 1"),
|
||||||
|
new(34, "Item 2"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var prompt = new SelectionPrompt<CustomSelectionItem>()
|
||||||
|
.Title("Select one")
|
||||||
|
.EnableSearch()
|
||||||
|
.UseConverter(o => o.Name)
|
||||||
|
.AddChoices(choices);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var selection = prompt.Show(console);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
selection.ShouldBe(choices[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file sealed class CustomSelectionItem
|
||||||
|
{
|
||||||
|
public int Value { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public CustomSelectionItem(int value, string name)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user