1 | /* |
2 | nanogui/slider.cpp -- Fractional slider widget with mouse control |
3 | |
4 | NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. |
5 | The widget drawing code is based on the NanoVG demo application |
6 | by Mikko Mononen. |
7 | |
8 | All rights reserved. Use of this source code is governed by a |
9 | BSD-style license that can be found in the LICENSE.txt file. |
10 | */ |
11 | |
12 | #include <nanogui/slider.h> |
13 | #include <nanogui/theme.h> |
14 | #include <nanogui/opengl.h> |
15 | #include <nanogui/serializer/core.h> |
16 | |
17 | NAMESPACE_BEGIN(nanogui) |
18 | |
19 | Slider::Slider(Widget *parent) |
20 | : Widget(parent), mValue(0.0f), mRange(0.f, 1.f), |
21 | mHighlightedRange(0.f, 0.f) { |
22 | mHighlightColor = Color(255, 80, 80, 70); |
23 | } |
24 | |
25 | Vector2i Slider::preferredSize(NVGcontext *) const { |
26 | return Vector2i(70, 16); |
27 | } |
28 | |
29 | bool Slider::mouseDragEvent(const Vector2i &p, const Vector2i & /* rel */, |
30 | int /* button */, int /* modifiers */) { |
31 | if (!mEnabled) |
32 | return false; |
33 | |
34 | const float kr = (int) (mSize.y() * 0.4f), kshadow = 3; |
35 | const float startX = kr + kshadow + mPos.x() - 1; |
36 | const float widthX = mSize.x() - 2 * (kr + kshadow); |
37 | |
38 | float value = (p.x() - startX) / widthX; |
39 | value = value * (mRange.second - mRange.first) + mRange.first; |
40 | mValue = std::min(std::max(value, mRange.first), mRange.second); |
41 | if (mCallback) |
42 | mCallback(mValue); |
43 | return true; |
44 | } |
45 | |
46 | bool Slider::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, int /* modifiers */) { |
47 | if (!mEnabled) |
48 | return false; |
49 | |
50 | const float kr = (int) (mSize.y() * 0.4f), kshadow = 3; |
51 | const float startX = kr + kshadow + mPos.x() - 1; |
52 | const float widthX = mSize.x() - 2 * (kr + kshadow); |
53 | |
54 | float value = (p.x() - startX) / widthX; |
55 | value = value * (mRange.second - mRange.first) + mRange.first; |
56 | mValue = std::min(std::max(value, mRange.first), mRange.second); |
57 | if (mCallback) |
58 | mCallback(mValue); |
59 | if (mFinalCallback && !down) |
60 | mFinalCallback(mValue); |
61 | return true; |
62 | } |
63 | |
64 | void Slider::draw(NVGcontext* ctx) { |
65 | Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f; |
66 | float kr = (int) (mSize.y() * 0.4f), kshadow = 3; |
67 | |
68 | float startX = kr + kshadow + mPos.x(); |
69 | float widthX = mSize.x() - 2*(kr+kshadow); |
70 | |
71 | Vector2f knobPos(startX + (mValue - mRange.first) / |
72 | (mRange.second - mRange.first) * widthX, |
73 | center.y() + 0.5f); |
74 | |
75 | NVGpaint bg = nvgBoxGradient( |
76 | ctx, startX, center.y() - 3 + 1, widthX, 6, 3, 3, |
77 | Color(0, mEnabled ? 32 : 10), Color(0, mEnabled ? 128 : 210)); |
78 | |
79 | nvgBeginPath(ctx); |
80 | nvgRoundedRect(ctx, startX, center.y() - 3 + 1, widthX, 6, 2); |
81 | nvgFillPaint(ctx, bg); |
82 | nvgFill(ctx); |
83 | |
84 | if (mHighlightedRange.second != mHighlightedRange.first) { |
85 | nvgBeginPath(ctx); |
86 | nvgRoundedRect(ctx, startX + mHighlightedRange.first * mSize.x(), |
87 | center.y() - kshadow + 1, |
88 | widthX * |
89 | (mHighlightedRange.second - mHighlightedRange.first), |
90 | kshadow * 2, 2); |
91 | nvgFillColor(ctx, mHighlightColor); |
92 | nvgFill(ctx); |
93 | } |
94 | |
95 | NVGpaint knobShadow = |
96 | nvgRadialGradient(ctx, knobPos.x(), knobPos.y(), kr - kshadow, |
97 | kr + kshadow, Color(0, 64), mTheme->mTransparent); |
98 | |
99 | nvgBeginPath(ctx); |
100 | nvgRect(ctx, knobPos.x() - kr - 5, knobPos.y() - kr - 5, kr * 2 + 10, |
101 | kr * 2 + 10 + kshadow); |
102 | nvgCircle(ctx, knobPos.x(), knobPos.y(), kr); |
103 | nvgPathWinding(ctx, NVG_HOLE); |
104 | nvgFillPaint(ctx, knobShadow); |
105 | nvgFill(ctx); |
106 | |
107 | NVGpaint knob = nvgLinearGradient(ctx, |
108 | mPos.x(), center.y() - kr, mPos.x(), center.y() + kr, |
109 | mTheme->mBorderLight, mTheme->mBorderMedium); |
110 | NVGpaint knobReverse = nvgLinearGradient(ctx, |
111 | mPos.x(), center.y() - kr, mPos.x(), center.y() + kr, |
112 | mTheme->mBorderMedium, |
113 | mTheme->mBorderLight); |
114 | |
115 | nvgBeginPath(ctx); |
116 | nvgCircle(ctx, knobPos.x(), knobPos.y(), kr); |
117 | nvgStrokeColor(ctx, mTheme->mBorderDark); |
118 | nvgFillPaint(ctx, knob); |
119 | nvgStroke(ctx); |
120 | nvgFill(ctx); |
121 | nvgBeginPath(ctx); |
122 | nvgCircle(ctx, knobPos.x(), knobPos.y(), kr/2); |
123 | nvgFillColor(ctx, Color(150, mEnabled ? 255 : 100)); |
124 | nvgStrokePaint(ctx, knobReverse); |
125 | nvgStroke(ctx); |
126 | nvgFill(ctx); |
127 | } |
128 | |
129 | void Slider::save(Serializer &s) const { |
130 | Widget::save(s); |
131 | s.set("value" , mValue); |
132 | s.set("range" , mRange); |
133 | s.set("highlightedRange" , mHighlightedRange); |
134 | s.set("highlightColor" , mHighlightColor); |
135 | } |
136 | |
137 | bool Slider::load(Serializer &s) { |
138 | if (!Widget::load(s)) return false; |
139 | if (!s.get("value" , mValue)) return false; |
140 | if (!s.get("range" , mRange)) return false; |
141 | if (!s.get("highlightedRange" , mHighlightedRange)) return false; |
142 | if (!s.get("highlightColor" , mHighlightColor)) return false; |
143 | return true; |
144 | } |
145 | |
146 | NAMESPACE_END(nanogui) |
147 | |