add knob widget
This commit is contained in:
parent
d0c557dd34
commit
3d5eb27f85
|
@ -0,0 +1,250 @@
|
||||||
|
#include "nuklear.h"
|
||||||
|
#include "nuklear_internal.h"
|
||||||
|
|
||||||
|
/* ===============================================================
|
||||||
|
*
|
||||||
|
* KNOB
|
||||||
|
*
|
||||||
|
* ===============================================================*/
|
||||||
|
|
||||||
|
NK_LIB float
|
||||||
|
nk_knob_behavior(nk_flags *state, struct nk_input *in,
|
||||||
|
struct nk_rect bounds, float knob_min, float knob_max, float knob_value,
|
||||||
|
float knob_step, float knob_steps,
|
||||||
|
enum nk_heading zero_direction, float dead_zone_percent)
|
||||||
|
{
|
||||||
|
struct nk_vec2 origin;
|
||||||
|
float angle = 0.0f;
|
||||||
|
origin.x = bounds.x + (bounds.w / 2);
|
||||||
|
origin.y = bounds.y + (bounds.h / 2);
|
||||||
|
|
||||||
|
nk_widget_state_reset(state);
|
||||||
|
|
||||||
|
/* handle click and drag input */
|
||||||
|
if(in &&
|
||||||
|
in->mouse.buttons[NK_BUTTON_LEFT].down &&
|
||||||
|
nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, bounds, nk_true)){
|
||||||
|
/* calculate angle from origin and rotate */
|
||||||
|
const float direction_rads[4] = {
|
||||||
|
NK_PI * 2.5f, /* 90 NK_UP */
|
||||||
|
NK_PI * 2.0f, /* 0 NK_RIGHT */
|
||||||
|
NK_PI * 1.5f, /* 270 NK_DOWN */
|
||||||
|
NK_PI, /* 180 NK_LEFT */
|
||||||
|
};
|
||||||
|
*state = NK_WIDGET_STATE_ACTIVE;
|
||||||
|
|
||||||
|
angle = NK_ATAN2(in->mouse.pos.y - origin.y, in->mouse.pos.x - origin.x) + direction_rads[zero_direction];
|
||||||
|
angle -= (angle > NK_PI * 2) ? NK_PI * 3 : NK_PI;
|
||||||
|
|
||||||
|
/* account for dead space applied when drawing */
|
||||||
|
angle *= 1.0f / (1.0f - dead_zone_percent);
|
||||||
|
angle = NK_CLAMP(-NK_PI, angle, NK_PI);
|
||||||
|
|
||||||
|
/* convert -pi -> pi range to 0.0 -> 1.0 */
|
||||||
|
angle = (angle + NK_PI) / (NK_PI * 2);
|
||||||
|
|
||||||
|
/* click to closest step */
|
||||||
|
knob_value = knob_min + ( (int)(angle * knob_steps + (knob_step / 2)) ) * knob_step;
|
||||||
|
knob_value = NK_CLAMP(knob_min, knob_value, knob_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* knob widget state */
|
||||||
|
if (nk_input_is_mouse_hovering_rect(in, bounds)){
|
||||||
|
*state = NK_WIDGET_STATE_HOVERED;
|
||||||
|
if (in) {
|
||||||
|
/* handle scroll and arrow inputs */
|
||||||
|
if (in->mouse.scroll_delta.y > 0 ||
|
||||||
|
(in->keyboard.keys[NK_KEY_UP].down && in->keyboard.keys[NK_KEY_UP].clicked))
|
||||||
|
knob_value += knob_step;
|
||||||
|
|
||||||
|
if (in->mouse.scroll_delta.y < 0 ||
|
||||||
|
(in->keyboard.keys[NK_KEY_DOWN].down && in->keyboard.keys[NK_KEY_DOWN].clicked))
|
||||||
|
knob_value -= knob_step;
|
||||||
|
}
|
||||||
|
knob_value = NK_CLAMP(knob_min, knob_value, knob_max);
|
||||||
|
}
|
||||||
|
if (*state & NK_WIDGET_STATE_HOVER &&
|
||||||
|
!nk_input_is_mouse_prev_hovering_rect(in, bounds))
|
||||||
|
*state |= NK_WIDGET_STATE_ENTERED;
|
||||||
|
else if (nk_input_is_mouse_prev_hovering_rect(in, bounds))
|
||||||
|
*state |= NK_WIDGET_STATE_LEFT;
|
||||||
|
|
||||||
|
return knob_value;
|
||||||
|
}
|
||||||
|
NK_LIB void
|
||||||
|
nk_draw_knob(struct nk_command_buffer *out, nk_flags state,
|
||||||
|
const struct nk_style_knob *style, const struct nk_rect *bounds, float min, float value, float max,
|
||||||
|
enum nk_heading zero_direction, float dead_zone_percent)
|
||||||
|
{
|
||||||
|
const struct nk_style_item *background;
|
||||||
|
struct nk_color knob_color, cursor;
|
||||||
|
|
||||||
|
NK_UNUSED(min);
|
||||||
|
NK_UNUSED(max);
|
||||||
|
NK_UNUSED(value);
|
||||||
|
|
||||||
|
if (state & NK_WIDGET_STATE_ACTIVED) {
|
||||||
|
background = &style->active;
|
||||||
|
knob_color = style->knob_active;
|
||||||
|
cursor = style->cursor_active;
|
||||||
|
} else if (state & NK_WIDGET_STATE_HOVER) {
|
||||||
|
background = &style->hover;
|
||||||
|
knob_color = style->knob_hover;
|
||||||
|
cursor = style->cursor_hover;
|
||||||
|
} else {
|
||||||
|
background = &style->normal;
|
||||||
|
knob_color = style->knob_normal;
|
||||||
|
cursor = style->cursor_normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw background */
|
||||||
|
switch(background->type) {
|
||||||
|
case NK_STYLE_ITEM_IMAGE:
|
||||||
|
nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
|
||||||
|
break;
|
||||||
|
case NK_STYLE_ITEM_NINE_SLICE:
|
||||||
|
nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
|
||||||
|
break;
|
||||||
|
case NK_STYLE_ITEM_COLOR:
|
||||||
|
nk_fill_rect(out, *bounds, 0, nk_rgb_factor(background->data.color, style->color_factor));
|
||||||
|
nk_stroke_rect(out, *bounds, 0, style->border, nk_rgb_factor(style->border_color, style->color_factor));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw knob */
|
||||||
|
nk_fill_circle(out, *bounds, nk_rgb_factor(knob_color, style->color_factor));
|
||||||
|
if(style->knob_border > 0){
|
||||||
|
struct nk_rect border_bounds = *bounds;
|
||||||
|
border_bounds.x += style->knob_border / 2;
|
||||||
|
border_bounds.y += style->knob_border / 2;
|
||||||
|
border_bounds.w -= style->knob_border;
|
||||||
|
border_bounds.h -= style->knob_border;
|
||||||
|
nk_stroke_circle(out, border_bounds, style->knob_border, nk_rgb_factor(style->knob_border_color, style->color_factor));
|
||||||
|
}
|
||||||
|
{ /* calculate cursor line cords */
|
||||||
|
float half_circle_size = (bounds->w / 2);
|
||||||
|
float angle = (value - min) / (max - min);
|
||||||
|
float alive_zone = 1.0f - dead_zone_percent;
|
||||||
|
struct nk_vec2 cursor_start, cursor_end;
|
||||||
|
const float direction_rads[4] = {
|
||||||
|
NK_PI * 1.5f, /* 90 NK_UP */
|
||||||
|
0.0f, /* 0 NK_RIGHT */
|
||||||
|
NK_PI * 0.5f, /* 270 NK_DOWN */
|
||||||
|
NK_PI, /* 180 NK_LEFT */
|
||||||
|
};
|
||||||
|
/* calculate + apply dead zone */
|
||||||
|
angle = (angle * alive_zone) + (dead_zone_percent / 2);
|
||||||
|
|
||||||
|
/* percentage 0.0 -> 1.0 to radians, rads are 0.0 to (2*pi) NOT -pi to pi */
|
||||||
|
angle *= NK_PI * 2;
|
||||||
|
|
||||||
|
/* apply zero angle */
|
||||||
|
angle += direction_rads[zero_direction];
|
||||||
|
if(angle > NK_PI * 2)
|
||||||
|
angle -= NK_PI * 2;
|
||||||
|
|
||||||
|
cursor_start.x = bounds->x + half_circle_size + (angle > NK_PI);
|
||||||
|
cursor_start.y = bounds->y + half_circle_size + (angle < NK_PI_HALF || angle > (NK_PI * 1.5f));
|
||||||
|
|
||||||
|
cursor_end.x = cursor_start.x + (half_circle_size * NK_COS(angle));
|
||||||
|
cursor_end.y = cursor_start.y + (half_circle_size * NK_SIN(angle));
|
||||||
|
|
||||||
|
/* cut off half of the cursor */
|
||||||
|
cursor_start.x = (cursor_start.x + cursor_end.x) / 2;
|
||||||
|
cursor_start.y = (cursor_start.y + cursor_end.y) / 2;
|
||||||
|
|
||||||
|
/* draw cursor */
|
||||||
|
nk_stroke_line(out, cursor_start.x, cursor_start.y, cursor_end.x, cursor_end.y, 2, nk_rgb_factor(cursor, style->color_factor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NK_LIB float
|
||||||
|
nk_do_knob(nk_flags *state,
|
||||||
|
struct nk_command_buffer *out, struct nk_rect bounds,
|
||||||
|
float min, float val, float max, float step,
|
||||||
|
enum nk_heading zero_direction, float dead_zone_percent,
|
||||||
|
const struct nk_style_knob *style, struct nk_input *in)
|
||||||
|
{
|
||||||
|
float knob_range;
|
||||||
|
float knob_min;
|
||||||
|
float knob_max;
|
||||||
|
float knob_value;
|
||||||
|
float knob_steps;
|
||||||
|
|
||||||
|
NK_ASSERT(style);
|
||||||
|
NK_ASSERT(out);
|
||||||
|
if (!out || !style)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* remove padding from knob bounds */
|
||||||
|
bounds.y = bounds.y + style->padding.y;
|
||||||
|
bounds.x = bounds.x + style->padding.x;
|
||||||
|
bounds.h = NK_MAX(bounds.h, 2*style->padding.y);
|
||||||
|
bounds.w = NK_MAX(bounds.w, 2*style->padding.x);
|
||||||
|
bounds.w -= 2 * style->padding.x;
|
||||||
|
bounds.h -= 2 * style->padding.y;
|
||||||
|
if(bounds.h < bounds.w){
|
||||||
|
bounds.x += (bounds.w - bounds.h) / 2;
|
||||||
|
bounds.w = bounds.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure the provided values are correct */
|
||||||
|
knob_max = NK_MAX(min, max);
|
||||||
|
knob_min = NK_MIN(min, max);
|
||||||
|
knob_value = NK_CLAMP(knob_min, val, knob_max);
|
||||||
|
knob_range = knob_max - knob_min;
|
||||||
|
knob_steps = knob_range / step;
|
||||||
|
|
||||||
|
knob_value = nk_knob_behavior(state, in, bounds, knob_min, knob_max, knob_value, step, knob_steps, zero_direction, dead_zone_percent);
|
||||||
|
|
||||||
|
/* draw knob */
|
||||||
|
if (style->draw_begin) style->draw_begin(out, style->userdata);
|
||||||
|
nk_draw_knob(out, *state, style, &bounds, knob_min, knob_value, knob_max, zero_direction, dead_zone_percent);
|
||||||
|
if (style->draw_end) style->draw_end(out, style->userdata);
|
||||||
|
return knob_value;
|
||||||
|
}
|
||||||
|
NK_API nk_bool
|
||||||
|
nk_knob_float(struct nk_context *ctx, float min_value, float *value, float max_value,
|
||||||
|
float value_step, enum nk_heading zero_direction, float dead_zone_degrees)
|
||||||
|
{
|
||||||
|
struct nk_window *win;
|
||||||
|
struct nk_panel *layout;
|
||||||
|
struct nk_input *in;
|
||||||
|
const struct nk_style *style;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
float old_value;
|
||||||
|
struct nk_rect bounds;
|
||||||
|
enum nk_widget_layout_states state;
|
||||||
|
|
||||||
|
NK_ASSERT(ctx);
|
||||||
|
NK_ASSERT(ctx->current);
|
||||||
|
NK_ASSERT(ctx->current->layout);
|
||||||
|
NK_ASSERT(value);
|
||||||
|
NK_ASSERT(NK_BETWEEN(dead_zone_degrees, 0.0f, 360.0f));
|
||||||
|
if (!ctx || !ctx->current || !ctx->current->layout || !value)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
win = ctx->current;
|
||||||
|
style = &ctx->style;
|
||||||
|
layout = win->layout;
|
||||||
|
|
||||||
|
state = nk_widget(&bounds, ctx);
|
||||||
|
if (!state) return ret;
|
||||||
|
in = (state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
|
||||||
|
|
||||||
|
old_value = *value;
|
||||||
|
*value = nk_do_knob(&ctx->last_widget_state, &win->buffer, bounds, min_value,
|
||||||
|
old_value, max_value, value_step, zero_direction, dead_zone_degrees / 360.0f, &style->knob, in);
|
||||||
|
|
||||||
|
return (old_value > *value || old_value < *value);
|
||||||
|
}
|
||||||
|
NK_API nk_bool
|
||||||
|
nk_knob_int(struct nk_context *ctx, int min, int *val, int max, int step,
|
||||||
|
enum nk_heading zero_direction, float dead_zone_degrees)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
float value = (float)*val;
|
||||||
|
ret = nk_knob_float(ctx, (float)min, &value, (float)max, (float)step, zero_direction, dead_zone_degrees);
|
||||||
|
*val = (int)value;
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue