| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- namespace Terminal.Gui.Views;
- /// <summary>Masked text editor that validates input through a <see cref="ITextValidateProvider"/></summary>
- public class TextValidateField : View, IDesignable
- {
- private const int DEFAULT_LENGTH = 10;
- private int _cursorPosition;
- private ITextValidateProvider? _provider;
- /// <summary>
- /// Initializes a new instance of the <see cref="TextValidateField"/> class.
- /// </summary>
- public TextValidateField ()
- {
- Height = Dim.Auto (minimumContentDim: 1);
- CanFocus = true;
- // Things this view knows how to do
- AddCommand (
- Command.LeftStart,
- () =>
- {
- HomeKeyHandler ();
- return true;
- }
- );
- AddCommand (
- Command.RightEnd,
- () =>
- {
- EndKeyHandler ();
- return true;
- }
- );
- AddCommand (
- Command.DeleteCharRight,
- () =>
- {
- DeleteKeyHandler ();
- return true;
- }
- );
- AddCommand (
- Command.DeleteCharLeft,
- () =>
- {
- BackspaceKeyHandler ();
- return true;
- }
- );
- AddCommand (
- Command.Left,
- () =>
- {
- CursorLeft ();
- return true;
- }
- );
- AddCommand (
- Command.Right,
- () =>
- {
- CursorRight ();
- return true;
- }
- );
- // Default keybindings for this view
- KeyBindings.Add (Key.Home, Command.LeftStart);
- KeyBindings.Add (Key.End, Command.RightEnd);
- KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
- KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
- KeyBindings.Add (Key.CursorLeft, Command.Left);
- KeyBindings.Add (Key.CursorRight, Command.Right);
- }
- /// <summary>This property returns true if the input is valid.</summary>
- public virtual bool IsValid
- {
- get
- {
- if (_provider is null)
- {
- return false;
- }
- return _provider.IsValid;
- }
- }
- /// <summary>Provider</summary>
- public ITextValidateProvider? Provider
- {
- get => _provider;
- set
- {
- _provider = value;
- if (_provider!.Fixed)
- {
- Width = _provider.DisplayText == string.Empty
- ? DEFAULT_LENGTH
- : _provider.DisplayText.Length;
- }
- // HomeKeyHandler already call SetNeedsDisplay
- HomeKeyHandler ();
- }
- }
- /// <summary>Text</summary>
- public new string Text
- {
- get
- {
- if (_provider is null)
- {
- return string.Empty;
- }
- return _provider.Text;
- }
- set
- {
- if (_provider is null)
- {
- return;
- }
- _provider.Text = value;
- SetNeedsDraw ();
- }
- }
- /// <inheritdoc/>
- protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
- {
- if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
- {
- int c = _provider!.Cursor (mouseEvent.Position.X - GetMargins (Viewport.Width).left);
- if (_provider.Fixed == false && TextAlignment == Alignment.End && Text.Length > 0)
- {
- c++;
- }
- _cursorPosition = c;
- SetFocus ();
- SetNeedsDraw ();
- return true;
- }
- return false;
- }
- /// <inheritdoc/>
- protected override bool OnDrawingContent (DrawContext? context)
- {
- if (_provider is null)
- {
- Move (0, 0);
- AddStr ("Error: ITextValidateProvider not set!");
- return true;
- }
- VisualRole role = HasFocus ? VisualRole.Focus : VisualRole.Editable;
- Attribute textColor = IsValid ? GetAttributeForRole (role) : SchemeManager.GetScheme (Schemes.Error).GetAttributeForRole (role);
- (int marginLeft, int marginRight) = GetMargins (Viewport.Width);
- Move (0, 0);
- // Left Margin
- SetAttribute (textColor);
- for (var i = 0; i < marginLeft; i++)
- {
- AddRune ((Rune)' ');
- }
- // Content
- SetAttribute (textColor);
- // Content
- for (var i = 0; i < _provider.DisplayText.Length; i++)
- {
- AddRune ((Rune)_provider.DisplayText [i]);
- }
- // Right Margin
- SetAttribute (textColor);
- for (var i = 0; i < marginRight; i++)
- {
- AddRune ((Rune)' ');
- }
- return true;
- }
- /// <inheritdoc/>
- protected override bool OnKeyDownNotHandled (Key key)
- {
- if (_provider is null)
- {
- return false;
- }
- if (key.AsRune == default (Rune) || key == Application.QuitKey)
- {
- return false;
- }
- Rune rune = key.AsRune;
- bool inserted = _provider.InsertAt ((char)rune.Value, _cursorPosition);
- if (inserted)
- {
- CursorRight ();
- return true;
- }
- return false;
- }
- /// <inheritdoc/>
- public override Point? PositionCursor ()
- {
- (int left, _) = GetMargins (Viewport.Width);
- // Fixed = true, is for inputs that have fixed width, like masked ones.
- // Fixed = false, is for normal input.
- // When it's right-aligned and it's a normal input, the cursor behaves differently.
- int curPos;
- if (_provider?.Fixed == false && TextAlignment == Alignment.End)
- {
- curPos = _cursorPosition + left - 1;
- }
- else
- {
- curPos = _cursorPosition + left;
- }
- Move (curPos, 0);
- return new (curPos, 0);
- }
- /// <summary>Delete char at cursor position - 1, moving the cursor.</summary>
- /// <returns></returns>
- private bool BackspaceKeyHandler ()
- {
- if (_provider!.Fixed == false && TextAlignment == Alignment.End && _cursorPosition <= 1)
- {
- return false;
- }
- _cursorPosition = _provider.CursorLeft (_cursorPosition);
- _provider.Delete (_cursorPosition);
- SetNeedsDraw ();
- return true;
- }
- /// <summary>Try to move the cursor to the left.</summary>
- /// <returns>True if moved.</returns>
- private bool CursorLeft ()
- {
- if (_provider is null)
- {
- return false;
- }
- int current = _cursorPosition;
- _cursorPosition = _provider.CursorLeft (_cursorPosition);
- SetNeedsDraw ();
- return current != _cursorPosition;
- }
- /// <summary>Try to move the cursor to the right.</summary>
- /// <returns>True if moved.</returns>
- private bool CursorRight ()
- {
- if (_provider is null)
- {
- return false;
- }
- int current = _cursorPosition;
- _cursorPosition = _provider.CursorRight (_cursorPosition);
- SetNeedsDraw ();
- return current != _cursorPosition;
- }
- /// <summary>Deletes char at current position.</summary>
- /// <returns></returns>
- private bool DeleteKeyHandler ()
- {
- if (_provider!.Fixed == false && TextAlignment == Alignment.End)
- {
- _cursorPosition = _provider.CursorLeft (_cursorPosition);
- }
- _provider.Delete (_cursorPosition);
- SetNeedsDraw ();
- return true;
- }
- /// <summary>Moves the cursor to the last char.</summary>
- /// <returns></returns>
- private bool EndKeyHandler ()
- {
- _cursorPosition = _provider!.CursorEnd ();
- SetNeedsDraw ();
- return true;
- }
- /// <summary>Margins for text alignment.</summary>
- /// <param name="width">Total width</param>
- /// <returns>Left and right margins</returns>
- private (int left, int right) GetMargins (int width)
- {
- int count = Text.Length;
- int total = width - count;
- return TextAlignment switch
- {
- Alignment.Start => (0, total),
- Alignment.Center => (total / 2, total / 2 + total % 2),
- Alignment.End => (total, 0),
- _ => (0, total)
- };
- }
- /// <summary>Moves the cursor to first char.</summary>
- /// <returns></returns>
- private bool HomeKeyHandler ()
- {
- _cursorPosition = _provider!.CursorStart ();
- SetNeedsDraw ();
- return true;
- }
- /// <inheritdoc />
- public bool EnableForDesign ()
- {
- TextRegexProvider provider = new ("^([0-9]?[0-9]?[0-9]|1000)$") { ValidateOnInput = false };
- BorderStyle = LineStyle.Single;
- Title = provider.Pattern;
- Provider = provider;
- Text = "999";
- return true;
- }
- }
|