nanogui-experiments/fancyknob.cpp

239 lines
6.2 KiB
C++

#include <algorithm>
#include <nanogui/opengl.h>
#include <nanogui/theme.h>
#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)