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.
 
Loading...
Searching...
No Matches
nuklear_knob.c
1#include "nuklear.h"
2#include "nuklear_internal.h"
3
4/* ===============================================================
5 *
6 * KNOB
7 *
8 * ===============================================================*/
9
10NK_LIB float
11nk_knob_behavior(nk_flags *state, struct nk_input *in,
12 struct nk_rect bounds, float knob_min, float knob_max, float knob_value,
13 float knob_step, float knob_steps,
14 enum nk_heading zero_direction, float dead_zone_percent)
15{
16 struct nk_vec2 origin;
17 float angle = 0.0f;
18 origin.x = bounds.x + (bounds.w / 2);
19 origin.y = bounds.y + (bounds.h / 2);
20
21 nk_widget_state_reset(state);
22
23 /* handle click and drag input */
24 if(in &&
25 in->mouse.buttons[NK_BUTTON_LEFT].down &&
26 nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, bounds, nk_true)){
27 /* calculate angle from origin and rotate */
28 const float direction_rads[4] = {
29 NK_PI * 2.5f, /* 90 NK_UP */
30 NK_PI * 2.0f, /* 0 NK_RIGHT */
31 NK_PI * 1.5f, /* 270 NK_DOWN */
32 NK_PI, /* 180 NK_LEFT */
33 };
35
36 angle = NK_ATAN2(in->mouse.pos.y - origin.y, in->mouse.pos.x - origin.x) + direction_rads[zero_direction];
37 angle -= (angle > NK_PI * 2) ? NK_PI * 3 : NK_PI;
38
39 /* account for dead space applied when drawing */
40 angle *= 1.0f / (1.0f - dead_zone_percent);
41 angle = NK_CLAMP(-NK_PI, angle, NK_PI);
42
43 /* convert -pi -> pi range to 0.0 -> 1.0 */
44 angle = (angle + NK_PI) / (NK_PI * 2);
45
46 /* click to closest step */
47 knob_value = knob_min + ( (int)(angle * knob_steps + (knob_step / 2)) ) * knob_step;
48 knob_value = NK_CLAMP(knob_min, knob_value, knob_max);
49 }
50
51 /* knob widget state */
52 if (nk_input_is_mouse_hovering_rect(in, bounds)){
54 /* handle scroll and arrow inputs */
55 if (in->mouse.scroll_delta.y > 0 ||
56 (in->keyboard.keys[NK_KEY_UP].down && in->keyboard.keys[NK_KEY_UP].clicked)) {
57 knob_value += knob_step;
58 }
59
60 if (in->mouse.scroll_delta.y < 0 ||
61 (in->keyboard.keys[NK_KEY_DOWN].down && in->keyboard.keys[NK_KEY_DOWN].clicked)) {
62 knob_value -= knob_step;
63 }
64 /* easiest way to disable scrolling of parent panels..knob eats scrolling */
65 in->mouse.scroll_delta.y = 0;
66 knob_value = NK_CLAMP(knob_min, knob_value, knob_max);
67 }
68 if (*state & NK_WIDGET_STATE_HOVER &&
69 !nk_input_is_mouse_prev_hovering_rect(in, bounds))
71 else if (nk_input_is_mouse_prev_hovering_rect(in, bounds))
72 *state |= NK_WIDGET_STATE_LEFT;
73
74 return knob_value;
75}
76NK_LIB void
77nk_draw_knob(struct nk_command_buffer *out, nk_flags state,
78 const struct nk_style_knob *style, const struct nk_rect *bounds, float min, float value, float max,
79 enum nk_heading zero_direction, float dead_zone_percent)
80{
81 const struct nk_style_item *background;
82 struct nk_color knob_color, cursor;
83
84 NK_UNUSED(min);
85 NK_UNUSED(max);
86 NK_UNUSED(value);
87
88 if (state & NK_WIDGET_STATE_ACTIVED) {
89 background = &style->active;
90 knob_color = style->knob_active;
91 cursor = style->cursor_active;
92 } else if (state & NK_WIDGET_STATE_HOVER) {
93 background = &style->hover;
94 knob_color = style->knob_hover;
95 cursor = style->cursor_hover;
96 } else {
97 background = &style->normal;
98 knob_color = style->knob_normal;
99 cursor = style->cursor_normal;
100 }
101
102 /* draw background */
103 switch(background->type) {
104 case NK_STYLE_ITEM_IMAGE:
105 nk_draw_image(out, *bounds, &background->data.image, nk_rgb_factor(nk_white, style->color_factor));
106 break;
107 case NK_STYLE_ITEM_NINE_SLICE:
108 nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_rgb_factor(nk_white, style->color_factor));
109 break;
110 case NK_STYLE_ITEM_COLOR:
111 nk_fill_rect(out, *bounds, 0, nk_rgb_factor(background->data.color, style->color_factor));
112 nk_stroke_rect(out, *bounds, 0, style->border, nk_rgb_factor(style->border_color, style->color_factor));
113 break;
114 }
115
116 /* draw knob */
117 nk_fill_circle(out, *bounds, nk_rgb_factor(knob_color, style->color_factor));
118 if(style->knob_border > 0){
119 struct nk_rect border_bounds = *bounds;
120 border_bounds.x += style->knob_border / 2;
121 border_bounds.y += style->knob_border / 2;
122 border_bounds.w -= style->knob_border;
123 border_bounds.h -= style->knob_border;
124 nk_stroke_circle(out, border_bounds, style->knob_border, nk_rgb_factor(style->knob_border_color, style->color_factor));
125 }
126 { /* calculate cursor line cords */
127 float half_circle_size = (bounds->w / 2);
128 float angle = (value - min) / (max - min);
129 float alive_zone = 1.0f - dead_zone_percent;
130 struct nk_vec2 cursor_start, cursor_end;
131 const float direction_rads[4] = {
132 NK_PI * 1.5f, /* 90 NK_UP */
133 0.0f, /* 0 NK_RIGHT */
134 NK_PI * 0.5f, /* 270 NK_DOWN */
135 NK_PI, /* 180 NK_LEFT */
136 };
137 /* calculate + apply dead zone */
138 angle = (angle * alive_zone) + (dead_zone_percent / 2);
139
140 /* percentage 0.0 -> 1.0 to radians, rads are 0.0 to (2*pi) NOT -pi to pi */
141 angle *= NK_PI * 2;
142
143 /* apply zero angle */
144 angle += direction_rads[zero_direction];
145 if(angle > NK_PI * 2)
146 angle -= NK_PI * 2;
147
148 cursor_start.x = bounds->x + half_circle_size + (angle > NK_PI);
149 cursor_start.y = bounds->y + half_circle_size + (angle < NK_PI_HALF || angle > (NK_PI * 1.5f));
150
151 cursor_end.x = cursor_start.x + (half_circle_size * NK_COS(angle));
152 cursor_end.y = cursor_start.y + (half_circle_size * NK_SIN(angle));
153
154 /* cut off half of the cursor */
155 cursor_start.x = (cursor_start.x + cursor_end.x) / 2;
156 cursor_start.y = (cursor_start.y + cursor_end.y) / 2;
157
158 /* draw cursor */
159 nk_stroke_line(out, cursor_start.x, cursor_start.y, cursor_end.x, cursor_end.y, 2, nk_rgb_factor(cursor, style->color_factor));
160 }
161}
162NK_LIB float
163nk_do_knob(nk_flags *state,
164 struct nk_command_buffer *out, struct nk_rect bounds,
165 float min, float val, float max, float step,
166 enum nk_heading zero_direction, float dead_zone_percent,
167 const struct nk_style_knob *style, struct nk_input *in)
168{
169 float knob_range;
170 float knob_min;
171 float knob_max;
172 float knob_value;
173 float knob_steps;
174
175 NK_ASSERT(style);
176 NK_ASSERT(out);
177 if (!out || !style)
178 return 0;
179
180 /* remove padding from knob bounds */
181 bounds.y = bounds.y + style->padding.y;
182 bounds.x = bounds.x + style->padding.x;
183 bounds.h = NK_MAX(bounds.h, 2*style->padding.y);
184 bounds.w = NK_MAX(bounds.w, 2*style->padding.x);
185 bounds.w -= 2 * style->padding.x;
186 bounds.h -= 2 * style->padding.y;
187 if(bounds.h < bounds.w){
188 bounds.x += (bounds.w - bounds.h) / 2;
189 bounds.w = bounds.h;
190 }
191
192 /* make sure the provided values are correct */
193 knob_max = NK_MAX(min, max);
194 knob_min = NK_MIN(min, max);
195 knob_value = NK_CLAMP(knob_min, val, knob_max);
196 knob_range = knob_max - knob_min;
197 knob_steps = knob_range / step;
198
199 knob_value = nk_knob_behavior(state, in, bounds, knob_min, knob_max, knob_value, step, knob_steps, zero_direction, dead_zone_percent);
200
201 /* draw knob */
202 if (style->draw_begin) style->draw_begin(out, style->userdata);
203 nk_draw_knob(out, *state, style, &bounds, knob_min, knob_value, knob_max, zero_direction, dead_zone_percent);
204 if (style->draw_end) style->draw_end(out, style->userdata);
205 return knob_value;
206}
207NK_API nk_bool
208nk_knob_float(struct nk_context *ctx, float min_value, float *value, float max_value,
209 float value_step, enum nk_heading zero_direction, float dead_zone_degrees)
210{
211 struct nk_window *win;
212 struct nk_panel *layout;
213 struct nk_input *in;
214 const struct nk_style *style;
215
216 int ret = 0;
217 float old_value;
218 struct nk_rect bounds;
219 enum nk_widget_layout_states state;
220
221 NK_ASSERT(ctx);
222 NK_ASSERT(ctx->current);
223 NK_ASSERT(ctx->current->layout);
224 NK_ASSERT(value);
225 NK_ASSERT(NK_BETWEEN(dead_zone_degrees, 0.0f, 360.0f));
226 if (!ctx || !ctx->current || !ctx->current->layout || !value)
227 return ret;
228
229 win = ctx->current;
230 style = &ctx->style;
231 layout = win->layout;
232
233 state = nk_widget(&bounds, ctx);
234 if (!state) return ret;
235 in = (state == NK_WIDGET_DISABLED || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;
236
237 old_value = *value;
238 *value = nk_do_knob(&ctx->last_widget_state, &win->buffer, bounds, min_value,
239 old_value, max_value, value_step, zero_direction, dead_zone_degrees / 360.0f, &style->knob, in);
240
241 return (old_value > *value || old_value < *value);
242}
243NK_API nk_bool
244nk_knob_int(struct nk_context *ctx, int min, int *val, int max, int step,
245 enum nk_heading zero_direction, float dead_zone_degrees)
246{
247 int ret;
248 float value = (float)*val;
249 ret = nk_knob_float(ctx, (float)min, &value, (float)max, (float)step, zero_direction, dead_zone_degrees);
250 *val = (int)value;
251 return ret;
252}
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
NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color)
shape outlines
NK_API void nk_fill_rect(struct nk_command_buffer *, struct nk_rect, float rounding, struct nk_color)
filled shades
@ 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