// // TextFieldAutoComplete.cs: TextField with AutoComplete // // Author: // Ross Ferguson (ross.c.ferguson@btinternet.com) // using System; using System.Linq; using System.Collections.Generic; using NStack; namespace Terminal.Gui { /// /// TextField with AutoComplete /// public class TextFieldAutoComplete : View { /// /// Changed event, raised when the selection has been confirmed. /// /// /// Client code can hook up to this event, it is /// raised when the selection has been confirmed. /// public event EventHandler Changed; readonly IList listsource; IList searchset; ustring text = ""; readonly TextField search; readonly ListView listview; readonly int height; readonly int width; readonly bool isAutoHide; /// /// Public constructor /// /// The x coordinate /// The y coordinate /// The width /// The height /// Auto completetion source /// Completetion list hidden until start of typing. Use when hosting in Window as opposed to a Dialog public TextFieldAutoComplete(int x, int y, int w, int h, IList source, bool autoHide = false) { listsource = new List(source); isAutoHide = autoHide; searchset = isAutoHide ? new List () : listsource; height = h; width = w; isAutoHide = autoHide; search = new TextField(x, y, w, ""); search.Changed += Search_Changed; listview = new ListView(new Rect(x, y + 1, w, CalculatetHeight()), listsource.ToList()) { LayoutStyle = LayoutStyle.Computed, ColorScheme = Colors.Dialog }; listview.SelectedChanged += () => { SetValue (searchset [listview.SelectedItem]); }; // Needs to be re-applied for LayoutStyle.Computed listview.X = x; listview.Y = y + 1; listview.Width = w; listview.Height = CalculatetHeight (); this.Add(listview); this.Add(search); this.SetFocus(search); } public override bool OnEnter () { if (!search.HasFocus) this.SetFocus (search); search.CursorPosition = search.Text.Length; return true; } public override bool ProcessKey(KeyEvent e) { if (e.Key == Key.Tab) { base.ProcessKey(e); return false; // allow tab-out to next control } if (e.Key == Key.Enter && listview.HasFocus) { if (listview.Source.Count == 0 || searchset.Count == 0) { text = ""; return true; } SetValue( searchset [listview.SelectedItem]); search.CursorPosition = search.Text.Length; Changed?.Invoke (this, text); searchset.Clear(); listview.SetSource(new List ()); listview.Height = 0; this.SetFocus(search); return true; } if (e.Key == Key.CursorDown && search.HasFocus && listview.SelectedItem == 0) { // jump to list this.SetFocus (listview); SetValue (searchset [listview.SelectedItem]); return true; } if (e.Key == Key.CursorUp && listview.HasFocus && listview.SelectedItem == 0) // jump back to search { search.CursorPosition = search.Text.Length; this.SetFocus (search); return true; } if (e.Key == Key.Esc) { this.SetFocus (search); search.Text = text = ""; Changed?.Invoke (this, search.Text); return true; } // Unix emulation if (e.Key == Key.ControlU) { Reset(); return true; } return base.ProcessKey(e); } /// /// The currenlty selected list item /// public ustring Text { get { return text; } set { search.Text = text = value; } } private void SetValue(ustring text) { search.Changed -= Search_Changed; this.text = search.Text = text; search.CursorPosition = 0; search.Changed += Search_Changed; } /// /// Reset to full original list /// private void Reset() { search.Text = text = ""; Changed?.Invoke (this, search.Text); searchset = isAutoHide ? new List () : listsource; listview.SetSource(searchset.ToList()); listview.Height = CalculatetHeight (); this.SetFocus(search); } private void Search_Changed (object sender, ustring text) { if (string.IsNullOrEmpty (search.Text.ToString())) { searchset = isAutoHide ? new List () : listsource; } else searchset = listsource.Where (x => x.StartsWith (search.Text.ToString (), StringComparison.CurrentCultureIgnoreCase)).ToList (); listview.SetSource (searchset.ToList ()); listview.Height = CalculatetHeight (); listview.Redraw (new Rect (0, 0, width, height)); // for any view behind this } /// /// Internal height of dynamic search list /// /// private int CalculatetHeight () { return Math.Min (height, searchset.Count); } } }