reactphysics3d/testbed/nanogui/src/tabwidget.cpp

202 lines
6.3 KiB
C++

/*
nanogui/tabwidget.cpp -- A wrapper around the widgets TabHeader and StackedWidget
which hooks the two classes together.
The tab widget was contributed by Stefan Ivanov.
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/tabwidget.h>
#include <nanogui/tabheader.h>
#include <nanogui/stackedwidget.h>
#include <nanogui/theme.h>
#include <nanogui/opengl.h>
#include <nanogui/window.h>
#include <nanogui/screen.h>
#include <algorithm>
NAMESPACE_BEGIN(nanogui)
TabWidget::TabWidget(Widget* parent)
: Widget(parent)
, mHeader(new TabHeader(nullptr)) // create using nullptr, add children below
, mContent(new StackedWidget(nullptr)) {
// since TabWidget::addChild is going to throw an exception to prevent
// mis-use of this class, add the child directly
Widget::addChild(childCount(), mHeader);
Widget::addChild(childCount(), mContent);
mHeader->setCallback([this](int i) {
mContent->setSelectedIndex(i);
if (mCallback)
mCallback(i);
});
}
void TabWidget::addChild(int /*index*/, Widget * /*widget*/) {
// there may only be two children: mHeader and mContent, created in the constructor
throw std::runtime_error(
"TabWidget: do not add children directly to the TabWidget, create tabs "
"and add children to the tabs. See TabWidget class documentation for "
"example usage."
);
}
void TabWidget::setActiveTab(int tabIndex) {
mHeader->setActiveTab(tabIndex);
mContent->setSelectedIndex(tabIndex);
}
int TabWidget::activeTab() const {
assert(mHeader->activeTab() == mContent->selectedIndex());
return mContent->selectedIndex();
}
int TabWidget::tabCount() const {
assert(mContent->childCount() == mHeader->tabCount());
return mHeader->tabCount();
}
Widget* TabWidget::createTab(int index, const std::string &label) {
Widget* tab = new Widget(nullptr);
addTab(index, label, tab);
return tab;
}
Widget* TabWidget::createTab(const std::string &label) {
return createTab(tabCount(), label);
}
void TabWidget::addTab(const std::string &name, Widget *tab) {
addTab(tabCount(), name, tab);
}
void TabWidget::addTab(int index, const std::string &label, Widget *tab) {
assert(index <= tabCount());
// It is important to add the content first since the callback
// of the header will automatically fire when a new tab is added.
mContent->addChild(index, tab);
mHeader->addTab(index, label);
assert(mHeader->tabCount() == mContent->childCount());
}
int TabWidget::tabLabelIndex(const std::string &label) {
return mHeader->tabIndex(label);
}
int TabWidget::tabIndex(Widget* tab) {
return mContent->childIndex(tab);
}
void TabWidget::ensureTabVisible(int index) {
if (!mHeader->isTabVisible(index))
mHeader->ensureTabVisible(index);
}
const Widget *TabWidget::tab(const std::string &tabName) const {
int index = mHeader->tabIndex(tabName);
if (index == -1 || index == mContent->childCount())
return nullptr;
return mContent->children()[index];
}
Widget *TabWidget::tab(const std::string &tabName) {
int index = mHeader->tabIndex(tabName);
if (index == -1 || index == mContent->childCount())
return nullptr;
return mContent->children()[index];
}
const Widget *TabWidget::tab(int index) const {
if (index < 0 || index >= mContent->childCount())
return nullptr;
return mContent->children()[index];
}
Widget *TabWidget::tab(int index) {
if (index < 0 || index >= mContent->childCount())
return nullptr;
return mContent->children()[index];
}
bool TabWidget::removeTab(const std::string &tabName) {
int index = mHeader->removeTab(tabName);
if (index == -1)
return false;
mContent->removeChild(index);
return true;
}
void TabWidget::removeTab(int index) {
assert(mContent->childCount() < index);
mHeader->removeTab(index);
mContent->removeChild(index);
if (activeTab() == index)
setActiveTab(index == (index - 1) ? index - 1 : 0);
}
const std::string &TabWidget::tabLabelAt(int index) const {
return mHeader->tabLabelAt(index);
}
void TabWidget::performLayout(NVGcontext* ctx) {
int headerHeight = mHeader->preferredSize(ctx).y();
int margin = mTheme->mTabInnerMargin;
mHeader->setPosition({ 0, 0 });
mHeader->setSize({ mSize.x(), headerHeight });
mHeader->performLayout(ctx);
mContent->setPosition({ margin, headerHeight + margin });
mContent->setSize({ mSize.x() - 2 * margin, mSize.y() - 2*margin - headerHeight });
mContent->performLayout(ctx);
}
Vector2i TabWidget::preferredSize(NVGcontext* ctx) const {
auto contentSize = mContent->preferredSize(ctx);
auto headerSize = mHeader->preferredSize(ctx);
int margin = mTheme->mTabInnerMargin;
auto borderSize = Vector2i(2 * margin, 2 * margin);
Vector2i tabPreferredSize = contentSize + borderSize + Vector2i(0, headerSize.y());
return tabPreferredSize;
}
void TabWidget::draw(NVGcontext* ctx) {
int tabHeight = mHeader->preferredSize(ctx).y();
auto activeArea = mHeader->activeButtonArea();
for (int i = 0; i < 3; ++i) {
nvgSave(ctx);
if (i == 0)
nvgIntersectScissor(ctx, mPos.x(), mPos.y(), activeArea.first.x() + 1, mSize.y());
else if (i == 1)
nvgIntersectScissor(ctx, mPos.x() + activeArea.second.x(), mPos.y(), mSize.x() - activeArea.second.x(), mSize.y());
else
nvgIntersectScissor(ctx, mPos.x(), mPos.y() + tabHeight + 2, mSize.x(), mSize.y());
nvgBeginPath(ctx);
nvgStrokeWidth(ctx, 1.0f);
nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + tabHeight + 1.5f, mSize.x() - 1,
mSize.y() - tabHeight - 2, mTheme->mButtonCornerRadius);
nvgStrokeColor(ctx, mTheme->mBorderLight);
nvgStroke(ctx);
nvgBeginPath(ctx);
nvgRoundedRect(ctx, mPos.x() + 0.5f, mPos.y() + tabHeight + 0.5f, mSize.x() - 1,
mSize.y() - tabHeight - 2, mTheme->mButtonCornerRadius);
nvgStrokeColor(ctx, mTheme->mBorderDark);
nvgStroke(ctx);
nvgRestore(ctx);
}
Widget::draw(ctx);
}
NAMESPACE_END(nanogui)