203 lines
6.0 KiB
C++
203 lines
6.0 KiB
C++
/*
|
|
src/window.cpp -- Top-level window 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/window.h>
|
|
#include <nanogui/theme.h>
|
|
#include <nanogui/opengl.h>
|
|
#include <nanogui/screen.h>
|
|
#include <nanogui/layout.h>
|
|
#include <nanogui/serializer/core.h>
|
|
|
|
NAMESPACE_BEGIN(nanogui)
|
|
|
|
Window::Window(Widget *parent, const std::string &title)
|
|
: Widget(parent), mTitle(title), mButtonPanel(nullptr), mModal(false), mDrag(false) { }
|
|
|
|
Vector2i Window::preferredSize(NVGcontext *ctx) const {
|
|
if (mButtonPanel)
|
|
mButtonPanel->setVisible(false);
|
|
Vector2i result = Widget::preferredSize(ctx);
|
|
if (mButtonPanel)
|
|
mButtonPanel->setVisible(true);
|
|
|
|
nvgFontSize(ctx, 18.0f);
|
|
nvgFontFace(ctx, "sans-bold");
|
|
float bounds[4];
|
|
nvgTextBounds(ctx, 0, 0, mTitle.c_str(), nullptr, bounds);
|
|
|
|
return result.cwiseMax(Vector2i(
|
|
bounds[2]-bounds[0] + 20, bounds[3]-bounds[1]
|
|
));
|
|
}
|
|
|
|
Widget *Window::buttonPanel() {
|
|
if (!mButtonPanel) {
|
|
mButtonPanel = new Widget(this);
|
|
mButtonPanel->setLayout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 0, 4));
|
|
}
|
|
return mButtonPanel;
|
|
}
|
|
|
|
void Window::performLayout(NVGcontext *ctx) {
|
|
if (!mButtonPanel) {
|
|
Widget::performLayout(ctx);
|
|
} else {
|
|
mButtonPanel->setVisible(false);
|
|
Widget::performLayout(ctx);
|
|
for (auto w : mButtonPanel->children()) {
|
|
w->setFixedSize(Vector2i(22, 22));
|
|
w->setFontSize(15);
|
|
}
|
|
mButtonPanel->setVisible(true);
|
|
mButtonPanel->setSize(Vector2i(width(), 22));
|
|
mButtonPanel->setPosition(Vector2i(width() - (mButtonPanel->preferredSize(ctx).x() + 5), 3));
|
|
mButtonPanel->performLayout(ctx);
|
|
}
|
|
}
|
|
|
|
void Window::draw(NVGcontext *ctx) {
|
|
int ds = mTheme->mWindowDropShadowSize, cr = mTheme->mWindowCornerRadius;
|
|
int hh = mTheme->mWindowHeaderHeight;
|
|
|
|
/* Draw window */
|
|
nvgSave(ctx);
|
|
nvgBeginPath(ctx);
|
|
nvgRoundedRect(ctx, mPos.x(), mPos.y(), mSize.x(), mSize.y(), cr);
|
|
|
|
nvgFillColor(ctx, mMouseFocus ? mTheme->mWindowFillFocused
|
|
: mTheme->mWindowFillUnfocused);
|
|
nvgFill(ctx);
|
|
|
|
|
|
/* Draw a drop shadow */
|
|
NVGpaint shadowPaint = nvgBoxGradient(
|
|
ctx, mPos.x(), mPos.y(), mSize.x(), mSize.y(), cr*2, ds*2,
|
|
mTheme->mDropShadow, mTheme->mTransparent);
|
|
|
|
nvgSave(ctx);
|
|
nvgResetScissor(ctx);
|
|
nvgBeginPath(ctx);
|
|
nvgRect(ctx, mPos.x()-ds,mPos.y()-ds, mSize.x()+2*ds, mSize.y()+2*ds);
|
|
nvgRoundedRect(ctx, mPos.x(), mPos.y(), mSize.x(), mSize.y(), cr);
|
|
nvgPathWinding(ctx, NVG_HOLE);
|
|
nvgFillPaint(ctx, shadowPaint);
|
|
nvgFill(ctx);
|
|
nvgRestore(ctx);
|
|
|
|
if (!mTitle.empty()) {
|
|
/* Draw header */
|
|
NVGpaint headerPaint = nvgLinearGradient(
|
|
ctx, mPos.x(), mPos.y(), mPos.x(),
|
|
mPos.y() + hh,
|
|
mTheme->mWindowHeaderGradientTop,
|
|
mTheme->mWindowHeaderGradientBot);
|
|
|
|
nvgBeginPath(ctx);
|
|
nvgRoundedRect(ctx, mPos.x(), mPos.y(), mSize.x(), hh, cr);
|
|
|
|
nvgFillPaint(ctx, headerPaint);
|
|
nvgFill(ctx);
|
|
|
|
nvgBeginPath(ctx);
|
|
nvgRoundedRect(ctx, mPos.x(), mPos.y(), mSize.x(), hh, cr);
|
|
nvgStrokeColor(ctx, mTheme->mWindowHeaderSepTop);
|
|
|
|
nvgSave(ctx);
|
|
nvgIntersectScissor(ctx, mPos.x(), mPos.y(), mSize.x(), 0.5f);
|
|
nvgStroke(ctx);
|
|
nvgRestore(ctx);
|
|
|
|
nvgBeginPath(ctx);
|
|
nvgMoveTo(ctx, mPos.x() + 0.5f, mPos.y() + hh - 1.5f);
|
|
nvgLineTo(ctx, mPos.x() + mSize.x() - 0.5f, mPos.y() + hh - 1.5);
|
|
nvgStrokeColor(ctx, mTheme->mWindowHeaderSepBot);
|
|
nvgStroke(ctx);
|
|
|
|
nvgFontSize(ctx, 18.0f);
|
|
nvgFontFace(ctx, "sans-bold");
|
|
nvgTextAlign(ctx, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
|
|
|
|
nvgFontBlur(ctx, 2);
|
|
nvgFillColor(ctx, mTheme->mDropShadow);
|
|
nvgText(ctx, mPos.x() + mSize.x() / 2,
|
|
mPos.y() + hh / 2, mTitle.c_str(), nullptr);
|
|
|
|
nvgFontBlur(ctx, 0);
|
|
nvgFillColor(ctx, mFocused ? mTheme->mWindowTitleFocused
|
|
: mTheme->mWindowTitleUnfocused);
|
|
nvgText(ctx, mPos.x() + mSize.x() / 2, mPos.y() + hh / 2 - 1,
|
|
mTitle.c_str(), nullptr);
|
|
}
|
|
|
|
nvgRestore(ctx);
|
|
Widget::draw(ctx);
|
|
}
|
|
|
|
void Window::dispose() {
|
|
Widget *widget = this;
|
|
while (widget->parent())
|
|
widget = widget->parent();
|
|
((Screen *) widget)->disposeWindow(this);
|
|
}
|
|
|
|
void Window::center() {
|
|
Widget *widget = this;
|
|
while (widget->parent())
|
|
widget = widget->parent();
|
|
((Screen *) widget)->centerWindow(this);
|
|
}
|
|
|
|
bool Window::mouseDragEvent(const Vector2i &, const Vector2i &rel,
|
|
int button, int /* modifiers */) {
|
|
if (mDrag && (button & (1 << GLFW_MOUSE_BUTTON_1)) != 0) {
|
|
mPos += rel;
|
|
mPos = mPos.cwiseMax(Vector2i::Zero());
|
|
mPos = mPos.cwiseMin(parent()->size() - mSize);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Window::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) {
|
|
if (Widget::mouseButtonEvent(p, button, down, modifiers))
|
|
return true;
|
|
if (button == GLFW_MOUSE_BUTTON_1) {
|
|
mDrag = down && (p.y() - mPos.y()) < mTheme->mWindowHeaderHeight;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Window::scrollEvent(const Vector2i &p, const Vector2f &rel) {
|
|
Widget::scrollEvent(p, rel);
|
|
return true;
|
|
}
|
|
|
|
void Window::refreshRelativePlacement() {
|
|
/* Overridden in \ref Popup */
|
|
}
|
|
|
|
void Window::save(Serializer &s) const {
|
|
Widget::save(s);
|
|
s.set("title", mTitle);
|
|
s.set("modal", mModal);
|
|
}
|
|
|
|
bool Window::load(Serializer &s) {
|
|
if (!Widget::load(s)) return false;
|
|
if (!s.get("title", mTitle)) return false;
|
|
if (!s.get("modal", mModal)) return false;
|
|
mDrag = false;
|
|
return true;
|
|
}
|
|
|
|
NAMESPACE_END(nanogui)
|