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
17NAMESPACE_BEGIN(nanogui)
18
19Slider::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
25Vector2i Slider::preferredSize(NVGcontext *) const {
26 return Vector2i(70, 16);
27}
28
29bool 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
46bool 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
64void 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
129void 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
137bool 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
146NAMESPACE_END(nanogui)
147