OpenSubdiv/examples/common/hud.cpp
manuelk 7100f41f6c Minor improvements to HUD pulldown
- don't close the pull-down when selecting an item
- change color of down-arrow to yellow when the pulldown is open
2014-04-30 15:41:05 -07:00

591 lines
16 KiB
C++

//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cassert>
#include "hud.h"
#include "font_image.h"
#if _MSC_VER
#define snprintf _snprintf
#endif
Hud::Hud() : _visible(true), _windowWidth(0), _windowHeight(0),
_requiresRebuildStatic(true)
{
_capturedSlider = -1;
}
Hud::~Hud()
{
}
void
Hud::Init(int width, int height)
{
_windowWidth = width;
_windowHeight = height;
}
int
Hud::GetWidth() const
{
return _windowWidth;
}
int
Hud::GetHeight() const
{
return _windowHeight;
}
void
Hud::Clear()
{
_radioButtons.clear();
_checkBoxes.clear();
_sliders.clear();
_vboSource.clear();
_requiresRebuildStatic = true;
}
bool
Hud::KeyDown(int key)
{
for (std::vector<RadioButton>::iterator it = _radioButtons.begin();
it != _radioButtons.end(); ++it) {
if (key == it->shortcut) {
int nextLocalIndex = it->localIndex;
if (it->sharedShortcut) {
// find checked radio button in this group
int maxLocalIndex = 0;
for (std::vector<RadioButton>::iterator it2 = _radioButtons.begin();
it2 != _radioButtons.end(); ++it2) {
if (it2->group == it->group) {
maxLocalIndex = std::max(maxLocalIndex, it2->localIndex);
if (it2->checked) {
nextLocalIndex = it2->localIndex+1;
}
}
}
if (nextLocalIndex > maxLocalIndex) nextLocalIndex = 0;
}
for (std::vector<RadioButton>::iterator it2 = _radioButtons.begin();
it2 != _radioButtons.end(); ++it2) {
if (it2->group == it->group) {
if (it2->localIndex == nextLocalIndex) {
it2->checked = true;
it2->callback(it2->callbackData);
} else {
it2->checked = false;
}
}
}
_requiresRebuildStatic = true;
return true;
}
}
for (std::vector<CheckBox>::iterator it = _checkBoxes.begin();
it != _checkBoxes.end(); ++it) {
if (key == it->shortcut) {
it->checked = !it->checked;
it->callback(it->checked, it->callbackData);
_requiresRebuildStatic = true;
return true;
}
}
for (std::vector<PullDown>::iterator it = _pulldowns.begin();
it != _pulldowns.end(); ++it) {
if (key==it->shortcut) {
// cycle through selections
++it->selected;
if (it->selected>=(int)it->labels.size()) {
it->selected=0;
}
it->callback(it->values[it->selected]);
_requiresRebuildStatic = true;
return true;
}
}
return false;
}
bool
Hud::MouseClick(int x, int y)
{
if (!IsVisible()) {
return false;
}
for (std::vector<RadioButton>::iterator it = _radioButtons.begin();
it != _radioButtons.end(); ++it) {
if (hitTest(*it, x, y)) {
for (std::vector<RadioButton>::iterator it2 = _radioButtons.begin();
it2 != _radioButtons.end(); ++it2) {
if (it2->group == it->group && it != it2) it2->checked = false;
}
it->checked = true;
it->callback(it->callbackData);
_requiresRebuildStatic = true;
return true;
}
}
for (std::vector<CheckBox>::iterator it = _checkBoxes.begin();
it != _checkBoxes.end(); ++it) {
if (hitTest(*it, x, y)) {
it->checked = !it->checked;
it->callback(it->checked, it->callbackData);
_requiresRebuildStatic = true;
return true;
}
}
for (std::vector<Slider>::iterator it = _sliders.begin();
it != _sliders.end(); ++it) {
if (hitTest(*it, x, y)) {
int bx = 0, by = 0;
getWindowPos(*it, &bx, &by);
it->SetValue(((x-bx-FONT_CHAR_WIDTH/2)/float(it->w))*(it->max - it->min) + it->min);
it->callback(it->value, it->callbackData);
_capturedSlider = (int)(it - _sliders.begin());
_requiresRebuildStatic = true;
return true;
}
}
for (std::vector<PullDown>::iterator it = _pulldowns.begin();
it != _pulldowns.end(); ++it) {
if (hitTest(*it, x, y)) {
if (not it->open) {
it->h = FONT_CHAR_HEIGHT;
it->h *= (int)it->labels.size();
it->open=true;
} else {
int label_width = (3+(int)it->label.size()) * FONT_CHAR_WIDTH;
if (x > it->x + label_width) {
int sel = it->selected;
it->SetSelected((y - it->y) / FONT_CHAR_HEIGHT);
if (it->selected!=sel) {
it->callback(it->values[it->selected]);
}
} else {
it->open=not it->open;
}
}
_requiresRebuildStatic = true;
return true;
}
}
return false;
}
void
Hud::MouseMotion(int x, int y)
{
if (_capturedSlider != -1) {
std::vector<Slider>::iterator it = _sliders.begin() + _capturedSlider;
int bx = it->x > 0 ? it->x : _windowWidth + it->x;
it->SetValue(((x-bx-FONT_CHAR_WIDTH/2)/float(it->w))*(it->max - it->min) + it->min);
it->callback(it->value, it->callbackData);
_requiresRebuildStatic = true;
}
}
bool
Hud::MouseCapture() const
{
return _capturedSlider != -1;
}
void
Hud::MouseRelease()
{
_capturedSlider = -1;
}
void
Hud::AddLabel(const char *label, int x, int y)
{
Item l;
l.label = label;
l.x = x;
l.y = y;
_labels.push_back(l);
_requiresRebuildStatic = true;
}
void
Hud::AddCheckBox(const char *label, bool checked, int x, int y,
CheckBoxCallback callback, int data, int shortcut)
{
CheckBox cb;
cb.label = label;
cb.checked = checked;
cb.x = x;
cb.y = y;
cb.w = (int)(strlen(label)+1) * FONT_CHAR_WIDTH;
cb.h = FONT_CHAR_HEIGHT;
cb.callback = callback;
cb.callbackData = data;
cb.shortcut = shortcut;
_checkBoxes.push_back(cb);
_requiresRebuildStatic = true;
}
void
Hud::AddRadioButton(int group, const char *label, bool checked, int x, int y,
RadioButtonCallback callback, int data, int shortcut)
{
RadioButton rb;
rb.group = group;
rb.label = label;
rb.checked = checked;
rb.x = x;
rb.y = y;
rb.w = (int)(strlen(label)+1) * FONT_CHAR_WIDTH;
rb.h = FONT_CHAR_HEIGHT;
rb.callback = callback;
rb.callbackData = data;
rb.shortcut = shortcut;
rb.sharedShortcut = false;
rb.localIndex = 0;
for (std::vector<RadioButton>::iterator it = _radioButtons.begin();
it != _radioButtons.end(); ++it) {
if (it->group == group) {
rb.localIndex = it->localIndex+1;
if (it->shortcut == shortcut) {
it->sharedShortcut = true;
rb.sharedShortcut = true;
}
}
}
_radioButtons.push_back(rb);
_requiresRebuildStatic = true;
}
void
Hud::AddSlider(const char *label, float min, float max, float value,
int x, int y, int width, bool intStep, SliderCallback callback, int data)
{
Slider slider;
slider.label = label;
slider.x = x;
slider.y = y;
slider.w = width * FONT_CHAR_WIDTH;
slider.h = FONT_CHAR_HEIGHT * 2;
slider.min = min;
slider.max = max;
slider.value = value;
slider.callback = callback;
slider.callbackData = data;
slider.intStep = intStep;
_sliders.push_back(slider);
_requiresRebuildStatic = true;
}
int
Hud::AddPullDown(const char *label, int x, int y, int width,
PullDownCallback callback, int shortcut)
{
PullDown pd;
pd.label = label;
pd.x = x;
pd.y = y;
pd.w = width;
pd.h = FONT_CHAR_HEIGHT;
pd.open = false;
pd.selected = 0;
pd.callback = callback;
pd.shortcut = shortcut;
_pulldowns.push_back(pd);
_requiresRebuildStatic = true;
return (int)_pulldowns.size()-1;
}
void
Hud::AddPullDownButton(int handle, const char *label, int value, bool checked)
{
if (handle < (int)_pulldowns.size()) {
PullDown & pulldown = _pulldowns[handle];
pulldown.labels.push_back(label);
pulldown.values.push_back(value);
if (checked) {
pulldown.selected = (int)pulldown.labels.size()-1;
}
}
}
int
Hud::drawChar(std::vector<float> &vboSource,
int x, int y, float r, float g, float b, char ch)
{
const float w = 1.0f/FONT_TEXTURE_COLUMNS;
const float h = 1.0f/FONT_TEXTURE_ROWS;
float u = (ch%FONT_TEXTURE_COLUMNS)/(float)FONT_TEXTURE_COLUMNS;
float v = (ch/FONT_TEXTURE_COLUMNS)/(float)FONT_TEXTURE_ROWS;
vboSource.push_back(float(x));
vboSource.push_back(float(y));
vboSource.push_back(r);
vboSource.push_back(g);
vboSource.push_back(b);
vboSource.push_back(u);
vboSource.push_back(v);
vboSource.push_back(float(x));
vboSource.push_back(float(y+FONT_CHAR_HEIGHT));
vboSource.push_back(r);
vboSource.push_back(g);
vboSource.push_back(b);
vboSource.push_back(u);
vboSource.push_back(v+h);
vboSource.push_back(float(x+FONT_CHAR_WIDTH));
vboSource.push_back(float(y));
vboSource.push_back(r);
vboSource.push_back(g);
vboSource.push_back(b);
vboSource.push_back(u+w);
vboSource.push_back(v);
vboSource.push_back(float(x+FONT_CHAR_WIDTH));
vboSource.push_back(float(y));
vboSource.push_back(r);
vboSource.push_back(g);
vboSource.push_back(b);
vboSource.push_back(u+w);
vboSource.push_back(v);
vboSource.push_back(float(x));
vboSource.push_back(float(y+FONT_CHAR_HEIGHT));
vboSource.push_back(r);
vboSource.push_back(g);
vboSource.push_back(b);
vboSource.push_back(u);
vboSource.push_back(v+h);
vboSource.push_back(float(x+FONT_CHAR_WIDTH));
vboSource.push_back(float(y+FONT_CHAR_HEIGHT));
vboSource.push_back(r);
vboSource.push_back(g);
vboSource.push_back(b);
vboSource.push_back(u+w);
vboSource.push_back(v+h);
return x + FONT_CHAR_WIDTH;
}
int
Hud::drawString(std::vector<float> &vboSource,
int x, int y, float r, float g, float b, const char *c)
{
while (*c) {
char ch = (*c) & 0x7f;
x = drawChar(vboSource, x, y, r, g, b, ch);
c++;
}
return x;
}
void
Hud::DrawString(int x, int y, const char *fmt, ...)
{
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
x = x > 0 ? x : _windowWidth + x;
y = y > 0 ? y : _windowHeight + y;
drawString(_vboSource, x, y, 1, 1, 1, buf);
}
void
Hud::DrawString(int x, int y, float r, float g, float b, const char *fmt, ...)
{
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
x = x > 0 ? x : _windowWidth + x;
y = y > 0 ? y : _windowHeight + y;
drawString(_vboSource, x, y, r, g, b, buf);
}
void
Hud::Rebuild(int width, int height)
{
_requiresRebuildStatic = false;
_windowWidth = width;
_windowHeight = height;
_staticVboSource.clear();
int x, y;
// add UI elements
for (std::vector<Item>::const_iterator it = _labels.begin();
it != _labels.end(); ++it) {
getWindowPos(*it, &x, &y);
drawString(_staticVboSource, x, y, 1, 1, 1, it->label.c_str());
}
// draw radio buttons
for (std::vector<RadioButton>::const_iterator it = _radioButtons.begin();
it != _radioButtons.end(); ++it) {
getWindowPos(*it, &x, &y);
if (it->checked) {
x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_RADIO_BUTTON_ON);
drawString(_staticVboSource, x, y, 1, 1, 0, it->label.c_str());
} else {
x = drawChar(_staticVboSource, x, y, 1, 1, 1, ' ');
drawString(_staticVboSource, x, y, .5f, .5f, .5f, it->label.c_str());
}
}
// draw checkboxes
for (std::vector<CheckBox>::const_iterator it = _checkBoxes.begin();
it != _checkBoxes.end(); ++it) {
getWindowPos(*it, &x, &y);
if (it->checked) {
x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_CHECK_BOX_ON);
drawString(_staticVboSource, x, y, 1, 1, 0, it->label.c_str());
} else {
x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_CHECK_BOX_OFF);
drawString(_staticVboSource, x, y, .5f, .5f, .5f, it->label.c_str());
}
}
// draw sliders
for (std::vector<Slider>::const_iterator it = _sliders.begin();
it != _sliders.end(); ++it) {
getWindowPos(*it, &x, &y);
int sx = x;
x = drawString(_staticVboSource, x, y, 1, 1, 1, it->label.c_str());
char value[16];
snprintf(value, 16, " : %.2f", it->value);
drawString(_staticVboSource, x, y, 1, 1, 1, value);
// new line
y += FONT_CHAR_HEIGHT;
x = sx;
x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_SLIDER_LEFT);
int nw = it->w / FONT_CHAR_WIDTH;
for (int i = 1; i < nw; ++i) {
x = drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_SLIDER_MIDDLE);
}
drawChar(_staticVboSource, x, y, 1, 1, 1, FONT_SLIDER_RIGHT);
int pos = (int)((it->value/float(it->max-it->min))*it->w);
drawChar(_staticVboSource, sx+pos, y, 1, 1, 0, FONT_SLIDER_CURSOR);
}
// draw pulldowns
for (std::vector<PullDown>::const_iterator it = _pulldowns.begin();
it != _pulldowns.end(); ++it) {
getWindowPos(*it, &x, &y);
x = drawString(_staticVboSource, x, y, .5f, .5f, .5f, it->label.c_str());
x += FONT_CHAR_WIDTH;
if (it->open) {
x = drawChar(_staticVboSource, x, y, 1, 1, 0, FONT_ARROW_DOWN);
x += FONT_CHAR_WIDTH;
for (int i=0; i<(int)it->labels.size(); ++i, y+=FONT_CHAR_HEIGHT) {
if (i==it->selected) {
drawString(_staticVboSource, x, y, 1, 1, 0, it->labels[i]);
} else {
drawString(_staticVboSource, x, y, 0.5f, 0.5f, 0.5f, it->labels[i]);
}
}
} else {
x = drawChar(_staticVboSource, x, y, .5f, .5f, .5f, FONT_ARROW_RIGHT);
x += FONT_CHAR_WIDTH;
drawString(_staticVboSource, x, y, 1, 1, 0, it->labels[it->selected]);
}
}
drawString(_staticVboSource, _windowWidth-80, _windowHeight-48, .5, .5, .5,
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f");
drawString(_staticVboSource, _windowWidth-80, _windowHeight-32, .5, .5, .5,
"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f");
}
bool
Hud::Flush()
{
if (!_visible) {
_vboSource.clear();
return false;
}
if (_requiresRebuildStatic)
Rebuild(_windowWidth, _windowHeight);
return true;
}
bool
Hud::IsVisible() const
{
return _visible;
}
void
Hud::SetVisible(bool visible)
{
_visible = visible;
}
std::vector<float> &
Hud::getVboSource()
{
return _vboSource;
}
std::vector<float> &
Hud::getStaticVboSource()
{
return _staticVboSource;
}