using System; using System.Collections.Generic; using System.Linq; namespace Terminal { /// /// Text data entry widget /// /// /// The Entry widget provides Emacs-like editing /// functionality, and mouse support. /// public class TextField : View { string text, kill; int first, point; bool used; /// /// Changed event, raised when the text has clicked. /// /// /// Client code can hook up to this event, it is /// raised when the text in the entry changes. /// public event EventHandler Changed; /// /// Public constructor. /// /// /// public TextField (int x, int y, int w, string s) : base (new Rect (x, y, w, 1)) { if (s == null) s = ""; text = s; point = s.Length; first = point > w ? point - w : 0; CanFocus = true; Color = Colors.Dialog.Focus; } /// /// Sets or gets the text in the entry. /// /// /// public string Text { get { return text; } set { text = value; if (point > text.Length) point = text.Length; first = point > Frame.Width ? point - Frame.Width : 0; SetNeedsDisplay (); } } /// /// Sets the secret property. /// /// /// This makes the text entry suitable for entering passwords. /// public bool Secret { get; set; } Attribute color; /// /// Sets the color attribute to use (includes foreground and background). /// /// The color. public Attribute Color { get => color; set { color = value; SetNeedsDisplay (); } } /// /// The current cursor position. /// public int CursorPosition { get { return point; } } /// /// Sets the cursor position. /// public override void PositionCursor () { Move (point - first, 0); } public override void Redraw (Rect region) { Driver.SetAttribute (Color); Move (0, 0); for (int i = 0; i < Frame.Width; i++) { int p = first + i; if (p < text.Length) { Driver.AddCh (Secret ? '*' : text [p]); } else Driver.AddCh ('_'); } PositionCursor (); } void Adjust () { if (point < first) first = point; else if (first + point >= Frame.Width) first = point - (Frame.Width / 3); SetNeedsDisplay (); } void SetText (string new_text) { text = new_text; if (Changed != null) Changed (this, EventArgs.Empty); } public override bool CanFocus { get => true; set { base.CanFocus = value; } } public override bool ProcessKey (KeyEvent kb) { switch (kb.Key) { case Key.Delete: case Key.Backspace: if (point == 0) return true; SetText (text.Substring (0, point - 1) + text.Substring (point)); point--; Adjust (); break; // Home, C-A case Key.Home: case Key.ControlA: point = 0; Adjust (); break; case Key.CursorLeft: case Key.ControlB: if (point > 0) { point--; Adjust (); } break; case Key.ControlD: // Delete if (point == text.Length) break; SetText (text.Substring (0, point) + text.Substring (point + 1)); Adjust (); break; case Key.ControlE: // End point = text.Length; Adjust (); break; case Key.CursorRight: case Key.ControlF: if (point == text.Length) break; point++; Adjust (); break; case Key.ControlK: // kill-to-end kill = text.Substring (point); SetText (text.Substring (0, point)); Adjust (); break; case Key.ControlY: // Control-y, yank if (kill == null) return true; if (point == text.Length) { SetText (text + kill); point = text.Length; } else { SetText (text.Substring (0, point) + kill + text.Substring (point)); point += kill.Length; } Adjust (); break; case (Key)((int)'b' + Key.AltMask): int bw = WordBackward (point); if (bw != -1) point = bw; Adjust (); break; case (Key)((int)'f' + Key.AltMask): int fw = WordForward (point); if (fw != -1) point = fw; Adjust (); break; default: // Ignore other control characters. if (kb.Key < Key.Space || kb.Key > Key.CharMask) return false; if (used) { if (point == text.Length) { SetText (text + (char)kb.Key); } else { SetText (text.Substring (0, point) + (char)kb.Key + text.Substring (point)); } point++; } else { SetText ("" + (char)kb.Key); first = 0; point = 1; } used = true; Adjust (); return true; } used = true; return true; } int WordForward (int p) { if (p >= text.Length) return -1; int i = p; if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text [p])) { for (; i < text.Length; i++) { if (Char.IsLetterOrDigit (text [i])) break; } for (; i < text.Length; i++) { if (!Char.IsLetterOrDigit (text [i])) break; } } else { for (; i < text.Length; i++) { if (!Char.IsLetterOrDigit (text [i])) break; } } if (i != p) return i; return -1; } int WordBackward (int p) { if (p == 0) return -1; int i = p - 1; if (i == 0) return 0; if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text [i])) { for (; i >= 0; i--) { if (Char.IsLetterOrDigit (text [i])) break; } for (; i >= 0; i--) { if (!Char.IsLetterOrDigit (text [i])) break; } } else { for (; i >= 0; i--) { if (!Char.IsLetterOrDigit (text [i])) break; } } i++; if (i != p) return i; return -1; } #if false public override void ProcessMouse (Curses.MouseEvent ev) { if ((ev.ButtonState & Curses.Event.Button1Clicked) == 0) return; .SetFocus (this); // We could also set the cursor position. point = first + (ev.X - x); if (point > text.Length) point = text.Length; if (point < first) point = 0; SetNeedsDisplay (); } #endif } }