/// <summary> /// Sets the prefix keys used by the collection from one of the <see cref="MenuLabeling"/> values. /// </summary> /// <param name="label">The set of prefix keys to use.</param> public void SetKeys(MenuLabeling label) { switch (label) { case MenuLabeling.None: SetKeys(new char[0]); break; case MenuLabeling.Numbers: SetKeys(GetRange('1', '9')); break; case MenuLabeling.Letters: SetKeys(GetRange('a', 'z')); break; case MenuLabeling.LettersUpper: SetKeys(GetRange('A', 'Z')); break; case MenuLabeling.NumbersAndLetters: SetKeys(GetRange('1', '9').Concat(GetRange('a', 'z'))); break; case MenuLabeling.NumbersAndLettersUpper: SetKeys(GetRange('1', '9').Concat(GetRange('A', 'Z'))); break; } }
/// <summary> /// Displays a menu where a enumeration value of type <typeparamref name="TEnum"/> can be selected. /// </summary> /// <typeparam name="TEnum">The type of the enum.</typeparam> /// <param name="console">The console on which the action is carried out.</param> /// <param name="keySelector">A function that gets the <see cref="ConsoleString"/> that should be displayed for each enum value.</param> /// <param name="labeling">The type of labeling (option prefix) that should be applied when displaying the menu.</param> /// <param name="cleanup">The cleanup applied after displaying the menu.</param> /// <param name="allowflags">If set to <c>true</c> a combination of values can be selected; otherwise only a single value can be selected. /// <c>null</c> indicates that multiple values can be selected if the type has the <see cref="FlagsAttribute"/>. /// </param> /// <returns>The selected <typeparamref name="TEnum"/> value.</returns> public static TEnum MenuSelectEnum <TEnum>(this IConsole console, Func <TEnum, ConsoleString> keySelector = null, MenuLabeling labeling = MenuLabeling.NumbersAndLetters, MenuCleanup cleanup = MenuCleanup.None, bool?allowflags = null) { var typeinfo = typeof(TEnum).GetTypeInfo(); if (!typeinfo.IsEnum) { throw new ArgumentException($"The {nameof(MenuSelectEnum)} method only support Enum types as type-parameter."); } if (!allowflags.HasValue) { allowflags = typeinfo.GetCustomAttribute <FlagsAttribute>(false) != null; } var values = (TEnum[])Enum.GetValues(typeof(TEnum)); Func <IEnumerable <TEnum>, TEnum> merge = x => { int val = (int)Convert.ChangeType(x.First(), typeof(int)); foreach (var v in x.Skip(1)) { val |= (int)Convert.ChangeType(v, typeof(int)); } return((TEnum)Enum.ToObject(typeof(TEnum), val)); }; if (allowflags.Value) { var selection = console.MenuSelectMultiple(values, isSelectionValid: x => x.Count() >= 1, onKeySelector: keySelector, labeling: labeling, cleanup: cleanup == MenuCleanup.RemoveMenuShowChoice ? MenuCleanup.RemoveMenu : cleanup); if (cleanup == MenuCleanup.RemoveMenuShowChoice) { for (int i = 0; i < selection.Length; i++) { if (i > 0) { console.Render(", "); } console.Render(selection[i].ToString()); } console.WriteLine(); } long val = (long)Convert.ChangeType(selection[0], typeof(long)); for (int i = 1; i < selection.Length; i++) { val |= (long)Convert.ChangeType(selection[i], typeof(long)); } return((TEnum)Enum.ToObject(typeof(TEnum), val)); } else { return(console.MenuSelect(values, keySelector, labeling, cleanup)); } }
/// <summary> /// Displays a menu where a set of elements can be selected from the collection. /// Displays the key part of each element in the menu and returns the selected value part. /// </summary> /// <typeparam name="TKey">The type of the Key part of elements in <paramref name="collection"/>.</typeparam> /// <typeparam name="TValue">The type of the Value part of elements in <paramref name="collection"/>.</typeparam> /// <param name="isSelectionValid">A function that determines if the current selected collection is a valid selection. If the function returns <c>true</c> the done option is enabled.</param> /// <param name="collection">The collection of elements from which the menu should be created.</param> /// <param name="selected">A function that returns a boolean value indicating if an element should be pre-selected when the menu is displayed.</param> /// <param name="labeling">The type of labeling (option prefix) that should be applied when displaying the menu.</param> /// <param name="cleanup">The cleanup applied after displaying the menu.</param> /// <param name="doneText">The <see cref="ConsoleString"/> that is displayed as the bottommost option in the menu. Selecting this option will cause the function to return.</param> /// <returns>The elements that were selected using the displayed menu.</returns> public static TValue[] MenuSelectMultiple <TKey, TValue>(this IConsole console, IEnumerable <KeyValuePair <TKey, TValue> > collection, Func <IEnumerable <TValue>, bool> isSelectionValid = null, Func <KeyValuePair <TKey, TValue>, bool> selected = null, MenuLabeling labeling = MenuLabeling.NumbersAndLetters, MenuCleanup cleanup = MenuCleanup.None, ConsoleString doneText = null) { return(MenuSelectMultiple(console, collection, selection => isSelectionValid(selection.Select(x => x.Value)), null, null, selected, labeling, cleanup, doneText).Select(x => x.Value).ToArray()); }
/// <summary> /// Displays a menu where a single element can be selected from the collection. /// </summary> /// <typeparam name="T">The type of the elements in <paramref name="collection"/>.</typeparam> /// <param name="collection">The collection of element from which the menu should be created.</param> /// <param name="keySelector">A function that gets the <see cref="ConsoleString"/> that should be displayed for an item in the collection.</param> /// <param name="labeling">The type of labeling (option prefix) that should be applied when displaying the menu.</param> /// <param name="cleanup">The cleanup applied after displaying the menu.</param> /// <param name="cancelKey">If not <c>null</c>, this string is displayed as a "cancel option" in the bottom of the menu. /// <paramref name="cancelValue"/> will be returned if this option is selected.</param> /// <param name="cancelValue">The value returned if <paramref name="cancelKey"/> is not <c>null</c>.</param> /// <returns>The element that was selected using the displayed menu.</returns> public static T MenuSelect <T>(this IConsole console, IEnumerable <T> collection, Func <T, ConsoleString> keySelector = null, MenuLabeling labeling = MenuLabeling.NumbersAndLetters, MenuCleanup cleanup = MenuCleanup.None, ConsoleString cancelKey = null, T cancelValue = default(T)) { if (collection == null) { throw new ArgumentNullException(nameof(collection)); } if (!collection.Any()) { throw new ArgumentOutOfRangeException(nameof(collection), $"{nameof(MenuSelect)} can not on be executed on non-empty collections."); } if (keySelector == null) { keySelector = x => x.ToString(); } MenuOption <T> result = null; console.CursorVisible = false; using (var display = new MenuDisplay <MenuOption <T> >(console)) { display.Cleanup = cleanup == MenuCleanup.None ? InputCleanup.None : InputCleanup.Clean; display.PrefixesTop.SetKeys(labeling); foreach (var item in collection) { display.Options.Add(new MenuOption <T>(keySelector(item), item)); } if (cancelKey != null || (cancelValue != null && !cancelValue.Equals(default(T)))) { if (labeling != MenuLabeling.None) { display.PrefixesBottom.SetKeys(new char[] { '0' }); } if (cancelKey == null) { cancelKey = keySelector(cancelValue); } display.Options.Add(new MenuOption <T>(cancelKey, cancelValue)); } display.SelectedIndex = 0; bool done = false; ConsoleKeyInfo info; do { info = console.ReadKey(true); switch (info.Key) { case ConsoleKey.DownArrow: case ConsoleKey.UpArrow: display.HandleKey(info); break; case ConsoleKey.Enter: result = display.Options[display.SelectedIndex]; done = true; break; default: var index = display.IndexFromPrefix(info.KeyChar); if (index >= 0) { display.HandleKey(info); result = display.Options[index]; done = true; } break; } } while (!done); if (cleanup == MenuCleanup.None) { console.SetCursorPosition(display.Origin + new ConsoleSize(0, display.Options.Count)); } } if (cleanup == MenuCleanup.RemoveMenuShowChoice) { console.WriteLine(result.Text); } console.CursorVisible = true; return(result.Value); }
/// <summary> /// Displays a menu where a set of elements can be selected from the collection. /// </summary> /// <typeparam name="T">The type of the elements in <paramref name="collection"/>.</typeparam> /// <param name="isSelectionValid">A function that determines if the current selected collection is a valid selection. If the function returns <c>true</c> the done option is enabled.</param> /// <param name="collection">The collection of element from which the menu should be created.</param> /// <param name="onKeySelector">A function that gets the <see cref="ConsoleString"/> that should be displayed for an item in the collection, when the option is selected.</param> /// <param name="offKeySelector">A function that gets the <see cref="ConsoleString"/> that should be displayed for an item in the collection, when the option is not selected.</param> /// <param name="selected">A function that returns a boolean value indicating if an element should be pre-selected when the menu is displayed.</param> /// <param name="labeling">The type of labeling (option prefix) that should be applied when displaying the menu.</param> /// <param name="cleanup">The cleanup applied after displaying the menu.</param> /// <param name="doneText">The <see cref="ConsoleString"/> that is displayed as the bottommost option in the menu. Selecting this option will cause the function to return.</param> /// <returns>The elements that were selected using the displayed menu.</returns> public static T[] MenuSelectMultiple <T>(this IConsole console, IEnumerable <T> collection, Func <IEnumerable <T>, bool> isSelectionValid = null, Func <T, ConsoleString> onKeySelector = null, Func <T, ConsoleString> offKeySelector = null, Func <T, bool> selected = null, MenuLabeling labeling = MenuLabeling.NumbersAndLetters, MenuCleanup cleanup = MenuCleanup.None, ConsoleString doneText = null) { if (collection == null) { throw new ArgumentNullException(nameof(collection)); } var items = collection.ToArray(); if (!items.Any()) { throw new ArgumentOutOfRangeException(nameof(collection), $"{nameof(MenuSelect)} can not on be executed on non-empty collections."); } if (isSelectionValid == null) { isSelectionValid = x => true; } if (onKeySelector == null) { onKeySelector = x => x.ToString(); } if (offKeySelector == null) { offKeySelector = x => { var text = onKeySelector(x); if (text.HasColors) { return(text.ClearColors()); } else { return($"[DarkGray:{text.Content}]"); } } } ; if (selected == null) { selected = x => false; } List <MenuOnOffOption <T> > result = null; console.CursorVisible = false; using (var display = new MenuDisplay <MenuOnOffOption <T> >(console)) { display.Cleanup = cleanup == MenuCleanup.None ? InputCleanup.None : InputCleanup.Clean; display.PrefixesTop.SetKeys(labeling); display.PrefixesBottom.SetKeys(new char[] { '0' }); foreach (var item in items) { display.Options.Add(new MenuOnOffOption <T>(onKeySelector(item), offKeySelector(item), selected(item), item)); } if (doneText == null) { doneText = "Done"; } var doneOption = new MenuOnOffOption <T>(doneText, $"[DarkGray:{doneText.Content}]", true, default(T)); display.Options.Add(doneOption); doneOption.On = isSelectionValid(display.Options.Where(x => x != doneOption && x.On).Select(x => x.Value)); display.SelectedIndex = 0; bool done = false; ConsoleKeyInfo info; do { info = console.ReadKey(true); switch (info.Key) { case ConsoleKey.DownArrow: case ConsoleKey.UpArrow: display.HandleKey(info); break; case ConsoleKey.Enter: default: var index = info.Key == ConsoleKey.Enter ? display.SelectedIndex : display.IndexFromPrefix(info.KeyChar); if (index >= 0) { var option = display.Options[index]; if (option == doneOption && doneOption.On) { done = true; } else if (option != doneOption) { option.On = !option.On; doneOption.On = isSelectionValid(display.Options.Where(x => x != doneOption && x.On).Select(x => x.Value)); } } break; } } while (!done); result = new List <MenuOnOffOption <T> >(display.Options.Where(x => x != doneOption && x.On)); if (cleanup == MenuCleanup.None) { console.SetCursorPosition(display.Origin + new ConsoleSize(0, display.Options.Count)); } } if (cleanup == MenuCleanup.RemoveMenuShowChoice) { foreach (var r in result) { console.WriteLine(r.Text); } } console.CursorVisible = true; return(result.Select(x => x.Value).ToArray()); }
/// <summary> /// Displays a menu where a single element can be selected from the collection. /// Displays the key part of each element in the menu and returns the selected value part. /// </summary> /// <typeparam name="TKey">The type of the Key part of elements in <paramref name="collection"/>.</typeparam> /// <typeparam name="TValue">The type of the Value part of elements in <paramref name="collection"/>.</typeparam> /// <param name="collection">The collection of elements from which the menu should be created.</param> /// <param name="labeling">The type of labeling (option prefix) that should be applied when displaying the menu.</param> /// <param name="cleanup">The cleanup applied after displaying the menu.</param> /// <param name="cancelKey">If not <c>null</c>, this string is displayed as a "cancel option" in the bottom of the menu. /// <paramref name="cancelValue"/> will be returned if this option is selected.</param> /// <param name="cancelValue">The value returned if <paramref name="cancelKey"/> is not <c>null</c>.</param> /// <returns>The element that was selected using the displayed menu.</returns> public static TValue MenuSelect <TKey, TValue>(IConsole console, IEnumerable <KeyValuePair <TKey, TValue> > collection, MenuLabeling labeling = MenuLabeling.NumbersAndLetters, MenuCleanup cleanup = MenuCleanup.None, ConsoleString cancelKey = null, TValue cancelValue = default(TValue)) { return(MenuSelect(console, collection, x => x.Key.ToString(), labeling, cleanup, cancelKey, new KeyValuePair <TKey, TValue>(default(TKey), cancelValue)).Value); }