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_property.c
1 #include "nuklear.h"
2 #include "nuklear_internal.h"
3 
4 /* ===============================================================
5  *
6  * PROPERTY
7  *
8  * ===============================================================*/
9 NK_LIB void
10 nk_drag_behavior(nk_flags *state, const struct nk_input *in,
11  struct nk_rect drag, struct nk_property_variant *variant,
12  float inc_per_pixel)
13 {
14  int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down;
15  int left_mouse_click_in_cursor = in &&
16  nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true);
17 
18  nk_widget_state_reset(state);
19  if (nk_input_is_mouse_hovering_rect(in, drag))
20  *state = NK_WIDGET_STATE_HOVERED;
21 
22  if (left_mouse_down && left_mouse_click_in_cursor) {
23  float delta, pixels;
24  pixels = in->mouse.delta.x;
25  delta = pixels * inc_per_pixel;
26  switch (variant->kind) {
27  default: break;
28  case NK_PROPERTY_INT:
29  variant->value.i = variant->value.i + (int)delta;
30  variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i);
31  break;
32  case NK_PROPERTY_FLOAT:
33  variant->value.f = variant->value.f + (float)delta;
34  variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f);
35  break;
36  case NK_PROPERTY_DOUBLE:
37  variant->value.d = variant->value.d + (double)delta;
38  variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d);
39  break;
40  }
41  *state = NK_WIDGET_STATE_ACTIVE;
42  }
43  if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag))
44  *state |= NK_WIDGET_STATE_ENTERED;
45  else if (nk_input_is_mouse_prev_hovering_rect(in, drag))
46  *state |= NK_WIDGET_STATE_LEFT;
47 }
48 NK_LIB void
49 nk_property_behavior(nk_flags *ws, const struct nk_input *in,
50  struct nk_rect property, struct nk_rect label, struct nk_rect edit,
51  struct nk_rect empty, int *state, struct nk_property_variant *variant,
52  float inc_per_pixel)
53 {
54  nk_widget_state_reset(ws);
55  if (in && *state == NK_PROPERTY_DEFAULT) {
56  if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT))
57  *state = NK_PROPERTY_EDIT;
58  else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true))
59  *state = NK_PROPERTY_DRAG;
60  else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true))
61  *state = NK_PROPERTY_DRAG;
62  }
63  if (*state == NK_PROPERTY_DRAG) {
64  nk_drag_behavior(ws, in, property, variant, inc_per_pixel);
65  if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT;
66  }
67 }
68 NK_LIB void
69 nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style,
70  const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state,
71  const char *name, int len, const struct nk_user_font *font)
72 {
73  struct nk_text text;
74  const struct nk_style_item *background;
75 
76  /* select correct background and text color */
77  if (state & NK_WIDGET_STATE_ACTIVED) {
78  background = &style->active;
79  text.text = style->label_active;
80  } else if (state & NK_WIDGET_STATE_HOVER) {
81  background = &style->hover;
82  text.text = style->label_hover;
83  } else {
84  background = &style->normal;
85  text.text = style->label_normal;
86  }
87 
88  text.text = nk_rgb_factor(text.text, style->color_factor);
89 
90  /* draw background */
91  switch(background->type) {
92  case NK_STYLE_ITEM_IMAGE:
93  text.background = nk_rgba(0, 0, 0, 0);
94  nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
95  break;
96  case NK_STYLE_ITEM_NINE_SLICE:
97  text.background = nk_rgba(0, 0, 0, 0);
98  nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
99  break;
100  case NK_STYLE_ITEM_COLOR:
101  text.background = background->data.color;
102  nk_fill_rect(out, *bounds, style->rounding, nk_rgb_factor(background->data.color, style->color_factor));
103  nk_stroke_rect(out, *bounds, style->rounding, style->border, nk_rgb_factor(background->data.color, style->color_factor));
104  break;
105  }
106 
107  /* draw label */
108  text.padding = nk_vec2(0,0);
109  if (name && name[0] != '#') {
110  nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font);
111  }
112 }
113 NK_LIB void
114 nk_do_property(nk_flags *ws,
115  struct nk_command_buffer *out, struct nk_rect property,
116  const char *name, struct nk_property_variant *variant,
117  float inc_per_pixel, char *buffer, int *len,
118  int *state, int *cursor, int *select_begin, int *select_end,
119  const struct nk_style_property *style,
120  enum nk_property_filter filter, struct nk_input *in,
121  const struct nk_user_font *font, struct nk_text_edit *text_edit,
122  enum nk_button_behavior behavior)
123 {
124  const nk_plugin_filter filters[] = {
125  nk_filter_decimal,
126  nk_filter_float
127  };
128  nk_bool active, old;
129  int num_len = 0, name_len = 0;
130  char string[NK_MAX_NUMBER_BUFFER];
131  float size;
132 
133  char *dst = 0;
134  int *length;
135 
136  struct nk_rect left;
137  struct nk_rect right;
138  struct nk_rect label;
139  struct nk_rect edit;
140  struct nk_rect empty;
141 
142  /* left decrement button */
143  left.h = font->height/2;
144  left.w = left.h;
145  left.x = property.x + style->border + style->padding.x;
146  left.y = property.y + style->border + property.h/2.0f - left.h/2;
147 
148  /* text label */
149  if (name && name[0] != '#') {
150  name_len = nk_strlen(name);
151  }
152  size = font->width(font->userdata, font->height, name, name_len);
153  label.x = left.x + left.w + style->padding.x;
154  label.w = (float)size + 2 * style->padding.x;
155  label.y = property.y + style->border + style->padding.y;
156  label.h = property.h - (2 * style->border + 2 * style->padding.y);
157 
158  /* right increment button */
159  right.y = left.y;
160  right.w = left.w;
161  right.h = left.h;
162  right.x = property.x + property.w - (right.w + style->padding.x);
163 
164  /* edit */
165  if (*state == NK_PROPERTY_EDIT) {
166  size = font->width(font->userdata, font->height, buffer, *len);
167  size += style->edit.cursor_size;
168  length = len;
169  dst = buffer;
170  } else {
171  switch (variant->kind) {
172  default: break;
173  case NK_PROPERTY_INT:
174  nk_itoa(string, variant->value.i);
175  num_len = nk_strlen(string);
176  break;
177  case NK_PROPERTY_FLOAT:
178  NK_DTOA(string, (double)variant->value.f);
179  num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION);
180  break;
181  case NK_PROPERTY_DOUBLE:
182  NK_DTOA(string, variant->value.d);
183  num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION);
184  break;
185  }
186  size = font->width(font->userdata, font->height, string, num_len);
187  dst = string;
188  length = &num_len;
189  }
190 
191  edit.w = (float)size + 2 * style->padding.x;
192  edit.w = NK_MIN(edit.w, right.x - (label.x + label.w));
193  edit.x = right.x - (edit.w + style->padding.x);
194  edit.y = property.y + style->border;
195  edit.h = property.h - (2 * style->border);
196 
197  /* empty left space activator */
198  empty.w = edit.x - (label.x + label.w);
199  empty.x = label.x + label.w;
200  empty.y = property.y;
201  empty.h = property.h;
202 
203  /* update property */
204  old = (*state == NK_PROPERTY_EDIT);
205  nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel);
206 
207  /* draw property */
208  if (style->draw_begin) style->draw_begin(out, style->userdata);
209  nk_draw_property(out, style, &property, &label, *ws, name, name_len, font);
210  if (style->draw_end) style->draw_end(out, style->userdata);
211 
212  /* execute right button */
213  if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) {
214  switch (variant->kind) {
215  default: break;
216  case NK_PROPERTY_INT:
217  variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break;
218  case NK_PROPERTY_FLOAT:
219  variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break;
220  case NK_PROPERTY_DOUBLE:
221  variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break;
222  }
223  }
224  /* execute left button */
225  if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) {
226  switch (variant->kind) {
227  default: break;
228  case NK_PROPERTY_INT:
229  variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break;
230  case NK_PROPERTY_FLOAT:
231  variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break;
232  case NK_PROPERTY_DOUBLE:
233  variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break;
234  }
235  }
236  if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) {
237  /* property has been activated so setup buffer */
238  NK_MEMCPY(buffer, dst, (nk_size)*length);
239  *cursor = nk_utf_len(buffer, *length);
240  *len = *length;
241  length = len;
242  dst = buffer;
243  active = 0;
244  } else active = (*state == NK_PROPERTY_EDIT);
245 
246  /* execute and run text edit field */
247  nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]);
248  text_edit->active = (unsigned char)active;
249  text_edit->string.len = *length;
250  text_edit->cursor = NK_CLAMP(0, *cursor, *length);
251  text_edit->select_start = NK_CLAMP(0,*select_begin, *length);
252  text_edit->select_end = NK_CLAMP(0,*select_end, *length);
253  text_edit->string.buffer.allocated = (nk_size)*length;
254  text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER;
255  text_edit->string.buffer.memory.ptr = dst;
256  text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER;
257  text_edit->mode = NK_TEXT_EDIT_MODE_INSERT;
258  nk_do_edit(ws, out, edit, (int)NK_EDIT_FIELD|(int)NK_EDIT_AUTO_SELECT,
259  filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font);
260 
261  *length = text_edit->string.len;
262  *cursor = text_edit->cursor;
263  *select_begin = text_edit->select_start;
264  *select_end = text_edit->select_end;
265  if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER))
266  text_edit->active = nk_false;
267 
268  if (active && !text_edit->active) {
269  /* property is now not active so convert edit text to value*/
270  *state = NK_PROPERTY_DEFAULT;
271  buffer[*len] = '\0';
272  switch (variant->kind) {
273  default: break;
274  case NK_PROPERTY_INT:
275  variant->value.i = nk_strtoi(buffer, 0);
276  variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i);
277  break;
278  case NK_PROPERTY_FLOAT:
279  nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION);
280  variant->value.f = nk_strtof(buffer, 0);
281  variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f);
282  break;
283  case NK_PROPERTY_DOUBLE:
284  nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION);
285  variant->value.d = nk_strtod(buffer, 0);
286  variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d);
287  break;
288  }
289  }
290 }
291 NK_LIB struct nk_property_variant
292 nk_property_variant_int(int value, int min_value, int max_value, int step)
293 {
294  struct nk_property_variant result;
295  result.kind = NK_PROPERTY_INT;
296  result.value.i = value;
297  result.min_value.i = min_value;
298  result.max_value.i = max_value;
299  result.step.i = step;
300  return result;
301 }
302 NK_LIB struct nk_property_variant
303 nk_property_variant_float(float value, float min_value, float max_value, float step)
304 {
305  struct nk_property_variant result;
306  result.kind = NK_PROPERTY_FLOAT;
307  result.value.f = value;
308  result.min_value.f = min_value;
309  result.max_value.f = max_value;
310  result.step.f = step;
311  return result;
312 }
313 NK_LIB struct nk_property_variant
314 nk_property_variant_double(double value, double min_value, double max_value,
315  double step)
316 {
317  struct nk_property_variant result;
318  result.kind = NK_PROPERTY_DOUBLE;
319  result.value.d = value;
320  result.min_value.d = min_value;
321  result.max_value.d = max_value;
322  result.step.d = step;
323  return result;
324 }
325 NK_LIB void
326 nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant,
327  float inc_per_pixel, const enum nk_property_filter filter)
328 {
329  struct nk_window *win;
330  struct nk_panel *layout;
331  struct nk_input *in;
332  const struct nk_style *style;
333 
334  struct nk_rect bounds;
336 
337  int *state = 0;
338  nk_hash hash = 0;
339  char *buffer = 0;
340  int *len = 0;
341  int *cursor = 0;
342  int *select_begin = 0;
343  int *select_end = 0;
344  int old_state;
345 
346  char dummy_buffer[NK_MAX_NUMBER_BUFFER];
347  int dummy_state = NK_PROPERTY_DEFAULT;
348  int dummy_length = 0;
349  int dummy_cursor = 0;
350  int dummy_select_begin = 0;
351  int dummy_select_end = 0;
352 
353  NK_ASSERT(ctx);
354  NK_ASSERT(ctx->current);
355  NK_ASSERT(ctx->current->layout);
356  if (!ctx || !ctx->current || !ctx->current->layout)
357  return;
358 
359  win = ctx->current;
360  layout = win->layout;
361  style = &ctx->style;
362  s = nk_widget(&bounds, ctx);
363  if (!s) return;
364 
365  /* calculate hash from name */
366  if (name[0] == '#') {
367  hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++);
368  name++; /* special number hash */
369  } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42);
370 
371  /* check if property is currently hot item */
372  if (win->property.active && hash == win->property.name) {
373  buffer = win->property.buffer;
374  len = &win->property.length;
375  cursor = &win->property.cursor;
376  state = &win->property.state;
377  select_begin = &win->property.select_start;
378  select_end = &win->property.select_end;
379  } else {
380  buffer = dummy_buffer;
381  len = &dummy_length;
382  cursor = &dummy_cursor;
383  state = &dummy_state;
384  select_begin = &dummy_select_begin;
385  select_end = &dummy_select_end;
386  }
387 
388  /* execute property widget */
389  old_state = *state;
390  ctx->text_edit.clip = ctx->clip;
391  in = ((s == NK_WIDGET_ROM && !win->property.active) ||
392  layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_DISABLED) ? 0 : &ctx->input;
393  nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name,
394  variant, inc_per_pixel, buffer, len, state, cursor, select_begin,
395  select_end, &style->property, filter, in, style->font, &ctx->text_edit,
396  ctx->button_behavior);
397 
398  if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) {
399  /* current property is now hot */
400  win->property.active = 1;
401  NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len);
402  win->property.length = *len;
403  win->property.cursor = *cursor;
404  win->property.state = *state;
405  win->property.name = hash;
406  win->property.select_start = *select_begin;
407  win->property.select_end = *select_end;
408  if (*state == NK_PROPERTY_DRAG) {
409  ctx->input.mouse.grab = nk_true;
410  ctx->input.mouse.grabbed = nk_true;
411  }
412  }
413  /* check if previously active property is now inactive */
414  if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) {
415  if (old_state == NK_PROPERTY_DRAG) {
416  ctx->input.mouse.grab = nk_false;
417  ctx->input.mouse.grabbed = nk_false;
418  ctx->input.mouse.ungrab = nk_true;
419  }
420  win->property.select_start = 0;
421  win->property.select_end = 0;
422  win->property.active = 0;
423  }
424 }
425 NK_API void
426 nk_property_int(struct nk_context *ctx, const char *name,
427  int min, int *val, int max, int step, float inc_per_pixel)
428 {
429  struct nk_property_variant variant;
430  NK_ASSERT(ctx);
431  NK_ASSERT(name);
432  NK_ASSERT(val);
433 
434  if (!ctx || !ctx->current || !name || !val) return;
435  variant = nk_property_variant_int(*val, min, max, step);
436  nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT);
437  *val = variant.value.i;
438 }
439 NK_API void
440 nk_property_float(struct nk_context *ctx, const char *name,
441  float min, float *val, float max, float step, float inc_per_pixel)
442 {
443  struct nk_property_variant variant;
444  NK_ASSERT(ctx);
445  NK_ASSERT(name);
446  NK_ASSERT(val);
447 
448  if (!ctx || !ctx->current || !name || !val) return;
449  variant = nk_property_variant_float(*val, min, max, step);
450  nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
451  *val = variant.value.f;
452 }
453 NK_API void
454 nk_property_double(struct nk_context *ctx, const char *name,
455  double min, double *val, double max, double step, float inc_per_pixel)
456 {
457  struct nk_property_variant variant;
458  NK_ASSERT(ctx);
459  NK_ASSERT(name);
460  NK_ASSERT(val);
461 
462  if (!ctx || !ctx->current || !name || !val) return;
463  variant = nk_property_variant_double(*val, min, max, step);
464  nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
465  *val = variant.value.d;
466 }
467 NK_API int
468 nk_propertyi(struct nk_context *ctx, const char *name, int min, int val,
469  int max, int step, float inc_per_pixel)
470 {
471  struct nk_property_variant variant;
472  NK_ASSERT(ctx);
473  NK_ASSERT(name);
474 
475  if (!ctx || !ctx->current || !name) return val;
476  variant = nk_property_variant_int(val, min, max, step);
477  nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT);
478  val = variant.value.i;
479  return val;
480 }
481 NK_API float
482 nk_propertyf(struct nk_context *ctx, const char *name, float min,
483  float val, float max, float step, float inc_per_pixel)
484 {
485  struct nk_property_variant variant;
486  NK_ASSERT(ctx);
487  NK_ASSERT(name);
488 
489  if (!ctx || !ctx->current || !name) return val;
490  variant = nk_property_variant_float(val, min, max, step);
491  nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
492  val = variant.value.f;
493  return val;
494 }
495 NK_API double
496 nk_propertyd(struct nk_context *ctx, const char *name, double min,
497  double val, double max, double step, float inc_per_pixel)
498 {
499  struct nk_property_variant variant;
500  NK_ASSERT(ctx);
501  NK_ASSERT(name);
502 
503  if (!ctx || !ctx->current || !name) return val;
504  variant = nk_property_variant_double(val, min, max, step);
505  nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT);
506  val = variant.value.d;
507  return val;
508 }
509 
main API and documentation file
NK_API float nk_propertyf(struct nk_context *, const char *name, float min, float val, float max, float step, float inc_per_pixel)
NK_API void nk_property_float(struct nk_context *, const char *name, float min, float *val, float max, float step, float inc_per_pixel)
@ 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_property_double(struct nk_context *, const char *name, double min, double *val, double max, double step, float inc_per_pixel)
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_WIDGET_STATE_LEFT
!< widget is currently activated
Definition: nuklear.h:3093
@ NK_WIDGET_STATE_ACTIVED
!< widget is being hovered
Definition: nuklear.h:3092
@ NK_WIDGET_STATE_ENTERED
!< widget is neither active nor hovered
Definition: nuklear.h:3090
@ 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_WIDGET_ROM
The widget is partially visible and cannot be updated.
Definition: nuklear.h:3084
NK_API double nk_propertyd(struct nk_context *, const char *name, double min, double val, double max, double step, float inc_per_pixel)
NK_API int nk_propertyi(struct nk_context *, const char *name, int min, int val, int max, int step, float inc_per_pixel)
struct nk_memory memory
!< memory management type
Definition: nuklear.h:4193
nk_size size
!< number of allocation calls
Definition: nuklear.h:4198
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