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