Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the option to create a vertical slider, defaults to horizontal. #406

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
227 changes: 158 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,136 @@ 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) {
float value = mRange.second - mHighlightedRange.second;
float range = mRange.second - mRange.first;

nvgBeginPath(ctx);
nvgRoundedRect(ctx,
center.x() - kshadow + 1,
startY + value / range * heightY,
kshadow * 2,
heightY - heightY * value / range,
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