@@ -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