276 #ifndef INCLUDE_STB_TEXTEDIT_H
277 #define INCLUDE_STB_TEXTEDIT_H
288 #ifndef STB_TEXTEDIT_UNDOSTATECOUNT
289 #define STB_TEXTEDIT_UNDOSTATECOUNT 99
291 #ifndef STB_TEXTEDIT_UNDOCHARCOUNT
292 #define STB_TEXTEDIT_UNDOCHARCOUNT 999
294 #ifndef STB_TEXTEDIT_CHARTYPE
295 #define STB_TEXTEDIT_CHARTYPE int
297 #ifndef STB_TEXTEDIT_POSITIONTYPE
298 #define STB_TEXTEDIT_POSITIONTYPE int
369 #endif //INCLUDE_STB_TEXTEDIT_H
382 #ifdef STB_TEXTEDIT_IMPLEMENTATION
384 #ifndef STB_TEXTEDIT_memmove
386 #define STB_TEXTEDIT_memmove memmove
399 int n = STB_TEXTEDIT_STRINGLEN(str);
400 float base_y = 0, prev_x;
409 STB_TEXTEDIT_LAYOUTROW(&r, str, i);
413 if (i==0 &&
y < base_y + r.
ymin)
416 if (
y < base_y + r.
ymax)
436 float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
449 if (STB_TEXTEDIT_GETCHAR(str, i+r.
num_chars-1) == STB_TEXTEDIT_NEWLINE)
460 if(
state->single_line )
463 STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
467 state->cursor = stb_text_locate_coord(str,
x,
y);
470 state->has_preferred_x = 0;
480 if(
state->single_line )
483 STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
490 p = stb_text_locate_coord(str,
x,
y);
510 int first_char, length;
516 static void stb_textedit_find_charpos(StbFindState *find,
STB_TEXTEDIT_STRING *str,
int n,
int single_line)
520 int z = STB_TEXTEDIT_STRINGLEN(str);
527 STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
529 find->first_char = 0;
538 STB_TEXTEDIT_LAYOUTROW(&r, str, i);
542 find->first_char = i;
544 find->prev_first = prev_start;
553 STB_TEXTEDIT_LAYOUTROW(&r, str, i);
561 find->first_char = first = i;
564 find->prev_first = prev_start;
568 for (i=0; first+i < n; ++i)
569 find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
572 #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
577 int n = STB_TEXTEDIT_STRINGLEN(str);
578 if (STB_TEXT_HAS_SELECTION(
state)) {
579 if (
state->select_start > n)
state->select_start = n;
580 if (
state->select_end > n)
state->select_end = n;
591 stb_text_makeundo_delete(str,
state, where, len);
592 STB_TEXTEDIT_DELETECHARS(str, where, len);
593 state->has_preferred_x = 0;
599 stb_textedit_clamp(str,
state);
600 if (STB_TEXT_HAS_SELECTION(
state)) {
601 if (
state->select_start <
state->select_end) {
608 state->has_preferred_x = 0;
615 if (
state->select_end <
state->select_start) {
616 int temp =
state->select_end;
618 state->select_start = temp;
625 if (STB_TEXT_HAS_SELECTION(
state)) {
626 stb_textedit_sortselection(
state);
629 state->has_preferred_x = 0;
636 if (STB_TEXT_HAS_SELECTION(
state)) {
637 stb_textedit_sortselection(
state);
638 stb_textedit_clamp(str,
state);
641 state->has_preferred_x = 0;
645 #ifdef STB_TEXTEDIT_IS_SPACE
648 return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
651 #ifndef STB_TEXTEDIT_MOVEWORDLEFT
655 while( c >= 0 && !is_word_boundary( str, c ) )
663 #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous
666 #ifndef STB_TEXTEDIT_MOVEWORDRIGHT
669 const int len = STB_TEXTEDIT_STRINGLEN(str);
671 while( c < len && !is_word_boundary( str, c ) )
679 #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next
687 if (!STB_TEXT_HAS_SELECTION(
state))
696 if (STB_TEXT_HAS_SELECTION(
state)) {
697 stb_textedit_delete_selection(str,
state);
698 state->has_preferred_x = 0;
708 stb_textedit_clamp(str,
state);
709 stb_textedit_delete_selection(str,
state);
711 if (STB_TEXTEDIT_INSERTCHARS(str,
state->cursor, text, len)) {
712 stb_text_makeundo_insert(
state,
state->cursor, len);
713 state->cursor += len;
714 state->has_preferred_x = 0;
718 if (
state->undostate.undo_point)
719 --
state->undostate.undo_point;
723 #ifndef STB_TEXTEDIT_KEYTYPE
724 #define STB_TEXTEDIT_KEYTYPE int
733 int c = STB_TEXTEDIT_KEYTOTEXT(key);
738 if (c ==
'\n' &&
state->single_line)
741 if (
state->insert_mode && !STB_TEXT_HAS_SELECTION(
state) &&
state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
742 stb_text_makeundo_replace(str,
state,
state->cursor, 1, 1);
743 STB_TEXTEDIT_DELETECHARS(str,
state->cursor, 1);
744 if (STB_TEXTEDIT_INSERTCHARS(str,
state->cursor, &ch, 1)) {
746 state->has_preferred_x = 0;
749 stb_textedit_delete_selection(str,
state);
750 if (STB_TEXTEDIT_INSERTCHARS(str,
state->cursor, &ch, 1)) {
751 stb_text_makeundo_insert(
state,
state->cursor, 1);
753 state->has_preferred_x = 0;
760 #ifdef STB_TEXTEDIT_K_INSERT
761 case STB_TEXTEDIT_K_INSERT:
767 stb_text_undo(str,
state);
768 state->has_preferred_x = 0;
772 stb_text_redo(str,
state);
773 state->has_preferred_x = 0;
778 if (STB_TEXT_HAS_SELECTION(
state))
779 stb_textedit_move_to_first(
state);
781 if (
state->cursor > 0)
783 state->has_preferred_x = 0;
788 if (STB_TEXT_HAS_SELECTION(
state))
789 stb_textedit_move_to_last(str,
state);
792 stb_textedit_clamp(str,
state);
793 state->has_preferred_x = 0;
797 stb_textedit_clamp(str,
state);
798 stb_textedit_prep_selection_at_cursor(
state);
800 if (
state->select_end > 0)
803 state->has_preferred_x = 0;
806 #ifdef STB_TEXTEDIT_MOVEWORDLEFT
808 if (STB_TEXT_HAS_SELECTION(
state))
809 stb_textedit_move_to_first(
state);
812 stb_textedit_clamp( str,
state );
817 if( !STB_TEXT_HAS_SELECTION(
state ) )
818 stb_textedit_prep_selection_at_cursor(
state);
823 stb_textedit_clamp( str,
state );
827 #ifdef STB_TEXTEDIT_MOVEWORDRIGHT
829 if (STB_TEXT_HAS_SELECTION(
state))
830 stb_textedit_move_to_last(str,
state);
833 stb_textedit_clamp( str,
state );
838 if( !STB_TEXT_HAS_SELECTION(
state ) )
839 stb_textedit_prep_selection_at_cursor(
state);
844 stb_textedit_clamp( str,
state );
849 stb_textedit_prep_selection_at_cursor(
state);
852 stb_textedit_clamp(str,
state);
854 state->has_preferred_x = 0;
863 if (
state->single_line) {
870 stb_textedit_prep_selection_at_cursor(
state);
871 else if (STB_TEXT_HAS_SELECTION(
state))
872 stb_textedit_move_to_last(str,
state);
875 stb_textedit_clamp(str,
state);
876 stb_textedit_find_charpos(&find, str,
state->cursor,
state->single_line);
880 float goal_x =
state->has_preferred_x ?
state->preferred_x : find.x;
882 int start = find.first_char + find.length;
883 state->cursor = start;
884 STB_TEXTEDIT_LAYOUTROW(&row, str,
state->cursor);
887 float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
888 #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
897 stb_textedit_clamp(str,
state);
899 state->has_preferred_x = 1;
900 state->preferred_x = goal_x;
914 if (
state->single_line) {
921 stb_textedit_prep_selection_at_cursor(
state);
922 else if (STB_TEXT_HAS_SELECTION(
state))
923 stb_textedit_move_to_first(
state);
926 stb_textedit_clamp(str,
state);
927 stb_textedit_find_charpos(&find, str,
state->cursor,
state->single_line);
930 if (find.prev_first != find.first_char) {
932 float goal_x =
state->has_preferred_x ?
state->preferred_x : find.x;
934 state->cursor = find.prev_first;
935 STB_TEXTEDIT_LAYOUTROW(&row, str,
state->cursor);
938 float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
939 #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
948 stb_textedit_clamp(str,
state);
950 state->has_preferred_x = 1;
951 state->preferred_x = goal_x;
961 if (STB_TEXT_HAS_SELECTION(
state))
962 stb_textedit_delete_selection(str,
state);
964 int n = STB_TEXTEDIT_STRINGLEN(str);
965 if (
state->cursor < n)
966 stb_textedit_delete(str,
state,
state->cursor, 1);
968 state->has_preferred_x = 0;
973 if (STB_TEXT_HAS_SELECTION(
state))
974 stb_textedit_delete_selection(str,
state);
976 stb_textedit_clamp(str,
state);
977 if (
state->cursor > 0) {
978 stb_textedit_delete(str,
state,
state->cursor-1, 1);
982 state->has_preferred_x = 0;
985 #ifdef STB_TEXTEDIT_K_TEXTSTART2
986 case STB_TEXTEDIT_K_TEXTSTART2:
990 state->has_preferred_x = 0;
993 #ifdef STB_TEXTEDIT_K_TEXTEND2
994 case STB_TEXTEDIT_K_TEXTEND2:
997 state->cursor = STB_TEXTEDIT_STRINGLEN(str);
999 state->has_preferred_x = 0;
1002 #ifdef STB_TEXTEDIT_K_TEXTSTART2
1006 stb_textedit_prep_selection_at_cursor(
state);
1008 state->has_preferred_x = 0;
1011 #ifdef STB_TEXTEDIT_K_TEXTEND2
1015 stb_textedit_prep_selection_at_cursor(
state);
1016 state->cursor =
state->select_end = STB_TEXTEDIT_STRINGLEN(str);
1017 state->has_preferred_x = 0;
1021 #ifdef STB_TEXTEDIT_K_LINESTART2
1022 case STB_TEXTEDIT_K_LINESTART2:
1025 stb_textedit_clamp(str,
state);
1026 stb_textedit_move_to_first(
state);
1027 if (
state->single_line)
1029 else while (
state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str,
state->cursor-1) != STB_TEXTEDIT_NEWLINE)
1031 state->has_preferred_x = 0;
1034 #ifdef STB_TEXTEDIT_K_LINEEND2
1035 case STB_TEXTEDIT_K_LINEEND2:
1038 int n = STB_TEXTEDIT_STRINGLEN(str);
1039 stb_textedit_clamp(str,
state);
1040 stb_textedit_move_to_first(
state);
1041 if (
state->single_line)
1043 else while (
state->cursor < n && STB_TEXTEDIT_GETCHAR(str,
state->cursor) != STB_TEXTEDIT_NEWLINE)
1045 state->has_preferred_x = 0;
1049 #ifdef STB_TEXTEDIT_K_LINESTART2
1053 stb_textedit_clamp(str,
state);
1054 stb_textedit_prep_selection_at_cursor(
state);
1055 if (
state->single_line)
1057 else while (
state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str,
state->cursor-1) != STB_TEXTEDIT_NEWLINE)
1060 state->has_preferred_x = 0;
1063 #ifdef STB_TEXTEDIT_K_LINEEND2
1067 int n = STB_TEXTEDIT_STRINGLEN(str);
1068 stb_textedit_clamp(str,
state);
1069 stb_textedit_prep_selection_at_cursor(
state);
1070 if (
state->single_line)
1072 else while (
state->cursor < n && STB_TEXTEDIT_GETCHAR(str,
state->cursor) != STB_TEXTEDIT_NEWLINE)
1075 state->has_preferred_x = 0;
1100 if (
state->undo_point > 0) {
1102 if (
state->undo_rec[0].char_storage >= 0) {
1103 int n =
state->undo_rec[0].insert_length, i;
1105 state->undo_char_point -= n;
1107 for (i=0; i <
state->undo_point; ++i)
1108 if (
state->undo_rec[i].char_storage >= 0)
1109 state->undo_rec[i].char_storage -= n;
1111 --
state->undo_point;
1112 STB_TEXTEDIT_memmove(
state->undo_rec,
state->undo_rec+1, (
size_t) (
state->undo_point*
sizeof(
state->undo_rec[0])));
1124 if (
state->redo_point <= k) {
1126 if (
state->undo_rec[k].char_storage >= 0) {
1127 int n =
state->undo_rec[k].insert_length, i;
1129 state->redo_char_point += n;
1132 for (i=
state->redo_point; i < k; ++i)
1133 if (
state->undo_rec[i].char_storage >= 0)
1134 state->undo_rec[i].char_storage += n;
1139 const char* buf_begin = (
char*)
state->undo_rec; (
void)buf_begin;
1140 const char* buf_end = (
char*)
state->undo_rec +
sizeof(
state->undo_rec); (void)buf_end;
1143 STB_TEXTEDIT_memmove(
state->undo_rec +
state->redo_point+1,
state->undo_rec +
state->redo_point, move_size);
1146 ++
state->redo_point;
1153 stb_textedit_flush_redo(
state);
1158 stb_textedit_discard_undo(
state);
1162 state->undo_point = 0;
1163 state->undo_char_point = 0;
1169 stb_textedit_discard_undo(
state);
1171 return &
state->undo_rec[
state->undo_point++];
1184 if (insert_len == 0) {
1189 state->undo_char_point += insert_len;
1233 stb_textedit_discard_redo(s);
1315 stb_text_createundo(&
state->undostate, where, 0, length);
1323 for (i=0; i < length; ++i)
1324 p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
1333 for (i=0; i < old_length; ++i)
1334 p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
1341 state->undostate.undo_point = 0;
1342 state->undostate.undo_char_point = 0;
1347 state->has_preferred_x = 0;
1348 state->preferred_x = 0;
1349 state->cursor_at_end_of_line = 0;
1350 state->initialized = 1;
1351 state->single_line = (
unsigned char) is_single_line;
1352 state->insert_mode = 0;
1358 stb_textedit_clear_state(
state, is_single_line);
1361 #if defined(__GNUC__) || defined(__clang__)
1362 #pragma GCC diagnostic push
1363 #pragma GCC diagnostic ignored "-Wcast-qual"
1371 #if defined(__GNUC__) || defined(__clang__)
1372 #pragma GCC diagnostic pop
1375 #endif//STB_TEXTEDIT_IMPLEMENTATION