diff --git a/Core.cs b/Core.cs index 1bc6c1ec6..58b725b9b 100644 --- a/Core.cs +++ b/Core.cs @@ -343,7 +343,7 @@ namespace Terminal { /// /// Utility function to draw strings that contain a hotkey /// - /// String to display, the underscoore before a letter flags the next letter as the hotkey. + /// String to display, the underscoore before a letter flags the next letter as the hotkey. /// Hot color. /// Normal color. public void DrawHotString (string text, Attribute hotColor, Attribute normalColor) @@ -359,6 +359,20 @@ namespace Terminal { } } + /// + /// Utility function to draw strings that contains a hotkey using a colorscheme and the "focused" state. + /// + /// String to display, the underscoore before a letter flags the next letter as the hotkey. + /// If set to true this uses the focused colors from the color scheme, otherwise the regular ones. + /// The color scheme to use. + public void DrawHotString (string text, bool focused, ColorScheme scheme) + { + if (focused) + DrawHotString (text, scheme.HotFocus, scheme.Focus); + else + DrawHotString (text, scheme.HotNormal, scheme.Normal); + } + /// /// This moves the cursor to the specified column and row in the view. /// diff --git a/TODO.md b/TODO.md index ceca82360..ee0860aad 100644 --- a/TODO.md +++ b/TODO.md @@ -29,11 +29,17 @@ Should include another theme, like the TurboPascal 6 theme Replaces `Colors.Base.Normal` with `Attributes.Normal`, and perhaps attributes points to the container. +Widgets should not use Colors.Base or Colors.Dialog, they should likely use +the colors defined in the toplevel container, so that the Dialog vs Toplevel +colors are set there only. + ## Views Checkbox, ListView, Menu. Wanted: +- HotLabels (should be labelsw ith a hotkey that take a focus view as an argument) +- MessageBox - Function Bar - ScrollView - Multi-line text editing @@ -43,6 +49,11 @@ Wanted: - Submenus in menus. - Popup menus - Make windows draggable +- ListView +- TreeView +- View + Attribute for SolidFills? +- Scrollbar +- Frame container (with label) High-level widgets: - Time selector @@ -50,10 +61,18 @@ High-level widgets: - File selector - Masked input +Graphs: +- Progress bar + +Should Views support Padding/Margin/Border? Would make it simpler for Forms backend and perhaps +adopt the Forms CSS as-is + ## Layout manager Unclear what to do about that right now. Perhaps use Flex? +Will at least need the protocol for sizing + # Unicode Needs to move to `ustring` from `NStack.Core` to get full Unicode support. diff --git a/Terminal.csproj b/Terminal.csproj index f0965ea1e..55a6a60ee 100644 --- a/Terminal.csproj +++ b/Terminal.csproj @@ -46,6 +46,7 @@ + diff --git a/Views/RadioGroup.cs b/Views/RadioGroup.cs new file mode 100644 index 000000000..be138eb89 --- /dev/null +++ b/Views/RadioGroup.cs @@ -0,0 +1,143 @@ +using System; +namespace Terminal { + /// + /// Radio group shows a group of labels, only one of those can be selected at a given time + /// + public class RadioGroup : View { + int selected, cursor; + + /// + /// Initializes a new instance of the class + /// setting up the initial set of radio labels and the item that should be selected. + /// + /// Boundaries for the radio group. + /// Radio labels, the strings can contain hotkeys using an undermine before the letter. + /// The item to be selected, the value is clamped to the number of items. + public RadioGroup (Rect rect, string [] radioLabels, int selected = 0) : base (rect) + { + this.selected = selected; + this.radioLabels = radioLabels; + CanFocus = true; + } + + static Rect MakeRect (int x, int y, string [] radioLabels) + { + int width = 0; + + foreach (var s in radioLabels) + width = Math.Max (radioLabels.Length + 4, width); + return new Rect (x, y, width, radioLabels.Length); + } + /// + /// Initializes a new instance of the class + /// setting up the initial set of radio labels and the item that should be selected, + /// the view frame is computed from the provided radioLabels. + /// + /// The x coordinate. + /// The y coordinate. + /// Radio labels, the strings can contain hotkeys using an undermine before the letter. + /// The item to be selected, the value is clamped to the number of items. /// + public RadioGroup (int x, int y, string [] radioLabels, int selected = 0) : this (MakeRect (x, y, radioLabels), radioLabels, selected) + { + } + + string [] radioLabels; + + /// + /// The radio labels to display + /// + /// The radio labels. + public string [] RadioLabels { + get => radioLabels; + set { + radioLabels = value; + selected = 0; + cursor = 0; + SetNeedsDisplay (); + } + } + + public override void Redraw (Rect region) + { + base.Redraw (region); + for (int i = 0; i < radioLabels.Length; i++) { + Move (0, i); + Driver.SetAttribute (Colors.Base.Normal); + Driver.AddStr (i == selected ? "(o) " : "( ) "); + DrawHotString (radioLabels [i], HasFocus && i == cursor, Colors.Base); + } + } + + public override void PositionCursor () + { + Move (1, cursor); + } + + public Action SelectionChanged; + + /// + /// The currently selected item from the list of radio labels + /// + /// The selected. + public int Selected { + get => selected; + set { + selected = value; + SelectionChanged?.Invoke (selected); + SetNeedsDisplay (); + } + } + + public override bool ProcessHotKey (KeyEvent kb) + { + var key = kb.KeyValue; + if (key < Char.MaxValue && Char.IsLetterOrDigit ((char)key)) { + int i = 0; + key = Char.ToUpper ((char)key); + foreach (var l in radioLabels) { + bool nextIsHot = false; + foreach (var c in l) { + if (c == '_') + nextIsHot = true; + else { + if (nextIsHot && c == key) { + Selected = i; + cursor = i; + if (!HasFocus) + SuperView.SetFocus (this); + return true; + } + nextIsHot = false; + } + } + i++; + } + } + return false; + } + + public override bool ProcessKey (KeyEvent kb) + { + switch (kb.Key) { + case Key.CursorUp: + if (cursor > 0) { + cursor--; + SetNeedsDisplay (); + } + return true; + case Key.CursorDown: + if (cursor + 1 < radioLabels.Length) { + cursor++; + SetNeedsDisplay (); + } + return true; + case Key.Space: + Selected = cursor; + return true; + default: + + return false; + } + } + } +} diff --git a/demo.cs b/demo.cs index 01c5d894d..7149cf385 100644 --- a/demo.cs +++ b/demo.cs @@ -18,8 +18,9 @@ class Demo { new Label (3, 4, "Password: "), new TextField (14, 4, 40, "") { Secret = true }, new CheckBox (3, 6, "Remember me"), - new Button (3, 8, "Ok"), - new Button (10, 8, "Cancel"), + new RadioGroup (3, 8, new [] { "_Personal", "_Company" }), + new Button (3, 14, "Ok"), + new Button (10, 14, "Cancel"), new Label (3, 18, "Press ESC and 9 to activate the menubar") ); }