From 4bde71a147b0ca8cb7b4b9bcb5af9d903cf8c8d7 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Thu, 1 Oct 2020 15:31:11 -0700 Subject: [PATCH] Revert "Revert "Use glyphs for checkmarks & selection"" This reverts commit db8f591b569a9b30748763f2a3cc5fe872e8dbd4. --- .../CursesDriver/CursesDriver.cs | 10 -- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 27 --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 27 --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 27 --- Terminal.Gui/Core/ConsoleDriver.cs | 52 +++--- Terminal.Gui/Views/Checkbox.cs | 9 +- Terminal.Gui/Views/ListView.cs | 24 ++- Terminal.Gui/Views/Menu.cs | 5 +- UICatalog/Scenarios/ListViewWithSelection.cs | 155 ++++++++++++++++++ 9 files changed, 207 insertions(+), 129 deletions(-) create mode 100644 UICatalog/Scenarios/ListViewWithSelection.cs diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 4ba273444..828dca838 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -531,20 +531,10 @@ namespace Terminal.Gui { RightTee = Curses.ACS_RTEE; TopTee = Curses.ACS_TTEE; BottomTee = Curses.ACS_BTEE; - Checked = '\u221a'; - UnChecked = ' '; - Selected = '\u25cf'; - UnSelected = '\u25cc'; RightArrow = Curses.ACS_RARROW; LeftArrow = Curses.ACS_LARROW; UpArrow = Curses.ACS_UARROW; DownArrow = Curses.ACS_DARROW; - LeftDefaultIndicator = '\u25e6'; - RightDefaultIndicator = '\u25e6'; - LeftBracket = '['; - RightBracket = ']'; - OnMeterSegment = '\u258c'; - OffMeterSegement = ' '; Colors.TopLevel = new ColorScheme (); Colors.Base = new ColorScheme (); diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 11cd1c912..13e670c4f 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -182,33 +182,6 @@ namespace Terminal.Gui { Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red); Colors.Error.HotFocus = Colors.Error.HotNormal; - HLine = '\u2500'; - VLine = '\u2502'; - Stipple = '\u2592'; - Diamond = '\u25c6'; - ULCorner = '\u250C'; - LLCorner = '\u2514'; - URCorner = '\u2510'; - LRCorner = '\u2518'; - LeftTee = '\u251c'; - RightTee = '\u2524'; - TopTee = '\u22a4'; - BottomTee = '\u22a5'; - Checked = '\u221a'; - UnChecked = ' '; - Selected = '\u25cf'; - UnSelected = '\u25cc'; - RightArrow = '\u25ba'; - LeftArrow = '\u25c4'; - UpArrow = '\u25b2'; - DownArrow = '\u25bc'; - LeftDefaultIndicator = '\u25e6'; - RightDefaultIndicator = '\u25e6'; - LeftBracket = '['; - RightBracket = ']'; - OnMeterSegment = '\u258c'; - OffMeterSegement = ' '; - //MockConsole.Clear (); } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 1fe8f01c7..cf2f9e31d 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -152,33 +152,6 @@ namespace Terminal.Gui { Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red); Colors.Error.HotFocus = Colors.Error.HotNormal; Console.Clear (); - - HLine = '\u2500'; - VLine = '\u2502'; - Stipple = '\u2592'; - Diamond = '\u25c6'; - ULCorner = '\u250C'; - LLCorner = '\u2514'; - URCorner = '\u2510'; - LRCorner = '\u2518'; - LeftTee = '\u251c'; - RightTee = '\u2524'; - TopTee = '\u22a4'; - BottomTee = '\u22a5'; - Checked = '\u221a'; - UnChecked = ' '; - Selected = '\u25cf'; - UnSelected = '\u25cc'; - RightArrow = '\u25ba'; - LeftArrow = '\u25c4'; - UpArrow = '\u25b2'; - DownArrow = '\u25bc'; - LeftDefaultIndicator = '\u25e6'; - RightDefaultIndicator = '\u25e6'; - LeftBracket = '['; - RightBracket = ']'; - OnMeterSegment = '\u258c'; - OffMeterSegement = ' '; } public override Attribute MakeAttribute (Color fore, Color back) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index aaf324449..ab2e083b9 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -563,33 +563,6 @@ namespace Terminal.Gui { Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed); Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White); Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed); - - HLine = '\u2500'; - VLine = '\u2502'; - Stipple = '\u2591'; - Diamond = '\u25ca'; - ULCorner = '\u250C'; - LLCorner = '\u2514'; - URCorner = '\u2510'; - LRCorner = '\u2518'; - LeftTee = '\u251c'; - RightTee = '\u2524'; - TopTee = '\u252c'; - BottomTee = '\u2534'; - Checked = '\u221a'; - UnChecked = ' '; - Selected = '\u25cf'; - UnSelected = '\u25cc'; - RightArrow = '\u25ba'; - LeftArrow = '\u25c4'; - UpArrow = '\u25b2'; - DownArrow = '\u25bc'; - LeftDefaultIndicator = '\u25e6'; - RightDefaultIndicator = '\u25e6'; - LeftBracket = '['; - RightBracket = ']'; - OnMeterSegment = '\u258c'; - OffMeterSegement = ' '; } [StructLayout (LayoutKind.Sequential)] diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 612c22c60..2ad8e9954 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -913,132 +913,132 @@ namespace Terminal.Gui { /// /// Horizontal line character. /// - public Rune HLine; + public Rune HLine = '\u2500'; /// /// Vertical line character. /// - public Rune VLine; + public Rune VLine = '\u2502'; /// /// Stipple pattern /// - public Rune Stipple; + public Rune Stipple = '\u2591'; /// /// Diamond character /// - public Rune Diamond; + public Rune Diamond = '\u25ca'; /// /// Upper left corner /// - public Rune ULCorner; + public Rune ULCorner = '\u250C'; /// /// Lower left corner /// - public Rune LLCorner; + public Rune LLCorner = '\u2514'; /// /// Upper right corner /// - public Rune URCorner; + public Rune URCorner = '\u2510'; /// /// Lower right corner /// - public Rune LRCorner; + public Rune LRCorner = '\u2518'; /// /// Left tee /// - public Rune LeftTee; + public Rune LeftTee = '\u251c'; /// /// Right tee /// - public Rune RightTee; + public Rune RightTee = '\u2524'; /// /// Top tee /// - public Rune TopTee; + public Rune TopTee = '\u252c'; /// /// The bottom tee. /// - public Rune BottomTee; + public Rune BottomTee = '\u2534'; /// /// Checkmark. /// - public Rune Checked; + public Rune Checked = '\u221a'; /// /// Un-checked checkmark. /// - public Rune UnChecked; + public Rune UnChecked = '\u2574'; /// /// Selected mark. /// - public Rune Selected; + public Rune Selected = '\u25cf'; /// /// Un-selected selected mark. /// - public Rune UnSelected; + public Rune UnSelected = '\u25cc'; /// /// Right Arrow. /// - public Rune RightArrow; + public Rune RightArrow = '\u25ba'; /// /// Left Arrow. /// - public Rune LeftArrow; + public Rune LeftArrow = '\u25c4'; /// /// Down Arrow. /// - public Rune DownArrow; + public Rune DownArrow = '\u25bc'; /// /// Up Arrow. /// - public Rune UpArrow; + public Rune UpArrow = '\u25b2'; /// /// Left indicator for default action (e.g. for ). /// - public Rune LeftDefaultIndicator; + public Rune LeftDefaultIndicator = '\u25e6'; /// /// Right indicator for default action (e.g. for ). /// - public Rune RightDefaultIndicator; + public Rune RightDefaultIndicator = '\u25e6'; /// /// Left frame/bracket (e.g. '[' for ). /// - public Rune LeftBracket; + public Rune LeftBracket = '['; /// /// Right frame/bracket (e.g. ']' for ). /// - public Rune RightBracket; + public Rune RightBracket = ']'; /// /// On Segment indicator for meter views (e.g. . /// - public Rune OnMeterSegment; + public Rune OnMeterSegment = '\u258c'; /// /// Off Segment indicator for meter views (e.g. . /// - public Rune OffMeterSegement; + public Rune OffMeterSegement = ' '; /// /// Make the attribute for the foreground and background colors. diff --git a/Terminal.Gui/Views/Checkbox.cs b/Terminal.Gui/Views/Checkbox.cs index 898c4658f..8d6b8a9a0 100644 --- a/Terminal.Gui/Views/Checkbox.cs +++ b/Terminal.Gui/Views/Checkbox.cs @@ -115,11 +115,12 @@ namespace Terminal.Gui { { Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); Move (0, 0); - Driver.AddStr (Checked ? "[x] " : "[ ] "); - Move (4, 0); + Driver.AddRune (Checked ? Driver.Checked : Driver.UnChecked); + Driver.AddRune (' '); + Move (2, 0); Driver.AddStr (Text); if (hot_pos != -1) { - Move (4 + hot_pos, 0); + Move (2 + hot_pos, 0); Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal); Driver.AddRune (hot_key); } @@ -128,7 +129,7 @@ namespace Terminal.Gui { /// public override void PositionCursor () { - Move (1, 0); + Move (0, 0); } /// diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 914199dbc..ec8bcfa87 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -180,7 +180,19 @@ namespace Terminal.Gui { /// /// If set to true allows more than one item to be selected. If false only allow one item selected. /// - public bool AllowsMultipleSelection { get; set; } = true; + public bool AllowsMultipleSelection { get => allowsMultipleSelection; + set { + allowsMultipleSelection = value; + if (Source != null && !allowsMultipleSelection) { + // Clear all selections except selected + for (int i = 0; i < Source.Count; i++) { + if (Source.IsMarked (i) && i != selected) { + Source.SetMark (i, false); + } + } + } + } + } /// /// Gets or sets the item that is displayed at the top of the . @@ -293,7 +305,7 @@ namespace Terminal.Gui { } var item = top; bool focused = HasFocus; - int col = allowsMarking ? 4 : 0; + int col = allowsMarking ? 2 : 0; for (int row = 0; row < f.Height; row++, item++) { bool isSelected = item == selected; @@ -310,7 +322,8 @@ namespace Terminal.Gui { Driver.AddRune (' '); } else { if (allowsMarking) { - Driver.AddStr (source.IsMarked (item) ? (AllowsMultipleSelection ? "[x] " : "(o)") : (AllowsMultipleSelection ? "[ ] " : "( )")); + Driver.AddRune (source.IsMarked (item) ? (AllowsMultipleSelection ? Driver.Selected : Driver.Checked) : (AllowsMultipleSelection ? Driver.UnSelected : Driver.UnChecked)); + Driver.AddRune (' '); } Source.Render (this, Driver, isSelected, item, col, row, f.Width - col); } @@ -539,6 +552,7 @@ namespace Terminal.Gui { } int lastSelectedItem = -1; + private bool allowsMultipleSelection = true; /// /// Invokes the SelectedChanged event if it is defined. @@ -595,9 +609,9 @@ namespace Terminal.Gui { public override void PositionCursor () { if (allowsMarking) - Move (1, selected - top); - else Move (0, selected - top); + else + Move (Bounds.Width - 1, selected - top); } /// diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index fb6c31ab9..67d27a85f 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -398,11 +398,10 @@ namespace Terminal.Gui { uncheckedChar = Driver.UnChecked; } - // Support Checked even though CHeckType wasn't set + // Support Checked even though CheckType wasn't set if (item.Checked) { textToDraw = ustring.Make (new Rune [] { checkChar, ' ' }) + item.Title; - } else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || - item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) { + } else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) { textToDraw = ustring.Make (new Rune [] { uncheckedChar, ' ' }) + item.Title; } else { textToDraw = item.Title; diff --git a/UICatalog/Scenarios/ListViewWithSelection.cs b/UICatalog/Scenarios/ListViewWithSelection.cs new file mode 100644 index 000000000..5a0e03daa --- /dev/null +++ b/UICatalog/Scenarios/ListViewWithSelection.cs @@ -0,0 +1,155 @@ +using NStack; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Terminal.Gui; + +namespace UICatalog { + [ScenarioMetadata (Name: "List View With Selection", Description: "ListView with colunns and selection")] + [ScenarioCategory ("Controls")] + class ListViewWithSelection : Scenario { + + public CheckBox _customRenderCB; + public CheckBox _allowMarkingCB; + public CheckBox _allowMultipleCB; + public ListView _listView; + + public List _scenarios = Scenario.GetDerivedClasses().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList (); + + public override void Setup () + { + _customRenderCB = new CheckBox ("Render with columns") { + X = 0, + Y = 0, + Height = 1, + }; + Win.Add (_customRenderCB); + _customRenderCB.Toggled += _customRenderCB_Toggled; ; + + _allowMarkingCB = new CheckBox ("Allow Marking") { + X = Pos.Right (_customRenderCB) + 1, + Y = 0, + Height = 1, + }; + Win.Add (_allowMarkingCB); + _allowMarkingCB.Toggled += AllowMarkingCB_Toggled; + + _allowMultipleCB = new CheckBox ("Allow Multi-Select") { + X = Pos.Right (_allowMarkingCB) + 1, + Y = 0, + Height = 1, + Visible = _allowMarkingCB.Checked + }; + Win.Add (_allowMultipleCB); + _allowMultipleCB.Toggled += AllowMultipleCB_Toggled; + + _listView = new ListView () { + X = 1, + Y = 2, + Height = Dim.Fill (), + Width = Dim.Fill (1), + //ColorScheme = Colors.TopLevel, + AllowsMarking = false, + AllowsMultipleSelection = false + }; + Win.Add (_listView); + + + _listView.SetSource (_scenarios); + + } + + private void _customRenderCB_Toggled (bool prev) + { + if (prev) { + _listView.SetSource (_scenarios); + } else { + _listView.Source = new ScenarioListDataSource (_scenarios); + } + + Win.SetNeedsDisplay (); + } + + private void AllowMarkingCB_Toggled (bool prev) + { + _listView.AllowsMarking = !prev; + _allowMultipleCB.Visible = _listView.AllowsMarking; + Win.SetNeedsDisplay (); + } + + private void AllowMultipleCB_Toggled (bool prev) + { + _listView.AllowsMultipleSelection = !prev; + Win.SetNeedsDisplay (); + } + + // This is basicaly the same implementation used by the UICatalog main window + internal class ScenarioListDataSource : IListDataSource { + int _nameColumnWidth = 30; + private List scenarios; + BitArray marks; + int count; + + public List Scenarios { + get => scenarios; + set { + if (value != null) { + count = value.Count; + marks = new BitArray (count); + scenarios = value; + } + } + } + public bool IsMarked (int item) + { + if (item >= 0 && item < count) + return marks [item]; + return false; + } + + public int Count => Scenarios != null ? Scenarios.Count : 0; + + public ScenarioListDataSource (List itemList) => Scenarios = itemList; + + public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width) + { + container.Move (col, line); + // Equivalent to an interpolated string like $"{Scenarios[item].Name, -widtestname}"; if such a thing were possible + var s = String.Format (String.Format ("{{0,{0}}}", -_nameColumnWidth), Scenario.ScenarioMetadata.GetName (Scenarios [item])); + RenderUstr (driver, $"{s} {Scenario.ScenarioMetadata.GetDescription (Scenarios [item])}", col, line, width); + } + + public void SetMark (int item, bool value) + { + if (item >= 0 && item < count) + marks [item] = value; + } + + // A slightly adapted method from: https://github.com/migueldeicaza/gui.cs/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461 + private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width) + { + int used = 0; + int index = 0; + while (index < ustr.Length) { + (var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length); + var count = Rune.ColumnWidth (rune); + if (used + count >= width) break; + driver.AddRune (rune); + used += count; + index += size; + } + + while (used < width) { + driver.AddRune (' '); + used++; + } + } + + public IList ToList () + { + return Scenarios; + } + } + } +} \ No newline at end of file