aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-19 23:17:45 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-19 23:17:45 +0200
commit985ae80d7f82cef7c273fae953c73f8bd909ebef (patch)
tree18d4305e2bdd6f0069f431f2dce04a446ef216f2
parent8da6a7e849513e1cb63410d95b121df729260d0c (diff)
downloadbokf-0.1.41.tar.gz
bokf-0.1.41.zip
tui: backspace deletes backwards while editing; esc restores the valuev0.1.41
-rw-r--r--clients/tui.c43
-rw-r--r--docs/TUI-GUIDELINES.md3
-rw-r--r--tests/test_tui.c49
3 files changed, 76 insertions, 19 deletions
diff --git a/clients/tui.c b/clients/tui.c
index 473cc0e..8f410c2 100644
--- a/clients/tui.c
+++ b/clients/tui.c
@@ -306,8 +306,7 @@ int tui_date_field_edit(char *buf, size_t cap, size_t *pos, int ch,
}
if (*fresh) {
- int editing = ch == KEY_BACKSPACE || ch == 127 || ch == 8 ||
- ch == KEY_DC || (ch >= '0' && ch <= '9');
+ int editing = ch >= '0' && ch <= '9';
int moving = ch == KEY_LEFT || ch == KEY_RIGHT || ch == KEY_HOME ||
ch == KEY_END || ch == 1 || ch == 5;
if (editing || moving) {
@@ -394,8 +393,7 @@ int tui_field_edit(char *buf, size_t cap, size_t *pos, int ch, int *fresh)
if (*pos != (size_t)-1 && *pos <= e.len)
e.pos = *pos;
if (*fresh) {
- int editing = ch == KEY_BACKSPACE || ch == 127 || ch == 8 ||
- ch == KEY_DC || (ch >= 32 && ch < 256);
+ int editing = ch >= 32 && ch < 256;
int moving = ch == KEY_LEFT || ch == KEY_RIGHT || ch == KEY_HOME ||
ch == KEY_END || ch == 1 || ch == 5;
if (editing || moving) {
@@ -705,8 +703,9 @@ int tui_confirm(const char *title, const char *fmt, ...)
int tui_prompt_into(char *buf, size_t cap, const char *label,
const char *def, int mask)
{
- if (def && def != buf)
- snprintf(buf, cap, "%s", def);
+ char scratch[512];
+ size_t scap = cap < sizeof scratch ? cap : sizeof scratch;
+ snprintf(scratch, scap, "%s", def ? def : buf);
int y = LINES - 3;
move(y, 2);
@@ -716,16 +715,19 @@ int tui_prompt_into(char *buf, size_t cap, const char *label,
tui_style_reset();
refresh();
int prev = curs_set(1);
- int rc = tui_edit_field(y, (int)strlen(label) + 3, buf, cap, mask);
+ int rc = tui_edit_field(y, (int)strlen(label) + 3, scratch, scap, mask);
curs_set(prev == ERR ? 0 : prev);
+ if (rc != 0)
+ snprintf(buf, cap, "%s", scratch);
return rc != 0;
}
int tui_date_prompt_into(char *buf, size_t cap, const char *label,
const char *def)
{
- if (def && def != buf)
- snprintf(buf, cap, "%s", def);
+ char scratch[16];
+ size_t scap = cap < sizeof scratch ? cap : sizeof scratch;
+ snprintf(scratch, scap, "%s", def ? def : buf);
int y = LINES - 3;
move(y, 2);
@@ -736,26 +738,29 @@ int tui_date_prompt_into(char *buf, size_t cap, const char *label,
size_t pos = (size_t)-1;
int fresh = 1;
int prev = curs_set(1);
+ int rc = 0;
for (;;) {
move(y, (int)strlen(label) + 3);
clrtoeol();
- addnstr(buf, (int)cap);
+ addnstr(scratch, (int)scap);
clrtoeol();
- size_t slen = strlen(buf);
+ size_t slen = strlen(scratch);
size_t cp = pos == (size_t)-1 || pos > slen ? slen : pos;
- move(y, (int)strlen(label) + 3 + (int)tui_disp_width_n(buf, cp));
+ move(y, (int)strlen(label) + 3 + (int)tui_disp_width_n(scratch, cp));
refresh();
int ch = in_key();
if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
- curs_set(prev == ERR ? 0 : prev);
- return 1;
- }
- if (ch == 27) {
- curs_set(prev == ERR ? 0 : prev);
- return 0;
+ rc = 1;
+ break;
}
- tui_date_field_edit(buf, cap, &pos, ch, &fresh);
+ if (ch == 27)
+ break;
+ tui_date_field_edit(scratch, scap, &pos, ch, &fresh);
}
+ curs_set(prev == ERR ? 0 : prev);
+ if (rc)
+ snprintf(buf, cap, "%s", scratch);
+ return rc;
}
int tui_amount_prompt_into(int64_t *out, const char *label, int64_t def_ore)
diff --git a/docs/TUI-GUIDELINES.md b/docs/TUI-GUIDELINES.md
index 631a516..decca09 100644
--- a/docs/TUI-GUIDELINES.md
+++ b/docs/TUI-GUIDELINES.md
@@ -70,6 +70,9 @@ written compactly as `^N`, `^A`, `^C`, `^R` to save width.
`Ctrl+U` clears it (see `field_edit`).
- First keystroke in a freshly focused field replaces its content
(`field_fresh`), so prefilled values like dates can be typed over.
+ Backspace/Del/`^U` still delete normally instead of replacing.
+- A prompt edits a scratch copy: only `Enter` commits it to the field;
+ `Esc` leaves the field exactly as it was.
- Date fields (`date_field_edit`, `date_prompt`) accept digits only and insert
the dashes themselves: type `20260315` and the field shows `2026-03-15`.
Backspace deletes a digit (with its separator), ←/→/Home/End move by digit,
diff --git a/tests/test_tui.c b/tests/test_tui.c
index f4405b4..93aece0 100644
--- a/tests/test_tui.c
+++ b/tests/test_tui.c
@@ -104,6 +104,42 @@ static void test_ledit(void)
CHECK(tui_le_key(&e, KEY_UP) == 0); /* not an editing key */
}
+static void test_field_fresh(void)
+{
+ char buf[16];
+ size_t pos = (size_t)-1;
+ int fresh = 1;
+
+ /* fresh + backspace deletes the last character, it does not clear */
+ snprintf(buf, sizeof buf, "abc");
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, KEY_BACKSPACE, &fresh));
+ CHECK(strcmp(buf, "ab") == 0);
+
+ /* the first printable still replaces the whole prefilled value */
+ snprintf(buf, sizeof buf, "abc");
+ pos = (size_t)-1;
+ fresh = 1;
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, 'x', &fresh));
+ CHECK(strcmp(buf, "x") == 0);
+
+ /* movement keeps the content, clears fresh; the next printable is
+ inserted at the caret */
+ snprintf(buf, sizeof buf, "abc");
+ pos = (size_t)-1;
+ fresh = 1;
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, KEY_LEFT, &fresh));
+ CHECK(strcmp(buf, "abc") == 0 && pos == 2 && fresh == 0);
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, 'x', &fresh));
+ CHECK(strcmp(buf, "abxc") == 0);
+
+ /* fresh + delete removes the character at the caret, no clear */
+ snprintf(buf, sizeof buf, "abc");
+ pos = 0;
+ fresh = 1;
+ CHECK(tui_field_edit(buf, sizeof buf, &pos, KEY_DC, &fresh));
+ CHECK(strcmp(buf, "bc") == 0);
+}
+
static void date_feed(char *buf, size_t cap, const char *digits)
{
size_t pos = (size_t)-1;
@@ -139,6 +175,18 @@ static void test_date_field(void)
fresh = 0;
tui_date_field_edit(buf, sizeof buf, &pos, '9', &fresh);
CHECK(strcmp(buf, "2026-06-09") == 0);
+
+ /* fresh + backspace deletes the last digit; fresh + ^U clears */
+ snprintf(buf, sizeof buf, "2026-06-04");
+ pos = (size_t)-1;
+ fresh = 1;
+ CHECK(tui_date_field_edit(buf, sizeof buf, &pos, KEY_BACKSPACE, &fresh));
+ CHECK(strcmp(buf, "2026-06-0") == 0);
+ snprintf(buf, sizeof buf, "2026-06-04");
+ pos = (size_t)-1;
+ fresh = 1;
+ CHECK(tui_date_field_edit(buf, sizeof buf, &pos, 21, &fresh));
+ CHECK(strcmp(buf, "") == 0);
}
static void nav_init(struct tui_list_nav *v, int n)
@@ -479,6 +527,7 @@ int main(void)
test_formats();
test_parse_kr();
test_ledit();
+ test_field_fresh();
test_date_field();
test_styles();
test_markup();