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

Custom fonts and button Icons, fixed a few Windows issues, fixed focus issues within the same widget #444

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
17,023 changes: 16,997 additions & 26 deletions ext/glad/include/glad/glad.h

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions include/nanogui/button.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ class NANOGUI_EXPORT Button : public Widget {
/// Sets the state of this Button provided the given Serializer.
virtual bool load(Serializer &s) override;

void setButtonIcons(const char* ButtonIcon) {
mButtonIcons = ButtonIcon;
}

const char* buttonIcons() { return mButtonIcons; }

protected:
/// The caption of this Button.
std::string mCaption;
Expand All @@ -146,6 +152,17 @@ class NANOGUI_EXPORT Button : public Widget {
*/
int mIcon;

/**
* \brief string of characters from icon font to be displayed
*
* \rst
* Enables custom vector icons,
* load a font containing desired vectors into the themes icon font,
* assign mButtonIcons to the desired character codes
* leave "" to ignore
*/
const char* mButtonIcons = "";

/// The position to draw the icon at.
IconPosition mIconPosition;

Expand Down
1 change: 1 addition & 0 deletions include/nanogui/nanogui.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <nanogui/imagepanel.h>
#include <nanogui/imageview.h>
#include <nanogui/vscrollpanel.h>
#include <nanogui/scrollbar.h>
#include <nanogui/colorwheel.h>
#include <nanogui/graph.h>
#include <nanogui/formhelper.h>
Expand Down
65 changes: 65 additions & 0 deletions include/nanogui/scrollbar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
nanogui/vscrollpanel.h -- Adds a vertical scrollbar around a widget
that is too big to fit into a certain area
NanoGUI was developed by Wenzel Jakob <[email protected]>.
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.
*/
/** \file */

#pragma once

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

NAMESPACE_BEGIN(nanogui)

/**
* \class VScrollPanel vscrollpanel.h nanogui/vscrollpanel.h
*
* \brief Adds a vertical scrollbar around a widget that is too big to fit into
* a certain area.
*/
class NANOGUI_EXPORT ScrollBar : public Widget {
private:
bool scrollMethod(const Vector2i& p, const Vector2f& rel);
public:
enum Alignment {
VerticalRight = 0,
HorizontalBottom = 1,
VerticalLeft = 2,
HorizontalTop = 3
};
float scrollBarWidth = 12.0f;
ScrollBar(Widget* parent, Alignment align = VerticalRight);

/// Return the current scroll amount as a value between 0 and 1. 0 means scrolled to the top and 1 to the bottom.
float scroll() const { return mScroll; }
/// Set the scroll amount to a value between 0 and 1. 0 means scrolled to the top and 1 to the bottom.
void setScroll(float scroll) { mScroll = scroll; }

virtual void performLayout(NVGcontext* ctx) override;
virtual Vector2i preferredSize(NVGcontext* ctx) const override;
virtual bool mouseDragEvent(const Vector2i& p, const Vector2i& rel, int button, int modifiers) override;
virtual bool scrollEvent(const Vector2i& p, const Vector2f& rel) override;
virtual void draw(NVGcontext* ctx) override;
virtual void save(Serializer& s) const override;
virtual bool load(Serializer& s) override;

/// The current callback to execute (for any type of scrollbar).
std::function<void(float)> callback() const { return mCallback; }

/// Set the change callback (for any type of scrollbar).
void setCallback(const std::function<void(float)>& callback) { mCallback = callback; }


protected:
int mSliderPreferredSide;
float mScroll;
int mAlign;
std::function<void(float)> mCallback;
};

NAMESPACE_END(nanogui)
13 changes: 10 additions & 3 deletions src/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ void Button::draw(NVGcontext *ctx) {

int fontSize = mFontSize == -1 ? mTheme->mButtonFontSize : mFontSize;
nvgFontSize(ctx, fontSize);
nvgFontFace(ctx, "sans-bold");
nvgFontFaceId(ctx, mTheme->mFontNormal);
float tw = nvgTextBounds(ctx, 0,0, mCaption.c_str(), nullptr, nullptr);

Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
Expand Down Expand Up @@ -209,13 +209,20 @@ void Button::draw(NVGcontext *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);

if (buttonIcons() != "") {
nvgFontFaceId(ctx, mTheme->mFontIcons);
nvgFontSize(ctx, fontSize * icon_scale());
float iw = nvgTextBounds(ctx, 0, 0, buttonIcons(), nullptr, nullptr);
nvgText(ctx, textPos.x() - iw * 0.5f, textPos.y() + 1, buttonIcons(), nullptr);
}


}

void Button::save(Serializer &s) const {
Expand Down
7 changes: 4 additions & 3 deletions src/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,12 @@ loadImageDirectory(NVGcontext *ctx, const std::string &path) {
#else
WIN32_FIND_DATA ffd;
std::string searchPath = path + "/*.*";
HANDLE handle = FindFirstFileA(searchPath.c_str(), &ffd);
HANDLE handle = FindFirstFileA(searchPath.c_str(), (LPWIN32_FIND_DATAA)&ffd);
if (handle == INVALID_HANDLE_VALUE)
throw std::runtime_error("Could not open image directory!");
do {
const char *fname = ffd.cFileName;
std::wstring temp = std::wstring((wchar_t*)ffd.cFileName);
const char* fname = std::string(temp.begin(), temp.end()).c_str();
#endif
if (strstr(fname, "png") == nullptr)
continue;
Expand All @@ -196,7 +197,7 @@ loadImageDirectory(NVGcontext *ctx, const std::string &path) {
}
closedir(dp);
#else
} while (FindNextFileA(handle, &ffd) != 0);
} while (FindNextFileA(handle, (LPWIN32_FIND_DATAA)&ffd) != 0);
FindClose(handle);
#endif
return result;
Expand Down
183 changes: 183 additions & 0 deletions src/scrollbar.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
src/vscrollbar.cpp -- Adds a vertical scrollbar around a widget
that is too big to fit into a certain area
NanoGUI was developed by Wenzel Jakob <[email protected]>.
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/scrollbar.h>
#include <nanogui/theme.h>
#include <nanogui/opengl.h>
#include <nanogui/serializer/core.h>
#include <iostream>
#include <string>
NAMESPACE_BEGIN(nanogui)

ScrollBar::ScrollBar(Widget* parent, Alignment align)
: Widget(parent),
mSliderPreferredSide(1),
mScroll(0.0f),
mAlign(align)
{}

void ScrollBar::performLayout(NVGcontext* ctx) {
Widget::performLayout(ctx);

Widget* p = parent();
if (mAlign == Alignment::VerticalLeft || mAlign == Alignment::VerticalRight) {
setSize({ scrollBarWidth, p->height() } );
if (mAlign == Alignment::VerticalRight)
setPosition({ p->width() - height(), 0 });
else
setPosition({ 0, 0 });
try{
mSliderPreferredSide = std::max(p->childAt(0)->width(), height());
}
catch (const std::exception & e) {
std::cerr << "Caught exception in event handler: " << e.what()
<< std::endl;

}
}
else{
setSize({ p->width(), scrollBarWidth });
if (mAlign == Alignment::HorizontalBottom)
setPosition({ 0, p->height() - height() });
else
setPosition({ 0, 0 });
try {
mSliderPreferredSide = std::max(p->childAt(0)->width(), width());
}
catch (const std::exception & e) {
std::cerr << "Caught exception in event handler: " << e.what()
<< std::endl;
}
//std::cout << "prefered: " + std::to_string(mSliderPreferredSide) + ", " + std::to_string(p->width()) << std::endl;
}
}

Vector2i ScrollBar::preferredSize(NVGcontext* ctx) const {
if (mAlign == Alignment::VerticalLeft || mAlign == Alignment::VerticalRight) {
return Vector2i(scrollBarWidth, parent()->height());
}
else
return Vector2i(parent()->width(), scrollBarWidth);
}

bool ScrollBar::scrollMethod(const Vector2i& p, const Vector2f& rel) {
if (mAlign == Alignment::VerticalLeft || mAlign == Alignment::VerticalRight) {
if (mSliderPreferredSide > mSize.y()) {
float scrollh = height() * std::min(1.0f, height() / (float)mSliderPreferredSide);

mScroll = std::max((float)0.0f, std::min((float)1.0f, mScroll + rel.y() / (float)(mSize.y() - 8 - scrollh)));
if (mCallback)
mCallback(mScroll);
return true;
}
}
else if (mAlign == Alignment::HorizontalBottom || mAlign == Alignment::HorizontalTop) {
if (mSliderPreferredSide > mSize.x()) {
float scrollw = width() * std::min(1.0f, width() / (float)mSliderPreferredSide);

mScroll = std::max((float)0.0f, std::min((float)1.0f, mScroll + (rel.x() / (float)(mSize.x() - 8 - scrollw))));

if (mCallback)
std::cout << "Scroll: " << mScroll << std::endl;
mCallback(mScroll);
return true;
}

}
}

bool ScrollBar::mouseDragEvent(const Vector2i& p, const Vector2i& rel,
int button, int modifiers) {
Vector2f a = { rel.x(),rel.y()/4 };
if (scrollMethod(p, a)) {
return true;
}
return Widget::mouseDragEvent(p, rel, button, modifiers);
}

bool ScrollBar::scrollEvent(const Vector2i& p, const Vector2f& rel) {
if (scrollMethod(p, { rel.y()*4, rel.x() } )) {
return true;
}
return Widget::scrollEvent(p, rel);
}

void ScrollBar::draw(NVGcontext* ctx) {
if (mAlign == Alignment::VerticalLeft || mAlign == Alignment::VerticalRight) {
float scrollh = height() * std::min(1.0f, height() / (float)mSliderPreferredSide);

if (mSliderPreferredSide <= mSize.y())
return;

int ww = mAlign == Alignment::VerticalLeft ? 0 : mSize.x();
int wx = mAlign == Alignment::VerticalLeft ? 0 : scrollBarWidth;
int dx = mAlign == Alignment::VerticalLeft ? -2 : 0;
NVGpaint paint = nvgBoxGradient(ctx, mPos.x() + ww - wx + 1, mPos.y() + 4 + 1, 8, mSize.y() - 8,
3, 4, Color(0, 32), Color(0, 92));
//body
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + ww - wx + dx, mPos.y() + 4, 8, mSize.y() - 8, 3);
nvgFillPaint(ctx, paint);
nvgFill(ctx);

paint = nvgBoxGradient(ctx, mPos.x() + ww - wx - 1,
mPos.y() + 4 + (mSize.y() - 8 - scrollh) * mScroll - 1, 8, scrollh,
3, 4, Color(220, 100), Color(128, 100));

//slider
int sx = mAlign == Alignment::VerticalLeft ? -1 : 1;
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + ww - wx + sx,
mPos.y() + 4 + 1 + (mSize.y() - 8 - scrollh) * mScroll, 8 - 2,
scrollh - 2, 2);
nvgFillPaint(ctx, paint);
nvgFill(ctx);
}
else if (mAlign == Alignment::HorizontalBottom || mAlign == Alignment::HorizontalTop) {
float scrollw = width() * std::min(1.0f, width() / (float)mSliderPreferredSide);

if (mSliderPreferredSide <= mSize.x())
return;

NVGpaint paint = nvgBoxGradient(ctx, mPos.x() + 4 + 1, mPos.y() + mSize.y() - 12 + 1, mSize.x() - 8, 8,
3, 4, Color(0, 32), Color(0, 92));
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 4, mPos.y() + mSize.y() - 12, mSize.x() - 8, 8, 3);
nvgFillPaint(ctx, paint);
nvgFill(ctx);

paint = nvgBoxGradient(ctx, mPos.x() + 4 + (mSize.x() - 8 - scrollw) * mScroll - 1,
mPos.y() + mSize.y() - 12 - 1, scrollw, 9,
3, 4, Color(220, 100), Color(128, 100));

nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 4 + 1 + (mSize.x() - 8 - scrollw) * mScroll,
mPos.y() + mSize.y() - 12 + 1, scrollw - 2, 8 - 2, 2);
nvgFillPaint(ctx, paint);
nvgFill(ctx);
}
}

