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 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
450 state->mode = NK_TEXT_EDIT_MODE_INSERT;
452 case NK_KEY_TEXT_REPLACE_MODE:
453 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
454 state->mode = NK_TEXT_EDIT_MODE_REPLACE;
456 case NK_KEY_TEXT_RESET_MODE:
457 if (state->mode == NK_TEXT_EDIT_MODE_INSERT ||
458 state->mode == NK_TEXT_EDIT_MODE_REPLACE)
459 state->mode = NK_TEXT_EDIT_MODE_VIEW;
464 nk_textedit_clamp(state);
465 nk_textedit_prep_selection_at_cursor(state);
467 if (state->select_end > 0)
469 state->cursor = state->select_end;
470 state->has_preferred_x = 0;
474 if (NK_TEXT_HAS_SELECTION(state))
475 nk_textedit_move_to_first(state);
476 else if (state->cursor > 0)
478 state->has_preferred_x = 0;
483 nk_textedit_prep_selection_at_cursor(state);
486 nk_textedit_clamp(state);
487 state->cursor = state->select_end;
488 state->has_preferred_x = 0;
492 if (NK_TEXT_HAS_SELECTION(state))
493 nk_textedit_move_to_last(state);
494 else ++state->cursor;
495 nk_textedit_clamp(state);
496 state->has_preferred_x = 0;
499 case NK_KEY_TEXT_WORD_LEFT:
501 if( !NK_TEXT_HAS_SELECTION( state ) )
502 nk_textedit_prep_selection_at_cursor(state);
503 state->cursor = nk_textedit_move_to_word_previous(state);
504 state->select_end = state->cursor;
505 nk_textedit_clamp(state );
507 if (NK_TEXT_HAS_SELECTION(state))
508 nk_textedit_move_to_first(state);
510 state->cursor = nk_textedit_move_to_word_previous(state);
511 nk_textedit_clamp(state );
515 case NK_KEY_TEXT_WORD_RIGHT:
517 if( !NK_TEXT_HAS_SELECTION( state ) )
518 nk_textedit_prep_selection_at_cursor(state);
519 state->cursor = nk_textedit_move_to_word_next(state);
520 state->select_end = state->cursor;
521 nk_textedit_clamp(state);
523 if (NK_TEXT_HAS_SELECTION(state))
524 nk_textedit_move_to_last(state);
526 state->cursor = nk_textedit_move_to_word_next(state);
527 nk_textedit_clamp(state );
534 int i, sel = shift_mod;
536 if (state->single_line) {
543 nk_textedit_prep_selection_at_cursor(state);
544 else if (NK_TEXT_HAS_SELECTION(state))
545 nk_textedit_move_to_last(state);
548 nk_textedit_clamp(state);
549 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
556 float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
557 int start = find.first_char + find.length;
559 state->cursor = start;
560 nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
563 for (i=0; i < row.num_chars && x < row.x1; ++i) {
564 float dx = nk_textedit_get_width(state, start, i, font);
570 nk_textedit_clamp(state);
572 state->has_preferred_x = 1;
573 state->preferred_x = goal_x;
575 state->select_end = state->cursor;
582 int i, sel = shift_mod;
584 if (state->single_line) {
591 nk_textedit_prep_selection_at_cursor(state);
592 else if (NK_TEXT_HAS_SELECTION(state))
593 nk_textedit_move_to_first(state);
596 nk_textedit_clamp(state);
597 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
601 if (find.prev_first != find.first_char) {
604 float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
606 state->cursor = find.prev_first;
607 nk_textedit_layout_row(&row, state, state->cursor, row_height, font);
610 for (i=0; i < row.num_chars && x < row.x1; ++i) {
611 float dx = nk_textedit_get_width(state, find.prev_first, i, font);
617 nk_textedit_clamp(state);
619 state->has_preferred_x = 1;
620 state->preferred_x = goal_x;
621 if (sel) state->select_end = state->cursor;
626 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
628 if (NK_TEXT_HAS_SELECTION(state))
629 nk_textedit_delete_selection(state);
631 int n = state->string.len;
632 if (state->cursor < n)
633 nk_textedit_delete(state, state->cursor, 1);
635 state->has_preferred_x = 0;
638 case NK_KEY_BACKSPACE:
639 if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
641 if (NK_TEXT_HAS_SELECTION(state))
642 nk_textedit_delete_selection(state);
644 nk_textedit_clamp(state);
645 if (state->cursor > 0) {
646 nk_textedit_delete(state, state->cursor-1, 1);
650 state->has_preferred_x = 0;
653 case NK_KEY_TEXT_START:
655 nk_textedit_prep_selection_at_cursor(state);
656 state->cursor = state->select_end = 0;
657 state->has_preferred_x = 0;
659 state->cursor = state->select_start = state->select_end = 0;
660 state->has_preferred_x = 0;
664 case NK_KEY_TEXT_END:
666 nk_textedit_prep_selection_at_cursor(state);
667 state->cursor = state->select_end = state->string.len;
668 state->has_preferred_x = 0;
670 state->cursor = state->string.len;
671 state->select_start = state->select_end = 0;
672 state->has_preferred_x = 0;
676 case NK_KEY_TEXT_LINE_START: {
679 nk_textedit_clamp(state);
680 nk_textedit_prep_selection_at_cursor(state);
681 if (state->string.len && state->cursor == state->string.len)
683 nk_textedit_find_charpos(&find, state,state->cursor, state->single_line,
685 state->cursor = state->select_end = find.first_char;
686 state->has_preferred_x = 0;
689 if (state->string.len && state->cursor == state->string.len)
691 nk_textedit_clamp(state);
692 nk_textedit_move_to_first(state);
693 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
695 state->cursor = find.first_char;
696 state->has_preferred_x = 0;
700 case NK_KEY_TEXT_LINE_END: {
703 nk_textedit_clamp(state);
704 nk_textedit_prep_selection_at_cursor(state);
705 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
707 state->has_preferred_x = 0;
708 state->cursor = find.first_char + find.length;
709 if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) ==
'\n')
711 state->select_end = state->cursor;
714 nk_textedit_clamp(state);
715 nk_textedit_move_to_first(state);
716 nk_textedit_find_charpos(&find, state, state->cursor, state->single_line,
719 state->has_preferred_x = 0;
720 state->cursor = find.first_char + find.length;
721 if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) ==
'\n')
729 state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
730 state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
736 if (state->undo_point > 0) {
738 if (state->undo_rec[0].char_storage >= 0) {
739 int n = state->undo_rec[0].insert_length, i;
741 state->undo_char_point = (short)(state->undo_char_point - n);
742 NK_MEMCPY(state->undo_char, state->undo_char + n,
743 (nk_size)state->undo_char_point*
sizeof(nk_rune));
744 for (i=0; i < state->undo_point; ++i) {
745 if (state->undo_rec[i].char_storage >= 0)
746 state->undo_rec[i].char_storage = (short)
747 (state->undo_rec[i].char_storage - n);
751 NK_MEMCPY(state->undo_rec, state->undo_rec+1,
752 (nk_size)((nk_size)state->undo_point *
sizeof(state->undo_rec[0])));
763 int k = NK_TEXTEDIT_UNDOSTATECOUNT-1;
764 if (state->redo_point <= k) {
766 if (state->undo_rec[k].char_storage >= 0) {
767 int n = state->undo_rec[k].insert_length, i;
769 state->redo_char_point = (short)(state->redo_char_point + n);
770 num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point);
771 NK_MEMCPY(state->undo_char + state->redo_char_point,
772 state->undo_char + state->redo_char_point-n, num *
sizeof(
char));
773 for (i = state->redo_point; i < k; ++i) {
774 if (state->undo_rec[i].char_storage >= 0) {
775 state->undo_rec[i].char_storage = (short)
776 (state->undo_rec[i].char_storage + n);
781 num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point);
782 if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1,
783 state->undo_rec + state->redo_point, num *
sizeof(state->undo_rec[0]));
790 nk_textedit_flush_redo(state);
794 if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
795 nk_textedit_discard_undo(state);
799 if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) {
800 state->undo_point = 0;
801 state->undo_char_point = 0;
807 while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT)
808 nk_textedit_discard_undo(state);
809 return &state->undo_rec[state->undo_point++];
813 int insert_len,
int delete_len)
820 r->insert_length = (short) insert_len;
821 r->delete_length = (short) delete_len;
823 if (insert_len == 0) {
824 r->char_storage = -1;
827 r->char_storage = state->undo_char_point;
828 state->undo_char_point = (short)(state->undo_char_point + insert_len);
829 return &state->undo_char[r->char_storage];
837 if (s->undo_point == 0)
841 u = s->undo_rec[s->undo_point-1];
842 r = &s->undo_rec[s->redo_point-1];
843 r->char_storage = -1;
845 r->insert_length = u.delete_length;
846 r->delete_length = u.insert_length;
859 if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) {
862 r->insert_length = 0;
866 while (s->undo_char_point + u.delete_length > s->redo_char_point) {
868 nk_textedit_discard_redo(s);
870 if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
874 r = &s->undo_rec[s->redo_point-1];
875 r->char_storage = (short)(s->redo_char_point - u.delete_length);
876 s->redo_char_point = (short)(s->redo_char_point - u.delete_length);
879 for (i=0; i < u.delete_length; ++i)
880 s->undo_char[r->char_storage + i] =
881 nk_str_rune_at(&state->string, u.where + i);
884 nk_str_delete_runes(&state->string, u.where, u.delete_length);
888 if (u.insert_length) {
890 nk_str_insert_text_runes(&state->string, u.where,
891 &s->undo_char[u.char_storage], u.insert_length);
892 s->undo_char_point = (short)(s->undo_char_point - u.insert_length);
894 state->cursor = (short)(u.where + u.insert_length);
904 if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT)
908 u = &s->undo_rec[s->undo_point];
909 r = s->undo_rec[s->redo_point];
913 u->delete_length = r.insert_length;
914 u->insert_length = r.delete_length;
916 u->char_storage = -1;
918 if (r.delete_length) {
921 if (s->undo_char_point + u->insert_length > s->redo_char_point) {
922 u->insert_length = 0;
923 u->delete_length = 0;
926 u->char_storage = s->undo_char_point;
927 s->undo_char_point = (short)(s->undo_char_point + u->insert_length);
930 for (i=0; i < u->insert_length; ++i) {
931 s->undo_char[u->char_storage + i] =
932 nk_str_rune_at(&state->string, u->where + i);
935 nk_str_delete_runes(&state->string, r.where, r.delete_length);
938 if (r.insert_length) {
940 nk_str_insert_text_runes(&state->string, r.where,
941 &s->undo_char[r.char_storage], r.insert_length);
943 state->cursor = r.where + r.insert_length;
949nk_textedit_makeundo_insert(
struct nk_text_edit *state,
int where,
int length)
951 nk_textedit_createundo(&state->undo, where, 0, length);
954nk_textedit_makeundo_delete(
struct nk_text_edit *state,
int where,
int length)
957 nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0);
959 for (i=0; i < length; ++i)
960 p[i] = nk_str_rune_at(&state->string, where+i);
964nk_textedit_makeundo_replace(
struct nk_text_edit *state,
int where,
965 int old_length,
int new_length)
968 nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length);
970 for (i=0; i < old_length; ++i)
971 p[i] = nk_str_rune_at(&state->string, where+i);
975nk_textedit_clear_state(
struct nk_text_edit *state,
enum nk_text_edit_type type,
976 nk_plugin_filter filter)
979 state->undo.undo_point = 0;
980 state->undo.undo_char_point = 0;
981 state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT;
982 state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT;
983 state->select_end = state->select_start = 0;
985 state->has_preferred_x = 0;
986 state->preferred_x = 0;
987 state->cursor_at_end_of_line = 0;
988 state->initialized = 1;
989 state->single_line = (
unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE);
990 state->mode = NK_TEXT_EDIT_MODE_VIEW;
991 state->filter = filter;
992 state->scrollbar =
nk_vec2(0,0);
995nk_textedit_init_fixed(
struct nk_text_edit *state,
void *memory, nk_size size)
999 if (!state || !memory || !size)
return;
1001 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1002 nk_str_init_fixed(&state->string, memory, size);
1009 if (!state || !alloc)
return;
1011 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1012 nk_str_init(&state->string, alloc, size);
1014#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR
1021 nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0);
1022 nk_str_init_default(&state->string);
1029 state->select_start = 0;
1030 state->select_end = state->string.len;
1037 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