/* src/window.cpp -- Top-level window widget NanoGUI was developed by Wenzel Jakob . 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 #include #include #include #include #include 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)