Skip to content

Commit 65b4926

Browse files
authored
Merge pull request #9 from sysprog21/refine-textfield
Extract Textfield helpers
2 parents cec283b + bfc4440 commit 65b4926

File tree

2 files changed

+244
-158
lines changed

2 files changed

+244
-158
lines changed

src/input.c

Lines changed: 160 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -401,16 +401,13 @@ static bool iui_has_selection(const iui_edit_state *state)
401401
return state->selection_start != state->selection_end;
402402
}
403403

404-
/* Process text input with selection support (UTF-8 aware) */
405-
static bool iui_process_text_input_selection(iui_context *ctx,
406-
char *buffer,
407-
size_t buffer_size,
408-
iui_edit_state *state)
404+
/* Clamp cursor and selection to valid UTF-8 boundaries and buffer length */
405+
static void textfield_clamp_state(const char *buffer,
406+
iui_edit_state *state,
407+
size_t len)
409408
{
410-
bool modified = false;
411-
size_t len = strlen(buffer);
412-
413-
/* Clamp cursor to valid UTF-8 boundary */
409+
if (!buffer)
410+
return;
414411
if (state->cursor > len)
415412
state->cursor = len;
416413
while (state->cursor > 0 &&
@@ -420,195 +417,139 @@ static bool iui_process_text_input_selection(iui_context *ctx,
420417
state->selection_start = len;
421418
if (state->selection_end > len)
422419
state->selection_end = len;
420+
}
423421

424-
bool shift_held = (ctx->modifiers & IUI_MOD_SHIFT) != 0;
425-
bool ctrl_held = (ctx->modifiers & IUI_MOD_CTRL) != 0;
422+
/* Handle UTF-8 character insertion at cursor position */
423+
static bool textfield_insert_char(iui_context *ctx,
424+
char *buffer,
425+
size_t buffer_size,
426+
size_t len,
427+
iui_edit_state *state)
428+
{
429+
if (!buffer || !state)
430+
return false;
431+
char utf8_buf[4];
432+
size_t cp_len = iui_utf8_encode(ctx->char_input, utf8_buf);
433+
if (len + cp_len < buffer_size && state->cursor + cp_len < buffer_size) {
434+
memmove(buffer + state->cursor + cp_len, buffer + state->cursor,
435+
len - state->cursor + 1);
436+
memcpy(buffer + state->cursor, utf8_buf, cp_len);
437+
state->cursor += cp_len;
438+
state->selection_start = state->selection_end = state->cursor;
439+
return true;
440+
}
441+
return false;
442+
}
426443

427-
/* Character input - replace selection if active (UTF-8 aware) */
428-
if (ctx->char_input >= 32) {
429-
/* Delete selection first if active */
430-
if (iui_has_selection(state)) {
431-
iui_delete_selection(buffer, state);
432-
len = strlen(buffer);
433-
modified = true;
444+
/* Move cursor left with Ctrl+word skip support */
445+
static void textfield_move_left(const char *buffer,
446+
size_t len,
447+
iui_edit_state *state,
448+
bool ctrl_held)
449+
{
450+
if (!buffer || !state)
451+
return;
452+
if (ctrl_held) {
453+
size_t prev = state->cursor;
454+
while (prev > 0) {
455+
prev = iui_utf8_prev(buffer, prev);
456+
uint32_t cp = iui_utf8_decode(buffer, prev, len);
457+
if (cp != ' ' && cp != '\t')
458+
break;
459+
state->cursor = prev;
434460
}
435-
436-
/* Encode and insert character at cursor */
437-
char utf8_buf[4];
438-
size_t cp_len = iui_utf8_encode(ctx->char_input, utf8_buf);
439-
if (len + cp_len < buffer_size &&
440-
state->cursor + cp_len < buffer_size) {
441-
memmove(buffer + state->cursor + cp_len, buffer + state->cursor,
442-
len - state->cursor + 1);
443-
memcpy(buffer + state->cursor, utf8_buf, cp_len);
444-
state->cursor += cp_len;
445-
state->selection_start = state->selection_end = state->cursor;
446-
modified = true;
461+
while (state->cursor > 0) {
462+
size_t prev = iui_utf8_prev(buffer, state->cursor);
463+
uint32_t cp = iui_utf8_decode(buffer, prev, len);
464+
if (!iui_utf8_is_word_char(cp))
465+
break;
466+
state->cursor = prev;
447467
}
468+
} else {
469+
state->cursor = iui_utf8_prev(buffer, state->cursor);
448470
}
471+
}
449472

450-
/* Key handling with UTF-8 awareness */
451-
switch (ctx->key_pressed) {
452-
case IUI_KEY_BACKSPACE:
453-
if (iui_has_selection(state)) {
454-
modified = iui_delete_selection(buffer, state);
455-
} else if (state->cursor > 0 && len > 0) {
456-
/* Delete previous UTF-8 code point */
457-
size_t prev_pos = iui_utf8_prev(buffer, state->cursor);
458-
memmove(buffer + prev_pos, buffer + state->cursor,
459-
len - state->cursor + 1);
460-
state->cursor = prev_pos;
461-
modified = true;
473+
/* Move cursor right with Ctrl+word skip support */
474+
static void textfield_move_right(const char *buffer,
475+
size_t len,
476+
iui_edit_state *state,
477+
bool ctrl_held)
478+
{
479+
if (!buffer || !state)
480+
return;
481+
if (ctrl_held) {
482+
while (state->cursor < len) {
483+
uint32_t cp = iui_utf8_decode(buffer, state->cursor, len);
484+
if (!iui_utf8_is_word_char(cp))
485+
break;
486+
state->cursor = iui_utf8_next(buffer, state->cursor, len);
462487
}
463-
state->selection_start = state->selection_end = state->cursor;
464-
break;
465-
466-
case IUI_KEY_DELETE:
467-
if (iui_has_selection(state)) {
468-
modified = iui_delete_selection(buffer, state);
469-
} else if (state->cursor < len) {
470-
/* Delete current UTF-8 code point */
471-
size_t next_pos = iui_utf8_next(buffer, state->cursor, len);
472-
memmove(buffer + state->cursor, buffer + next_pos,
473-
len - next_pos + 1);
474-
modified = true;
488+
while (state->cursor < len) {
489+
uint32_t cp = iui_utf8_decode(buffer, state->cursor, len);
490+
if (cp != ' ' && cp != '\t')
491+
break;
492+
state->cursor = iui_utf8_next(buffer, state->cursor, len);
475493
}
476-
state->selection_start = state->selection_end = state->cursor;
477-
break;
494+
} else {
495+
state->cursor = iui_utf8_next(buffer, state->cursor, len);
496+
}
497+
}
478498

479-
case IUI_KEY_LEFT:
499+
/* Handle cursor movement with selection extension */
500+
static void textfield_handle_cursor_movement(const char *buffer,
501+
size_t len,
502+
iui_edit_state *state,
503+
bool shift_held,
504+
bool ctrl_held,
505+
enum iui_key_code key)
506+
{
507+
if (!buffer || !state)
508+
return;
509+
if (key == IUI_KEY_LEFT) {
480510
if (shift_held) {
481-
/* Extend selection left (UTF-8 aware) */
482511
if (state->cursor > 0) {
483-
if (!iui_has_selection(state)) {
512+
if (!iui_has_selection(state))
484513
state->selection_start = state->selection_end =
485514
state->cursor;
486-
}
487-
if (ctrl_held) {
488-
/* Ctrl+Shift+Left: select word left (UTF-8 aware) */
489-
while (state->cursor > 0) {
490-
size_t prev = iui_utf8_prev(buffer, state->cursor);
491-
uint32_t cp = iui_utf8_decode(buffer, prev, len);
492-
if (cp != ' ' && cp != '\t')
493-
break;
494-
state->cursor = prev;
495-
}
496-
while (state->cursor > 0) {
497-
size_t prev = iui_utf8_prev(buffer, state->cursor);
498-
uint32_t cp = iui_utf8_decode(buffer, prev, len);
499-
if (!iui_utf8_is_word_char(cp))
500-
break;
501-
state->cursor = prev;
502-
}
503-
} else {
504-
state->cursor = iui_utf8_prev(buffer, state->cursor);
505-
}
506-
/* Update selection based on cursor movement */
515+
textfield_move_left(buffer, len, state, ctrl_held);
507516
if (state->cursor < state->selection_start)
508517
state->selection_start = state->cursor;
509518
else
510519
state->selection_end = state->cursor;
511520
}
512521
} else {
513-
/* No shift: move cursor, clear selection */
514522
if (iui_has_selection(state)) {
515523
iui_normalize_selection(state);
516524
state->cursor = state->selection_start;
517525
} else if (state->cursor > 0) {
518-
if (ctrl_held) {
519-
/* Ctrl+Left: skip word (UTF-8 aware) */
520-
while (state->cursor > 0) {
521-
size_t prev = iui_utf8_prev(buffer, state->cursor);
522-
uint32_t cp = iui_utf8_decode(buffer, prev, len);
523-
if (cp != ' ' && cp != '\t')
524-
break;
525-
state->cursor = prev;
526-
}
527-
while (state->cursor > 0) {
528-
size_t prev = iui_utf8_prev(buffer, state->cursor);
529-
uint32_t cp = iui_utf8_decode(buffer, prev, len);
530-
if (!iui_utf8_is_word_char(cp))
531-
break;
532-
state->cursor = prev;
533-
}
534-
} else {
535-
state->cursor = iui_utf8_prev(buffer, state->cursor);
536-
}
526+
textfield_move_left(buffer, len, state, ctrl_held);
537527
}
538528
state->selection_start = state->selection_end = state->cursor;
539529
}
540-
break;
541-
542-
case IUI_KEY_RIGHT:
530+
} else if (key == IUI_KEY_RIGHT) {
543531
if (shift_held) {
544-
/* Extend selection right (UTF-8 aware) */
545532
if (state->cursor < len) {
546-
if (!iui_has_selection(state)) {
533+
if (!iui_has_selection(state))
547534
state->selection_start = state->selection_end =
548535
state->cursor;
549-
}
550-
if (ctrl_held) {
551-
/* Ctrl+Shift+Right: select word right (UTF-8 aware) */
552-
while (state->cursor < len) {
553-
uint32_t cp =
554-
iui_utf8_decode(buffer, state->cursor, len);
555-
if (!iui_utf8_is_word_char(cp))
556-
break;
557-
state->cursor =
558-
iui_utf8_next(buffer, state->cursor, len);
559-
}
560-
while (state->cursor < len) {
561-
uint32_t cp =
562-
iui_utf8_decode(buffer, state->cursor, len);
563-
if (cp != ' ' && cp != '\t')
564-
break;
565-
state->cursor =
566-
iui_utf8_next(buffer, state->cursor, len);
567-
}
568-
} else {
569-
state->cursor = iui_utf8_next(buffer, state->cursor, len);
570-
}
571-
/* Update selection based on cursor movement */
536+
textfield_move_right(buffer, len, state, ctrl_held);
572537
if (state->cursor > state->selection_end)
573538
state->selection_end = state->cursor;
574539
else
575540
state->selection_start = state->cursor;
576541
}
577542
} else {
578-
/* No shift: move cursor, clear selection */
579543
if (iui_has_selection(state)) {
580544
iui_normalize_selection(state);
581545
state->cursor = state->selection_end;
582546
} else if (state->cursor < len) {
583-
if (ctrl_held) {
584-
/* Ctrl+Right: skip word (UTF-8 aware) */
585-
while (state->cursor < len) {
586-
uint32_t cp =
587-
iui_utf8_decode(buffer, state->cursor, len);
588-
if (!iui_utf8_is_word_char(cp))
589-
break;
590-
state->cursor =
591-
iui_utf8_next(buffer, state->cursor, len);
592-
}
593-
while (state->cursor < len) {
594-
uint32_t cp =
595-
iui_utf8_decode(buffer, state->cursor, len);
596-
if (cp != ' ' && cp != '\t')
597-
break;
598-
state->cursor =
599-
iui_utf8_next(buffer, state->cursor, len);
600-
}
601-
} else {
602-
state->cursor = iui_utf8_next(buffer, state->cursor, len);
603-
}
547+
textfield_move_right(buffer, len, state, ctrl_held);
604548
}
605549
state->selection_start = state->selection_end = state->cursor;
606550
}
607-
break;
608-
609-
case IUI_KEY_HOME:
551+
} else if (key == IUI_KEY_HOME) {
610552
if (shift_held) {
611-
/* Extend selection to start */
612553
if (!iui_has_selection(state))
613554
state->selection_end = state->cursor;
614555
state->cursor = 0;
@@ -617,11 +558,8 @@ static bool iui_process_text_input_selection(iui_context *ctx,
617558
state->cursor = 0;
618559
state->selection_start = state->selection_end = 0;
619560
}
620-
break;
621-
622-
case IUI_KEY_END:
561+
} else if (key == IUI_KEY_END) {
623562
if (shift_held) {
624-
/* Extend selection to end */
625563
if (!iui_has_selection(state))
626564
state->selection_start = state->cursor;
627565
state->cursor = len;
@@ -630,6 +568,70 @@ static bool iui_process_text_input_selection(iui_context *ctx,
630568
state->cursor = len;
631569
state->selection_start = state->selection_end = len;
632570
}
571+
}
572+
}
573+
574+
/* Process text input with selection support (UTF-8 aware) */
575+
static bool iui_process_text_input_selection(iui_context *ctx,
576+
char *buffer,
577+
size_t buffer_size,
578+
iui_edit_state *state)
579+
{
580+
bool modified = false;
581+
size_t len = strlen(buffer);
582+
583+
textfield_clamp_state(buffer, state, len);
584+
585+
bool shift_held = (ctx->modifiers & IUI_MOD_SHIFT) != 0;
586+
bool ctrl_held = (ctx->modifiers & IUI_MOD_CTRL) != 0;
587+
588+
/* Character input - replace selection if active (UTF-8 aware) */
589+
if (ctx->char_input >= 32) {
590+
if (iui_has_selection(state)) {
591+
iui_delete_selection(buffer, state);
592+
len = strlen(buffer);
593+
modified = true;
594+
}
595+
596+
if (textfield_insert_char(ctx, buffer, buffer_size, len, state))
597+
modified = true;
598+
}
599+
600+
/* Key handling with UTF-8 awareness */
601+
switch (ctx->key_pressed) {
602+
case IUI_KEY_LEFT:
603+
case IUI_KEY_RIGHT:
604+
case IUI_KEY_HOME:
605+
case IUI_KEY_END:
606+
textfield_handle_cursor_movement(buffer, len, state, shift_held,
607+
ctrl_held, ctx->key_pressed);
608+
break;
609+
610+
case IUI_KEY_BACKSPACE:
611+
if (iui_has_selection(state)) {
612+
modified = iui_delete_selection(buffer, state);
613+
} else if (state->cursor > 0 && len > 0) {
614+
/* Delete previous UTF-8 code point */
615+
size_t prev_pos = iui_utf8_prev(buffer, state->cursor);
616+
memmove(buffer + prev_pos, buffer + state->cursor,
617+
len - state->cursor + 1);
618+
state->cursor = prev_pos;
619+
modified = true;
620+
}
621+
state->selection_start = state->selection_end = state->cursor;
622+
break;
623+
624+
case IUI_KEY_DELETE:
625+
if (iui_has_selection(state)) {
626+
modified = iui_delete_selection(buffer, state);
627+
} else if (state->cursor < len) {
628+
/* Delete current UTF-8 code point */
629+
size_t next_pos = iui_utf8_next(buffer, state->cursor, len);
630+
memmove(buffer + state->cursor, buffer + next_pos,
631+
len - next_pos + 1);
632+
modified = true;
633+
}
634+
state->selection_start = state->selection_end = state->cursor;
633635
break;
634636

635637
default:

0 commit comments

Comments
 (0)