#include #include #include #include "fancyknob.hpp" NAMESPACE_BEGIN(nanogui) FancyKnob::FancyKnob(Widget* parent, int rad) : Widget(parent) , m_value(0.0f) , m_min_val(0.0f) , m_max_val(100.0f) , m_increment(0.1f) , m_fine_mode(false) , m_scroll_speed(2.0f) , m_gauge_width(0.125f) , m_indicator_size(0.35f) { // TBD: getters & setters for all colors set_gauge_color(Color(255, 80, 80, 255)); m_knob_color_1 = Color(86, 92, 95, 255); m_knob_color_2 = Color(39, 42, 43, 255); m_outline_color_1 = Color(190, 190, 190, 0); m_outline_color_2 = Color(23, 23, 23, 255); m_scroll_increment = (m_max_val - m_min_val) / 100.0 * m_scroll_speed; m_drag_increment = (m_max_val - m_min_val) / 100.0; set_cursor(Cursor::Hand); Widget::set_size({ rad, rad }); } const float FancyKnob::m_pi = std::acos(-1.0f); Vector2i FancyKnob::preferred_size(NVGcontext*) const { return m_size; } void FancyKnob::set_min_value(float value) { m_min_val = value; m_scroll_increment = (m_max_val - m_min_val) / 100.0 * m_scroll_speed; m_drag_increment = (m_max_val - m_min_val) / 100.0; } void FancyKnob::set_max_value(float value) { m_max_val = value; m_scroll_increment = (m_max_val - m_min_val) / 100.0 * m_scroll_speed; m_drag_increment = (m_max_val - m_min_val) / 100.0; } void FancyKnob::set_scroll_speed(float value) { m_scroll_speed = value; m_scroll_increment = (m_max_val - m_min_val) / 100.0 * m_scroll_speed; } void FancyKnob::set_gauge_color(const Color& color) { m_gauge_color = color; NVGcolor bg = nvgLerpRGBA(Color(40, 40, 40, 255), m_gauge_color, 0.3f); m_gauge_bg_color = Color(bg.r, bg.g, bg.b, bg.a); } bool FancyKnob::adjust_value(float value, float incr) { if (m_fine_mode) incr = m_increment; float new_val = m_value + value * incr; new_val = std::max(m_min_val, std::min(m_max_val, new_val)); if (new_val != m_value) { m_value = new_val; if (m_callback) m_callback(m_value); return true; } return false; } bool FancyKnob::scroll_event(const Vector2i& p, const Vector2f& rel) { if (!m_enabled) return false; adjust_value(rel[1], m_scroll_increment); return true; } bool FancyKnob::mouse_enter_event(const Vector2i& p, bool enter) { if (enter) request_focus(); set_focused(enter); return true; } bool FancyKnob::mouse_drag_event(const Vector2i& p, const Vector2i& rel, int /* button */, int /* modifiers */) { if (!m_enabled) return false; adjust_value((float)-rel[1], m_drag_increment); return true; } bool FancyKnob::mouse_button_event(const Vector2i& /* p */, int button, bool down, int modifiers) { if (!m_enabled) return false; if (button == GLFW_MOUSE_BUTTON_1 && modifiers & GLFW_MOD_CONTROL && down) { if (m_value != m_default) { m_value = m_default; if (m_callback) m_callback(m_value); } return true; } return false; } bool FancyKnob::keyboard_event(int key, int scancode, int action, int modifiers) { if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT) { if (action == GLFW_PRESS) m_fine_mode = true; else if (action == GLFW_RELEASE) m_fine_mode = false; return true; } else if (action == GLFW_PRESS || action == GLFW_REPEAT) { if (key == GLFW_KEY_UP) { adjust_value(1, m_drag_increment); return true; } else if (key == GLFW_KEY_DOWN) { adjust_value(-1, m_drag_increment); return true; } else if (key == GLFW_KEY_PAGE_UP) { adjust_value(10, m_drag_increment); return true; } else if (key == GLFW_KEY_PAGE_DOWN) { adjust_value(-10, m_drag_increment); return true; } } return false; } void FancyKnob::draw(NVGcontext* ctx) { float height = (float)m_size.y(); float radius = height / 2.0; float gauge_width = radius * m_gauge_width; float margin = gauge_width / 2.0; float percent_filled = (m_value - m_min_val) / (m_max_val - m_min_val); float knob_diameter = (radius - gauge_width) * 2.0 - margin; float indicator_length = radius * m_indicator_size; float indicator_start = radius - margin - indicator_length; nvgSave(ctx); nvgTranslate(ctx, m_pos.x(), m_pos.y()); // Gauge (background) nvgBeginPath(ctx); nvgStrokeWidth(ctx, gauge_width); nvgStrokeColor(ctx, m_gauge_bg_color); nvgLineCap(ctx, NVG_ROUND); nvgArc(ctx, radius, radius, radius - margin, 0.75 * m_pi, 0.25 * m_pi, NVG_CW); nvgStroke(ctx); // Gauge (fill) nvgBeginPath(ctx); nvgStrokeWidth(ctx, gauge_width); nvgStrokeColor(ctx, m_gauge_color); nvgLineCap(ctx, NVG_ROUND); nvgArc( ctx, radius, radius, radius - margin, 0.75 * m_pi, (0.75 + 1.5 * percent_filled) * m_pi, NVG_CW); nvgStroke(ctx); // Knob nvgBeginPath(ctx); nvgStrokeWidth(ctx, 2.0); NVGpaint outline_paint = nvgLinearGradient( ctx, 0, 0, 0, height - 10, m_outline_color_1, m_outline_color_2); nvgStrokePaint(ctx, outline_paint); NVGpaint knob_paint = nvgLinearGradient( ctx, radius, gauge_width, radius, knob_diameter, m_knob_color_1, m_knob_color_2); nvgFillPaint(ctx, knob_paint); nvgCircle(ctx, radius, radius, knob_diameter / 2.0); nvgFill(ctx); nvgStroke(ctx); // Indicator nvgBeginPath(ctx); nvgTranslate(ctx, radius, radius); nvgRotate(ctx, (2.0 + ((percent_filled - 0.5) * 1.5)) * m_pi); nvgStrokeColor(ctx, m_gauge_color); nvgStrokeWidth(ctx, gauge_width); nvgLineCap(ctx, NVG_ROUND); nvgMoveTo(ctx, 0, -indicator_start); nvgLineTo(ctx, 0, -(indicator_start + indicator_length)); nvgStroke(ctx); nvgRestore(ctx); nvgClosePath(ctx); } NAMESPACE_END(nanogui)