Skip to content

Commit

Permalink
Add the option to create a vertical slider, defaults to horizontal.
Browse files Browse the repository at this point in the history
  • Loading branch information
ermarch committed Jun 3, 2019
1 parent aeebc45 commit abe764a
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 75 deletions.
4 changes: 3 additions & 1 deletion include/nanogui/slider.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#pragma once

#include <nanogui/widget.h>
#include <nanogui/layout.h>

NAMESPACE_BEGIN(nanogui)

Expand All @@ -23,7 +24,7 @@ NAMESPACE_BEGIN(nanogui)
*/
class NANOGUI_EXPORT Slider : public Widget {
public:
Slider(Widget *parent);
Slider(Widget *parent, Orientation orientation = Orientation::Horizontal);

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

protected:
float mValue;
Orientation mOrientation;
std::function<void(float)> mCallback;
std::function<void(float)> mFinalCallback;
std::pair<float, float> mRange;
Expand Down
22 changes: 17 additions & 5 deletions src/example1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,24 +316,36 @@ class ExampleApplication : public nanogui::Screen {
panel->setLayout(new BoxLayout(Orientation::Horizontal,
Alignment::Middle, 0, 20));

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

TextBox *textBox = new TextBox(panel);
textBox->setFixedSize(Vector2i(60, 25));
textBox->setValue("50");
textBox->setUnits("%");
slider->setCallback([textBox](float value) {
vslider->setCallback([textBox](float value) {
textBox->setValue(std::to_string((int) (value * 100)));
});
slider->setFinalCallback([&](float value) {
vslider->setFinalCallback([&,v=vslider](float value) {
v->setHighlightedRange(std::pair<float,float>(0.0f,value));
cout << "Final slider value: " << (int) (value * 100) << endl;
});
textBox->setFixedSize(Vector2i(60,25));
textBox->setFontSize(20);
textBox->setAlignment(TextBox::Alignment::Right);

Slider *slider = new Slider(window);
slider->setValue(0.5f);
slider->setFixedWidth(100);
slider->setCallback([textBox](float value) {
textBox->setValue(std::to_string((int) (value * 100)));
});
slider->setFinalCallback([&,s=slider](float value) {
s->setHighlightedRange(std::pair<float,float>(0.0f,value));
cout << "Final slider value: " << (int) (value * 100) << endl;
});

window = new Window(this, "Misc. widgets");
window->setPosition(Vector2i(425,15));
window->setLayout(new GroupLayout());
Expand Down
225 changes: 156 additions & 69 deletions src/slider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,38 @@

NAMESPACE_BEGIN(nanogui)

Slider::Slider(Widget *parent)
: Widget(parent), mValue(0.0f), mRange(0.f, 1.f),
Slider::Slider(Widget *parent, Orientation orientation)
: Widget(parent), mValue(0.0f), mOrientation(orientation), mRange(0.f, 1.f),
mHighlightedRange(0.f, 0.f) {
mHighlightColor = Color(255, 80, 80, 70);
}

Vector2i Slider::preferredSize(NVGcontext *) const {
return Vector2i(70, 16);
return (mOrientation == Orientation::Horizontal) ? Vector2i(70, 16) : Vector2i(16, 70);
}

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

const float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
const float startX = kr + kshadow + mPos.x() - 1;
const float widthX = mSize.x() - 2 * (kr + kshadow);

float value = (p.x() - startX) / widthX;
value = value * (mRange.second - mRange.first) + mRange.first;
mValue = std::min(std::max(value, mRange.first), mRange.second);
if (mOrientation == Orientation::Horizontal) {
const float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
const float startX = kr + kshadow + mPos.x() - 1;
const float widthX = mSize.x() - 2 * (kr + kshadow);

float value = (p.x() - startX) / widthX;
value = value * (mRange.second - mRange.first) + mRange.first;
mValue = std::min(std::max(value, mRange.first), mRange.second);
} else {
const float kr = (int) (mSize.x() * 0.4f), kshadow = 3;
const float startY = kr + kshadow + mPos.y() - 1;
const float heightY = mSize.y() - 2 * (kr + kshadow);

float value = (p.y() - startY) / heightY;
value = value * (mRange.second - mRange.first) + mRange.first;
mValue = mRange.second - std::min(std::max(value, mRange.first), mRange.second);
}
if (mCallback)
mCallback(mValue);
return true;
Expand All @@ -47,13 +57,23 @@ bool Slider::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, in
if (!mEnabled)
return false;

const float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
const float startX = kr + kshadow + mPos.x() - 1;
const float widthX = mSize.x() - 2 * (kr + kshadow);

float value = (p.x() - startX) / widthX;
value = value * (mRange.second - mRange.first) + mRange.first;
mValue = std::min(std::max(value, mRange.first), mRange.second);
if (mOrientation == Orientation::Horizontal) {
const float kr = (int) (mSize.y() * 0.4f), kshadow = 3;
const float startX = kr + kshadow + mPos.x() - 1;
const float widthX = mSize.x() - 2 * (kr + kshadow);

float value = (p.x() - startX) / widthX;
value = value * (mRange.second - mRange.first) + mRange.first;
mValue = std::min(std::max(value, mRange.first), mRange.second);
} else {
const float kr = (int) (mSize.x() * 0.4f), kshadow = 3;
const float startY = kr + kshadow + mPos.y() - 1;
const float heightY = mSize.y() - 2 * (kr + kshadow);

float value = (p.y() - startY) / heightY;
value = value * (mRange.second - mRange.first) + mRange.first;
mValue = mRange.second - std::min(std::max(value, mRange.first), mRange.second);
}
if (mCallback)
mCallback(mValue);
if (mFinalCallback && !down)
Expand All @@ -63,67 +83,134 @@ bool Slider::mouseButtonEvent(const Vector2i &p, int /* button */, bool down, in

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

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

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

NVGpaint bg = nvgBoxGradient(
ctx, startX, center.y() - 3 + 1, widthX, 6, 3, 3,
Color(0, mEnabled ? 32 : 10), Color(0, mEnabled ? 128 : 210));
Vector2f knobPos(startX + (mValue - mRange.first) /
(mRange.second - mRange.first) * widthX,
center.y() + 0.5f);

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

if (mHighlightedRange.second != mHighlightedRange.first) {
nvgBeginPath(ctx);
nvgRoundedRect(ctx, startX + mHighlightedRange.first * mSize.x(),
center.y() - kshadow + 1,
widthX *
nvgRoundedRect(ctx, startX, center.y() - 3 + 1, widthX, 6, 2);
nvgFillPaint(ctx, bg);
nvgFill(ctx);

if (mHighlightedRange.second != mHighlightedRange.first) {
nvgBeginPath(ctx);
nvgRoundedRect(ctx, startX + mHighlightedRange.first * mSize.x(),
center.y() - kshadow + 1,
widthX *
(mHighlightedRange.second - mHighlightedRange.first),
kshadow * 2, 2);
nvgFillColor(ctx, mHighlightColor);
kshadow * 2, 2);
nvgFillColor(ctx, mHighlightColor);
nvgFill(ctx);
}

NVGpaint knobShadow =
nvgRadialGradient(ctx, knobPos.x(), knobPos.y(), kr - kshadow,
kr + kshadow, Color(0, 64), mTheme->mTransparent);

nvgBeginPath(ctx);
nvgRect(ctx, knobPos.x() - kr - 5, knobPos.y() - kr - 5, kr * 2 + 10,
kr * 2 + 10 + kshadow);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
nvgPathWinding(ctx, NVG_HOLE);
nvgFillPaint(ctx, knobShadow);
nvgFill(ctx);
}

NVGpaint knobShadow =
nvgRadialGradient(ctx, knobPos.x(), knobPos.y(), kr - kshadow,
kr + kshadow, Color(0, 64), mTheme->mTransparent);

nvgBeginPath(ctx);
nvgRect(ctx, knobPos.x() - kr - 5, knobPos.y() - kr - 5, kr * 2 + 10,
kr * 2 + 10 + kshadow);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
nvgPathWinding(ctx, NVG_HOLE);
nvgFillPaint(ctx, knobShadow);
nvgFill(ctx);

NVGpaint knob = nvgLinearGradient(ctx,
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
mTheme->mBorderLight, mTheme->mBorderMedium);
NVGpaint knobReverse = nvgLinearGradient(ctx,
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
mTheme->mBorderMedium,
mTheme->mBorderLight);

nvgBeginPath(ctx);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgFillPaint(ctx, knob);
nvgStroke(ctx);
nvgFill(ctx);
nvgBeginPath(ctx);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr/2);
nvgFillColor(ctx, Color(150, mEnabled ? 255 : 100));
nvgStrokePaint(ctx, knobReverse);
nvgStroke(ctx);
nvgFill(ctx);
NVGpaint knob = nvgLinearGradient(ctx,
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
mTheme->mBorderLight, mTheme->mBorderMedium);
NVGpaint knobReverse = nvgLinearGradient(ctx,
mPos.x(), center.y() - kr, mPos.x(), center.y() + kr,
mTheme->mBorderMedium,
mTheme->mBorderLight);

nvgBeginPath(ctx);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgFillPaint(ctx, knob);
nvgStroke(ctx);
nvgFill(ctx);
nvgBeginPath(ctx);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr/2);
nvgFillColor(ctx, Color(150, mEnabled ? 255 : 100));
nvgStrokePaint(ctx, knobReverse);
nvgStroke(ctx);
nvgFill(ctx);
} else {
float kr = (int) (mSize.x() * 0.4f), kshadow = 3;

float startY = kr + kshadow + mPos.y();
float heightY = mSize.y() - 2*(kr+kshadow);

Vector2f knobPos(center.x() + 0.5f,
startY + (mRange.second - mValue) /
(mRange.second - mRange.first) * heightY);

NVGpaint bg = nvgBoxGradient( ctx,
center.x() - 3 + 1, startY, 6, heightY, 3, 3,
nanogui::Color(0, mEnabled ? 32 : 10), nanogui::Color(0, mEnabled ? 128 : 210));

nvgBeginPath(ctx);
nvgRoundedRect(ctx, center.x() - 3 + 1, startY, 6, heightY, 2);
nvgFillPaint(ctx, bg);
nvgFill(ctx);

if (mHighlightedRange.second != mHighlightedRange.first) {
nvgBeginPath(ctx);
nvgRoundedRect(ctx,
center.x() - kshadow + 1,
mSize.y() - (kr+kshadow) - mHighlightedRange.second * heightY,
kshadow * 2,
heightY *
(mHighlightedRange.second - mHighlightedRange.first),
2);
nvgFillColor(ctx, mHighlightColor);
nvgFill(ctx);
}

NVGpaint knobShadow =
nvgRadialGradient(ctx, knobPos.x(), knobPos.y(), kr + kshadow,
kr - kshadow, nanogui::Color(0, 64), mTheme->mTransparent);

nvgBeginPath(ctx);
nvgRect(ctx, knobPos.x() - kr - 5, knobPos.y() - kr - 5,
kr * 2 + 10 + kshadow, kr * 2 + 10);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
nvgPathWinding(ctx, NVG_HOLE);
nvgFillPaint(ctx, knobShadow);
nvgFill(ctx);;

NVGpaint knob = nvgLinearGradient(ctx,
center.x(), knobPos.y() - kr, center.x(), knobPos.y() + kr,
mTheme->mBorderLight, mTheme->mBorderMedium);
NVGpaint knobReverse = nvgLinearGradient(ctx,
center.x(), knobPos.y() - kr, center.x(), knobPos.y() + kr,
mTheme->mBorderMedium,
mTheme->mBorderLight);

nvgBeginPath(ctx);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgFillPaint(ctx, knob);
nvgStroke(ctx);
nvgFill(ctx);
nvgBeginPath(ctx);
nvgCircle(ctx, knobPos.x(), knobPos.y(), kr/2);
nvgFillColor(ctx, nanogui::Color(150, mEnabled ? 255 : 100));
nvgStrokePaint(ctx, knobReverse);
nvgStroke(ctx);
nvgFill(ctx);
}
}

void Slider::save(Serializer &s) const {
Expand Down

0 comments on commit abe764a

Please sign in to comment.