reactphysics3d/testbed/nanogui/src/button.cpp

245 lines
8.5 KiB
C++

/*
src/button.cpp -- [Normal/Toggle/Radio/Popup] Button widget
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
The widget drawing code is based on the NanoVG demo application
by Mikko Mononen.
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#include <nanogui/button.h>
#include <nanogui/theme.h>
#include <nanogui/opengl.h>
#include <nanogui/serializer/core.h>
NAMESPACE_BEGIN(nanogui)
Button::Button(Widget *parent, const std::string &caption, int icon)
: Widget(parent), mCaption(caption), mIcon(icon),
mIconPosition(IconPosition::LeftCentered), mPushed(false),
mFlags(NormalButton), mBackgroundColor(Color(0, 0)),
mTextColor(Color(0, 0)) { }
Vector2i Button::preferredSize(NVGcontext *ctx) const {
int fontSize = mFontSize == -1 ? mTheme->mButtonFontSize : mFontSize;
nvgFontSize(ctx, fontSize);
nvgFontFace(ctx, "sans-bold");
float tw = nvgTextBounds(ctx, 0,0, mCaption.c_str(), nullptr, nullptr);
float iw = 0.0f, ih = fontSize;
if (mIcon) {
if (nvgIsFontIcon(mIcon)) {
ih *= icon_scale();
nvgFontFace(ctx, "icons");
nvgFontSize(ctx, ih);
iw = nvgTextBounds(ctx, 0, 0, utf8(mIcon).data(), nullptr, nullptr)
+ mSize.y() * 0.15f;
} else {
int w, h;
ih *= 0.9f;
nvgImageSize(ctx, mIcon, &w, &h);
iw = w * ih / h;
}
}
return Vector2i((int)(tw + iw) + 20, fontSize + 10);
}
bool Button::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) {
Widget::mouseButtonEvent(p, button, down, modifiers);
/* Temporarily increase the reference count of the button in case the
button causes the parent window to be destructed */
ref<Button> self = this;
if (button == GLFW_MOUSE_BUTTON_1 && mEnabled) {
bool pushedBackup = mPushed;
if (down) {
if (mFlags & RadioButton) {
if (mButtonGroup.empty()) {
for (auto widget : parent()->children()) {
Button *b = dynamic_cast<Button *>(widget);
if (b != this && b && (b->flags() & RadioButton) && b->mPushed) {
b->mPushed = false;
if (b->mChangeCallback)
b->mChangeCallback(false);
}
}
} else {
for (auto b : mButtonGroup) {
if (b != this && (b->flags() & RadioButton) && b->mPushed) {
b->mPushed = false;
if (b->mChangeCallback)
b->mChangeCallback(false);
}
}
}
}
if (mFlags & PopupButton) {
for (auto widget : parent()->children()) {
Button *b = dynamic_cast<Button *>(widget);
if (b != this && b && (b->flags() & PopupButton) && b->mPushed) {
b->mPushed = false;
if (b->mChangeCallback)
b->mChangeCallback(false);
}
}
}
if (mFlags & ToggleButton)
mPushed = !mPushed;
else
mPushed = true;
} else if (mPushed) {
if (contains(p) && mCallback)
mCallback();
if (mFlags & NormalButton)
mPushed = false;
}
if (pushedBackup != mPushed && mChangeCallback)
mChangeCallback(mPushed);
return true;
}
return false;
}
void Button::draw(NVGcontext *ctx) {
Widget::draw(ctx);
NVGcolor gradTop = mTheme->mButtonGradientTopUnfocused;
NVGcolor gradBot = mTheme->mButtonGradientBotUnfocused;
if (mPushed) {
gradTop = mTheme->mButtonGradientTopPushed;
gradBot = mTheme->mButtonGradientBotPushed;
} else if (mMouseFocus && mEnabled) {
gradTop = mTheme->mButtonGradientTopFocused;
gradBot = mTheme->mButtonGradientBotFocused;
}
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 1, mPos.y() + 1.0f, mSize.x() - 2,
mSize.y() - 2, mTheme->mButtonCornerRadius - 1);
if (mBackgroundColor.w() != 0) {
nvgFillColor(ctx, Color(mBackgroundColor.head<3>(), 1.f));
nvgFill(ctx);
if (mPushed) {
gradTop.a = gradBot.a = 0.8f;
} else {
double v = 1 - mBackgroundColor.w();
gradTop.a = gradBot.a = mEnabled ? v : v * .5f + .5f;
}
}
NVGpaint bg = nvgLinearGradient(ctx, mPos.x(), mPos.y(), mPos.x(),
mPos.y() + mSize.y(), gradTop, gradBot);
nvgFillPaint(ctx, bg);
nvgFill(ctx);
nvgBeginPath(ctx);
nvgStrokeWidth(ctx, 1.0f);
nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + (mPushed ? 0.5f : 1.5f), mSize.x() - 1,
mSize.y() - 1 - (mPushed ? 0.0f : 1.0f), mTheme->mButtonCornerRadius);
nvgStrokeColor(ctx, mTheme->mBorderLight);
nvgStroke(ctx);
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + 0.5f, mSize.x() - 1,
mSize.y() - 2, mTheme->mButtonCornerRadius);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgStroke(ctx);
int fontSize = mFontSize == -1 ? mTheme->mButtonFontSize : mFontSize;
nvgFontSize(ctx, fontSize);
nvgFontFace(ctx, "sans-bold");
float tw = nvgTextBounds(ctx, 0,0, mCaption.c_str(), nullptr, nullptr);
Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
Vector2f textPos(center.x() - tw * 0.5f, center.y() - 1);
NVGcolor textColor =
mTextColor.w() == 0 ? mTheme->mTextColor : mTextColor;
if (!mEnabled)
textColor = mTheme->mDisabledTextColor;
if (mIcon) {
auto icon = utf8(mIcon);
float iw, ih = fontSize;
if (nvgIsFontIcon(mIcon)) {
ih *= icon_scale();
nvgFontSize(ctx, ih);
nvgFontFace(ctx, "icons");
iw = nvgTextBounds(ctx, 0, 0, icon.data(), nullptr, nullptr);
} else {
int w, h;
ih *= 0.9f;
nvgImageSize(ctx, mIcon, &w, &h);
iw = w * ih / h;
}
if (mCaption != "")
iw += mSize.y() * 0.15f;
nvgFillColor(ctx, textColor);
nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
Vector2f iconPos = center;
iconPos.y() -= 1;
if (mIconPosition == IconPosition::LeftCentered) {
iconPos.x() -= (tw + iw) * 0.5f;
textPos.x() += iw * 0.5f;
} else if (mIconPosition == IconPosition::RightCentered) {
textPos.x() -= iw * 0.5f;
iconPos.x() += tw * 0.5f;
} else if (mIconPosition == IconPosition::Left) {
iconPos.x() = mPos.x() + 8;
} else if (mIconPosition == IconPosition::Right) {
iconPos.x() = mPos.x() + mSize.x() - iw - 8;
}
if (nvgIsFontIcon(mIcon)) {
nvgText(ctx, iconPos.x(), iconPos.y()+1, icon.data(), nullptr);
} else {
NVGpaint imgPaint = nvgImagePattern(ctx,
iconPos.x(), iconPos.y() - ih/2, iw, ih, 0, mIcon, mEnabled ? 0.5f : 0.25f);
nvgFillPaint(ctx, imgPaint);
nvgFill(ctx);
}
}
nvgFontSize(ctx, fontSize);
nvgFontFace(ctx, "sans-bold");
nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgFillColor(ctx, mTheme->mTextColorShadow);
nvgText(ctx, textPos.x(), textPos.y(), mCaption.c_str(), nullptr);
nvgFillColor(ctx, textColor);
nvgText(ctx, textPos.x(), textPos.y() + 1, mCaption.c_str(), nullptr);
}
void Button::save(Serializer &s) const {
Widget::save(s);
s.set("caption", mCaption);
s.set("icon", mIcon);
s.set("iconPosition", (int) mIconPosition);
s.set("pushed", mPushed);
s.set("flags", mFlags);
s.set("backgroundColor", mBackgroundColor);
s.set("textColor", mTextColor);
}
bool Button::load(Serializer &s) {
if (!Widget::load(s)) return false;
if (!s.get("caption", mCaption)) return false;
if (!s.get("icon", mIcon)) return false;
if (!s.get("iconPosition", mIconPosition)) return false;
if (!s.get("pushed", mPushed)) return false;
if (!s.get("flags", mFlags)) return false;
if (!s.get("backgroundColor", mBackgroundColor)) return false;
if (!s.get("textColor", mTextColor)) return false;
return true;
}
NAMESPACE_END(nanogui)