nuklear_knob.c 9.1 KB

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