|
@@ -4,6 +4,9 @@
|
|
|
// Author:
|
|
|
// Ross Ferguson ([email protected])
|
|
|
//
|
|
|
+// TODO:
|
|
|
+// * Completion list auto appears when hosted directly in a Window as opposed to a dialog
|
|
|
+//
|
|
|
|
|
|
using System;
|
|
|
using System.Linq;
|
|
@@ -15,13 +18,22 @@ namespace Terminal.Gui {
|
|
|
/// TextField with AutoComplete
|
|
|
/// </summary>
|
|
|
public class TextFieldAutoComplete : View {
|
|
|
+ /// <summary>
|
|
|
+ /// Changed event, raised when the selection has been confirmed.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// Client code can hook up to this event, it is
|
|
|
+ /// raised when the selection has been confirmed.
|
|
|
+ /// </remarks>
|
|
|
public event EventHandler<ustring> Changed;
|
|
|
|
|
|
readonly IList<string> listsource;
|
|
|
IList<string> searchset;
|
|
|
+ ustring text = "";
|
|
|
readonly TextField search;
|
|
|
readonly ListView listview;
|
|
|
readonly int height;
|
|
|
+ readonly int width;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Public constructor
|
|
@@ -31,23 +43,34 @@ namespace Terminal.Gui {
|
|
|
/// <param name="w">The width</param>
|
|
|
/// <param name="h">The height</param>
|
|
|
/// <param name="source">Auto completetion source</param>
|
|
|
- public TextFieldAutoComplete(int x, int y, int w, int h, IList<string> source) : base()
|
|
|
+ public TextFieldAutoComplete(int x, int y, int w, int h, IList<string> source)
|
|
|
{
|
|
|
listsource = searchset = source;
|
|
|
height = h;
|
|
|
+ width = w;
|
|
|
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,
|
|
|
+ listview = new ListView(new Rect(x, y + 1, w, CalculatetHeight()), listsource.ToList())
|
|
|
+ {
|
|
|
+ LayoutStyle = LayoutStyle.Computed,
|
|
|
+ ColorScheme = Colors.Dialog
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
+ // 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);
|
|
|
|
|
|
- this.OnEnter += (object sender, EventArgs e) => { this.SetFocus(search); };
|
|
|
+ this.OnEnter += (object sender, EventArgs e) => {
|
|
|
+ this.SetFocus(search);
|
|
|
+ search.CursorPosition = search.Text.Length;
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
public override bool ProcessKey(KeyEvent e)
|
|
@@ -57,23 +80,25 @@ namespace Terminal.Gui {
|
|
|
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;
|
|
|
+ if (e.Key == Key.Enter && listview.HasFocus) {
|
|
|
+
|
|
|
+ if (listview.Source.Count == 0 || searchset.Count == 0) {
|
|
|
+ text = "";
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ search.Text = text = searchset [listview.SelectedItem];
|
|
|
search.CursorPosition = search.Text.Length;
|
|
|
+ Changed?.Invoke (this, text);
|
|
|
+
|
|
|
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);
|
|
@@ -87,8 +112,15 @@ namespace Terminal.Gui {
|
|
|
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 || e.Key == Key.Esc)
|
|
|
+ if (e.Key == Key.ControlU)
|
|
|
{
|
|
|
Reset();
|
|
|
return true;
|
|
@@ -100,14 +132,14 @@ namespace Terminal.Gui {
|
|
|
/// <summary>
|
|
|
/// The currenlty selected list item
|
|
|
/// </summary>
|
|
|
- public string Text
|
|
|
+ public ustring Text
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- if (listview.Source.Count == 0 || searchset.Count() == 0)
|
|
|
- return search.Text.ToString();
|
|
|
-
|
|
|
- return searchset.ToList()[listview.SelectedItem] as string;
|
|
|
+ return text;
|
|
|
+ }
|
|
|
+ set {
|
|
|
+ search.Text = text = value;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -116,22 +148,52 @@ namespace Terminal.Gui {
|
|
|
/// </summary>
|
|
|
private void Reset()
|
|
|
{
|
|
|
- search.Text = "";
|
|
|
+ search.Text = text = "";
|
|
|
+ Changed?.Invoke (this, search.Text);
|
|
|
searchset = listsource;
|
|
|
+
|
|
|
listview.SetSource(searchset.ToList());
|
|
|
+ listview.Height = CalculatetHeight ();
|
|
|
+ listview.Redraw (new Rect (0, 0, width, height));
|
|
|
+
|
|
|
this.SetFocus(search);
|
|
|
}
|
|
|
|
|
|
- private void Search_Changed(object sender, ustring e)
|
|
|
+ private void Search_Changed (object sender, ustring text)
|
|
|
{
|
|
|
-
|
|
|
- if (string.IsNullOrEmpty(search.Text.ToString()))
|
|
|
+ // Cannot use text argument as its old value (pre-change)
|
|
|
+ if (string.IsNullOrEmpty (search.Text.ToString())) {
|
|
|
searchset = listsource;
|
|
|
+ }
|
|
|
else
|
|
|
- searchset = listsource.Where(x => x.ToLower().Contains(search.Text.ToString().ToLower())).ToList();
|
|
|
+ searchset = listsource.Where (x => x.StartsWith (search.Text.ToString (), StringComparison.CurrentCultureIgnoreCase)).ToList ();
|
|
|
|
|
|
- listview.SetSource(searchset.ToList());
|
|
|
- listview.Height = Math.Min(height, searchset.Count());
|
|
|
+ listview.SetSource (searchset.ToList ());
|
|
|
+ listview.Height = CalculatetHeight ();
|
|
|
+ listview.Redraw (new Rect (0, 0, width, height));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Internal height of dynamic search list
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ private int CalculatetHeight ()
|
|
|
+ {
|
|
|
+ return Math.Min (height, searchset.Count);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Determine if this view is hosted inside a dialog
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ private bool IsDialogHosted()
|
|
|
+ {
|
|
|
+ for (View v = this.SuperView; v != null; v = v.SuperView) {
|
|
|
+
|
|
|
+ if (v.GetType () == typeof (Dialog))
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
}
|