feat: add component infrastructure
- add focusable runtime state and invalidate entry - add theme primitives and reusable components - add component checks for focus and cancellation
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
using TinyTUI.Components;
|
||||
using TinyTUI.Input;
|
||||
using TinyTUI.Rendering;
|
||||
using TinyTUI.Text;
|
||||
|
||||
var measurer = new TerminalTextMeasurer();
|
||||
|
||||
var input = new Input(measurer) { Prompt = "> " };
|
||||
AssertFalse(input.Render(20)[0].Contains(CursorMarker.Marker, StringComparison.Ordinal), "unfocused input marker");
|
||||
input.Focused = true;
|
||||
AssertTrue(input.Render(20)[0].Contains(CursorMarker.Marker, StringComparison.Ordinal), "focused input marker");
|
||||
|
||||
var editor = new Editor(measurer) { Placeholder = "type here" };
|
||||
AssertFalse(editor.Render(20)[0].Contains(CursorMarker.Marker, StringComparison.Ordinal), "unfocused editor marker");
|
||||
editor.Focused = true;
|
||||
AssertTrue(editor.Render(20)[0].Contains(CursorMarker.Marker, StringComparison.Ordinal), "focused editor marker");
|
||||
|
||||
var list = new SelectList(measurer);
|
||||
list.SetItems(["alpha", "beta"]);
|
||||
AssertFalse(list.Render(20)[0].Contains(CursorMarker.Marker, StringComparison.Ordinal), "unfocused select marker");
|
||||
list.Focused = true;
|
||||
AssertTrue(list.Render(20)[0].Contains(CursorMarker.Marker, StringComparison.Ordinal), "focused select marker");
|
||||
|
||||
AssertEqual(3, new Spacer(3).Render(10).Count, "spacer line count");
|
||||
AssertEqual(0, new Spacer(-1).Render(10).Count, "spacer clamps negative lines");
|
||||
|
||||
var truncated = new TruncatedText("abcdef\nignored", measurer)
|
||||
{
|
||||
PaddingX = 1,
|
||||
PaddingY = 1,
|
||||
};
|
||||
var truncatedLines = truncated.Render(6);
|
||||
AssertEqual(3, truncatedLines.Count, "truncated text vertical padding");
|
||||
AssertEqual(6, measurer.GetWidth(truncatedLines[1]), "truncated text padded width");
|
||||
AssertTrue(truncatedLines[1].Contains("abc", StringComparison.Ordinal), "truncated text first line only");
|
||||
|
||||
var loader = new CancellableLoader(keybindings: KeybindingRegistry.CreateDefault());
|
||||
var canceled = false;
|
||||
loader.OnCanceled = () => canceled = true;
|
||||
loader.HandleInput(new TuiInputEvent(TuiInputEventKind.Key, KeyNames.Escape));
|
||||
AssertTrue(loader.IsCanceled, "cancellable loader token");
|
||||
AssertTrue(canceled, "cancellable loader callback");
|
||||
|
||||
Console.WriteLine("TinyTUI component checks passed");
|
||||
|
||||
static void AssertEqual<T>(T expected, T actual, string name)
|
||||
{
|
||||
if (!EqualityComparer<T>.Default.Equals(expected, actual))
|
||||
throw new InvalidOperationException($"{name}: expected '{expected}', actual '{actual}'");
|
||||
}
|
||||
|
||||
static void AssertTrue(bool condition, string name)
|
||||
{
|
||||
if (!condition)
|
||||
throw new InvalidOperationException($"{name}: assertion failed");
|
||||
}
|
||||
|
||||
static void AssertFalse(bool condition, string name) => AssertTrue(!condition, name);
|
||||
Reference in New Issue
Block a user