2 #include "nuklear_internal.h"
13 int first_char, length;
20 float baseline_y_delta;
28 NK_INTERN
void nk_textedit_makeundo_delete(
struct nk_text_edit*,
int,
int);
29 NK_INTERN
void nk_textedit_makeundo_insert(
struct nk_text_edit*,
int,
int);
30 NK_INTERN
void nk_textedit_makeundo_replace(
struct nk_text_edit*,
int,
int,
int);
31 #define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end)
34 nk_textedit_get_width(
const struct nk_text_edit *edit,
int line_start,
int char_id,
39 const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len);
40 return font->
width(font->userdata, font->
height, str, len);
44 int line_start_id,
float row_height,
const struct nk_user_font *font)
49 const char *remaining;
50 int len = nk_str_len_char(&edit->string);
51 const char *end = nk_str_get_const(&edit->string) + len;
52 const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l);
53 const struct nk_vec2 size = nk_text_calculate_text_bounds(font,
54 text, (
int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE);
58 r->baseline_y_delta = size.y;
61 r->num_chars = glyphs;
64 nk_textedit_locate_coord(
struct nk_text_edit *edit,
float x,
float y,
68 int n = edit->string.len;
69 float base_y = 0, prev_x;
78 nk_textedit_layout_row(&r, edit, i, row_height, font);
82 if (i==0 && y < base_y + r.ymin)
85 if (y < base_y + r.ymax)
89 base_y += r.baseline_y_delta;
105 for (i=0; i < r.num_chars; ++i) {
106 float w = nk_textedit_get_width(edit, k, i, font);
119 if (nk_str_rune_at(&edit->string, i+r.num_chars-1) ==
'\n')
120 return i+r.num_chars-1;
121 else return i+r.num_chars;
124 nk_textedit_click(
struct nk_text_edit *state,
float x,
float y,
129 state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height);
130 state->select_start = state->cursor;
131 state->select_end = state->cursor;
132 state->has_preferred_x = 0;
135 nk_textedit_drag(
struct nk_text_edit *state,
float x,
float y,
140 int p = nk_textedit_locate_coord(state, x, y, font, row_height);
141 if (state->select_start == state->select_end)
142 state->select_start = state->cursor;
143 state->cursor = state->select_end = p;
147 int n,
int single_line,
const struct nk_user_font *font,
float row_height)
153 int z = state->string.len;
160 nk_textedit_layout_row(&r, state, 0, row_height, font);
162 find->first_char = 0;
168 nk_textedit_layout_row(&r, state, i, row_height, font);
171 find->first_char = i;
172 find->length = r.num_chars;
176 find->height = r.ymax - r.ymin;
177 find->prev_first = prev_start;
185 nk_textedit_layout_row(&r, state, i, row_height, font);
186 if (n < i + r.num_chars)
break;
189 find->y += r.baseline_y_delta;
192 find->first_char = first = i;
193 find->length = r.num_chars;
194 find->height = r.ymax - r.ymin;
195 find->prev_first = prev_start;
199 for (i=0; first+i < n; ++i)
200 find->x += nk_textedit_get_width(state, first, i, font);
206 int n = state->string.len;
207 if (NK_TEXT_HAS_SELECTION(state)) {
208 if (state->select_start > n) state->select_start = n;
209 if (state->select_end > n) state->select_end = n;
211 if (state->select_start == state->select_end)
212 state->cursor = state->select_start;
214 if (state->cursor > n) state->cursor = n;
217 nk_textedit_delete(
struct nk_text_edit *state,
int where,
int len)
220 nk_textedit_makeundo_delete(state, where, len);
221 nk_str_delete_runes(&state->string, where, len);
222 state->has_preferred_x = 0;
225 nk_textedit_delete_selection(
struct nk_text_edit *state)
228 nk_textedit_clamp(state);
229 if (NK_TEXT_HAS_SELECTION(state)) {
230 if (state->select_start < state->select_end) {
231 nk_textedit_delete(state, state->select_start,
232 state->select_end - state->select_start);
233 state->select_end = state->cursor = state->select_start;
235 nk_textedit_delete(state, state->select_end,
236 state->select_start - state->select_end);
237 state->select_start = state->cursor = state->select_end;
239 state->has_preferred_x = 0;
246 if (state->select_end < state->select_start) {
247 int temp = state->select_end;
248 state->select_end = state->select_start;
249 state->select_start = temp;
256 if (NK_TEXT_HAS_SELECTION(state)) {
257 nk_textedit_sortselection(state);
258 state->cursor = state->select_start;
259 state->select_end = state->select_start;
260 state->has_preferred_x = 0;
267 if (NK_TEXT_HAS_SELECTION(state)) {
268 nk_textedit_sortselection(state);
269 nk_textedit_clamp(state);
270 state->cursor = state->select_end;
271 state->select_start = state->select_end;
272 state->has_preferred_x = 0;
276 nk_is_word_boundary(
struct nk_text_edit *state,
int idx)
280 if (idx <= 0)
return 1;
281 if (!nk_str_at_rune(&state->string, idx, &c, &len))
return 1;
282 return (c ==
' ' || c ==
'\t' ||c == 0x3000 || c ==
',' || c ==
';' ||
283 c ==
'(' || c ==
')' || c ==
'{' || c ==
'}' || c ==
'[' || c ==
']' ||
287 nk_textedit_move_to_word_previous(
struct nk_text_edit *state)
289 int c = state->cursor - 1;
290 while( c >= 0 && !nk_is_word_boundary(state, c))
299 nk_textedit_move_to_word_next(
struct nk_text_edit *state)
301 const int len = state->string.len;
302 int c = state->cursor+1;
303 while( c < len && !nk_is_word_boundary(state, c))
312 nk_textedit_prep_selection_at_cursor(
struct nk_text_edit *state)
315 if (!NK_TEXT_HAS_SELECTION(state))
316 state->select_start = state->select_end = state->cursor;
317 else state->cursor = state->select_end;
323 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
325 if (NK_TEXT_HAS_SELECTION(state)) {
326 nk_textedit_delete_selection(state);
327 state->has_preferred_x = 0;
333 nk_textedit_paste(
struct nk_text_edit *state,
char const *ctext,
int len)
337 const char *text = (
const char *) ctext;
338 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
return 0;
341 nk_textedit_clamp(state);
342 nk_textedit_delete_selection(state);
345 glyphs = nk_utf_len(ctext, len);
346 if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) {
347 nk_textedit_makeundo_insert(state, state->cursor, glyphs);
348 state->cursor += len;
349 state->has_preferred_x = 0;
353 if (state->undo.undo_point)
354 --state->undo.undo_point;
358 nk_textedit_text(
struct nk_text_edit *state,
const char *text,
int total_len)
366 if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW)
return;
368 glyph_len = nk_utf_decode(text, &unicode, total_len);
369 while ((text_len < total_len) && glyph_len)
372 if (unicode == 127)
goto next;
374 if (unicode ==
'\n' && state->single_line)
goto next;
376 if (state->filter && !state->filter(state, unicode))
goto next;
378 if (!NK_TEXT_HAS_SELECTION(state) &&
379 state->cursor < state->string.len)
381 if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) {
382 nk_textedit_makeundo_replace(state, state->cursor, 1, 1);
383 nk_str_delete_runes(&state->string, state->cursor, 1);
385 if (nk_str_insert_text_utf8(&state->string, state->cursor,
389 state->has_preferred_x = 0;
392 nk_textedit_delete_selection(state);
393 if (nk_str_insert_text_utf8(&state->string, state->cursor,
396 nk_textedit_makeundo_insert(state, state->cursor, 1);
397 state->cursor = NK_MIN(state->cursor + 1, state->string.len);
398 state->has_preferred_x = 0;
402 text_len += glyph_len;
403 glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len);
407 nk_textedit_key(
struct nk_text_edit *state,
enum nk_keys key,
int shift_mod,
423 case NK_KEY_TEXT_UNDO:
424 nk_textedit_undo(state);
425 state->has_preferred_x = 0;
428 case NK_KEY_TEXT_REDO:
429 nk_textedit_redo(state);
430 state->has_preferred_x = 0;
433 case NK_KEY_TEXT_SELECT_ALL:
434 nk_textedit_select_all(state);
435 state->has_preferred_x = 0;
438 case NK_KEY_TEXT_INSERT_MODE:
439 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
440 state->mode = NK_TEXT_EDIT_MODE_INSERT;
442 case NK_KEY_TEXT_REPLACE_MODE:
443 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
444 state->mode = NK_TEXT_EDIT_MODE_REPLACE;
446 case NK_KEY_TEXT_RESET_MODE:
447 if (state->mode == NK_TEXT_EDIT_MODE_INSERT ||
448 state->mode == NK_TEXT_EDIT_MODE_REPLACE)
449 state->mode = NK_TEXT_EDIT_MODE_VIEW;
454 nk_textedit_clamp(state);
455 nk_textedit_prep_selection_at_cursor(state);
457 if (state->select_end > 0)
459 state->cursor = state->select_end;
460 state->has_preferred_x = 0;
464 if (NK_TEXT_HAS_SELECTION(state))
465 nk_textedit_move_to_first(state);
466 else if (state->cursor > 0)
468 state->has_preferred_x = 0;
473 nk_textedit_prep_selection_at_cursor(state);
476 nk_textedit_clamp(state);
477 state->cursor = state->select_end;
478 state->has_preferred_x = 0;
482 if (NK_TEXT_HAS_SELECTION(state))
483 nk_textedit_move_to_last(state);
484 else ++state->cursor;
485 nk_textedit_clamp(state);
486 state->has_preferred_x = 0;
489 case NK_KEY_TEXT_WORD_LEFT:
491 if( !NK_TEXT_HAS_SELECTION( state ) )
492 nk_textedit_prep_selection_at_cursor(state);
493 state->cursor = nk_textedit_move_to_word_previous(state);
494 state->select_end = state->cursor;
495 nk_textedit_clamp(state );
497 if (NK_TEXT_HAS_SELECTION(state))
498 nk_textedit_move_to_first(state);
500 state->cursor = nk_textedit_move_to_word_previous(state);
501 nk_textedit_clamp(state );
505 case NK_KEY_TEXT_WORD_RIGHT:
507 if( !NK_TEXT_HAS_SELECTION( state ) )
508 nk_textedit_prep_selection_at_cursor(state);
509 state->cursor = nk_textedit_move_to_word_next(state);
510 state->select_end = state->cursor;
511 nk_textedit_clamp(state);
513 if (NK_TEXT_HAS_SELECTION(state))
514 nk_textedit_move_to_last(state);
516 state->cursor = nk_textedit_move_to_word_next(state);
517 nk_textedit_clamp(state );
524 int i, sel = shift_mod;
526 if (state->single_line) {
533 nk_textedit_prep_selection_at_cursor(state);
534 else if (NK_TEXT_HAS_SELECTION(state))
535 nk_textedit_move_to_last(state);
538 nk_textedit_clamp(state);
539 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
546 float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
547 int start = find.first_char + find.length;
549 state->cursor = start;
550 nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
553 for (i=0; i < row.num_chars && x < row.x1; ++i) {
554 float dx = nk_textedit_get_width(state, start, i, font);
560 nk_textedit_clamp(state);
562 state->has_preferred_x = 1;
563 state->preferred_x = goal_x;
565 state->select_end = state->cursor;
572 int i, sel = shift_mod;
574 if (state->single_line) {
581 nk_textedit_prep_selection_at_cursor(state);
582 else if (NK_TEXT_HAS_SELECTION(state))
583 nk_textedit_move_to_first(state);
586 nk_textedit_clamp(state);
587 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
591 if (find.prev_first != find.first_char) {
594 float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
596 state->cursor = find.prev_first;
597 nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
600 for (i=0; i < row.num_chars && x < row.x1; ++i) {
601 float dx = nk_textedit_get_width(state, find.prev_first, i, font);
607 nk_textedit_clamp(state);
609 state->has_preferred_x = 1;
610 state->preferred_x = goal_x;
611 if (sel) state->select_end = state->cursor;
616 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
618 if (NK_TEXT_HAS_SELECTION(state))
619 nk_textedit_delete_selection(state);
621 int n = state->string.len;
622 if (state->cursor < n)
623 nk_textedit_delete(state, state->cursor, 1);
625 state->has_preferred_x = 0;
628 case NK_KEY_BACKSPACE:
629 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
631 if (NK_TEXT_HAS_SELECTION(state))
632 nk_textedit_delete_selection(state);
634 nk_textedit_clamp(state);
635 if (state->cursor > 0) {
636 nk_textedit_delete(state, state->cursor-1, 1);
640 state->has_preferred_x = 0;
643 case NK_KEY_TEXT_START:
645 nk_textedit_prep_selection_at_cursor(state);
646 state->cursor = state->select_end = 0;
647 state->has_preferred_x = 0;
649 state->cursor = state->select_start = state->select_end = 0;
650 state->has_preferred_x = 0;
654 case NK_KEY_TEXT_END:
656 nk_textedit_prep_selection_at_cursor(state);
657 state->cursor = state->select_end = state->string.len;
658 state->has_preferred_x = 0;
660 state->cursor = state->string.len;
661 state->select_start = state->select_end = 0;
662 state->has_preferred_x = 0;
666 case NK_KEY_TEXT_LINE_START: {
669 nk_textedit_clamp(state);
670 nk_textedit_prep_selection_at_cursor(state);
671 if (state->string.len && state->cursor == state->string.len)
673 nk_textedit_find_charpos(&find, state,state->cursor, state->single_line,
675 state->cursor = state->select_end = find.first_char;
676 state->has_preferred_x = 0;
679 if (state->string.len && state->cursor == state->string.len)
681 nk_textedit_clamp(state);
682 nk_textedit_move_to_first(state);
683 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
685 state->cursor = find.first_char;
686 state->has_preferred_x = 0;
690 case NK_KEY_TEXT_LINE_END: {
693 nk_textedit_clamp(state);
694 nk_textedit_prep_selection_at_cursor(state);
695 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
697 state->has_preferred_x = 0;
698 state->cursor = find.first_char + find.length;
699 if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) ==
'\n')
701 state->select_end = state->cursor;
704 nk_textedit_clamp(state);
705 nk_textedit_move_to_first(state);
706 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
709 state->has_preferred_x = 0;
710 state->cursor = find.first_char + find.length;
711 if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) ==
'\n')
719 state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
720 state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
726 if (state->undo_point > 0) {
728 if (state->undo_rec[0].char_storage >= 0) {
729 int n = state->undo_rec[0].insert_length, i;
731 state->undo_char_point = (short)(state->undo_char_point - n);
732 NK_MEMCPY(state->undo_char, state->undo_char + n,
733 (nk_size)state->undo_char_point*
sizeof(nk_rune));
734 for (i=0; i < state->undo_point; ++i) {
735 if (state->undo_rec[i].char_storage >= 0)
736 state->undo_rec[i].char_storage = (short)
737 (state->undo_rec[i].char_storage - n);
741 NK_MEMCPY(state->undo_rec, state->undo_rec+1,
742 (nk_size)((nk_size)state->undo_point *
sizeof(state->undo_rec[0])));
753 int k = NK_TEXTEDIT_UNDOSTATECOUNT-1;
754 if (state->redo_point <= k) {
756 if (state->undo_rec[k].char_storage >= 0) {
757 int n = state->undo_rec[k].insert_length, i;
759 state->redo_char_point = (short)(state->redo_char_point + n);
760 num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point);
761 NK_MEMCPY(state->undo_char + state->redo_char_point,
762 state->undo_char + state->redo_char_point-n, num *
sizeof(
char));
763 for (i = state->redo_point; i < k; ++i) {
764 if (state->undo_rec[i].char_storage >= 0) {
765 state->undo_rec[i].char_storage = (short)
766 (state->undo_rec[i].char_storage + n);
771 num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point);
772 if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1,
773 state->undo_rec + state->redo_point, num *
sizeof(state->undo_rec[0]));
780 nk_textedit_flush_redo(state);
784 if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
785 nk_textedit_discard_undo(state);
789 if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) {
790 state->undo_point = 0;
791 state->undo_char_point = 0;
797 while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT)
798 nk_textedit_discard_undo(state);
799 return &state->undo_rec[state->undo_point++];
803 int insert_len,
int delete_len)
810 r->insert_length = (short) insert_len;
811 r->delete_length = (short) delete_len;
813 if (insert_len == 0) {
814 r->char_storage = -1;
817 r->char_storage = state->undo_char_point;
818 state->undo_char_point = (short)(state->undo_char_point + insert_len);
819 return &state->undo_char[r->char_storage];
827 if (s->undo_point == 0)
831 u = s->undo_rec[s->undo_point-1];
832 r = &s->undo_rec[s->redo_point-1];
833 r->char_storage = -1;
835 r->insert_length = u.delete_length;
836 r->delete_length = u.insert_length;
849 if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) {
852 r->insert_length = 0;
856 while (s->undo_char_point + u.delete_length > s->redo_char_point) {
858 nk_textedit_discard_redo(s);
860 if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
864 r = &s->undo_rec[s->redo_point-1];
865 r->char_storage = (short)(s->redo_char_point - u.delete_length);
866 s->redo_char_point = (short)(s->redo_char_point - u.delete_length);
869 for (i=0; i < u.delete_length; ++i)
870 s->undo_char[r->char_storage + i] =
871 nk_str_rune_at(&state->string, u.where + i);
874 nk_str_delete_runes(&state->string, u.where, u.delete_length);
878 if (u.insert_length) {
880 nk_str_insert_text_runes(&state->string, u.where,
881 &s->undo_char[u.char_storage], u.insert_length);
882 s->undo_char_point = (short)(s->undo_char_point - u.insert_length);
884 state->cursor = (short)(u.where + u.insert_length);
894 if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
898 u = &s->undo_rec[s->undo_point];
899 r = s->undo_rec[s->redo_point];
903 u->delete_length = r.insert_length;
904 u->insert_length = r.delete_length;
906 u->char_storage = -1;
908 if (r.delete_length) {
911 if (s->undo_char_point + u->insert_length > s->redo_char_point) {
912 u->insert_length = 0;
913 u->delete_length = 0;
916 u->char_storage = s->undo_char_point;
917 s->undo_char_point = (short)(s->undo_char_point + u->insert_length);
920 for (i=0; i < u->insert_length; ++i) {
921 s->undo_char[u->char_storage + i] =
922 nk_str_rune_at(&state->string, u->where + i);
925 nk_str_delete_runes(&state->string, r.where, r.delete_length);
928 if (r.insert_length) {
930 nk_str_insert_text_runes(&state->string, r.where,
931 &s->undo_char[r.char_storage], r.insert_length);
933 state->cursor = r.where + r.insert_length;
939 nk_textedit_makeundo_insert(
struct nk_text_edit *state,
int where,
int length)
941 nk_textedit_createundo(&state->undo, where, 0, length);
944 nk_textedit_makeundo_delete(
struct nk_text_edit *state,
int where,
int length)
947 nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0);
949 for (i=0; i < length; ++i)
950 p[i] = nk_str_rune_at(&state->string, where+i);
954 nk_textedit_makeundo_replace(
struct nk_text_edit *state,
int where,
955 int old_length,
int new_length)
958 nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length);
960 for (i=0; i < old_length; ++i)
961 p[i] = nk_str_rune_at(&state->string, where+i);
965 nk_textedit_clear_state(
struct nk_text_edit *state,
enum nk_text_edit_type type,
966 nk_plugin_filter filter)
969 state->undo.undo_point = 0;
970 state->undo.undo_char_point = 0;
971 state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
972 state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
973 state->select_end = state->select_start = 0;
975 state->has_preferred_x = 0;
976 state->preferred_x = 0;
977 state->cursor_at_end_of_line = 0;
978 state->initialized = 1;
979 state->single_line = (
unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE);
980 state->mode = NK_TEXT_EDIT_MODE_VIEW;
981 state->filter = filter;
982 state->scrollbar =
nk_vec2(0,0);
985 nk_textedit_init_fixed(
struct nk_text_edit *state,
void *memory, nk_size size)
989 if (!state || !memory || !size)
return;
991 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
992 nk_str_init_fixed(&state->string, memory, size);
999 if (!state || !alloc)
return;
1001 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1002 nk_str_init(&state->string, alloc, size);
1004 #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
1011 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1012 nk_str_init_default(&state->string);
1019 state->select_start = 0;
1020 state->select_end = state->string.len;
1027 nk_str_free(&state->string);
main API and documentation file
NK_API void nk_textedit_init(struct nk_text_edit *, const struct nk_allocator *, nk_size size)
text editor
nk_text_width_f width
!< max height of the font
float height
!< user provided font handle