From e04cbf2b97fe4fe752cb85d9802313505d5b11d8 Mon Sep 17 00:00:00 2001 From: Ross Ferguson Date: Sat, 9 May 2020 07:00:14 +0100 Subject: [PATCH] Feature/TextFieldAutoComplete --- Example/demo.cs | 15 +++ Terminal.Gui/Views/TextFieldAutoComplete.cs | 137 ++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 Terminal.Gui/Views/TextFieldAutoComplete.cs diff --git a/Example/demo.cs b/Example/demo.cs index 6db1bb40b..76e24ddbc 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -1,5 +1,7 @@ using Terminal.Gui; using System; +using System.Linq; +using System.IO; using Mono.Terminal; using System.Collections.Generic; using System.Diagnostics; @@ -410,6 +412,18 @@ static class Demo { } MessageBox.Query (60, 10, "Selected Animals", result == "" ? "No animals selected" : result, "Ok"); } + + static void TextFieldAutoCompleteDemo () + { + var items = Directory.GetFiles (@"..\..\..\Terminal.Gui", "*.cs", SearchOption.AllDirectories).Select (x => Path.GetFileName (x)).ToList (); + var list = new TextFieldAutoComplete (0, 0, 36, 7, items); + list.Changed += (object sender, ustring text) => { Application.RequestStop (); }; + + var d = new Dialog ("Select source file", 40, 12) { list }; + Application.Run (d); + + MessageBox.Query (60, 10, "Selected file", list.Text == null ? "Nothing selected" : list.Text, "Ok"); + } #endregion @@ -540,6 +554,7 @@ static class Demo { new MenuBarItem ("_List Demos", new MenuItem [] { new MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)), new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)), + new MenuItem ("Search Single Item", "", TextFieldAutoCompleteDemo) }), new MenuBarItem ("A_ssorted", new MenuItem [] { new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()), diff --git a/Terminal.Gui/Views/TextFieldAutoComplete.cs b/Terminal.Gui/Views/TextFieldAutoComplete.cs new file mode 100644 index 000000000..3c8c1f210 --- /dev/null +++ b/Terminal.Gui/Views/TextFieldAutoComplete.cs @@ -0,0 +1,137 @@ +// +// 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 { + public event EventHandler Changed; + + readonly IList listsource; + IList searchset; + readonly TextField search; + readonly ListView listview; + readonly int height; + + /// + /// Public constructor + /// + /// The x coordinate + /// The y coordinate + /// The width + /// The height + /// Auto completetion source + public TextFieldAutoComplete(int x, int y, int w, int h, IList source) : base() + { + listsource = searchset = source; + height = h; + search = new TextField(x, y, w, ""); + search.Changed += Search_Changed; + + listview = new ListView(new Rect(x, y + 1, w, Math.Min(height, searchset.Count())), listsource.ToList()) + { + //LayoutStyle = LayoutStyle.Computed, + }; + + this.Add(listview); + this.Add(search); + this.SetFocus(search); + + this.OnEnter += (object sender, EventArgs e) => { this.SetFocus(search); }; + } + + 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) + { + if (Text == null) + return false; // allow tab-out to next control + + search.Text = Text; + search.CursorPosition = search.Text.Length; + searchset.Clear(); + listview.Clear(); + this.SetFocus(search); + + Changed?.Invoke(this, search.Text); + + return true; + } + + if (e.Key == Key.CursorDown && search.HasFocus) // jump to list + { + this.SetFocus(listview); + listview.SelectedItem = 0; + return true; + } + + if(e.Key == Key.CursorUp && listview.SelectedItem == 0 && listview.HasFocus) // jump back to search + { + this.SetFocus(search); + return true; + } + + // Unix emulation + if (e.Key == Key.ControlU || e.Key == Key.Esc) + { + Reset(); + return true; + } + + return base.ProcessKey(e); + } + + /// + /// The currenlty selected list item + /// + public string Text + { + get + { + if (listview.Source.Count == 0 || searchset.Count() == 0) + return search.Text.ToString(); + + return searchset.ToList()[listview.SelectedItem] as string; + } + } + + /// + /// Reset to full original list + /// + private void Reset() + { + search.Text = ""; + searchset = listsource; + listview.SetSource(searchset.ToList()); + this.SetFocus(search); + } + + private void Search_Changed(object sender, ustring e) + { + + if (string.IsNullOrEmpty(search.Text.ToString())) + searchset = listsource; + else + searchset = listsource.Where(x => x.ToLower().Contains(search.Text.ToString().ToLower())).ToList(); + + listview.SetSource(searchset.ToList()); + listview.Height = Math.Min(height, searchset.Count()); + } + } +}