public ListPromptState <T> Show(ListPromptTree <T> tree, int requestedPageSize = 15) { if (tree is null) { throw new ArgumentNullException(nameof(tree)); } if (!_console.Profile.Capabilities.Interactive) { throw new NotSupportedException( "Cannot show selection prompt since the current " + "terminal isn't interactive."); } if (!_console.Profile.Capabilities.Ansi) { throw new NotSupportedException( "Cannot show selection prompt since the current " + "terminal does not support ANSI escape sequences."); } var nodes = tree.Traverse().ToList(); var state = new ListPromptState <T>(nodes, _strategy.CalculatePageSize(_console, nodes.Count, requestedPageSize)); var hook = new ListPromptRenderHook <T>(_console, () => BuildRenderable(state)); using (new RenderHookScope(_console, hook)) { _console.Cursor.Hide(); hook.Refresh(); while (true) { var key = _console.Input.ReadKey(true); var result = _strategy.HandleInput(key, state); if (result == ListPromptInputResult.Submit) { break; } if (state.Update(key.Key) || result == ListPromptInputResult.Refresh) { hook.Refresh(); } } } hook.Clear(); _console.Cursor.Show(); return(state); }
private IRenderable BuildRenderable(ListPromptState <T> state) { var pageSize = state.PageSize; var middleOfList = pageSize / 2; var skip = 0; var take = state.ItemCount; var cursorIndex = state.Index; var scrollable = state.ItemCount > pageSize; if (scrollable) { skip = Math.Max(0, state.Index - middleOfList); take = Math.Min(pageSize, state.ItemCount - skip); if (state.ItemCount - state.Index < middleOfList) { // Pointer should be below the end of the list var diff = middleOfList - (state.ItemCount - state.Index); skip -= diff; take += diff; cursorIndex = middleOfList + diff; } else { // Take skip into account cursorIndex -= skip; } } // Build the renderable return(_strategy.Render( _console, scrollable, cursorIndex, state.Items.Skip(skip).Take(take) .Select((node, index) => (index, node)))); }
/// <inheritdoc/> ListPromptInputResult IListPromptStrategy <T> .HandleInput(ConsoleKeyInfo key, ListPromptState <T> state) { if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Spacebar) { // Selecting a non leaf in "leaf mode" is not allowed if (state.Current.IsGroup && Mode == SelectionMode.Leaf) { return(ListPromptInputResult.None); } return(ListPromptInputResult.Submit); } return(ListPromptInputResult.None); }
/// <inheritdoc/> ListPromptInputResult IListPromptStrategy <T> .HandleInput(ConsoleKeyInfo key, ListPromptState <T> state) { if (key.Key == ConsoleKey.Enter) { if (Required && state.Items.None(x => x.Selected)) { // Selection not permitted return(ListPromptInputResult.None); } // Submit return(ListPromptInputResult.Submit); } if (key.Key == ConsoleKey.Spacebar) { var current = state.Items[state.Index]; var select = !current.Selected; if (Mode == SelectionMode.Leaf) { // Select the node and all it's children foreach (var item in current.Traverse(includeSelf: true)) { item.Selected = select; } // Visit every parent and evaluate if it's selection // status need to be updated var parent = current.Parent; while (parent != null) { parent.Selected = parent.Traverse(includeSelf: false).All(x => x.Selected); parent = parent.Parent; } } else { current.Selected = !current.Selected; } // Refresh the list return(ListPromptInputResult.Refresh); } return(ListPromptInputResult.None); }