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
}
}