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