summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-23 08:56:49 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-23 08:56:49 +0200
commit2ca284619125af0d5c12e9210ff1bfc8f9122739 (patch)
treea304096c2b58492d345e71f205571b913b555a23 /tests
parent278d89825dcb25c4cc55891b84b96b6ca9b84354 (diff)
downloadbokf-2ca284619125af0d5c12e9210ff1bfc8f9122739.tar.gz
bokf-2ca284619125af0d5c12e9210ff1bfc8f9122739.zip
tui: one shared field editor, in-place form editing, value-only focus
Forms edit fields in place in the value column instead of a bottom prompt, highlight only the focused value and scroll when taller than the screen; long text scrolls horizontally instead of wrapping. Fixes: typing after Backspace/Del no longer wipes the field, 'q' is text in row tables, the caret resets between cells, dialogs decode arrow keys, full buffers never take half a UTF-8 character, an invalid amount stays editable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/test_tui.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/tests/test_tui.c b/tests/test_tui.c
index aa062fc..b48544e 100644
--- a/tests/test_tui.c
+++ b/tests/test_tui.c
@@ -153,6 +153,108 @@ static void test_field_fresh(void)
fresh = 1;
CHECK(tui_field_edit(buf, sizeof buf, &pos, KEY_DC, &fresh));
CHECK(strcmp(buf, "bc") == 0);
+
+ /* after Backspace/Del/^U the next printable edits, it never replaces
+ the whole value (the "text disappears" bug) */
+ snprintf(buf, sizeof buf, "Kontor");
+ pos = (size_t)-1;
+ fresh = 1;
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, KEY_BACKSPACE, &fresh));
+ CHECK(fresh == 0);
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, 'x', &fresh));
+ CHECK(strcmp(buf, "Kontox") == 0);
+ snprintf(buf, sizeof buf, "abc");
+ pos = 0;
+ fresh = 1;
+ tui_field_edit(buf, sizeof buf, &pos, KEY_DC, &fresh);
+ tui_field_edit(buf, sizeof buf, &pos, 'x', &fresh);
+ CHECK(strcmp(buf, "xbc") == 0);
+ snprintf(buf, sizeof buf, "abc");
+ pos = (size_t)-1;
+ fresh = 1;
+ tui_field_edit(buf, sizeof buf, &pos, 21, &fresh);
+ tui_field_edit(buf, sizeof buf, &pos, 'x', &fresh);
+ tui_field_edit(buf, sizeof buf, &pos, 'y', &fresh);
+ CHECK(strcmp(buf, "xy") == 0);
+ /* a key the editor ignores keeps the field fresh */
+ snprintf(buf, sizeof buf, "abc");
+ pos = (size_t)-1;
+ fresh = 1;
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, KEY_F(7), &fresh) == 0);
+ CHECK(fresh == 1);
+ /* a multi-byte first key replaces the value once, as one character */
+ snprintf(buf, sizeof buf, "abc");
+ pos = (size_t)-1;
+ fresh = 1;
+ tui_field_edit(buf, sizeof buf, &pos, 0xC3, &fresh);
+ tui_field_edit(buf, sizeof buf, &pos, 0xA5, &fresh);
+ CHECK(strcmp(buf, "å") == 0);
+}
+
+static void test_ledit_utf8_full(void)
+{
+ char buf[4];
+ struct tui_ledit e;
+ snprintf(buf, sizeof buf, "ab");
+ tui_le_init(&e, buf, sizeof buf);
+ /* "å" needs two bytes, only one is free: the whole character is
+ dropped, including its continuation byte */
+ tui_le_key(&e, 0xC3);
+ tui_le_key(&e, 0xA5);
+ CHECK(strcmp(buf, "ab") == 0 && e.len == 2);
+ tui_le_key(&e, 'c');
+ CHECK(strcmp(buf, "abc") == 0);
+ /* a stray continuation byte is never inserted on its own */
+ char b2[8] = "";
+ tui_le_init(&e, b2, sizeof b2);
+ tui_le_key(&e, 0xA5);
+ CHECK(e.len == 0);
+ tui_le_key(&e, 0xC3);
+ tui_le_key(&e, 0xA5);
+ tui_le_key(&e, 0xA5); /* a third byte does not extend a 2-byte "å" */
+ CHECK(strcmp(b2, "å") == 0);
+}
+
+static void test_field_scroll(void)
+{
+ /* fits: no scroll */
+ CHECK(tui_field_scroll("abc", 3, 10, 0) == 0);
+ /* caret at the end of a 10-char text in a 5-column field: 4 chars
+ before the caret, the caret in the last column */
+ CHECK(tui_field_scroll("abcdefghij", 10, 5, 0) == 6);
+ /* moving the caret left inside the view keeps the scroll */
+ CHECK(tui_field_scroll("abcdefghij", 8, 5, 6) == 6);
+ /* moving left past the view scrolls back to the caret */
+ CHECK(tui_field_scroll("abcdefghij", 2, 5, 6) == 2);
+ /* Home */
+ CHECK(tui_field_scroll("abcdefghij", 0, 5, 6) == 0);
+ /* deleted text: scroll back as far as the tail fits */
+ CHECK(tui_field_scroll("abcdef", 6, 5, 4) == 2);
+ CHECK(tui_field_scroll("abc", 3, 5, 2) == 0);
+ /* multi-byte: offsets stay on character boundaries */
+ const char *s = "åäöåäö"; /* 12 bytes, 6 columns */
+ size_t st = tui_field_scroll(s, 12, 4, 0);
+ CHECK(st == 6 && tui_disp_width(s + st) == 3);
+ CHECK(tui_field_scroll(s, 12, 4, 7) == 6); /* mid-character start */
+ /* a stale start past the end (another cell's text) resets */
+ CHECK(tui_field_scroll("ab", 2, 5, 40) == 0);
+ CHECK(tui_field_scroll("", 0, 1, 0) == 0);
+}
+
+static void test_form_scroll(void)
+{
+ /* everything fits */
+ CHECK(tui_form_scroll(0, 3, 5, 10) == 0);
+ /* focus below the view scrolls down just enough */
+ CHECK(tui_form_scroll(0, 12, 20, 10) == 3);
+ /* focus inside the view keeps the offset */
+ CHECK(tui_form_scroll(3, 5, 20, 10) == 3);
+ /* focus above the view scrolls up to it */
+ CHECK(tui_form_scroll(8, 2, 20, 10) == 2);
+ /* never scroll past the last line */
+ CHECK(tui_form_scroll(15, 19, 20, 10) == 10);
+ /* a shrunken form clamps */
+ CHECK(tui_form_scroll(9, 0, 3, 10) == 0);
}
static void date_feed(char *buf, size_t cap, const char *digits)
@@ -202,6 +304,15 @@ static void test_date_field(void)
fresh = 1;
CHECK(tui_date_field_edit(buf, sizeof buf, &pos, 21, &fresh));
CHECK(strcmp(buf, "") == 0);
+
+ /* after Backspace the next digit is inserted, not a fresh date */
+ snprintf(buf, sizeof buf, "2026-03-15");
+ pos = (size_t)-1;
+ fresh = 1;
+ tui_date_field_edit(buf, sizeof buf, &pos, KEY_BACKSPACE, &fresh);
+ CHECK(fresh == 0);
+ tui_date_field_edit(buf, sizeof buf, &pos, '9', &fresh);
+ CHECK(strcmp(buf, "2026-03-19") == 0);
}
static void nav_init(struct tui_list_nav *v, int n)
@@ -635,6 +746,19 @@ static void test_rowtable(void)
CHECK(t.nrows == 1);
tui_rt_clear(&t);
CHECK(t.nrows == 1); /* never below one row */
+
+ /* moving to another cell forgets the old caret and scroll: the next
+ ←/→ works from the end of the new cell */
+ t.nrows = 3;
+ t.row = 0;
+ t.pos = 3;
+ t.hscroll = 5;
+ t.fresh = 0;
+ tui_rt_move(&t, 1, 0);
+ CHECK(t.pos == (size_t)-1 && t.hscroll == 0 && t.fresh == 1);
+ t.pos = 2;
+ tui_rt_tab(&t, 1);
+ CHECK(t.pos == (size_t)-1);
}
static void test_rt_fields(void)
@@ -813,6 +937,9 @@ int main(void)
test_parse_kr();
test_ledit();
test_field_fresh();
+ test_ledit_utf8_full();
+ test_field_scroll();
+ test_form_scroll();
test_date_field();
test_styles();
test_markup();