Nuklear
This is a minimal-state, immediate-mode graphical user interface toolkit written in ANSI C and licensed under public domain. It was designed as a simple embeddable user interface for application and does not have any dependencies, a default render backend or OS window/input handling but instead provides a highly modular, library-based approach, with simple input state for input and draw commands describing primitive shapes as output. So instead of providing a layered library that tries to abstract over a number of platform and render backends, it focuses only on the actual UI.
nuklear_edit.c
1 #include "nuklear.h"
2 #include "nuklear_internal.h"
3 
4 /* ===============================================================
5  *
6  * FILTER
7  *
8  * ===============================================================*/
9 NK_API nk_bool
10 nk_filter_default(const struct nk_text_edit *box, nk_rune unicode)
11 {
12  NK_UNUSED(unicode);
13  NK_UNUSED(box);
14  return nk_true;
15 }
16 NK_API nk_bool
17 nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode)
18 {
19  NK_UNUSED(box);
20  if (unicode > 128) return nk_false;
21  else return nk_true;
22 }
23 NK_API nk_bool
24 nk_filter_float(const struct nk_text_edit *box, nk_rune unicode)
25 {
26  NK_UNUSED(box);
27  if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-')
28  return nk_false;
29  else return nk_true;
30 }
31 NK_API nk_bool
32 nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode)
33 {
34  NK_UNUSED(box);
35  if ((unicode < '0' || unicode > '9') && unicode != '-')
36  return nk_false;
37  else return nk_true;
38 }
39 NK_API nk_bool
40 nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode)
41 {
42  NK_UNUSED(box);
43  if ((unicode < '0' || unicode > '9') &&
44  (unicode < 'a' || unicode > 'f') &&
45  (unicode < 'A' || unicode > 'F'))
46  return nk_false;
47  else return nk_true;
48 }
49 NK_API nk_bool
50 nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode)
51 {
52  NK_UNUSED(box);
53  if (unicode < '0' || unicode > '7')
54  return nk_false;
55  else return nk_true;
56 }
57 NK_API nk_bool
58 nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode)
59 {
60  NK_UNUSED(box);
61  if (unicode != '0' && unicode != '1')
62  return nk_false;
63  else return nk_true;
64 }
65 
66 /* ===============================================================
67  *
68  * EDIT
69  *
70  * ===============================================================*/
71 NK_LIB void
72 nk_edit_draw_text(struct nk_command_buffer *out,
73  const struct nk_style_edit *style, float pos_x, float pos_y,
74  float x_offset, const char *text, int byte_len, float row_height,
75  const struct nk_user_font *font, struct nk_color background,
76  struct nk_color foreground, nk_bool is_selected)
77 {
78  NK_ASSERT(out);
79  NK_ASSERT(font);
80  NK_ASSERT(style);
81  if (!text || !byte_len || !out || !style) return;
82 
83  {int glyph_len = 0;
84  nk_rune unicode = 0;
85  int text_len = 0;
86  float line_width = 0;
87  float glyph_width;
88  const char *line = text;
89  float line_offset = 0;
90  int line_count = 0;
91 
92  struct nk_text txt;
93  txt.padding = nk_vec2(0,0);
94  txt.background = background;
95  txt.text = foreground;
96 
97  foreground = nk_rgb_factor(foreground, style->color_factor);
98  background = nk_rgb_factor(background, style->color_factor);
99 
100  glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len);
101  if (!glyph_len) return;
102  while ((text_len < byte_len) && glyph_len)
103  {
104  if (unicode == '\n') {
105  /* new line separator so draw previous line */
106  struct nk_rect label;
107  label.y = pos_y + line_offset;
108  label.h = row_height;
109  label.w = line_width;
110  label.x = pos_x;
111  if (!line_count)
112  label.x += x_offset;
113 
114  if (is_selected) /* selection needs to draw different background color */
115  nk_fill_rect(out, label, 0, background);
116  nk_widget_text(out, label, line, (int)((text + text_len) - line),
117  &txt, NK_TEXT_CENTERED, font);
118 
119  text_len++;
120  line_count++;
121  line_width = 0;
122  line = text + text_len;
123  line_offset += row_height;
124  glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len));
125  continue;
126  }
127  if (unicode == '\r') {
128  text_len++;
129  glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len);
130  continue;
131  }
132  glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len);
133  line_width += (float)glyph_width;
134  text_len += glyph_len;
135  glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len);
136  continue;
137  }
138  if (line_width > 0) {
139  /* draw last line */
140  struct nk_rect label;
141  label.y = pos_y + line_offset;
142  label.h = row_height;
143  label.w = line_width;
144  label.x = pos_x;
145  if (!line_count)
146  label.x += x_offset;
147 
148  if (is_selected)
149  nk_fill_rect(out, label, 0, background);
150  nk_widget_text(out, label, line, (int)((text + text_len) - line),
151  &txt, NK_TEXT_LEFT, font);
152  }}
153 }
154 NK_LIB nk_flags
155 nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
156  struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter,
157  struct nk_text_edit *edit, const struct nk_style_edit *style,
158  struct nk_input *in, const struct nk_user_font *font)
159 {
160  struct nk_rect area;
161  nk_flags ret = 0;
162  float row_height;
163  char prev_state = 0;
164  char is_hovered = 0;
165  char select_all = 0;
166  char cursor_follow = 0;
167  struct nk_rect old_clip;
168  struct nk_rect clip;
169 
170  NK_ASSERT(state);
171  NK_ASSERT(out);
172  NK_ASSERT(style);
173  if (!state || !out || !style)
174  return ret;
175 
176  /* visible text area calculation */
177  area.x = bounds.x + style->padding.x + style->border;
178  area.y = bounds.y + style->padding.y + style->border;
179  area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border);
180  area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border);
181  if (flags & NK_EDIT_MULTILINE)
182  area.w = NK_MAX(0, area.w - style->scrollbar_size.x);
183  row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h;
184 
185  /* calculate clipping rectangle */
186  old_clip = out->clip;
187  nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h);
188 
189  /* update edit state */
190  prev_state = (char)edit->active;
191  is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds);
192  if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) {
193  edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y,
194  bounds.x, bounds.y, bounds.w, bounds.h);
195  }
196 
197  /* (de)activate text editor */
198  if (!prev_state && edit->active) {
199  const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ?
200  NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE;
201  /* keep scroll position when re-activating edit widget */
202  struct nk_vec2 oldscrollbar = edit->scrollbar;
203  nk_textedit_clear_state(edit, type, filter);
204  edit->scrollbar = oldscrollbar;
205  if (flags & NK_EDIT_AUTO_SELECT)
206  select_all = nk_true;
207  if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) {
208  edit->cursor = edit->string.len;
209  in = 0;
210  }
211  } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW;
212  if (flags & NK_EDIT_READ_ONLY)
213  edit->mode = NK_TEXT_EDIT_MODE_VIEW;
214  else if (flags & NK_EDIT_ALWAYS_INSERT_MODE)
215  edit->mode = NK_TEXT_EDIT_MODE_INSERT;
216 
217  ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE;
218  if (prev_state != edit->active)
219  ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED;
220 
221  /* handle user input */
222  if (edit->active && in)
223  {
224  int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down;
225  const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x;
226  const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y;
227 
228  /* mouse click handler */
229  is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area);
230  if (select_all) {
231  nk_textedit_select_all(edit);
232  } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down &&
233  in->mouse.buttons[NK_BUTTON_LEFT].clicked) {
234  nk_textedit_click(edit, mouse_x, mouse_y, font, row_height);
235  } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down &&
236  (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) {
237  nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height);
238  cursor_follow = nk_true;
239  } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked &&
240  in->mouse.buttons[NK_BUTTON_RIGHT].down) {
241  nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height);
242  nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height);
243  cursor_follow = nk_true;
244  }
245 
246  {int i; /* keyboard input */
247  int old_mode = edit->mode;
248  for (i = 0; i < NK_KEY_MAX; ++i) {
249  if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */
250  if (nk_input_is_key_pressed(in, (enum nk_keys)i)) {
251  nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height);
252  cursor_follow = nk_true;
253  }
254  }
255  if (old_mode != edit->mode) {
256  in->keyboard.text_len = 0;
257  }}
258 
259  /* text input */
260  edit->filter = filter;
261  if (in->keyboard.text_len) {
262  nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len);
263  cursor_follow = nk_true;
264  in->keyboard.text_len = 0;
265  }
266 
267  /* enter key handler */
268  if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) {
269  cursor_follow = nk_true;
270  if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod)
271  nk_textedit_text(edit, "\n", 1);
272  else if (flags & NK_EDIT_SIG_ENTER)
273  ret |= NK_EDIT_COMMITED;
274  else nk_textedit_text(edit, "\n", 1);
275  }
276 
277  /* cut & copy handler */
278  {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY);
279  int cut = nk_input_is_key_pressed(in, NK_KEY_CUT);
280  if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD))
281  {
282  int glyph_len;
283  nk_rune unicode;
284  const char *text;
285  int b = edit->select_start;
286  int e = edit->select_end;
287 
288  int begin = NK_MIN(b, e);
289  int end = NK_MAX(b, e);
290  text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len);
291  if (edit->clip.copy)
292  edit->clip.copy(edit->clip.userdata, text, end - begin);
293  if (cut && !(flags & NK_EDIT_READ_ONLY)){
294  nk_textedit_cut(edit);
295  cursor_follow = nk_true;
296  }
297  }}
298 
299  /* paste handler */
300  {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE);
301  if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) {
302  edit->clip.paste(edit->clip.userdata, edit);
303  cursor_follow = nk_true;
304  }}
305 
306  /* tab handler */
307  {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB);
308  if (tab && (flags & NK_EDIT_ALLOW_TAB)) {
309  nk_textedit_text(edit, " ", 4);
310  cursor_follow = nk_true;
311  }}
312  }
313 
314  /* set widget state */
315  if (edit->active)
316  *state = NK_WIDGET_STATE_ACTIVE;
317  else nk_widget_state_reset(state);
318 
319  if (is_hovered)
320  *state |= NK_WIDGET_STATE_HOVERED;
321 
322  /* DRAW EDIT */
323  {const char *text = nk_str_get_const(&edit->string);
324  int len = nk_str_len_char(&edit->string);
325 
326  {/* select background colors/images */
327  const struct nk_style_item *background;
328  if (*state & NK_WIDGET_STATE_ACTIVED)
329  background = &style->active;
330  else if (*state & NK_WIDGET_STATE_HOVER)
331  background = &style->hover;
332  else background = &style->normal;
333 
334  /* draw background frame */
335  switch(background->type) {
336  case NK_STYLE_ITEM_IMAGE:
337  nk_draw_image(out, bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
338  break;
339  case NK_STYLE_ITEM_NINE_SLICE:
340  nk_draw_nine_slice(out, bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
341  break;
342  case NK_STYLE_ITEM_COLOR:
343  nk_fill_rect(out, bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor));
344  nk_stroke_rect(out, bounds, style->rounding, style->border, nk_rgb_factor(style->border_color, style->color_factor));
345  break;
346  }}
347 
348 
349  area.w = NK_MAX(0, area.w - style->cursor_size);
350  if (edit->active)
351  {
352  int total_lines = 1;
353  struct nk_vec2 text_size = nk_vec2(0,0);
354 
355  /* text pointer positions */
356  const char *cursor_ptr = 0;
357  const char *select_begin_ptr = 0;
358  const char *select_end_ptr = 0;
359 
360  /* 2D pixel positions */
361  struct nk_vec2 cursor_pos = nk_vec2(0,0);
362  struct nk_vec2 selection_offset_start = nk_vec2(0,0);
363  struct nk_vec2 selection_offset_end = nk_vec2(0,0);
364 
365  int selection_begin = NK_MIN(edit->select_start, edit->select_end);
366  int selection_end = NK_MAX(edit->select_start, edit->select_end);
367 
368  /* calculate total line count + total space + cursor/selection position */
369  float line_width = 0.0f;
370  if (text && len)
371  {
372  /* utf8 encoding */
373  float glyph_width;
374  int glyph_len = 0;
375  nk_rune unicode = 0;
376  int text_len = 0;
377  int glyphs = 0;
378  int row_begin = 0;
379 
380  glyph_len = nk_utf_decode(text, &unicode, len);
381  glyph_width = font->width(font->userdata, font->height, text, glyph_len);
382  line_width = 0;
383 
384  /* iterate all lines */
385  while ((text_len < len) && glyph_len)
386  {
387  /* set cursor 2D position and line */
388  if (!cursor_ptr && glyphs == edit->cursor)
389  {
390  int glyph_offset;
391  struct nk_vec2 out_offset;
392  struct nk_vec2 row_size;
393  const char *remaining;
394 
395  /* calculate 2d position */
396  cursor_pos.y = (float)(total_lines-1) * row_height;
397  row_size = nk_text_calculate_text_bounds(font, text+row_begin,
398  text_len-row_begin, row_height, &remaining,
399  &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE);
400  cursor_pos.x = row_size.x;
401  cursor_ptr = text + text_len;
402  }
403 
404  /* set start selection 2D position and line */
405  if (!select_begin_ptr && edit->select_start != edit->select_end &&
406  glyphs == selection_begin)
407  {
408  int glyph_offset;
409  struct nk_vec2 out_offset;
410  struct nk_vec2 row_size;
411  const char *remaining;
412 
413  /* calculate 2d position */
414  selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height;
415  row_size = nk_text_calculate_text_bounds(font, text+row_begin,
416  text_len-row_begin, row_height, &remaining,
417  &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE);
418  selection_offset_start.x = row_size.x;
419  select_begin_ptr = text + text_len;
420  }
421 
422  /* set end selection 2D position and line */
423  if (!select_end_ptr && edit->select_start != edit->select_end &&
424  glyphs == selection_end)
425  {
426  int glyph_offset;
427  struct nk_vec2 out_offset;
428  struct nk_vec2 row_size;
429  const char *remaining;
430 
431  /* calculate 2d position */
432  selection_offset_end.y = (float)(total_lines-1) * row_height;
433  row_size = nk_text_calculate_text_bounds(font, text+row_begin,
434  text_len-row_begin, row_height, &remaining,
435  &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE);
436  selection_offset_end.x = row_size.x;
437  select_end_ptr = text + text_len;
438  }
439  if (unicode == '\n') {
440  text_size.x = NK_MAX(text_size.x, line_width);
441  total_lines++;
442  line_width = 0;
443  text_len++;
444  glyphs++;
445  row_begin = text_len;
446  glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len);
447  glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len);
448  continue;
449  }
450 
451  glyphs++;
452  text_len += glyph_len;
453  line_width += (float)glyph_width;
454 
455  glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len);
456  glyph_width = font->width(font->userdata, font->height,
457  text+text_len, glyph_len);
458  continue;
459  }
460  text_size.y = (float)total_lines * row_height;
461 
462  /* handle case when cursor is at end of text buffer */
463  if (!cursor_ptr && edit->cursor == edit->string.len) {
464  cursor_pos.x = line_width;
465  cursor_pos.y = text_size.y - row_height;
466  }
467  }
468  {
469  /* scrollbar */
470  if (cursor_follow)
471  {
472  /* update scrollbar to follow cursor */
473  if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) {
474  /* horizontal scroll */
475  const float scroll_increment = area.w * 0.25f;
476  if (cursor_pos.x < edit->scrollbar.x)
477  edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment);
478  if (cursor_pos.x >= edit->scrollbar.x + area.w)
479  edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - area.w + scroll_increment);
480  } else edit->scrollbar.x = 0;
481 
482  if (flags & NK_EDIT_MULTILINE) {
483  /* vertical scroll */
484  if (cursor_pos.y < edit->scrollbar.y)
485  edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height);
486  if (cursor_pos.y >= edit->scrollbar.y + row_height)
487  edit->scrollbar.y = edit->scrollbar.y + row_height;
488  } else edit->scrollbar.y = 0;
489  }
490 
491  /* scrollbar widget */
492  if (flags & NK_EDIT_MULTILINE)
493  {
494  nk_flags ws;
495  struct nk_rect scroll;
496  float scroll_target;
497  float scroll_offset;
498  float scroll_step;
499  float scroll_inc;
500 
501  scroll = area;
502  scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x;
503  scroll.w = style->scrollbar_size.x;
504 
505  scroll_offset = edit->scrollbar.y;
506  scroll_step = scroll.h * 0.10f;
507  scroll_inc = scroll.h * 0.01f;
508  scroll_target = text_size.y;
509  edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0,
510  scroll_offset, scroll_target, scroll_step, scroll_inc,
511  &style->scrollbar, in, font);
512  }
513  }
514 
515  /* draw text */
516  {struct nk_color background_color;
517  struct nk_color text_color;
518  struct nk_color sel_background_color;
519  struct nk_color sel_text_color;
520  struct nk_color cursor_color;
521  struct nk_color cursor_text_color;
522  const struct nk_style_item *background;
523  nk_push_scissor(out, clip);
524 
525  /* select correct colors to draw */
526  if (*state & NK_WIDGET_STATE_ACTIVED) {
527  background = &style->active;
528  text_color = style->text_active;
529  sel_text_color = style->selected_text_hover;
530  sel_background_color = style->selected_hover;
531  cursor_color = style->cursor_hover;
532  cursor_text_color = style->cursor_text_hover;
533  } else if (*state & NK_WIDGET_STATE_HOVER) {
534  background = &style->hover;
535  text_color = style->text_hover;
536  sel_text_color = style->selected_text_hover;
537  sel_background_color = style->selected_hover;
538  cursor_text_color = style->cursor_text_hover;
539  cursor_color = style->cursor_hover;
540  } else {
541  background = &style->normal;
542  text_color = style->text_normal;
543  sel_text_color = style->selected_text_normal;
544  sel_background_color = style->selected_normal;
545  cursor_color = style->cursor_normal;
546  cursor_text_color = style->cursor_text_normal;
547  }
548  if (background->type == NK_STYLE_ITEM_IMAGE)
549  background_color = nk_rgba(0,0,0,0);
550  else
551  background_color = background->data.color;
552 
553  cursor_color = nk_rgb_factor(cursor_color, style->color_factor);
554  cursor_text_color = nk_rgb_factor(cursor_text_color, style->color_factor);
555 
556  if (edit->select_start == edit->select_end) {
557  /* no selection so just draw the complete text */
558  const char *begin = nk_str_get_const(&edit->string);
559  int l = nk_str_len_char(&edit->string);
560  nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
561  area.y - edit->scrollbar.y, 0, begin, l, row_height, font,
562  background_color, text_color, nk_false);
563  } else {
564  /* edit has selection so draw 1-3 text chunks */
565  if (edit->select_start != edit->select_end && selection_begin > 0){
566  /* draw unselected text before selection */
567  const char *begin = nk_str_get_const(&edit->string);
568  NK_ASSERT(select_begin_ptr);
569  nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
570  area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin),
571  row_height, font, background_color, text_color, nk_false);
572  }
573  if (edit->select_start != edit->select_end) {
574  /* draw selected text */
575  NK_ASSERT(select_begin_ptr);
576  if (!select_end_ptr) {
577  const char *begin = nk_str_get_const(&edit->string);
578  select_end_ptr = begin + nk_str_len_char(&edit->string);
579  }
580  nk_edit_draw_text(out, style,
581  area.x - edit->scrollbar.x,
582  area.y + selection_offset_start.y - edit->scrollbar.y,
583  selection_offset_start.x,
584  select_begin_ptr, (int)(select_end_ptr - select_begin_ptr),
585  row_height, font, sel_background_color, sel_text_color, nk_true);
586  }
587  if ((edit->select_start != edit->select_end &&
588  selection_end < edit->string.len))
589  {
590  /* draw unselected text after selected text */
591  const char *begin = select_end_ptr;
592  const char *end = nk_str_get_const(&edit->string) +
593  nk_str_len_char(&edit->string);
594  NK_ASSERT(select_end_ptr);
595  nk_edit_draw_text(out, style,
596  area.x - edit->scrollbar.x,
597  area.y + selection_offset_end.y - edit->scrollbar.y,
598  selection_offset_end.x,
599  begin, (int)(end - begin), row_height, font,
600  background_color, text_color, nk_true);
601  }
602  }
603 
604  /* cursor */
605  if (edit->select_start == edit->select_end)
606  {
607  if (edit->cursor >= nk_str_len(&edit->string) ||
608  (cursor_ptr && *cursor_ptr == '\n')) {
609  /* draw cursor at end of line */
610  struct nk_rect cursor;
611  cursor.w = style->cursor_size;
612  cursor.h = font->height;
613  cursor.x = area.x + cursor_pos.x - edit->scrollbar.x;
614  cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f;
615  cursor.y -= edit->scrollbar.y;
616  nk_fill_rect(out, cursor, 0, cursor_color);
617  } else {
618  /* draw cursor inside text */
619  int glyph_len;
620  struct nk_rect label;
621  struct nk_text txt;
622 
623  nk_rune unicode;
624  NK_ASSERT(cursor_ptr);
625  glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4);
626 
627  label.x = area.x + cursor_pos.x - edit->scrollbar.x;
628  label.y = area.y + cursor_pos.y - edit->scrollbar.y;
629  label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len);
630  label.h = row_height;
631 
632  txt.padding = nk_vec2(0,0);
633  txt.background = cursor_color;;
634  txt.text = cursor_text_color;
635  nk_fill_rect(out, label, 0, cursor_color);
636  nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font);
637  }
638  }}
639  } else {
640  /* not active so just draw text */
641  int l = nk_str_len_char(&edit->string);
642  const char *begin = nk_str_get_const(&edit->string);
643 
644  const struct nk_style_item *background;
645  struct nk_color background_color;
646  struct nk_color text_color;
647  nk_push_scissor(out, clip);
648  if (*state & NK_WIDGET_STATE_ACTIVED) {
649  background = &style->active;
650  text_color = style->text_active;
651  } else if (*state & NK_WIDGET_STATE_HOVER) {
652  background = &style->hover;
653  text_color = style->text_hover;
654  } else {
655  background = &style->normal;
656  text_color = style->text_normal;
657  }
658  if (background->type == NK_STYLE_ITEM_IMAGE)
659  background_color = nk_rgba(0,0,0,0);
660  else
661  background_color = background->data.color;
662 
663  background_color = nk_rgb_factor(background_color, style->color_factor);
664  text_color = nk_rgb_factor(text_color, style->color_factor);
665 
666  nk_edit_draw_text(out, style, area.x - edit->scrollbar.x,
667  area.y - edit->scrollbar.y, 0, begin, l, row_height, font,
668  background_color, text_color, nk_false);
669  }
670  nk_push_scissor(out, old_clip);}
671  return ret;
672 }
673 NK_API void
674 nk_edit_focus(struct nk_context *ctx, nk_flags flags)
675 {
676  nk_hash hash;
677  struct nk_window *win;
678 
679  NK_ASSERT(ctx);
680  NK_ASSERT(ctx->current);
681  if (!ctx || !ctx->current) return;
682 
683  win = ctx->current;
684  hash = win->edit.seq;
685  win->edit.active = nk_true;
686  win->edit.name = hash;
687  if (flags & NK_EDIT_ALWAYS_INSERT_MODE)
688  win->edit.mode = NK_TEXT_EDIT_MODE_INSERT;
689 }
690 NK_API void
691 nk_edit_unfocus(struct nk_context *ctx)
692 {
693  struct nk_window *win;
694  NK_ASSERT(ctx);
695  NK_ASSERT(ctx->current);
696  if (!ctx || !ctx->current) return;
697 
698  win = ctx->current;
699  win->edit.active = nk_false;
700  win->edit.name = 0;
701 }
702 NK_API nk_flags
703 nk_edit_string(struct nk_context *ctx, nk_flags flags,
704  char *memory, int *len, int max, nk_plugin_filter filter)
705 {
706  nk_hash hash;
707  nk_flags state;
708  struct nk_text_edit *edit;
709  struct nk_window *win;
710 
711  NK_ASSERT(ctx);
712  NK_ASSERT(memory);
713  NK_ASSERT(len);
714  if (!ctx || !memory || !len)
715  return 0;
716 
717  filter = (!filter) ? nk_filter_default: filter;
718  win = ctx->current;
719  hash = win->edit.seq;
720  edit = &ctx->text_edit;
721  nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)?
722  NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter);
723 
724  if (win->edit.active && hash == win->edit.name) {
725  if (flags & NK_EDIT_NO_CURSOR)
726  edit->cursor = nk_utf_len(memory, *len);
727  else edit->cursor = win->edit.cursor;
728  if (!(flags & NK_EDIT_SELECTABLE)) {
729  edit->select_start = win->edit.cursor;
730  edit->select_end = win->edit.cursor;
731  } else {
732  edit->select_start = win->edit.sel_start;
733  edit->select_end = win->edit.sel_end;
734  }
735  edit->mode = win->edit.mode;
736  edit->scrollbar.x = (float)win->edit.scrollbar.x;
737  edit->scrollbar.y = (float)win->edit.scrollbar.y;
738  edit->active = nk_true;
739  } else edit->active = nk_false;
740 
741  max = NK_MAX(1, max);
742  *len = NK_MIN(*len, max-1);
743  nk_str_init_fixed(&edit->string, memory, (nk_size)max);
744  edit->string.buffer.allocated = (nk_size)*len;
745  edit->string.len = nk_utf_len(memory, *len);
746  state = nk_edit_buffer(ctx, flags, edit, filter);
747  *len = (int)edit->string.buffer.allocated;
748 
749  if (edit->active) {
750  win->edit.cursor = edit->cursor;
751  win->edit.sel_start = edit->select_start;
752  win->edit.sel_end = edit->select_end;
753  win->edit.mode = edit->mode;
754  win->edit.scrollbar.x = (nk_uint)edit->scrollbar.x;
755  win->edit.scrollbar.y = (nk_uint)edit->scrollbar.y;
756  } return state;
757 }
758 NK_API nk_flags
759 nk_edit_buffer(struct nk_context *ctx, nk_flags flags,
760  struct nk_text_edit *edit, nk_plugin_filter filter)
761 {
762  struct nk_window *win;
763  struct nk_style *style;
764  struct nk_input *in;
765 
766  enum nk_widget_layout_states state;
767  struct nk_rect bounds;
768 
769  nk_flags ret_flags = 0;
770  unsigned char prev_state;
771  nk_hash hash;
772 
773  /* make sure correct values */
774  NK_ASSERT(ctx);
775  NK_ASSERT(edit);
776  NK_ASSERT(ctx->current);
777  NK_ASSERT(ctx->current->layout);
778  if (!ctx || !ctx->current || !ctx->current->layout)
779  return 0;
780 
781  win = ctx->current;
782  style = &ctx->style;
783  state = nk_widget(&bounds, ctx);
784  if (!state) return state;
785  else if (state == NK_WIDGET_DISABLED)
786  flags |= NK_EDIT_READ_ONLY;
787  in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
788 
789  /* check if edit is currently hot item */
790  hash = win->edit.seq++;
791  if (win->edit.active && hash == win->edit.name) {
792  if (flags & NK_EDIT_NO_CURSOR)
793  edit->cursor = edit->string.len;
794  if (!(flags & NK_EDIT_SELECTABLE)) {
795  edit->select_start = edit->cursor;
796  edit->select_end = edit->cursor;
797  }
798  if (flags & NK_EDIT_CLIPBOARD)
799  edit->clip = ctx->clip;
800  edit->active = (unsigned char)win->edit.active;
801  } else edit->active = nk_false;
802  edit->mode = win->edit.mode;
803 
804  filter = (!filter) ? nk_filter_default: filter;
805  prev_state = (unsigned char)edit->active;
806  in = (flags & NK_EDIT_READ_ONLY) ? 0: in;
807  ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags,
808  filter, edit, &style->edit, in, style->font);
809 
810  if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
811  ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT];
812  if (edit->active && prev_state != edit->active) {
813  /* current edit is now hot */
814  win->edit.active = nk_true;
815  win->edit.name = hash;
816  } else if (prev_state && !edit->active) {
817  /* current edit is now cold */
818  win->edit.active = nk_false;
819  } return ret_flags;
820 }
821 NK_API nk_flags
822 nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags,
823  char *buffer, int max, nk_plugin_filter filter)
824 {
825  nk_flags result;
826  int len = nk_strlen(buffer);
827  result = nk_edit_string(ctx, flags, buffer, &len, max, filter);
828  buffer[NK_MIN(NK_MAX(max-1,0), len)] = '\0';
829  return result;
830 }
831 
main API and documentation file
@ NK_WINDOW_ROM
sets window widgets into a read only mode and does not allow input changes
Definition: nuklear.h:5492
NK_API void nk_draw_image(struct nk_command_buffer *, struct nk_rect, const struct nk_image *, struct nk_color)
misc
Definition: nuklear_draw.c:395
NK_API void nk_fill_rect(struct nk_command_buffer *, struct nk_rect, float rounding, struct nk_color)
filled shades
Definition: nuklear_draw.c:152
NK_API nk_bool nk_filter_default(const struct nk_text_edit *, nk_rune unicode)
filter function
Definition: nuklear_edit.c:10
@ NK_WIDGET_STATE_ACTIVED
!< widget is being hovered
Definition: nuklear.h:3092
@ NK_WIDGET_STATE_HOVER
!< widget has been hovered on the current frame
Definition: nuklear.h:3091
@ NK_WIDGET_STATE_ACTIVE
!< widget is being hovered
Definition: nuklear.h:3095
@ NK_WIDGET_STATE_HOVERED
!< widget is from this frame on not hovered anymore
Definition: nuklear.h:3094
nk_widget_layout_states
Definition: nuklear.h:3081
@ NK_WIDGET_DISABLED
The widget is manually disabled and acts like NK_WIDGET_ROM.
Definition: nuklear.h:3085
@ NK_EDIT_INACTIVE
!< edit widget is currently being modified
Definition: nuklear.h:3503
@ NK_EDIT_DEACTIVATED
!< edit widget went from state inactive to state active
Definition: nuklear.h:3505
@ NK_EDIT_COMMITED
!< edit widget went from state active to state inactive
Definition: nuklear.h:3506
@ NK_EDIT_ACTIVATED
!< edit widget is not active and is not being modified
Definition: nuklear.h:3504
nk_size allocated
!< growing factor for dynamic memory management
Definition: nuklear.h:4195
struct nk_text_edit text_edit
text editor objects are quite big because of an internal undo/redo stack.
Definition: nuklear.h:5728
nk_text_width_f width
!< max height of the font
Definition: nuklear.h:4009
float height
!< user provided font handle
Definition: nuklear.h:4008