271 #ifndef INCLUDE_STB_TEXTEDIT_H
272 #define INCLUDE_STB_TEXTEDIT_H
283 #ifndef STB_TEXTEDIT_UNDOSTATECOUNT
284 #define STB_TEXTEDIT_UNDOSTATECOUNT 99
286 #ifndef STB_TEXTEDIT_UNDOCHARCOUNT
287 #define STB_TEXTEDIT_UNDOCHARCOUNT 999
289 #ifndef STB_TEXTEDIT_CHARTYPE
290 #define STB_TEXTEDIT_CHARTYPE int
292 #ifndef STB_TEXTEDIT_POSITIONTYPE
293 #define STB_TEXTEDIT_POSITIONTYPE int
364 #endif //INCLUDE_STB_TEXTEDIT_H
377 #ifdef STB_TEXTEDIT_IMPLEMENTATION
379 #ifndef STB_TEXTEDIT_memmove
381 #define STB_TEXTEDIT_memmove memmove
394 int n = STB_TEXTEDIT_STRINGLEN(str);
395 float base_y = 0, prev_x;
404 STB_TEXTEDIT_LAYOUTROW(&r, str, i);
408 if (i==0 && y < base_y + r.
ymin)
411 if (y < base_y + r.
ymax)
431 float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
444 if (STB_TEXTEDIT_GETCHAR(str, i+r.
num_chars-1) == STB_TEXTEDIT_NEWLINE)
453 state->
cursor = stb_text_locate_coord(str, x, y);
462 int p = stb_text_locate_coord(str, x, y);
490 static void stb_textedit_find_charpos(StbFindState *find,
STB_TEXTEDIT_STRING *str,
int n,
int single_line)
494 int z = STB_TEXTEDIT_STRINGLEN(str);
501 STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
503 find->first_char = 0;
512 STB_TEXTEDIT_LAYOUTROW(&r, str, i);
516 find->first_char = i;
518 find->prev_first = prev_start;
527 STB_TEXTEDIT_LAYOUTROW(&r, str, i);
535 find->first_char =
first = i;
538 find->prev_first = prev_start;
543 for (i=0;
first+i <
n; ++i)
544 find->x += STB_TEXTEDIT_GETWIDTH(str,
first, i);
547 #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
552 int n = STB_TEXTEDIT_STRINGLEN(str);
553 if (STB_TEXT_HAS_SELECTION(state)) {
566 stb_text_makeundo_delete(str, state, where, len);
567 STB_TEXTEDIT_DELETECHARS(str, where, len);
574 stb_textedit_clamp(str, state);
575 if (STB_TEXT_HAS_SELECTION(state)) {
600 if (STB_TEXT_HAS_SELECTION(state)) {
601 stb_textedit_sortselection(state);
611 if (STB_TEXT_HAS_SELECTION(state)) {
612 stb_textedit_sortselection(state);
613 stb_textedit_clamp(str, state);
620 #ifdef STB_TEXTEDIT_IS_SPACE
623 return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
626 #ifndef STB_TEXTEDIT_MOVEWORDLEFT
630 while( c >= 0 && !is_word_boundary( str, c ) )
638 #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous
641 #ifndef STB_TEXTEDIT_MOVEWORDRIGHT
644 const int len = STB_TEXTEDIT_STRINGLEN(str);
646 while( c < len && !is_word_boundary( str, c ) )
654 #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next
662 if (!STB_TEXT_HAS_SELECTION(state))
671 if (STB_TEXT_HAS_SELECTION(state)) {
672 stb_textedit_delete_selection(str,state);
684 stb_textedit_clamp(str, state);
685 stb_textedit_delete_selection(str,state);
687 if (STB_TEXTEDIT_INSERTCHARS(str, state->
cursor, text, len)) {
688 stb_text_makeundo_insert(state, state->
cursor, len);
705 int c = STB_TEXTEDIT_KEYTOTEXT(key);
713 if (state->
insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->
cursor < STB_TEXTEDIT_STRINGLEN(str)) {
714 stb_text_makeundo_replace(str, state, state->
cursor, 1, 1);
715 STB_TEXTEDIT_DELETECHARS(str, state->
cursor, 1);
716 if (STB_TEXTEDIT_INSERTCHARS(str, state->
cursor, &ch, 1)) {
721 stb_textedit_delete_selection(str,state);
722 if (STB_TEXTEDIT_INSERTCHARS(str, state->
cursor, &ch, 1)) {
723 stb_text_makeundo_insert(state, state->
cursor, 1);
732 #ifdef STB_TEXTEDIT_K_INSERT
733 case STB_TEXTEDIT_K_INSERT:
738 case STB_TEXTEDIT_K_UNDO:
739 stb_text_undo(str, state);
743 case STB_TEXTEDIT_K_REDO:
744 stb_text_redo(str, state);
748 case STB_TEXTEDIT_K_LEFT:
750 if (STB_TEXT_HAS_SELECTION(state))
751 stb_textedit_move_to_first(state);
758 case STB_TEXTEDIT_K_RIGHT:
760 if (STB_TEXT_HAS_SELECTION(state))
761 stb_textedit_move_to_last(str, state);
764 stb_textedit_clamp(str, state);
768 case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT:
769 stb_textedit_clamp(str, state);
770 stb_textedit_prep_selection_at_cursor(state);
778 #ifdef STB_TEXTEDIT_MOVEWORDLEFT
779 case STB_TEXTEDIT_K_WORDLEFT:
780 if (STB_TEXT_HAS_SELECTION(state))
781 stb_textedit_move_to_first(state);
783 state->
cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->
cursor);
784 stb_textedit_clamp( str, state );
788 case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT:
789 if( !STB_TEXT_HAS_SELECTION( state ) )
790 stb_textedit_prep_selection_at_cursor(state);
792 state->
cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->
cursor);
795 stb_textedit_clamp( str, state );
799 #ifdef STB_TEXTEDIT_MOVEWORDRIGHT
800 case STB_TEXTEDIT_K_WORDRIGHT:
801 if (STB_TEXT_HAS_SELECTION(state))
802 stb_textedit_move_to_last(str, state);
804 state->
cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->
cursor);
805 stb_textedit_clamp( str, state );
809 case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:
810 if( !STB_TEXT_HAS_SELECTION( state ) )
811 stb_textedit_prep_selection_at_cursor(state);
813 state->
cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->
cursor);
816 stb_textedit_clamp( str, state );
820 case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
821 stb_textedit_prep_selection_at_cursor(state);
824 stb_textedit_clamp(str, state);
829 case STB_TEXTEDIT_K_DOWN:
830 case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: {
833 int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
837 key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);
842 stb_textedit_prep_selection_at_cursor(state);
843 else if (STB_TEXT_HAS_SELECTION(state))
844 stb_textedit_move_to_last(str,state);
847 stb_textedit_clamp(str, state);
854 int start = find.first_char + find.length;
856 STB_TEXTEDIT_LAYOUTROW(&row, str, state->
cursor);
859 float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
860 #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
869 stb_textedit_clamp(str, state);
880 case STB_TEXTEDIT_K_UP:
881 case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: {
884 int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
888 key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);
893 stb_textedit_prep_selection_at_cursor(state);
894 else if (STB_TEXT_HAS_SELECTION(state))
895 stb_textedit_move_to_first(state);
898 stb_textedit_clamp(str, state);
902 if (find.prev_first != find.first_char) {
906 state->
cursor = find.prev_first;
907 STB_TEXTEDIT_LAYOUTROW(&row, str, state->
cursor);
910 float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
911 #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
920 stb_textedit_clamp(str, state);
931 case STB_TEXTEDIT_K_DELETE:
932 case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT:
933 if (STB_TEXT_HAS_SELECTION(state))
934 stb_textedit_delete_selection(str, state);
936 int n = STB_TEXTEDIT_STRINGLEN(str);
938 stb_textedit_delete(str, state, state->
cursor, 1);
943 case STB_TEXTEDIT_K_BACKSPACE:
944 case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT:
945 if (STB_TEXT_HAS_SELECTION(state))
946 stb_textedit_delete_selection(str, state);
948 stb_textedit_clamp(str, state);
950 stb_textedit_delete(str, state, state->
cursor-1, 1);
957 #ifdef STB_TEXTEDIT_K_TEXTSTART2
958 case STB_TEXTEDIT_K_TEXTSTART2:
960 case STB_TEXTEDIT_K_TEXTSTART:
965 #ifdef STB_TEXTEDIT_K_TEXTEND2
966 case STB_TEXTEDIT_K_TEXTEND2:
968 case STB_TEXTEDIT_K_TEXTEND:
969 state->
cursor = STB_TEXTEDIT_STRINGLEN(str);
974 #ifdef STB_TEXTEDIT_K_TEXTSTART2
975 case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT:
977 case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT:
978 stb_textedit_prep_selection_at_cursor(state);
983 #ifdef STB_TEXTEDIT_K_TEXTEND2
984 case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT:
986 case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT:
987 stb_textedit_prep_selection_at_cursor(state);
993 #ifdef STB_TEXTEDIT_K_LINESTART2
994 case STB_TEXTEDIT_K_LINESTART2:
996 case STB_TEXTEDIT_K_LINESTART:
997 stb_textedit_clamp(str, state);
998 stb_textedit_move_to_first(state);
1001 else while (state->
cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->
cursor-1) != STB_TEXTEDIT_NEWLINE)
1006 #ifdef STB_TEXTEDIT_K_LINEEND2
1007 case STB_TEXTEDIT_K_LINEEND2:
1009 case STB_TEXTEDIT_K_LINEEND: {
1010 int n = STB_TEXTEDIT_STRINGLEN(str);
1011 stb_textedit_clamp(str, state);
1012 stb_textedit_move_to_first(state);
1015 else while (state->
cursor < n && STB_TEXTEDIT_GETCHAR(str, state->
cursor) != STB_TEXTEDIT_NEWLINE)
1021 #ifdef STB_TEXTEDIT_K_LINESTART2
1022 case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:
1024 case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
1025 stb_textedit_clamp(str, state);
1026 stb_textedit_prep_selection_at_cursor(state);
1029 else while (state->
cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->
cursor-1) != STB_TEXTEDIT_NEWLINE)
1035 #ifdef STB_TEXTEDIT_K_LINEEND2
1036 case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
1038 case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
1039 int n = STB_TEXTEDIT_STRINGLEN(str);
1040 stb_textedit_clamp(str, state);
1041 stb_textedit_prep_selection_at_cursor(state);
1044 else while (state->
cursor < n && STB_TEXTEDIT_GETCHAR(str, state->
cursor) != STB_TEXTEDIT_NEWLINE)
1063 static void stb_textedit_flush_redo(
StbUndoState *state)
1070 static void stb_textedit_discard_undo(
StbUndoState *state)
1092 static void stb_textedit_discard_redo(
StbUndoState *state)
1115 stb_textedit_flush_redo(state);
1120 stb_textedit_discard_undo(state);
1131 stb_textedit_discard_undo(state);
1138 StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
1146 if (insert_len == 0) {
1192 stb_textedit_discard_redo(s);
1277 stb_text_createundo(&state->
undostate, where, 0, length);
1285 for (i=0; i <
length; ++i)
1286 p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
1295 for (i=0; i < old_length; ++i)
1296 p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
1301 static void stb_textedit_clear_state(
STB_TexteditState *state,
int is_single_line)
1313 state->
single_line = (
unsigned char) is_single_line;
1318 static void stb_textedit_initialize_state(
STB_TexteditState *state,
int is_single_line)
1320 stb_textedit_clear_state(state, is_single_line);
1322 #endif//STB_TEXTEDIT_IMPLEMENTATION