From ef3c020c226b85a0146ee9209d0254a990fc2ecc Mon Sep 17 00:00:00 2001 From: Ross Ferguson Date: Sun, 28 Jun 2020 12:12:34 +0100 Subject: [PATCH] ComboBox. Supports Unicode. Add dropdown arrow from @BDisp. Support PageUp / PageDown --- Terminal.Gui/Views/ComboBox.cs | 166 ++++++++++++-------------- UICatalog/Scenarios/AllViewsTester.cs | 2 +- 2 files changed, 80 insertions(+), 88 deletions(-) diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index c2cc19ee9..41523acb4 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -8,8 +8,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; - using NStack; namespace Terminal.Gui { @@ -30,8 +28,11 @@ namespace Terminal.Gui { get => source; set { source = value; - Search_Changed (""); - SetNeedsDisplay (); + + if(isAdded) { + Search_Changed (""); + SetNeedsDisplay (); + } } } @@ -64,9 +65,8 @@ namespace Terminal.Gui { ustring text = ""; readonly TextField search; readonly ListView listview; - int height; - int width; bool autoHide = true; + bool isAdded; /// /// Public constructor @@ -99,10 +99,7 @@ namespace Terminal.Gui { /// public ComboBox (Rect rect, IList source) : base (rect) { - this.height = rect.Height; - this.width = rect.Width; - - search = new TextField ("") { Width = width }; + search = new TextField ("") { Width = rect.Width }; listview = new ListView (rect, source) { LayoutStyle = LayoutStyle.Computed }; Initialize (); @@ -119,8 +116,14 @@ namespace Terminal.Gui { ColorScheme = Colors.Base; search.TextChanged += Search_Changed; + search.MouseClick += Search_MouseClick; + + listview.Y = Pos.Bottom (search); listview.OpenSelectedItem += (ListViewItemEventArgs a) => Selected (); + this.Add (listview, search); + this.SetFocus (search); + // On resize LayoutComplete += (LayoutEventArgs a) => { search.Width = Bounds.Width; @@ -131,11 +134,18 @@ namespace Terminal.Gui { listview.SelectedItemChanged += (ListViewItemEventArgs e) => { if (searchset.Count > 0) { - SetValue ((ustring)searchset [listview.SelectedItem]); + SetValue (searchset [listview.SelectedItem]); } }; + // This is needed in addition to 'Adding' to trigger the capture the Bounds.Width & Height Application.Loaded += (Application.ResizedEventArgs a) => { + SetNeedsLayout (); + Search_Changed (Text); + }; + + Adding += (View v) => { + // Determine if this view is hosted inside a dialog for (View view = this.SuperView; view != null; view = view.SuperView) { if (view is Dialog) { @@ -144,70 +154,43 @@ namespace Terminal.Gui { } } - ResetSearchSet (); - ColorScheme = autoHide ? Colors.Base : ColorScheme = null; - listview.Y = Pos.Bottom (search); - - if (Width != null && width == 0) { // new ComboBox() { Width = - width = Bounds.Width; - } - - search.Width = width; - listview.Width = CalculateWidth (); - - if (Height != null && height == 0) { // new ComboBox() { Height = - height = Bounds.Height; - } - - listview.Height = CalculatetHeight (); - SetNeedsLayout (); - - if (this.Text != null) { - Search_Changed (Text); - } + Search_Changed (Text); if (autoHide) { listview.ColorScheme = Colors.Menu; } else { search.ColorScheme = Colors.Menu; } + + isAdded = true; }; - - search.MouseClick += Search_MouseClick; - - this.Add (listview, search); - this.SetFocus (search); } -#if COMBO_FEATURE bool isShow = false; -#endif private void Search_MouseClick (MouseEventArgs me) { -#if !COMBO_FEATURE - - if (me.MouseEvent.Flags != MouseFlags.Button1Clicked) - return; -#else if (me.MouseEvent.X == Bounds.Right - 1 && me.MouseEvent.Y == Bounds.Top && me.MouseEvent.Flags == MouseFlags.Button1Pressed - && search.Text == "" && autoHide) { + && search.Text == "" && autoHide) { if (isShow) { HideList (); isShow = false; } else { - searchset = Source.ToList().Cast().ToList(); // force deep copy + // force deep copy + foreach (var item in Source.ToList()) { + searchset.Add (item); + } + ShowList (); isShow = true; } + } else { + SuperView.SetFocus (search); } - else -#endif - SuperView.SetFocus (search); } /// @@ -235,7 +218,6 @@ namespace Terminal.Gui { return true; } -#if COMBO_FEATURE /// public override void Redraw (Rect bounds) { @@ -248,7 +230,7 @@ namespace Terminal.Gui { Move (Bounds.Right - 1, 0); Driver.AddRune (Driver.DownArrow); } -#endif + /// public override bool ProcessKey (KeyEvent e) { @@ -262,9 +244,9 @@ namespace Terminal.Gui { return true; } - if (e.Key == Key.CursorDown && search.HasFocus && listview.SelectedItem == 0 && searchset.Count > 0) { // jump to list + if (e.Key == Key.CursorDown && search.HasFocus && searchset.Count > 0) { // jump to list this.SetFocus (listview); - SetValue ((ustring)searchset [listview.SelectedItem]); + SetValue (searchset [listview.SelectedItem]); return true; } @@ -279,6 +261,16 @@ namespace Terminal.Gui { return true; } + if(e.Key == Key.PageDown) { + listview.MovePageDown (); + return true; + } + + if (e.Key == Key.PageUp) { + listview.MovePageUp (); + return true; + } + if (e.Key == Key.Esc) { this.SetFocus (search); search.Text = text = ""; @@ -298,8 +290,7 @@ namespace Terminal.Gui { /// /// The currently selected list item /// - public new ustring Text - { + public new ustring Text { get { return text; } @@ -308,10 +299,10 @@ namespace Terminal.Gui { } } - private void SetValue (ustring text) + private void SetValue (object text) { search.TextChanged -= Search_Changed; - this.text = search.Text = text; + this.text = search.Text = text.ToString(); search.CursorPosition = 0; search.TextChanged += Search_Changed; } @@ -323,9 +314,10 @@ namespace Terminal.Gui { return; } - SetValue ((ustring)searchset [listview.SelectedItem]); - search.CursorPosition = search.Text.Length; + SetValue (searchset [listview.SelectedItem]); + search.CursorPosition = search.Text.RuneCount; Search_Changed (search.Text); + OnSelectedChanged (); Reset (keepSearchText: true); } @@ -338,7 +330,6 @@ namespace Terminal.Gui { search.Text = text = ""; } - OnSelectedChanged (); ResetSearchSet (); listview.SetSource (searchset); @@ -347,16 +338,20 @@ namespace Terminal.Gui { this.SetFocus (search); } - private void ResetSearchSet () + private void ResetSearchSet (bool noCopy = false) { - if (autoHide) { - if (searchset == null) { - searchset = new List (); - } else { - searchset.Clear (); - } - } else { - searchset = source.ToList (); + if (searchset == null) { + searchset = new List (); + } else { + searchset.Clear (); + } + + if (autoHide || noCopy) + return; + + // force deep copy + foreach (var item in Source.ToList ()) { + searchset.Add (item); } } @@ -366,10 +361,16 @@ namespace Terminal.Gui { return; } - if (string.IsNullOrEmpty (search.Text.ToString ())) { + if (ustring.IsNullOrEmpty (search.Text)) { ResetSearchSet (); } else { - searchset = source.ToList ().Cast ().Where (x => x.ToString ().StartsWith (search.Text.ToString (), StringComparison.CurrentCultureIgnoreCase)).ToList (); + ResetSearchSet (noCopy: true); + + foreach (var item in source.ToList ()) { // Iterate to preserver object type and force deep copy + if (item.ToString().StartsWith (search.Text.ToString(), StringComparison.CurrentCultureIgnoreCase)) { + searchset.Add (item); + } + } } ShowList (); @@ -380,14 +381,12 @@ namespace Terminal.Gui { /// /// /// Consider making public - private void ShowList() + private void ShowList () { listview.SetSource (searchset); listview.Height = CalculatetHeight (); - - listview.Redraw (new Rect (0, 0, width, height)); // for any view behind this + listview.Redraw (new Rect (0, 0, Bounds.Width, Bounds.Height)); // Ensure list shrinks in Dialog this.SuperView?.BringSubviewToFront (this); - } /// @@ -395,28 +394,21 @@ namespace Terminal.Gui { /// /// /// Consider making public - private void HideList() + private void HideList () { Reset (); } - /// - /// Internal width of search list - /// - /// - private int CalculateWidth () - { - return autoHide ? Math.Max (1, width - 1) : width; - } - /// /// Internal height of dynamic search list /// /// private int CalculatetHeight () { - var h = (Height is Dim.DimAbsolute) ? height : Bounds.Height; - return Math.Min (Math.Max (0, h - 1), searchset?.Count ?? 0); + if (Bounds.Height == 0) + return 0; + + return Math.Min (Bounds.Height - 1, searchset?.Count ?? 0); } } } diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index 32bc6f48d..6efe6f820 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -369,7 +369,7 @@ namespace UICatalog { // If the view supports a Source property, set it so we have something to look at if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType().GetProperty("Source").PropertyType == typeof(Terminal.Gui.IListDataSource)) { - var source = new ListWrapper (new List () { ustring.Make ("List Item #1"), ustring.Make ("List Item #2"), ustring.Make ("List Item #3")}); + var source = new ListWrapper (new List () { ustring.Make ("Test Text #1"), ustring.Make ("Test Text #2"), ustring.Make ("Test Text #3") }); view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source }); }