Skip to content

Commit abe764a

Browse files
committed
Add the option to create a vertical slider, defaults to horizontal.
1 parent aeebc45 commit abe764a

File tree

3 files changed

+176
-75
lines changed

3 files changed

+176
-75
lines changed

include/nanogui/slider.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#pragma once
1414

1515
#include <nanogui/widget.h>
16+
#include <nanogui/layout.h>
1617

1718
NAMESPACE_BEGIN(nanogui)
1819

@@ -23,7 +24,7 @@ NAMESPACE_BEGIN(nanogui)
2324
*/
2425
class NANOGUI_EXPORT Slider : public Widget {
2526
public:
26-
Slider(Widget *parent);
27+
Slider(Widget *parent, Orientation orientation = Orientation::Horizontal);
2728

2829
float value() const { return mValue; }
2930
void setValue(float value) { mValue = value; }
@@ -52,6 +53,7 @@ class NANOGUI_EXPORT Slider : public Widget {
5253

5354
protected:
5455
float mValue;
56+
Orientation mOrientation;
5557
std::function<void(float)> mCallback;
5658
std::function<void(float)> mFinalCallback;
5759
std::pair<float, float> mRange;

src/example1.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,24 +316,36 @@ class ExampleApplication : public nanogui::Screen {
316316
panel->setLayout(new BoxLayout(Orientation::Horizontal,
317317
Alignment::Middle, 0, 20));
318318

319-
Slider *slider = new Slider(panel);
320-
slider->setValue(0.5f);
321-
slider->setFixedWidth(80);
319+
Slider *vslider = new Slider(panel, Orientation::Vertical);
320+
vslider->setValue(0.5f);
321+
vslider->setFixedHeight(60);
322322

323323
TextBox *textBox = new TextBox(panel);
324324
textBox->setFixedSize(Vector2i(60, 25));
325325
textBox->setValue("50");
326326
textBox->setUnits("%");
327-
slider->setCallback([textBox](float value) {
327+
vslider->setCallback([textBox](float value) {
328328
textBox->setValue(std::to_string((int) (value * 100)));
329329
});
330-
slider->setFinalCallback([&](float value) {
330+
vslider->setFinalCallback([&,v=vslider](float value) {
331+
v->setHighlightedRange(std::pair<float,float>(0.0f,value));
331332
cout << "Final slider value: " << (int) (value * 100) << endl;
332333
});
333334
textBox->setFixedSize(Vector2i(60,25));
334335
textBox->setFontSize(20);
335336
textBox->setAlignment(TextBox::Alignment::Right);
336337

338+
Slider *slider = new Slider(window);
339+
slider->setValue(0.5f);
340+
slider->setFixedWidth(100);
341+
slider->setCallback([textBox](float value) {
342+
textBox->setValue(std::to_string((int) (value * 100)));
343+
});
344+
slider->setFinalCallback([&,s=slider](float value) {
345+
s->setHighlightedRange(std::pair<float,float>(0.0f,value));
346+
cout << "Final slider value: " << (int) (value * 100) << endl;
347+
});
348+
337349
window = new Window(this, "Misc. widgets");
338350
window->setPosition(Vector2i(425,15));
339351
window->setLayout(new GroupLayout());

src/slider.cpp

Lines changed: 156 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,38 @@
1616

1717
NAMESPACE_BEGIN(nanogui)
1818

19-
Slider::Slider(Widget *parent)
20-
: Widget(parent), mValue(0.0f), mRange(0.f, 1.f),
19+
Slider::Slider(Widget *parent, Orientation orientation)
20+
: Widget(parent), mValue(0.0f), mOrientation(orientation), mRange(0.f, 1.f),
2121
mHighlightedRange(0.f, 0.f) {
2222
mHighlightColor = Color(255, 80, 80, 70);
2323
}
2424

2525
Vector2i Slider::preferredSize(NVGcontext *) const {
26-
return Vector2i(70, 16);
26+
return (mOrientation == Orientation::Horizontal) ? Vector2i(70, 16) : Vector2i(16, 70);
2727
}
2828

2929
bool Slider::mouseDragEvent(const Vector2i &p, const Vector2i & /* rel */,
3030
int /* button */, int /* modifiers */) {
3131
if (!mEnabled)
3232
return false;
3333

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);
34+
if (mOrientation == Orientation::Horizontal) {
35+
const float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
36+
const float startX = kr + kshadow + mPos.x() - 1;
37+
const float widthX = mSize.x() - 2 * (kr + kshadow);
38+
39+
float value = (p.x() - startX) / widthX;
40+
value = value * (mRange.second - mRange.first) + mRange.first;
41+
mValue = std::min(std::max(value, mRange.first), mRange.second);
42+
} else {
43+
const float kr = (int) (mSize.x() * 0.4f), kshadow = 3;
44+
const float startY = kr + kshadow + mPos.y() - 1;
45+
const float heightY = mSize.y() - 2 * (kr + kshadow);
46+
47+
float value = (p.y() - startY) / heightY;
48+
value = value * (mRange.second - mRange.first) + mRange.first;
49+
mValue = mRange.second - std::min(std::max(value, mRange.first), mRange.second);
50+
}
4151
if (mCallback)
4252
mCallback(mValue);
4353
return true;
@@ -47,13 +57,23 @@ bool Slider::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, in
4757
if (!mEnabled)
4858
return false;
4959

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);
60+
if (mOrientation == Orientation::Horizontal) {
61+
const float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
62+
const float startX = kr + kshadow + mPos.x() - 1;
63+
const float widthX = mSize.x() - 2 * (kr + kshadow);
64+
65+
float value = (p.x() - startX) / widthX;
66+
value = value * (mRange.second - mRange.first) + mRange.first;
67+
mValue = std::min(std::max(value, mRange.first), mRange.second);
68+
} else {
69+
const float kr = (int) (mSize.x() * 0.4f), kshadow = 3;
70+
const float startY = kr + kshadow + mPos.y() - 1;
71+
const float heightY = mSize.y() - 2 * (kr + kshadow);
72+
73+
float value = (p.y() - startY) / heightY;
74+
value = value * (mRange.second - mRange.first) + mRange.first;
75+
mValue = mRange.second - std::min(std::max(value, mRange.first), mRange.second);
76+
}
5777
if (mCallback)
5878
mCallback(mValue);
5979
if (mFinalCallback && !down)
@@ -63,67 +83,134 @@ bool Slider::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, in
6383

6484
void Slider::draw(NVGcontext* ctx) {
6585
Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
66-
float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
6786

68-
float startX = kr + kshadow + mPos.x();
69-
float widthX = mSize.x() - 2*(kr+kshadow);
87+
if (mOrientation == Orientation::Horizontal) {
88+
float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
7089

71-
Vector2f knobPos(startX + (mValue - mRange.first) /
72-
(mRange.second - mRange.first) * widthX,
73-
center.y() + 0.5f);
90+
float startX = kr + kshadow + mPos.x();
91+
float widthX = mSize.x() - 2*(kr+kshadow);
7492

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));
93+
Vector2f knobPos(startX + (mValue - mRange.first) /
94+
(mRange.second - mRange.first) * widthX,
95+
center.y() + 0.5f);
7896

79-
nvgBeginPath(ctx);
80-
nvgRoundedRect(ctx, startX, center.y() - 3 + 1, widthX, 6, 2);
81-
nvgFillPaint(ctx, bg);
82-
nvgFill(ctx);
97+
NVGpaint bg = nvgBoxGradient(
98+
ctx, startX, center.y() - 3 + 1, widthX, 6, 3, 3,
99+
Color(0, mEnabled ? 32 : 10), Color(0, mEnabled ? 128 : 210));
83100

84-
if (mHighlightedRange.second != mHighlightedRange.first) {
85101
nvgBeginPath(ctx);
86-
nvgRoundedRect(ctx, startX + mHighlightedRange.first * mSize.x(),
87-
center.y() - kshadow + 1,
88-
widthX *
102+
nvgRoundedRect(ctx, startX, center.y() - 3 + 1, widthX, 6, 2);
103+
nvgFillPaint(ctx, bg);
104+
nvgFill(ctx);
105+
106+
if (mHighlightedRange.second != mHighlightedRange.first) {
107+
nvgBeginPath(ctx);
108+
nvgRoundedRect(ctx, startX + mHighlightedRange.first * mSize.x(),
109+
center.y() - kshadow + 1,
110+
widthX *
89111
(mHighlightedRange.second - mHighlightedRange.first),
90-
kshadow * 2, 2);
91-
nvgFillColor(ctx, mHighlightColor);
112+
kshadow * 2, 2);
113+
nvgFillColor(ctx, mHighlightColor);
114+
nvgFill(ctx);
115+
}
116+
117+
NVGpaint knobShadow =
118+
nvgRadialGradient(ctx, knobPos.x(), knobPos.y(), kr - kshadow,
119+
kr + kshadow, Color(0, 64), mTheme->mTransparent);
120+
121+
nvgBeginPath(ctx);
122+
nvgRect(ctx, knobPos.x() - kr - 5, knobPos.y() - kr - 5, kr * 2 + 10,
123+
kr * 2 + 10 + kshadow);
124+
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
125+
nvgPathWinding(ctx, NVG_HOLE);
126+
nvgFillPaint(ctx, knobShadow);
92127
nvgFill(ctx);
93-
}
94128

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);
129+
NVGpaint knob = nvgLinearGradient(ctx,
130+
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
131+
mTheme->mBorderLight, mTheme->mBorderMedium);
132+
NVGpaint knobReverse = nvgLinearGradient(ctx,
133+
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
134+
mTheme->mBorderMedium,
135+
mTheme->mBorderLight);
136+
137+
nvgBeginPath(ctx);
138+
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
139+
nvgStrokeColor(ctx, mTheme->mBorderDark);
140+
nvgFillPaint(ctx, knob);
141+
nvgStroke(ctx);
142+
nvgFill(ctx);
143+
nvgBeginPath(ctx);
144+
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr/2);
145+
nvgFillColor(ctx, Color(150, mEnabled ? 255 : 100));
146+
nvgStrokePaint(ctx, knobReverse);
147+
nvgStroke(ctx);
148+
nvgFill(ctx);
149+
} else {
150+
float kr = (int) (mSize.x() * 0.4f), kshadow = 3;
151+
152+
float startY = kr + kshadow + mPos.y();
153+
float heightY = mSize.y() - 2*(kr+kshadow);
154+
155+
Vector2f knobPos(center.x() + 0.5f,
156+
startY + (mRange.second - mValue) /
157+
(mRange.second - mRange.first) * heightY);
158+
159+
NVGpaint bg = nvgBoxGradient( ctx,
160+
center.x() - 3 + 1, startY, 6, heightY, 3, 3,
161+
nanogui::Color(0, mEnabled ? 32 : 10), nanogui::Color(0, mEnabled ? 128 : 210));
162+
163+
nvgBeginPath(ctx);
164+
nvgRoundedRect(ctx, center.x() - 3 + 1, startY, 6, heightY, 2);
165+
nvgFillPaint(ctx, bg);
166+
nvgFill(ctx);
167+
168+
if (mHighlightedRange.second != mHighlightedRange.first) {
169+
nvgBeginPath(ctx);
170+
nvgRoundedRect(ctx,
171+
center.x() - kshadow + 1,
172+
mSize.y() - (kr+kshadow) - mHighlightedRange.second * heightY,
173+
kshadow * 2,
174+
heightY *
175+
(mHighlightedRange.second - mHighlightedRange.first),
176+
2);
177+
nvgFillColor(ctx, mHighlightColor);
178+
nvgFill(ctx);
179+
}
180+
181+
NVGpaint knobShadow =
182+
nvgRadialGradient(ctx, knobPos.x(), knobPos.y(), kr + kshadow,
183+
kr - kshadow, nanogui::Color(0, 64), mTheme->mTransparent);
184+
185+
nvgBeginPath(ctx);
186+
nvgRect(ctx, knobPos.x() - kr - 5, knobPos.y() - kr - 5,
187+
kr * 2 + 10 + kshadow, kr * 2 + 10);
188+
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
189+
nvgPathWinding(ctx, NVG_HOLE);
190+
nvgFillPaint(ctx, knobShadow);
191+
nvgFill(ctx);;
192+
193+
NVGpaint knob = nvgLinearGradient(ctx,
194+
center.x(), knobPos.y() - kr, center.x(), knobPos.y() + kr,
195+
mTheme->mBorderLight, mTheme->mBorderMedium);
196+
NVGpaint knobReverse = nvgLinearGradient(ctx,
197+
center.x(), knobPos.y() - kr, center.x(), knobPos.y() + kr,
198+
mTheme->mBorderMedium,
199+
mTheme->mBorderLight);
200+
201+
nvgBeginPath(ctx);
202+
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
203+
nvgStrokeColor(ctx, mTheme->mBorderDark);
204+
nvgFillPaint(ctx, knob);
205+
nvgStroke(ctx);
206+
nvgFill(ctx);
207+
nvgBeginPath(ctx);
208+
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr/2);
209+
nvgFillColor(ctx, nanogui::Color(150, mEnabled ? 255 : 100));
210+
nvgStrokePaint(ctx, knobReverse);
211+
nvgStroke(ctx);
212+
nvgFill(ctx);
213+
}
127214
}
128215

129216
void Slider::save(Serializer &s) const {

0 commit comments

Comments
 (0)