diff --git a/Terminal.Gui/Views/FlagSelector.cs b/Terminal.Gui/Views/FlagSelector.cs index 523565934..fbffb6e68 100644 --- a/Terminal.Gui/Views/FlagSelector.cs +++ b/Terminal.Gui/Views/FlagSelector.cs @@ -24,7 +24,7 @@ public class FlagSelector : View, IOrientation, IDesignable // Accept (Enter key or DoubleClick) - Raise Accept event - DO NOT advance state AddCommand (Command.Accept, HandleAcceptCommand); - CreateSubViews (); + CreateCheckBoxes (); } private bool? HandleAcceptCommand (ICommandContext? ctx) { return RaiseAccepting (ctx); } @@ -101,7 +101,7 @@ public class FlagSelector : View, IOrientation, IDesignable _styles = value; - CreateSubViews (); + CreateCheckBoxes (); } } @@ -112,7 +112,8 @@ public class FlagSelector : View, IOrientation, IDesignable public virtual void SetFlags (IReadOnlyDictionary flags) { Flags = flags; - CreateSubViews (); + CreateCheckBoxes (); + UpdateChecked (); } @@ -170,14 +171,57 @@ public class FlagSelector : View, IOrientation, IDesignable SetFlags (flagsDictionary); } + private IReadOnlyDictionary? _flags; + /// /// Gets the flag values and names. /// - public IReadOnlyDictionary? Flags { get; internal set; } + public IReadOnlyDictionary? Flags + { + get => _flags; + internal set + { + _flags = value; + + if (_value is null) + { + Value = Convert.ToUInt16 (_flags?.Keys.ElementAt (0)); + } + } + } private TextField? ValueEdit { get; set; } - private void CreateSubViews () + private bool _assignHotKeysToCheckBoxes; + + /// + /// If the CheckBoxes will each be automatically assigned a hotkey. + /// will be used to ensure unique keys are assigned. Set + /// before setting with any hotkeys that may conflict with other Views. + /// + public bool AssignHotKeysToCheckBoxes + { + get => _assignHotKeysToCheckBoxes; + set + { + if (_assignHotKeysToCheckBoxes == value) + { + return; + } + _assignHotKeysToCheckBoxes = value; + CreateCheckBoxes (); + UpdateChecked(); + } + } + + /// + /// Gets the list of hotkeys already used by the CheckBoxes or that should not be used if + /// + /// is enabled. + /// + public List UsedHotKeys { get; } = []; + + private void CreateCheckBoxes () { if (Flags is null) { @@ -189,14 +233,14 @@ public class FlagSelector : View, IOrientation, IDesignable cb.Dispose (); } - if (Styles.HasFlag (FlagSelectorStyles.ShowNone) && !Flags.ContainsKey (default!)) + if (Styles.HasFlag (FlagSelectorStyles.ShowNone) && !Flags.ContainsKey (0)) { - Add (CreateCheckBox ("None", default!)); + Add (CreateCheckBox ("None", 0)); } for (var index = 0; index < Flags.Count; index++) { - if (!Styles.HasFlag (FlagSelectorStyles.ShowNone) && Flags.ElementAt (index).Key == default!) + if (!Styles.HasFlag (FlagSelectorStyles.ShowNone) && Flags.ElementAt (index).Key == 0) { continue; } @@ -224,6 +268,7 @@ public class FlagSelector : View, IOrientation, IDesignable } + /// /// /// @@ -232,10 +277,33 @@ public class FlagSelector : View, IOrientation, IDesignable /// protected virtual CheckBox CreateCheckBox (string name, uint flag) { + string nameWithHotKey = name; + if (AssignHotKeysToCheckBoxes) + { + // Find the first char in label that is [a-z], [A-Z], or [0-9] + for (var i = 0; i < name.Length; i++) + { + char c = char.ToLowerInvariant (name [i]); + if (UsedHotKeys.Contains (new (c)) || !char.IsAsciiLetterOrDigit (c)) + { + continue; + } + + if (char.IsAsciiLetterOrDigit (c)) + { + char? hotChar = c; + nameWithHotKey = name.Insert (i, HotKeySpecifier.ToString ()); + UsedHotKeys.Add (new (hotChar)); + + break; + } + } + } + var checkbox = new CheckBox { CanFocus = false, - Title = name, + Title = nameWithHotKey, Id = name, Data = flag, HighlightStyle = HighlightStyle @@ -357,7 +425,7 @@ public class FlagSelector : View, IOrientation, IDesignable f => f switch { FlagSelectorStyles.None => "_No Style", - FlagSelectorStyles.ShowNone => "Show _None Value Style", + FlagSelectorStyles.ShowNone => "_Show None Value Style", FlagSelectorStyles.ShowValueEdit => "Show _Value Editor Style", FlagSelectorStyles.All => "_All Styles", _ => f.ToString () diff --git a/Terminal.Gui/Views/FlagSelectorTEnum.cs b/Terminal.Gui/Views/FlagSelectorTEnum.cs index 8a2b4e747..dab794731 100644 --- a/Terminal.Gui/Views/FlagSelectorTEnum.cs +++ b/Terminal.Gui/Views/FlagSelectorTEnum.cs @@ -5,7 +5,7 @@ namespace Terminal.Gui; /// Provides a user interface for displaying and selecting flags. /// Flags can be set from a dictionary or directly from an enum type. /// -public class FlagSelector : FlagSelector where TEnum : struct, Enum +public sealed class FlagSelector : FlagSelector where TEnum : struct, Enum { /// /// Initializes a new instance of the class. @@ -34,7 +34,7 @@ public class FlagSelector : FlagSelector where TEnum : struct, Enum /// /// /// // Use enum values with custom display names - /// var flagSelector = new FlagSelector(); + /// var flagSelector = new FlagSelector<FlagSelectorStyles>(); /// flagSelector.SetFlagNames(f => f switch { /// FlagSelectorStyles.ShowNone => "Show None Value", /// FlagSelectorStyles.ShowValueEdit => "Show Value Editor", diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index daefd8b44..2484cc445 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -281,23 +281,24 @@ public class RadioGroup : View, IDesignable, IOrientation // Pick a unique hotkey for each radio label for (var labelIndex = 0; labelIndex < value.Length; labelIndex++) { - string label = value [labelIndex]; - string? newLabel = label; + string name = value [labelIndex]; + string? nameWithHotKey = name; if (AssignHotKeysToRadioLabels) { // Find the first char in label that is [a-z], [A-Z], or [0-9] - for (var i = 0; i < label.Length; i++) + for (var i = 0; i < name.Length; i++) { - if (UsedHotKeys.Contains (new (label [i])) || !char.IsAsciiLetterOrDigit (label [i])) + char c = char.ToLowerInvariant (name [i]); + if (UsedHotKeys.Contains (new (c)) || !char.IsAsciiLetterOrDigit (c)) { continue; } - if (char.IsAsciiLetterOrDigit (label [i])) + if (char.IsAsciiLetterOrDigit (c)) { - char? hotChar = label [i]; - newLabel = label.Insert (i, HotKeySpecifier.ToString ()); + char? hotChar = c; + nameWithHotKey = name.Insert (i, HotKeySpecifier.ToString ()); UsedHotKeys.Add (new (hotChar)); break; @@ -305,9 +306,9 @@ public class RadioGroup : View, IDesignable, IOrientation } } - _radioLabels.Add (newLabel); + _radioLabels.Add (nameWithHotKey); - if (TextFormatter.FindHotKey (newLabel, HotKeySpecifier, out _, out Key hotKey)) + if (TextFormatter.FindHotKey (nameWithHotKey, HotKeySpecifier, out _, out Key hotKey)) { AddKeyBindingsForHotKey (Key.Empty, hotKey, labelIndex); } diff --git a/UICatalog/UICatalogTop.cs b/UICatalog/UICatalogTop.cs index 06b932390..5f61e538f 100644 --- a/UICatalog/UICatalogTop.cs +++ b/UICatalog/UICatalogTop.cs @@ -240,12 +240,14 @@ public class UICatalogTop : Toplevel Styles = FlagSelectorStyles.ShowNone, HighlightStyle = HighlightStyle.None, }; + _diagnosticFlagsSelector.UsedHotKeys.Add (Key.D); + _diagnosticFlagsSelector.AssignHotKeysToCheckBoxes = true; _diagnosticFlagsSelector.Value = Diagnostics; _diagnosticFlagsSelector.ValueChanged += (sender, args) => - { - _diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value; - Diagnostics = _diagnosticFlags; - }; + { + _diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value; + Diagnostics = _diagnosticFlags; + }; menuItems.Add ( new MenuItemv2