using TinyTUI.Input; using TinyTUI.Text; namespace TinyTUI.Components; /// /// 支持多行文本编辑的基础编辑器组件 /// public sealed partial class Editor(ITextMeasurer? textMeasurer = null) : IInputComponent { private readonly ITextMeasurer _textMeasurer = textMeasurer ?? new TerminalTextMeasurer(); private readonly List _lines = [string.Empty]; private readonly Stack _undoStack = []; private readonly Stack _redoStack = []; private readonly List _history = []; private int _cursorRow; private int _cursorColumn; private int _historyIndex = -1; private string _historyDraft = string.Empty; private int _lastRenderWidth = 80; private int _scrollOffset; private int? _preferredVisualColumn; /// /// 获取或设置编辑器高度 /// public int Height { get; set; } = 5; /// /// 获取或设置是否按可用宽度软换行 /// public bool SoftWrap { get; set; } = true; /// /// 获取或设置空编辑器的输入提示 /// public string Placeholder { get; set; } = string.Empty; /// /// 获取当前编辑器文本 /// public string Value { get => string.Join('\n', _lines); set => SetValue(value); } /// /// 在文本变化时触发 /// public Action? OnChanged { get; set; } /// /// 在用户取消编辑时触发 /// public Action? OnCanceled { get; set; } /// /// 在用户提交编辑内容时触发 /// public Action? OnSubmitted { get; set; } /// public void HandleInput(TuiInputEvent input) { switch (input) { case { Kind: TuiInputEventKind.Text }: Edit(() => InsertText(input.Value)); break; case { Kind: TuiInputEventKind.Paste }: Edit(() => InsertText(input.Value.Replace("\r\n", "\n"))); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl("z"): Undo(); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl("y"): Redo(); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl("c"): Edit(ClearText); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl(KeyNames.Left): MoveWordLeft(); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl(KeyNames.Right): MoveWordRight(); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl(KeyNames.Backspace): Edit(DeleteWordBackward); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl(KeyNames.Delete): Edit(DeleteWordForward); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Enter }: Edit(SplitLine); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl(KeyNames.Enter): Submit(); break; case { Kind: TuiInputEventKind.Key, Value: var key } when key == KeyNames.Ctrl("s"): Submit(); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Backspace }: Edit(Backspace); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Delete }: Edit(Delete); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Left }: MoveLeft(); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Right }: MoveRight(); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Up }: if (!TryNavigateHistory(-1)) MoveVertical(-1); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Down }: if (!TryNavigateHistory(1)) MoveVertical(1); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Home }: MoveToLineStart(); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.End }: MoveToLineEnd(); break; case { Kind: TuiInputEventKind.Key, Value: KeyNames.Escape }: Cancel(); break; } } /// /// 提交当前编辑内容 /// public void Submit() => OnSubmitted?.Invoke(Value); /// /// 取消当前编辑 /// public void Cancel() => OnCanceled?.Invoke(); /// /// 清空编辑器内容 /// public void Clear() => SetValue(string.Empty); }