void ScrollBar::save(Serializer& s) const {
Widget::save(s);
s.set("sliderPreferredSide", mSliderPreferredSide);
s.set("alignment", mAlign);
s.set("scroll", mScroll);
}

bool ScrollBar::load(Serializer& s) {
if (!Widget::load(s)) return false;
if (!s.get("sliderPreferredSide", mSliderPreferredSide)) return false;
if (!s.get("alignment", mAlign)) return false;
if (!s.get("scroll", mScroll)) return false;
return true;
}

NAMESPACE_END(nanogui)
20 changes: 12 additions & 8 deletions src/theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
#include <nanogui/theme.h>
#include <nanogui/opengl.h>
#include <nanogui/entypo.h>
#include <nanogui_resources.h>
//#include <nanogui_resources.h>

NAMESPACE_BEGIN(nanogui)
#define LOAD_FROM_BUILTIN_FONTS

Theme::Theme(NVGcontext *ctx) {
mStandardFontSize = 16;
Expand Down Expand Up @@ -80,14 +81,17 @@ Theme::Theme(NVGcontext *ctx) {
mTextBoxUpIcon = ENTYPO_ICON_CHEVRON_UP;
mTextBoxDownIcon = ENTYPO_ICON_CHEVRON_DOWN;

mFontNormal = nvgCreateFontMem(ctx, "sans", roboto_regular_ttf,
roboto_regular_ttf_size, 0);
mFontBold = nvgCreateFontMem(ctx, "sans-bold", roboto_bold_ttf,
roboto_bold_ttf_size, 0);
mFontIcons = nvgCreateFontMem(ctx, "icons", entypo_ttf,
entypo_ttf_size, 0);
//mFontNormal = nvgCreateFontMem(ctx, "sans", roboto_regular_ttf,
// roboto_regular_ttf_size, 0);
//mFontBold = nvgCreateFontMem(ctx, "sans-bold", roboto_bold_ttf,
// roboto_bold_ttf_size, 0);
//mFontIcons = nvgCreateFontMem(ctx, "icons", entypo_ttf,entypo_ttf_size, 0); uncomment for legacy font support, using reasource nanogui_reasource.h (uncomment include)

mFontNormal = nvgCreateFont(ctx, "sans", ".\\Roboto-Regular.ttf");
mFontBold = nvgCreateFont(ctx, "sans-bold", ".\\Roboto-Bold.ttf");
mFontIcons = nvgCreateFont(ctx, "icons", ".\\entypo.ttf");
if (mFontNormal == -1 || mFontBold == -1 || mFontIcons == -1)
throw std::runtime_error("Could not load fonts!");
throw std::runtime_error("Could not load fonts! You may have to copy the font files: Roboto-Regular.ttf, Roboto-Bold.ttf, entypo.ttf to your project directory");
}

NAMESPACE_END(nanogui)
Loading