2#include "nuklear_internal.h"
13 int first_char, length;
20 float baseline_y_delta;
28NK_INTERN
void nk_textedit_makeundo_delete(
struct nk_text_edit*,
int,
int);
29NK_INTERN
void nk_textedit_makeundo_insert(
struct nk_text_edit*,
int,
int);
30NK_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)
34nk_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;
64nk_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;
124nk_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;
135nk_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;
217nk_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;
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;
276nk_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#ifndef NK_IS_WORD_BOUNDARY
283 return (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f' ||
284 c ==
'\v' || c == 0x3000);
286 return NK_IS_WORD_BOUNDARY(c);
290nk_textedit_move_to_word_previous(
struct nk_text_edit *state)
292 int c = state->cursor - 1;
294 if (nk_is_word_boundary(state, c)) {
295 while (c > 0 && nk_is_word_boundary(state, --c));
297 while (!nk_is_word_boundary(state, --c));
306nk_textedit_move_to_word_next(
struct nk_text_edit *state)
308 const int len = state->string.len;
309 int c = state->cursor;
311 if (!nk_is_word_boundary(state, c)) {
312 while (c < len && !nk_is_word_boundary(state, ++c));
314 while (c < len && nk_is_word_boundary(state, ++c));
322nk_textedit_prep_selection_at_cursor(
struct nk_text_edit *state)
325 if (!NK_TEXT_HAS_SELECTION(state))
326 state->select_start = state->select_end = state->cursor;
327 else state->cursor = state->select_end;
333 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
335 if (NK_TEXT_HAS_SELECTION(state)) {
336 nk_textedit_delete_selection(state);
337 state->has_preferred_x = 0;
343nk_textedit_paste(
struct nk_text_edit *state,
char const *ctext,
int len)
347 const char *text = (
const char *) ctext;
348 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
return 0;
351 nk_textedit_clamp(state);
352 nk_textedit_delete_selection(state);
355 glyphs = nk_utf_len(ctext, len);
356 if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) {
357 nk_textedit_makeundo_insert(state, state->cursor, glyphs);
358 state->cursor += len;
359 state->has_preferred_x = 0;
363 if (state->undo.undo_point)
364 --state->undo.undo_point;
368nk_textedit_text(
struct nk_text_edit *state,
const char *text,
int total_len)
376 if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW)
return;
378 glyph_len = nk_utf_decode(text, &unicode, total_len);
379 while ((text_len < total_len) && glyph_len)
382 if (unicode == 127)
goto next;
384 if (unicode ==
'\n' && state->single_line)
goto next;
386 if (state->filter && !state->filter(state, unicode))
goto next;
388 if (!NK_TEXT_HAS_SELECTION(state) &&
389 state->cursor < state->string.len)
391 if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) {
392 nk_textedit_makeundo_replace(state, state->cursor, 1, 1);
393 nk_str_delete_runes(&state->string, state->cursor, 1);
395 if (nk_str_insert_text_utf8(&state->string, state->cursor,
399 state->has_preferred_x = 0;
402 nk_textedit_delete_selection(state);
403 if (nk_str_insert_text_utf8(&state->string, state->cursor,
406 nk_textedit_makeundo_insert(state, state->cursor, 1);
407 state->cursor = NK_MIN(state->cursor + 1, state->string.len);
408 state->has_preferred_x = 0;
412 text_len += glyph_len;
413 glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len);
417nk_textedit_key(
struct nk_text_edit *state,
enum nk_keys key,
int shift_mod,
433 case NK_KEY_TEXT_UNDO:
434 nk_textedit_undo(state);
435 state->has_preferred_x = 0;
438 case NK_KEY_TEXT_REDO:
439 nk_textedit_redo(state);
440 state->has_preferred_x = 0;
443 case NK_KEY_TEXT_SELECT_ALL:
444 nk_textedit_select_all(state);
445 state->has_preferred_x = 0;
448 case NK_KEY_TEXT_INSERT_MODE:
449 state->mode = NK_TEXT_EDIT_MODE_INSERT;
451 case NK_KEY_TEXT_REPLACE_MODE:
452 state->mode = NK_TEXT_EDIT_MODE_REPLACE;
454 case NK_KEY_TEXT_RESET_MODE:
455 state->mode = NK_TEXT_EDIT_MODE_VIEW;
460 nk_textedit_clamp(state);
461 nk_textedit_prep_selection_at_cursor(state);
463 if (state->select_end > 0)
465 state->cursor = state->select_end;
466 state->has_preferred_x = 0;
470 if (NK_TEXT_HAS_SELECTION(state))
471 nk_textedit_move_to_first(state);
472 else if (state->cursor > 0)
474 state->has_preferred_x = 0;
479 nk_textedit_prep_selection_at_cursor(state);
482 nk_textedit_clamp(state);
483 state->cursor = state->select_end;
484 state->has_preferred_x = 0;
488 if (NK_TEXT_HAS_SELECTION(state))
489 nk_textedit_move_to_last(state);
490 else ++state->cursor;
491 nk_textedit_clamp(state);
492 state->has_preferred_x = 0;
495 case NK_KEY_TEXT_WORD_LEFT:
497 if( !NK_TEXT_HAS_SELECTION( state ) )
498 nk_textedit_prep_selection_at_cursor(state);
499 state->cursor = nk_textedit_move_to_word_previous(state);
500 state->select_end = state->cursor;
501 nk_textedit_clamp(state );
503 if (NK_TEXT_HAS_SELECTION(state))
504 nk_textedit_move_to_first(state);
506 state->cursor = nk_textedit_move_to_word_previous(state);
507 nk_textedit_clamp(state );
511 case NK_KEY_TEXT_WORD_RIGHT:
513 if( !NK_TEXT_HAS_SELECTION( state ) )
514 nk_textedit_prep_selection_at_cursor(state);
515 state->cursor = nk_textedit_move_to_word_next(state);
516 state->select_end = state->cursor;
517 nk_textedit_clamp(state);
519 if (NK_TEXT_HAS_SELECTION(state))
520 nk_textedit_move_to_last(state);
522 state->cursor = nk_textedit_move_to_word_next(state);
523 nk_textedit_clamp(state );
530 int i, sel = shift_mod;
532 if (state->single_line) {
539 nk_textedit_prep_selection_at_cursor(state);
540 else if (NK_TEXT_HAS_SELECTION(state))
541 nk_textedit_move_to_last(state);
544 nk_textedit_clamp(state);
545 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
552 float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
553 int start = find.first_char + find.length;
555 state->cursor = start;
556 nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
559 for (i=0; i < row.num_chars && x < row.x1; ++i) {
560 float dx = nk_textedit_get_width(state, start, i, font);
566 nk_textedit_clamp(state);
568 state->has_preferred_x = 1;
569 state->preferred_x = goal_x;
571 state->select_end = state->cursor;
578 int i, sel = shift_mod;
580 if (state->single_line) {
587 nk_textedit_prep_selection_at_cursor(state);
588 else if (NK_TEXT_HAS_SELECTION(state))
589 nk_textedit_move_to_first(state);
592 nk_textedit_clamp(state);
593 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
597 if (find.prev_first != find.first_char) {
600 float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
602 state->cursor = find.prev_first;
603 nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
606 for (i=0; i < row.num_chars && x < row.x1; ++i) {
607 float dx = nk_textedit_get_width(state, find.prev_first, i, font);
613 nk_textedit_clamp(state);
615 state->has_preferred_x = 1;
616 state->preferred_x = goal_x;
617 if (sel) state->select_end = state->cursor;
622 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
624 if (NK_TEXT_HAS_SELECTION(state))
625 nk_textedit_delete_selection(state);
627 int n = state->string.len;
628 if (state->cursor < n)
629 nk_textedit_delete(state, state->cursor, 1);
631 state->has_preferred_x = 0;
634 case NK_KEY_BACKSPACE:
635 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
637 if (NK_TEXT_HAS_SELECTION(state))
638 nk_textedit_delete_selection(state);
640 nk_textedit_clamp(state);
641 if (state->cursor > 0) {
642 nk_textedit_delete(state, state->cursor-1, 1);
646 state->has_preferred_x = 0;
649 case NK_KEY_TEXT_START:
651 nk_textedit_prep_selection_at_cursor(state);
652 state->cursor = state->select_end = 0;
653 state->has_preferred_x = 0;
655 state->cursor = state->select_start = state->select_end = 0;
656 state->has_preferred_x = 0;
660 case NK_KEY_TEXT_END:
662 nk_textedit_prep_selection_at_cursor(state);
663 state->cursor = state->select_end = state->string.len;
664 state->has_preferred_x = 0;
666 state->cursor = state->string.len;
667 state->select_start = state->select_end = 0;
668 state->has_preferred_x = 0;
672 case NK_KEY_TEXT_LINE_START: {
675 nk_textedit_clamp(state);
676 nk_textedit_prep_selection_at_cursor(state);
677 if (state->string.len && state->cursor == state->string.len)
679 nk_textedit_find_charpos(&find, state,state->cursor, state->single_line,
681 state->cursor = state->select_end = find.first_char;
682 state->has_preferred_x = 0;
685 if (state->string.len && state->cursor == state->string.len)
687 nk_textedit_clamp(state);
688 nk_textedit_move_to_first(state);
689 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
691 state->cursor = find.first_char;
692 state->has_preferred_x = 0;
696 case NK_KEY_TEXT_LINE_END: {
699 nk_textedit_clamp(state);
700 nk_textedit_prep_selection_at_cursor(state);
701 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
703 state->has_preferred_x = 0;
704 state->cursor = find.first_char + find.length;
705 if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) ==
'\n')
707 state->select_end = state->cursor;
710 nk_textedit_clamp(state);
711 nk_textedit_move_to_first(state);
712 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
715 state->has_preferred_x = 0;
716 state->cursor = find.first_char + find.length;
717 if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) ==
'\n')
725 state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
726 state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
732 if (state->undo_point > 0) {
734 if (state->undo_rec[0].char_storage >= 0) {
735 int n = state->undo_rec[0].insert_length, i;
737 state->undo_char_point = (short)(state->undo_char_point - n);
738 NK_MEMCPY(state->undo_char, state->undo_char + n,
739 (nk_size)state->undo_char_point*
sizeof(nk_rune));
740 for (i=0; i < state->undo_point; ++i) {
741 if (state->undo_rec[i].char_storage >= 0)
742 state->undo_rec[i].char_storage = (short)
743 (state->undo_rec[i].char_storage - n);
747 NK_MEMCPY(state->undo_rec, state->undo_rec+1,
748 (nk_size)((nk_size)state->undo_point *
sizeof(state->undo_rec[0])));
759 int k = NK_TEXTEDIT_UNDOSTATECOUNT-1;
760 if (state->redo_point <= k) {
762 if (state->undo_rec[k].char_storage >= 0) {
763 int n = state->undo_rec[k].insert_length, i;
765 state->redo_char_point = (short)(state->redo_char_point + n);
766 num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point);
767 NK_MEMCPY(state->undo_char + state->redo_char_point,
768 state->undo_char + state->redo_char_point-n, num *
sizeof(
char));
769 for (i = state->redo_point; i < k; ++i) {
770 if (state->undo_rec[i].char_storage >= 0) {
771 state->undo_rec[i].char_storage = (short)
772 (state->undo_rec[i].char_storage + n);
777 num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point);
778 if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1,
779 state->undo_rec + state->redo_point, num *
sizeof(state->undo_rec[0]));
786 nk_textedit_flush_redo(state);
790 if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
791 nk_textedit_discard_undo(state);
795 if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) {
796 state->undo_point = 0;
797 state->undo_char_point = 0;
803 while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT)
804 nk_textedit_discard_undo(state);
805 return &state->undo_rec[state->undo_point++];
809 int insert_len,
int delete_len)
816 r->insert_length = (short) insert_len;
817 r->delete_length = (short) delete_len;
819 if (insert_len == 0) {
820 r->char_storage = -1;
823 r->char_storage = state->undo_char_point;
824 state->undo_char_point = (short)(state->undo_char_point + insert_len);
825 return &state->undo_char[r->char_storage];
833 if (s->undo_point == 0)
837 u = s->undo_rec[s->undo_point-1];
838 r = &s->undo_rec[s->redo_point-1];
839 r->char_storage = -1;
841 r->insert_length = u.delete_length;
842 r->delete_length = u.insert_length;
855 if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) {
858 r->insert_length = 0;
862 while (s->undo_char_point + u.delete_length > s->redo_char_point) {
864 nk_textedit_discard_redo(s);
866 if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
870 r = &s->undo_rec[s->redo_point-1];
871 r->char_storage = (short)(s->redo_char_point - u.delete_length);
872 s->redo_char_point = (short)(s->redo_char_point - u.delete_length);
875 for (i=0; i < u.delete_length; ++i)
876 s->undo_char[r->char_storage + i] =
877 nk_str_rune_at(&state->string, u.where + i);
880 nk_str_delete_runes(&state->string, u.where, u.delete_length);
884 if (u.insert_length) {
886 nk_str_insert_text_runes(&state->string, u.where,
887 &s->undo_char[u.char_storage], u.insert_length);
888 s->undo_char_point = (short)(s->undo_char_point - u.insert_length);
890 state->cursor = (short)(u.where + u.insert_length);
900 if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
904 u = &s->undo_rec[s->undo_point];
905 r = s->undo_rec[s->redo_point];
909 u->delete_length = r.insert_length;
910 u->insert_length = r.delete_length;
912 u->char_storage = -1;
914 if (r.delete_length) {
917 if (s->undo_char_point + u->insert_length > s->redo_char_point) {
918 u->insert_length = 0;
919 u->delete_length = 0;
922 u->char_storage = s->undo_char_point;
923 s->undo_char_point = (short)(s->undo_char_point + u->insert_length);
926 for (i=0; i < u->insert_length; ++i) {
927 s->undo_char[u->char_storage + i] =
928 nk_str_rune_at(&state->string, u->where + i);
931 nk_str_delete_runes(&state->string, r.where, r.delete_length);
934 if (r.insert_length) {
936 nk_str_insert_text_runes(&state->string, r.where,
937 &s->undo_char[r.char_storage], r.insert_length);
939 state->cursor = r.where + r.insert_length;
945nk_textedit_makeundo_insert(
struct nk_text_edit *state,
int where,
int length)
947 nk_textedit_createundo(&state->undo, where, 0, length);
950nk_textedit_makeundo_delete(
struct nk_text_edit *state,
int where,
int length)
953 nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0);
955 for (i=0; i < length; ++i)
956 p[i] = nk_str_rune_at(&state->string, where+i);
960nk_textedit_makeundo_replace(
struct nk_text_edit *state,
int where,
961 int old_length,
int new_length)
964 nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length);
966 for (i=0; i < old_length; ++i)
967 p[i] = nk_str_rune_at(&state->string, where+i);
971nk_textedit_clear_state(
struct nk_text_edit *state,
enum nk_text_edit_type type,
972 nk_plugin_filter filter)
975 state->undo.undo_point = 0;
976 state->undo.undo_char_point = 0;
977 state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
978 state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
979 state->select_end = state->select_start = 0;
981 state->has_preferred_x = 0;
982 state->preferred_x = 0;
983 state->cursor_at_end_of_line = 0;
984 state->initialized = 1;
985 state->single_line = (
unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE);
986 state->mode = NK_TEXT_EDIT_MODE_VIEW;
987 state->filter = filter;
988 state->scrollbar =
nk_vec2(0,0);
991nk_textedit_init_fixed(
struct nk_text_edit *state,
void *memory, nk_size size)
995 if (!state || !memory || !size)
return;
997 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
998 nk_str_init_fixed(&state->string, memory, size);
1005 if (!state || !alloc)
return;
1007 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1008 nk_str_init(&state->string, alloc, size);
1010#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
1017 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1018 nk_str_init_default(&state->string);
1025 state->select_start = 0;
1026 state->select_end = state->string.len;
1033 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