summaryrefslogtreecommitdiff
path: root/tests/test_tui.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-23 09:24:40 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-23 09:24:40 +0200
commit6159042d0cb3870e6a7c6f8d9a97e175d59e6f80 (patch)
treec6422654298a744b0a791de8f1e8ccb8e1c55071 /tests/test_tui.c
parent53071bb69f5d21b35b8a61c2b1dd18832ce781d4 (diff)
downloadbokf-6159042d0cb3870e6a7c6f8d9a97e175d59e6f80.tar.gz
bokf-6159042d0cb3870e6a7c6f8d9a97e175d59e6f80.zip
tui: 'g' + a letter searches list rows
The goto prompt decides on its first key: digits jump to a row number as before, any other printable key searches the row texts of the list or menu (case-insensitive, also åäö), with up/down stepping through the matches. Keys typed into the prompt no longer reach the screen's key hook, and Enter in the action menu closes an open prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_tui.c')
-rw-r--r--tests/test_tui.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/tests/test_tui.c b/tests/test_tui.c
index 7ab969b..d01a546 100644
--- a/tests/test_tui.c
+++ b/tests/test_tui.c
@@ -453,6 +453,98 @@ static void test_sticky(void)
static const unsigned char SECTION_FLAGS[5] = { 0, 1, 1, 0, 1 };
+static void test_goto_search(void)
+{
+ const char *rows[] = {
+ " x A1 2026-01-01 Golden verifikat",
+ " A2 2026-01-05 Hyra kontor",
+ " A10 2026-02-01 Företagsförsäkring",
+ " F1 2026-02-03 Faktura 1 Testkund AB",
+ " A11 2026-03-01 Hyra kontor mars",
+ };
+ struct tui_list_nav v;
+ nav_init(&v, 5);
+ v.items = rows;
+
+ /* case-insensitive, also for åäö */
+ CHECK(tui_text_match("Företagsförsäkring", "FÖRSÄK"));
+ CHECK(tui_text_match("ÅRSREDOVISNING", "årsred"));
+ CHECK(tui_text_match("abc", ""));
+ CHECK(!tui_text_match("abc", "abd"));
+ CHECK(!tui_text_match(NULL, "a"));
+
+ /* a letter first makes the prompt a search from the top */
+ CHECK(tui_nav_key(&v, 'g', 0, 0, 0, 0) == TUI_NAV_NONE && v.goto_active);
+ CHECK(tui_nav_key(&v, 'h', 0, 0, 0, 0) == TUI_NAV_NONE);
+ CHECK(v.goto_search && v.sel == 1);
+ tui_nav_key(&v, 'y', 0, 0, 0, 0);
+ tui_nav_key(&v, 'r', 0, 0, 0, 0);
+ CHECK(v.sel == 1 && !v.goto_miss);
+ /* down/up step through the matches and wrap */
+ tui_nav_key(&v, KEY_DOWN, 0, 0, 0, 0);
+ CHECK(v.sel == 4);
+ tui_nav_key(&v, KEY_DOWN, 0, 0, 0, 0);
+ CHECK(v.sel == 1);
+ tui_nav_key(&v, KEY_UP, 0, 0, 0, 0);
+ CHECK(v.sel == 4);
+ /* digits, 'g' and 'q' are text once searching */
+ tui_nav_key(&v, KEY_BACKSPACE, 0, 0, 0, 0);
+ tui_nav_key(&v, KEY_BACKSPACE, 0, 0, 0, 0);
+ tui_nav_key(&v, KEY_BACKSPACE, 0, 0, 0, 0);
+ CHECK(!v.goto_search && v.goto_active && v.gotobuf[0] == '\0');
+ tui_nav_key(&v, 'a', 0, 0, 0, 0);
+ tui_nav_key(&v, '1', 0, 0, 0, 0);
+ tui_nav_key(&v, '0', 0, 0, 0, 0);
+ CHECK(v.goto_search && v.sel == 2 && strcmp(v.gotobuf, "a10") == 0);
+ /* no match keeps the selection and flags the miss */
+ tui_nav_key(&v, 'q', 0, 0, 0, 0);
+ CHECK(v.goto_active && v.goto_miss && v.sel == 2);
+ tui_nav_key(&v, KEY_BACKSPACE, 0, 0, 0, 0);
+ CHECK(!v.goto_miss);
+ /* multi-byte input searches once the character is complete */
+ tui_nav_key(&v, 21, 0, 0, 0, 0); /* ^U is ignored in the prompt */
+ while (v.gotobuf[0])
+ tui_nav_key(&v, KEY_BACKSPACE, 0, 0, 0, 0);
+ tui_nav_key(&v, 0xC3, 0, 0, 0, 0); /* "ö" */
+ tui_nav_key(&v, 0xB6, 0, 0, 0, 0);
+ CHECK(v.goto_search && v.sel == 2 && strcmp(v.gotobuf, "ö") == 0);
+ tui_nav_key(&v, KEY_BACKSPACE, 0, 0, 0, 0);
+ CHECK(v.gotobuf[0] == '\0' && !v.goto_search);
+ /* Enter closes without opening; the selection stays */
+ tui_nav_key(&v, 'f', 0, 0, 0, 0);
+ tui_nav_key(&v, '1', 0, 0, 0, 0);
+ CHECK(v.sel == 3);
+ CHECK(tui_nav_key(&v, '\n', 0, 0, 0, 0) == TUI_NAV_NONE);
+ CHECK(!v.goto_active && !v.goto_search && v.gotobuf[0] == '\0' &&
+ v.sel == 3);
+
+ /* digits first still jump to a row number */
+ tui_nav_key(&v, 'g', 0, 0, 0, 0);
+ tui_nav_key(&v, '2', 0, 0, 0, 0);
+ CHECK(!v.goto_search && v.sel == 1);
+ tui_nav_key(&v, 'g', 0, 0, 0, 0); /* 'g' closes a number prompt */
+ CHECK(!v.goto_active);
+
+ /* without row texts a letter does not search */
+ v.items = NULL;
+ tui_nav_key(&v, 'g', 0, 0, 0, 0);
+ tui_nav_key(&v, 'h', 0, 0, 0, 0);
+ CHECK(v.goto_active && !v.goto_search && v.sel == 1);
+ tui_nav_key(&v, 27, 0, 0, 0, 0);
+ CHECK(!v.goto_active);
+
+ /* menu section headers are never a match */
+ const char *menu[] = { "\001Hyra", "Hyra kontor", "Övrigt" };
+ const unsigned char sel[] = { 0, 1, 1 };
+ nav_init(&v, 3);
+ v.items = menu;
+ v.selectable = sel;
+ v.sel = 2;
+ tui_nav_key(&v, 'g', 0, 0, 0, 0);
+ tui_nav_key(&v, 'h', 0, 0, 0, 0);
+ CHECK(v.sel == 1);
+}
+
static void test_nav_sections(void)
{
struct tui_list_nav v;
@@ -1001,6 +1093,7 @@ int main(void)
test_sticky();
test_nav();
test_nav_sections();
+ test_goto_search();
test_form_nav();
test_form_actions();
test_form_focus();