Load GUI from XML
This commit is contained in:
32
FontProvider.cc
Normal file
32
FontProvider.cc
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "FontProvider.hh"
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::unique_ptr<FontProvider> Singleton<FontProvider>::instance{nullptr};
|
||||||
|
template<>
|
||||||
|
std::once_flag Singleton<FontProvider>::instance_flag{};
|
||||||
|
|
||||||
|
FontProvider::FontProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TTF_Font *FontProvider::getFont(std::string const& name, unsigned ptsize)
|
||||||
|
{
|
||||||
|
auto key = make_tuple(name, ptsize);
|
||||||
|
auto it = fontCache_.find(key);
|
||||||
|
|
||||||
|
if (it == fontCache_.end()) {
|
||||||
|
TTFFontUPtr font{TTF_OpenFont(name.c_str(), ptsize)};
|
||||||
|
if (!font)
|
||||||
|
throw TTFException{};
|
||||||
|
std::tie(it, std::ignore) = fontCache_.insert(make_pair(key, std::move(font)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontProvider::cleanup()
|
||||||
|
{
|
||||||
|
fontCache_.clear();
|
||||||
|
}
|
||||||
42
FontProvider.hh
Normal file
42
FontProvider.hh
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_FONTPROVIDER_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_FONTPROVIDER_HH__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <tuple>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common.hh"
|
||||||
|
#include "Singleton.hh"
|
||||||
|
|
||||||
|
using FontProviderKeyType = std::tuple<std::string, unsigned>;
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct hash<FontProviderKeyType> {
|
||||||
|
typedef FontProviderKeyType argument_type;
|
||||||
|
typedef std::size_t result_type;
|
||||||
|
|
||||||
|
result_type operator()(argument_type const& s) const {
|
||||||
|
const result_type h1{std::hash<std::string>()(std::get<0>(s))};
|
||||||
|
const result_type h2{std::hash<unsigned>()(std::get<1>(s))};
|
||||||
|
return h1 ^ h2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class FontProvider : public Singleton<FontProvider> {
|
||||||
|
private:
|
||||||
|
FontProvider();
|
||||||
|
friend class Singleton<FontProvider>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TTF_Font *getFont(std::string const& name, unsigned ptsize);
|
||||||
|
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::tuple<std::string, unsigned>, TTFFontUPtr> fontCache_;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
425
GUILoader.cc
Normal file
425
GUILoader.cc
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
#include <libxml/parser.h>
|
||||||
|
|
||||||
|
#include "GUILoader.hh"
|
||||||
|
#include "FontProvider.hh"
|
||||||
|
#include "ImageProvider.hh"
|
||||||
|
#include "Widget.hh"
|
||||||
|
#include "TextWidget.hh"
|
||||||
|
#include "LinearLayout.hh"
|
||||||
|
|
||||||
|
struct Style {
|
||||||
|
int width{WRAP_CONTENT}, height{WRAP_CONTENT};
|
||||||
|
SDL_Surface *background{nullptr};
|
||||||
|
SDL_Color bg{0, 0, 0, 255}, fg{255, 255, 255, 255};
|
||||||
|
int padLeft{0}, padTop{0}, padRight{0}, padBottom{0};
|
||||||
|
int alignHoriz{ALIGN_LEFT}, alignVert{ALIGN_TOP};
|
||||||
|
int alignContHoriz{ALIGN_LEFT}, alignContVert{ALIGN_TOP};
|
||||||
|
TTF_Font *font{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class GUISAXParser {
|
||||||
|
public:
|
||||||
|
GUISAXParser() : state_(State::S_INITIAL), root_(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> getResult() {
|
||||||
|
return std::move(root_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startDocument() {
|
||||||
|
printf("startDocument()\n");
|
||||||
|
state_ = State::S_INITIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void endDocument() {
|
||||||
|
printf("endDocument()\n");
|
||||||
|
if (state_ != State::S_FINISHED)
|
||||||
|
throw Exception{"Invalid state at document end"};
|
||||||
|
}
|
||||||
|
|
||||||
|
void startElement(const xmlChar *name, const xmlChar **attrs) {
|
||||||
|
printf("startElement(%s)\n", name);
|
||||||
|
const std::string ename{reinterpret_cast<char const*>(name)};
|
||||||
|
std::unordered_map<std::string, std::string> eattrs;
|
||||||
|
while (*attrs != nullptr) {
|
||||||
|
std::string k{reinterpret_cast<char const*>(*attrs)},
|
||||||
|
v{reinterpret_cast<char const*>(*(attrs+1))};
|
||||||
|
|
||||||
|
eattrs[k] = v;
|
||||||
|
attrs += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (state_ == State::S_STYLE)
|
||||||
|
throw Exception{"Invalid Element inside <style>"};
|
||||||
|
|
||||||
|
if ((state_ == State::S_DONE) || (state_ == State::S_FINISHED))
|
||||||
|
throw Exception{"Invalid state in startElement"};
|
||||||
|
|
||||||
|
if (ename == "gui") {
|
||||||
|
if (state_ != State::S_INITIAL)
|
||||||
|
throw Exception{"Invalid state in <gui>"};
|
||||||
|
printf("state -> S_GUI\n");
|
||||||
|
state_ = State::S_GUI;
|
||||||
|
} else if (ename == "style") {
|
||||||
|
if (state_ != State::S_GUI)
|
||||||
|
throw Exception{"Invalid state in <style>"};
|
||||||
|
|
||||||
|
printf("state -> S_STYLE\n");
|
||||||
|
state_ = State::S_STYLE;
|
||||||
|
|
||||||
|
auto it = eattrs.find("name");
|
||||||
|
if (it == eattrs.end())
|
||||||
|
throw Exception{"Style missing required attr \"name\""};
|
||||||
|
|
||||||
|
Style style;
|
||||||
|
for(auto& ent : eattrs) {
|
||||||
|
if (ent.first == "width")
|
||||||
|
style.width = parseDimension(ent.second);
|
||||||
|
else if (ent.first == "height")
|
||||||
|
style.height = parseDimension(ent.second);
|
||||||
|
else if (ent.first == "background")
|
||||||
|
style.background = ImageProvider::getInstance().getImage(ent.second);
|
||||||
|
else if (ent.first == "backgroundColor")
|
||||||
|
style.bg = parseColor(ent.second);
|
||||||
|
else if (ent.first == "color")
|
||||||
|
style.fg = parseColor(ent.second);
|
||||||
|
else if (ent.first == "paddingLeft")
|
||||||
|
style.padLeft = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "paddingTop")
|
||||||
|
style.padTop = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "paddingRight")
|
||||||
|
style.padRight = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "paddingBottom")
|
||||||
|
style.padBottom = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "alignHorizontal")
|
||||||
|
style.alignHoriz = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "alignVertical")
|
||||||
|
style.alignVert = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "containerAlignHorizontal")
|
||||||
|
style.alignContHoriz = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "containerAlignVertical")
|
||||||
|
style.alignContVert = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "font") {
|
||||||
|
auto fi = parseFont(ent.second);
|
||||||
|
style.font = FontProvider::getInstance().getFont(std::get<0>(fi), std::get<1>(fi));
|
||||||
|
} else if (ent.first == "name") {
|
||||||
|
} else
|
||||||
|
printf("Warning: Unknown style attribute \"%s\"\n", ent.first.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
styles_[eattrs["name"]] = style;
|
||||||
|
} else if ((ename == "LinearLayout") ||
|
||||||
|
(ename == "TextWidget")) {
|
||||||
|
if ((state_ != State::S_GUI) && (state_ != State::S_WIDGETS))
|
||||||
|
throw Exception{"Invalid state in <Widget>"};
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> widget;
|
||||||
|
|
||||||
|
// Find name
|
||||||
|
std::string name{""};
|
||||||
|
auto it = eattrs.find("name");
|
||||||
|
if (it != eattrs.end())
|
||||||
|
name = it->second;
|
||||||
|
|
||||||
|
// Find and copy style if given
|
||||||
|
Style style;
|
||||||
|
it = eattrs.find("style");
|
||||||
|
if (it != eattrs.end())
|
||||||
|
style = styles_.at(it->second);
|
||||||
|
|
||||||
|
int width = style.width, height = style.height;
|
||||||
|
|
||||||
|
it = eattrs.find("width");
|
||||||
|
if (it != eattrs.end())
|
||||||
|
width = parseDimension(it->second);
|
||||||
|
|
||||||
|
it = eattrs.find("height");
|
||||||
|
if (it != eattrs.end())
|
||||||
|
height = parseDimension(it->second);
|
||||||
|
|
||||||
|
// Construct widget
|
||||||
|
if (ename == "LinearLayout") {
|
||||||
|
int direction = DIR_VERTICAL;
|
||||||
|
it = eattrs.find("direction");
|
||||||
|
if (it != eattrs.end()) {
|
||||||
|
if (it->second == "horizontal")
|
||||||
|
direction = DIR_HORIZONTAL;
|
||||||
|
else if (it->second == "vertical")
|
||||||
|
direction = DIR_VERTICAL;
|
||||||
|
else
|
||||||
|
throw Exception{"Invalid direction"};
|
||||||
|
}
|
||||||
|
|
||||||
|
int childPadding = 0;
|
||||||
|
it = eattrs.find("childPadding");
|
||||||
|
if (it != eattrs.end())
|
||||||
|
childPadding = std::stoi(it->second);
|
||||||
|
|
||||||
|
widget = make_unique<LinearLayout>(direction, width, height, name);
|
||||||
|
static_cast<LinearLayout&>(*widget).setChildPadding(childPadding);
|
||||||
|
} else if (ename == "TextWidget") {
|
||||||
|
std::string text{};
|
||||||
|
TTF_Font *font = style.font;
|
||||||
|
|
||||||
|
it = eattrs.find("text");
|
||||||
|
if (it != eattrs.end())
|
||||||
|
text = it->second;
|
||||||
|
|
||||||
|
it = eattrs.find("font");
|
||||||
|
if (it != eattrs.end()) {
|
||||||
|
auto fi = parseFont(it->second);
|
||||||
|
font = FontProvider::getInstance().getFont(std::get<0>(fi), std::get<1>(fi));
|
||||||
|
}
|
||||||
|
|
||||||
|
widget = make_unique<TextWidget>(font, text, width, height, name);
|
||||||
|
} else
|
||||||
|
throw Exception{"Invalid Widget"};
|
||||||
|
|
||||||
|
// Apply style
|
||||||
|
for(auto& ent : eattrs) {
|
||||||
|
if (ent.first == "background")
|
||||||
|
style.background = ImageProvider::getInstance().getImage(ent.second);
|
||||||
|
else if (ent.first == "backgroundColor")
|
||||||
|
style.bg = parseColor(ent.second);
|
||||||
|
else if (ent.first == "color")
|
||||||
|
style.fg = parseColor(ent.second);
|
||||||
|
else if (ent.first == "paddingLeft")
|
||||||
|
style.padLeft = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "paddingTop")
|
||||||
|
style.padTop = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "paddingRight")
|
||||||
|
style.padRight = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "paddingBottom")
|
||||||
|
style.padBottom = std::stoi(ent.second);
|
||||||
|
else if (ent.first == "alignHorizontal")
|
||||||
|
style.alignHoriz = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "alignVertical")
|
||||||
|
style.alignVert = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "containerAlignHorizontal")
|
||||||
|
style.alignContHoriz = parseAlign(ent.second);
|
||||||
|
else if (ent.first == "containerAlignVertical")
|
||||||
|
style.alignContVert = parseAlign(ent.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.background)
|
||||||
|
widget->setBackground(style.background);
|
||||||
|
|
||||||
|
if (style.bg != SDL_Color{0, 0, 0, 255})
|
||||||
|
widget->setBackgroundColor(style.bg);
|
||||||
|
if (style.fg != SDL_Color{255, 255, 255, 255})
|
||||||
|
widget->setForegroundColor(style.fg);
|
||||||
|
|
||||||
|
if ((style.padLeft != 0) || (style.padTop != 0) || (style.padRight != 0) ||
|
||||||
|
(style.padBottom != 0))
|
||||||
|
widget->setPadding(style.padLeft, style.padTop, style.padRight,
|
||||||
|
style.padBottom);
|
||||||
|
|
||||||
|
if ((style.alignHoriz != ALIGN_LEFT) || (style.alignVert != ALIGN_TOP))
|
||||||
|
widget->setAlignment(style.alignHoriz, style.alignVert);
|
||||||
|
if ((style.alignContHoriz != ALIGN_LEFT) || (style.alignContVert != ALIGN_TOP))
|
||||||
|
widget->setContainerAlignment(style.alignContHoriz, style.alignContVert);
|
||||||
|
|
||||||
|
|
||||||
|
if (state_ == State::S_GUI) {
|
||||||
|
printf("state -> S_WIDGETS\n");
|
||||||
|
state_ = State::S_WIDGETS;
|
||||||
|
|
||||||
|
stack_.push_back(widget.get());
|
||||||
|
root_ = std::move(widget);
|
||||||
|
} else if (state_ == State::S_WIDGETS) {
|
||||||
|
View* view = dynamic_cast<View*>(stack_.back());
|
||||||
|
stack_.push_back(widget.get());
|
||||||
|
view->addChild(std::move(widget));
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw Exception{"Invalid element"};
|
||||||
|
}
|
||||||
|
|
||||||
|
void endElement(const xmlChar *name) {
|
||||||
|
printf("endElement(%s)\n", name);
|
||||||
|
const std::string ename{reinterpret_cast<char const*>(name)};
|
||||||
|
|
||||||
|
if (ename == "style") {
|
||||||
|
if (state_ != State::S_STYLE)
|
||||||
|
throw Exception{"Invalid state on </style>"};
|
||||||
|
printf("state -> S_GUI\n");
|
||||||
|
state_ = State::S_GUI;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ename == "LinearLayout") ||
|
||||||
|
(ename == "TextWidget")) {
|
||||||
|
if (state_ != State::S_WIDGETS)
|
||||||
|
throw Exception{"Invalid state on </Widget>"};
|
||||||
|
|
||||||
|
assert(!stack_.empty());
|
||||||
|
stack_.pop_back();
|
||||||
|
if (stack_.empty()) {
|
||||||
|
printf("state -> S_DONE\n");
|
||||||
|
state_ = State::S_DONE;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ename == "gui") {
|
||||||
|
if (state_ != State::S_DONE)
|
||||||
|
throw Exception{"Invalid state on </gui>"};
|
||||||
|
|
||||||
|
printf("state -> S_FINISHED\n");
|
||||||
|
state_ = State::S_FINISHED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw Exception{"Invalid element"};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class State {
|
||||||
|
S_INITIAL,
|
||||||
|
S_GUI,
|
||||||
|
S_STYLE,
|
||||||
|
S_WIDGETS,
|
||||||
|
S_DONE,
|
||||||
|
S_FINISHED
|
||||||
|
};
|
||||||
|
|
||||||
|
int parseDimension(std::string const& str) {
|
||||||
|
if (str == "match_parent")
|
||||||
|
return MATCH_PARENT;
|
||||||
|
else if (str == "wrap_content")
|
||||||
|
return WRAP_CONTENT;
|
||||||
|
else if (str == "wrap_content_fill")
|
||||||
|
return WRAP_CONTENT_FILL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return std::stoi(str);
|
||||||
|
} catch (std::invalid_argument &ex) {
|
||||||
|
throw Exception{"Invalid dimension"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int parseAlign(std::string const& str) {
|
||||||
|
if (str == "center")
|
||||||
|
return ALIGN_CENTER;
|
||||||
|
else if (str == "left")
|
||||||
|
return ALIGN_LEFT;
|
||||||
|
else if (str == "right")
|
||||||
|
return ALIGN_RIGHT;
|
||||||
|
else if (str == "top")
|
||||||
|
return ALIGN_TOP;
|
||||||
|
else if (str == "bottom")
|
||||||
|
return ALIGN_BOTTOM;
|
||||||
|
|
||||||
|
throw Exception{"Invalid alignment"};
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Color parseColor(std::string const& str) {
|
||||||
|
SDL_Color ret;
|
||||||
|
auto sep = str.find(',');
|
||||||
|
if (sep == std::string::npos)
|
||||||
|
throw Exception{"Invalid color"};
|
||||||
|
|
||||||
|
try {
|
||||||
|
ret.r = std::stoi(str.substr(0, sep));
|
||||||
|
|
||||||
|
auto lastsep = sep;
|
||||||
|
sep = str.find(',', sep+1);
|
||||||
|
if (sep == std::string::npos)
|
||||||
|
throw Exception{"Invalid color"};
|
||||||
|
ret.g = std::stoi(str.substr(lastsep+1, sep-lastsep));
|
||||||
|
|
||||||
|
lastsep = sep;
|
||||||
|
sep = str.find(',', sep+1);
|
||||||
|
bool alpha = true;
|
||||||
|
if (sep == std::string::npos)
|
||||||
|
alpha = false;
|
||||||
|
ret.b = std::stoi(str.substr(lastsep+1, sep-lastsep));
|
||||||
|
|
||||||
|
if (!alpha)
|
||||||
|
ret.a = 255;
|
||||||
|
else
|
||||||
|
ret.a = std::stoi(str.substr(sep+1));
|
||||||
|
} catch (std::invalid_argument &ex) {
|
||||||
|
throw Exception{"Invalid color"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::string, unsigned> parseFont(std::string const& str) {
|
||||||
|
auto sep = str.find(',');
|
||||||
|
if (sep == std::string::npos)
|
||||||
|
return make_tuple(str, 12);
|
||||||
|
else
|
||||||
|
return make_tuple(str.substr(0, sep), std::stoi(str.substr(sep+1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
State state_;
|
||||||
|
|
||||||
|
std::vector<Widget*> stack_;
|
||||||
|
std::unique_ptr<Widget> root_;
|
||||||
|
std::unordered_map<std::string, Style> styles_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void _startDocument(void *userdata) {
|
||||||
|
static_cast<GUISAXParser*>(userdata)->startDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _endDocument(void *userdata) {
|
||||||
|
static_cast<GUISAXParser*>(userdata)->endDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _startElement(void *userdata, const xmlChar *name,
|
||||||
|
const xmlChar **attrs) {
|
||||||
|
static_cast<GUISAXParser*>(userdata)->startElement(name, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _endElement(void *userdata, const xmlChar *name) {
|
||||||
|
static_cast<GUISAXParser*>(userdata)->endElement(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static xmlSAXHandler saxHandlers{
|
||||||
|
nullptr, // internalSubsetSAXFunc internalSubset;
|
||||||
|
nullptr, // isStandaloneSAXFunc isStandalone;
|
||||||
|
nullptr, // hasInternalSubsetSAXFunc hasInternalSubset;
|
||||||
|
nullptr, // hasExternalSubsetSAXFunc hasExternalSubset;
|
||||||
|
nullptr, // resolveEntitySAXFunc resolveEntity;
|
||||||
|
nullptr, // getEntitySAXFunc getEntity;
|
||||||
|
nullptr, // entityDeclSAXFunc entityDecl;
|
||||||
|
nullptr, // notationDeclSAXFunc notationDecl;
|
||||||
|
nullptr, // attributeDeclSAXFunc attributeDecl;
|
||||||
|
nullptr, // elementDeclSAXFunc elementDecl;
|
||||||
|
nullptr, // unparsedEntityDeclSAXFunc unparsedEntityDecl;
|
||||||
|
nullptr, // setDocumentLocatorSAXFunc setDocumentLocator;
|
||||||
|
&_startDocument, // startDocumentSAXFunc startDocument;
|
||||||
|
&_endDocument, // endDocumentSAXFunc endDocument;
|
||||||
|
&_startElement, // startElementSAXFunc startElement;
|
||||||
|
&_endElement, // endElementSAXFunc endElement;
|
||||||
|
nullptr, // referenceSAXFunc reference;
|
||||||
|
nullptr, // charactersSAXFunc characters;
|
||||||
|
nullptr, // ignorableWhitespaceSAXFunc ignorableWhitespace;
|
||||||
|
nullptr, // processingInstructionSAXFunc processingInstruction;
|
||||||
|
nullptr, // commentSAXFunc comment;
|
||||||
|
nullptr, // warningSAXFunc warning;
|
||||||
|
nullptr, // errorSAXFunc error;
|
||||||
|
nullptr, // fatalErrorSAXFunc fatalError; /* unused error() get all the errors */
|
||||||
|
nullptr, // getParameterEntitySAXFunc getParameterEntity;
|
||||||
|
nullptr, // cdataBlockSAXFunc cdataBlock;
|
||||||
|
nullptr, // externalSubsetSAXFunc externalSubset;
|
||||||
|
false, // unsigned int initialized;
|
||||||
|
// /* The following fields are extensions available only on version 2 */
|
||||||
|
nullptr, // void *_private;
|
||||||
|
nullptr, // startElementNsSAX2Func startElementNs;
|
||||||
|
nullptr, // endElementNsSAX2Func endElementNs;
|
||||||
|
nullptr // xmlStructuredErrorFunc serror;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> loadGUIFromFile(std::string const& filename)
|
||||||
|
{
|
||||||
|
GUISAXParser parser{};
|
||||||
|
xmlSAXUserParseFile(&saxHandlers, &parser, filename.c_str());
|
||||||
|
|
||||||
|
return parser.getResult();
|
||||||
|
}
|
||||||
11
GUILoader.hh
Normal file
11
GUILoader.hh
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_GUILOADER_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_GUILOADER_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "Widget.hh"
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> loadGUIFromFile(std::string const& filename);
|
||||||
|
|
||||||
|
#endif
|
||||||
33
ImageProvider.cc
Normal file
33
ImageProvider.cc
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
|
||||||
|
#include "ImageProvider.hh"
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::unique_ptr<ImageProvider> Singleton<ImageProvider>::instance{nullptr};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::once_flag Singleton<ImageProvider>::instance_flag{};
|
||||||
|
|
||||||
|
ImageProvider::ImageProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *ImageProvider::getImage(std::string const& name)
|
||||||
|
{
|
||||||
|
auto it = imageCache_.find(name);
|
||||||
|
|
||||||
|
if(it == imageCache_.end()) {
|
||||||
|
SDLSurfaceUPtr image{IMG_Load(name.c_str())};
|
||||||
|
if (!image)
|
||||||
|
throw SDLException{};
|
||||||
|
tie(it, std::ignore) = imageCache_.insert(make_pair(name, std::move(image)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ImageProvider::cleanup()
|
||||||
|
{
|
||||||
|
imageCache_.clear();
|
||||||
|
}
|
||||||
28
ImageProvider.hh
Normal file
28
ImageProvider.hh
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_IMAGEPROVIDER_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_IMAGEPROVIDER_HH__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "common.hh"
|
||||||
|
#include "Singleton.hh"
|
||||||
|
|
||||||
|
class ImageProvider : public Singleton<ImageProvider> {
|
||||||
|
private:
|
||||||
|
ImageProvider();
|
||||||
|
friend class Singleton<ImageProvider>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SDL_Surface* getImage(std::string const& name);
|
||||||
|
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, SDLSurfaceUPtr> imageCache_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
134
LinearLayout.cc
134
LinearLayout.cc
@@ -2,7 +2,7 @@
|
|||||||
#include "LinearLayout.hh"
|
#include "LinearLayout.hh"
|
||||||
|
|
||||||
LinearLayout::LinearLayout(int direction, int width, int height, std::string name)
|
LinearLayout::LinearLayout(int direction, int width, int height, std::string name)
|
||||||
: View(width, height, std::move(name)), direction_(direction)
|
: View(width, height, std::move(name)), direction_(direction), padChildren_(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,59 +19,149 @@ void LinearLayout::setChildPadding(int pad)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinearLayout::layout()
|
void LinearLayout::layout(Widget *caller)
|
||||||
{
|
{
|
||||||
// TODO: Respect parent padding
|
int width, height;
|
||||||
if ((width_ == MATCH_PARENT) && parent_)
|
std::tie(width, height) = Widget::baseLayout(caller);
|
||||||
realWidth_ = parent_->getRealWidth();
|
|
||||||
|
|
||||||
if ((height_ == MATCH_PARENT) && parent_)
|
|
||||||
realHeight_ = parent_->getRealHeight();
|
|
||||||
|
|
||||||
|
bool widthChanged = false, heightChanged = false;
|
||||||
|
|
||||||
if (direction_ == DIR_VERTICAL) {
|
if (direction_ == DIR_VERTICAL) {
|
||||||
int contentWidth = 0;
|
int contentWidth = 0;
|
||||||
|
|
||||||
if (width_ == WRAP_CONTENT) {
|
if (width == 0) {
|
||||||
|
// Clear realWidth_ to signal WRAP_CONTENT_FILL children to use WRAP_CONTENT
|
||||||
|
realWidth_ = 0;
|
||||||
// Determine content width
|
// Determine content width
|
||||||
|
if (children_.size() == 0)
|
||||||
|
return;
|
||||||
for(auto& ent : children_) {
|
for(auto& ent : children_) {
|
||||||
auto& child = *(std::get<1>(ent));
|
auto& child = *(std::get<1>(ent));
|
||||||
assert(child.getHeight() != MATCH_PARENT);
|
assert(child.getHeight() != MATCH_PARENT);
|
||||||
if (child.getWidth() == MATCH_PARENT)
|
if (child.getWidth() == MATCH_PARENT)
|
||||||
continue;
|
continue;
|
||||||
if (child.getWidth() == WRAP_CONTENT)
|
if ((child.getWidth() == WRAP_CONTENT) || (child.getWidth() == WRAP_CONTENT_FILL))
|
||||||
_layout(child);
|
_layout(child);
|
||||||
contentWidth = std::max(contentWidth, child.getRealWidth());
|
contentWidth = std::max(contentWidth, child.getRealWidth());
|
||||||
}
|
}
|
||||||
assert(contentWidth > 0);
|
assert(contentWidth > 0);
|
||||||
realWidth_ = contentWidth + padLeft_ + padRight_;
|
if (realWidth_ != contentWidth + padLeft_ + padRight_) {
|
||||||
|
realWidth_ = contentWidth + padLeft_ + padRight_;
|
||||||
|
widthChanged = true;
|
||||||
|
}
|
||||||
printf("Inner width: %d, outer width: %d\n", contentWidth, realWidth_);
|
printf("Inner width: %d, outer width: %d\n", contentWidth, realWidth_);
|
||||||
}
|
} else
|
||||||
|
contentWidth = realWidth_ - padLeft_ - padRight_;
|
||||||
int currentY = padTop_;
|
|
||||||
|
|
||||||
// Layout children
|
// Layout children
|
||||||
|
int currentY = padTop_;
|
||||||
|
|
||||||
for(auto& ent : children_) {
|
for(auto& ent : children_) {
|
||||||
auto& child = *(std::get<1>(ent));
|
auto& child = *(std::get<1>(ent));
|
||||||
if (child.getWidth() == MATCH_PARENT)
|
if (child.getWidth() == MATCH_PARENT)
|
||||||
_layout(child);
|
_layout(child);
|
||||||
|
// Second layout pass for WRAP_CONTENT_FILL children
|
||||||
|
if ((child.getWidth() == WRAP_CONTENT_FILL) &&
|
||||||
|
(child.getRealWidth() != contentWidth))
|
||||||
|
_layout(child);
|
||||||
|
|
||||||
SDL_Rect cr;
|
SDL_Rect cr;
|
||||||
cr.x = padLeft_;
|
cr.x = padLeft_;
|
||||||
cr.y = currentY;
|
cr.y = currentY;
|
||||||
if ((realWidth_ - padLeft_ - padRight_) > child.getRealWidth())
|
if (contentWidth > child.getRealWidth()) {
|
||||||
cr.w = child.getRealWidth();
|
cr.w = child.getRealWidth();
|
||||||
else
|
auto space = contentWidth - child.getRealWidth();
|
||||||
cr.w = realWidth_ - padLeft_ - padRight_;
|
if (child.getContainerAlignmentHoriz() == ALIGN_CENTER)
|
||||||
|
cr.x += space/2;
|
||||||
|
else if (child.getContainerAlignmentHoriz() == ALIGN_RIGHT)
|
||||||
|
cr.x += space;
|
||||||
|
} else
|
||||||
|
cr.w = contentWidth;
|
||||||
cr.h = child.getRealHeight();
|
cr.h = child.getRealHeight();
|
||||||
|
|
||||||
std::get<2>(ent) = cr;
|
std::get<2>(ent) = cr;
|
||||||
|
|
||||||
currentY += child.getRealHeight() + padChildren_;
|
currentY += child.getRealHeight() + padChildren_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height_ == WRAP_CONTENT)
|
if (height == 0)
|
||||||
realHeight_ = currentY - padChildren_ + padBottom_;
|
if (realHeight_ != currentY - padChildren_ + padBottom_) {
|
||||||
}
|
realHeight_ = currentY - padChildren_ + padBottom_;
|
||||||
|
heightChanged = true;
|
||||||
|
}
|
||||||
|
} else { // direction == DIR_HORIZONTAL
|
||||||
|
int contentHeight = 0;
|
||||||
|
|
||||||
|
if (height == 0) {
|
||||||
|
// Clear realHeight_ to signal WRAP_CONTENT_FILL children to use WRAP_CONTENT
|
||||||
|
realHeight_ = 0;
|
||||||
|
// Determine content height
|
||||||
|
if (children_.size() == 0)
|
||||||
|
return;
|
||||||
|
for(auto& ent : children_) {
|
||||||
|
auto& child = *(std::get<1>(ent));
|
||||||
|
assert(child.getWidth() != MATCH_PARENT);
|
||||||
|
if (child.getHeight() == MATCH_PARENT)
|
||||||
|
continue;
|
||||||
|
if ((child.getHeight() == WRAP_CONTENT) || (child.getHeight() == WRAP_CONTENT_FILL))
|
||||||
|
_layout(child);
|
||||||
|
contentHeight = std::max(contentHeight, child.getRealHeight());
|
||||||
|
}
|
||||||
|
assert(contentHeight > 0);
|
||||||
|
if (realHeight_ != contentHeight + padTop_ + padBottom_) {
|
||||||
|
realHeight_ = contentHeight + padTop_ + padBottom_;
|
||||||
|
heightChanged = true;
|
||||||
|
}
|
||||||
|
printf("Inner height: %d, outer height: %d\n", contentHeight, realHeight_);
|
||||||
|
} else
|
||||||
|
contentHeight = realHeight_ - padTop_ - padBottom_;
|
||||||
|
|
||||||
|
// Layout children
|
||||||
|
int currentX = padLeft_;
|
||||||
|
|
||||||
|
for(auto& ent : children_) {
|
||||||
|
auto& child = *(std::get<1>(ent));
|
||||||
|
if (child.getHeight() == MATCH_PARENT)
|
||||||
|
_layout(child);
|
||||||
|
// Second layout pass for WRAP_CONTENT_FILL children
|
||||||
|
if ((child.getHeight() == WRAP_CONTENT_FILL) &&
|
||||||
|
(child.getRealHeight() != contentHeight))
|
||||||
|
_layout(child);
|
||||||
|
|
||||||
|
SDL_Rect cr;
|
||||||
|
cr.x = currentX;
|
||||||
|
cr.y = padTop_;
|
||||||
|
cr.w = child.getRealWidth();
|
||||||
|
if (contentHeight > child.getRealHeight()) {
|
||||||
|
cr.h = child.getRealHeight();
|
||||||
|
auto space = contentHeight - child.getRealHeight();
|
||||||
|
if (child.getContainerAlignmentVert() == ALIGN_CENTER)
|
||||||
|
cr.y += space/2;
|
||||||
|
else if (child.getContainerAlignmentVert() == ALIGN_BOTTOM)
|
||||||
|
cr.y += space;
|
||||||
|
} else
|
||||||
|
cr.h = contentHeight;
|
||||||
|
|
||||||
|
std::get<2>(ent) = cr;
|
||||||
|
|
||||||
|
currentX += child.getRealWidth() + padChildren_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == 0)
|
||||||
|
if (realWidth_ != currentX - padChildren_ + padRight_) {
|
||||||
|
realWidth_ = currentX - padChildren_ + padRight_;
|
||||||
|
widthChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent_ && (parent_ != caller) &&
|
||||||
|
((widthChanged && ((parent_->getWidth() == WRAP_CONTENT) ||
|
||||||
|
(parent_->getWidth() == WRAP_CONTENT_FILL))) ||
|
||||||
|
(heightChanged && ((parent_->getHeight() == WRAP_CONTENT) ||
|
||||||
|
(parent_->getHeight() == WRAP_CONTENT_FILL)))))
|
||||||
|
_layout(*parent_);
|
||||||
|
|
||||||
|
printf("Layout %s now %d, %d\n", name_.c_str(), realWidth_, realHeight_);
|
||||||
|
|
||||||
invalidateGL();
|
invalidateGL();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "View.hh"
|
#include "View.hh"
|
||||||
|
|
||||||
static const int DIR_VERTICAL = 0;
|
static const int DIR_VERTICAL = 0;
|
||||||
static const int DIR_HORIZONTAL = 0;
|
static const int DIR_HORIZONTAL = 1;
|
||||||
|
|
||||||
class LinearLayout : public View {
|
class LinearLayout : public View {
|
||||||
public:
|
public:
|
||||||
@@ -18,7 +18,7 @@ public:
|
|||||||
void setChildPadding(int pad);
|
void setChildPadding(int pad);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void layout() override;
|
void layout(Widget* caller = nullptr) override;
|
||||||
|
|
||||||
int direction_;
|
int direction_;
|
||||||
int padChildren_;
|
int padChildren_;
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -1,8 +1,8 @@
|
|||||||
CXX=g++
|
CXX=g++
|
||||||
CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -Wno-sign-compare -std=c++14 -flto
|
CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -Wno-unused-function -Wno-unused-parameter -Wno-sign-compare -std=c++14 -flto -I/usr/include/libxml2
|
||||||
LDOPTS=
|
LDOPTS=
|
||||||
LIBS=-lglbinding -lSDL2 -lSDL2_image -lobj -lSDL2_ttf -lprotobuf
|
LIBS=-lglbinding -lSDL2 -lSDL2_image -lobj -lSDL2_ttf -lprotobuf -lxml2
|
||||||
CXXSRCS=main.cc objectParser.cc shaders.cc Object.cc VBOManager.cc texture.cc font.cc Overlay.cc TextWidget.cc Widget.cc View.cc LinearLayout.cc
|
CXXSRCS=main.cc objectParser.cc shaders.cc Object.cc VBOManager.cc texture.cc font.cc Overlay.cc TextWidget.cc Widget.cc View.cc LinearLayout.cc FontProvider.cc ImageProvider.cc GUILoader.cc
|
||||||
BINIFY_SRCS=binifyObj.cc objectParser.cc object.pb.cc
|
BINIFY_SRCS=binifyObj.cc objectParser.cc object.pb.cc
|
||||||
OBJS=$(addprefix objs/,$(CXXSRCS:.cc=.o))
|
OBJS=$(addprefix objs/,$(CXXSRCS:.cc=.o))
|
||||||
BINIFY_OBJS=$(addprefix objs/,$(BINIFY_SRCS:.cc=.o))
|
BINIFY_OBJS=$(addprefix objs/,$(BINIFY_SRCS:.cc=.o))
|
||||||
|
|||||||
24
Singleton.hh
Normal file
24
Singleton.hh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef __OPENGLPLAYGROUND_SINGLETON_HH__
|
||||||
|
#define __OPENGLPLAYGROUND_SINGLETON_HH__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Singleton {
|
||||||
|
public:
|
||||||
|
static T& getInstance() {
|
||||||
|
call_once(instance_flag, init);
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<T> instance;
|
||||||
|
static std::once_flag instance_flag;
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
instance.reset(new T{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
#include "font.hh"
|
#include "font.hh"
|
||||||
#include "TextWidget.hh"
|
#include "TextWidget.hh"
|
||||||
|
|
||||||
TextWidget::TextWidget(Font& font, std::string text, int width, int height)
|
TextWidget::TextWidget(TTF_Font *font, std::string text, int width, int height, std::string name)
|
||||||
: Widget(width, height), text_(std::move(text)), font_(font),
|
: Widget(width, height, std::move(name)), text_(std::move(text)), font_(font),
|
||||||
textSurf_(nullptr)
|
textSurf_(nullptr)
|
||||||
{
|
{
|
||||||
layout();
|
layout();
|
||||||
@@ -19,6 +19,7 @@ void TextWidget::setText(std::string text)
|
|||||||
if (text != text_) {
|
if (text != text_) {
|
||||||
text_ = std::move(text);
|
text_ = std::move(text);
|
||||||
invalidateGL();
|
invalidateGL();
|
||||||
|
textSurf_.release();
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,49 +29,72 @@ void TextWidget::setForegroundColor(SDL_Color fg)
|
|||||||
Widget::setForegroundColor(fg);
|
Widget::setForegroundColor(fg);
|
||||||
|
|
||||||
// Trigger text rerender
|
// Trigger text rerender
|
||||||
|
textSurf_.release();
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWidget::layout()
|
void TextWidget::layout(Widget *caller)
|
||||||
{
|
{
|
||||||
// Determine text size
|
|
||||||
int width, height;
|
int width, height;
|
||||||
if(TTF_SizeUTF8(font_.getFont(), text_.c_str(), &width, &height) != 0)
|
std::tie(width, height) = Widget::baseLayout(caller);
|
||||||
throw TTFException{};
|
|
||||||
|
|
||||||
if ((width > (width_ - padLeft_ - padRight_)) && (width_ != WRAP_CONTENT)) {
|
// Determine text size
|
||||||
textSurf_.reset(TTF_RenderUTF8_Blended_Wrapped(font_.getFont(),
|
int textWidth, textHeight;
|
||||||
text_.c_str(),
|
if ((height == 0) || (width == 0)) {
|
||||||
fg_, width_- padLeft_ - padRight_));
|
if(TTF_SizeUTF8(font_, text_.c_str(), &textWidth, &textHeight) != 0)
|
||||||
if (!textSurf_)
|
|
||||||
throw TTFException{};
|
throw TTFException{};
|
||||||
|
|
||||||
if (height_ == WRAP_CONTENT)
|
|
||||||
// TODO: Get proper tight bounding box for multiline text
|
|
||||||
if (realHeight_ != (textSurf_->h - // TTF_FontLineSkip(font_.getFont())
|
|
||||||
+ padTop_ + padBottom_)) {
|
|
||||||
realHeight_ = textSurf_->h - // TTF_FontLineSkip(font_.getFont())
|
|
||||||
+ padTop_ + padBottom_;
|
|
||||||
invalidateGL();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
textSurf_.reset(TTF_RenderUTF8_Blended(font_.getFont(), text_.c_str(), fg_));
|
|
||||||
|
|
||||||
if (!textSurf_)
|
|
||||||
throw TTFException{};
|
|
||||||
|
|
||||||
if (width_ == WRAP_CONTENT)
|
|
||||||
if (realWidth_ != (textSurf_->w + padLeft_ + padRight_)) {
|
|
||||||
realWidth_ = textSurf_->w + padLeft_ + padRight_;
|
|
||||||
invalidateGL();
|
|
||||||
}
|
|
||||||
if (height_ == WRAP_CONTENT)
|
|
||||||
if (realHeight_ != (textSurf_->h + padTop_ + padBottom_)) {
|
|
||||||
realHeight_ = textSurf_->h + padTop_ + padBottom_;
|
|
||||||
invalidateGL();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool multiline = false;
|
||||||
|
if (height == 0) {
|
||||||
|
if (width != 0) {
|
||||||
|
textSurf_.reset(TTF_RenderUTF8_Blended_Wrapped(font_,
|
||||||
|
text_.c_str(),
|
||||||
|
fg_, width - padLeft_ - padRight_));
|
||||||
|
if (!textSurf_)
|
||||||
|
throw TTFException{};
|
||||||
|
|
||||||
|
multiline = true;
|
||||||
|
// TODO: Get proper tight bounding box for multiline text
|
||||||
|
height = textSurf_->h - // TTF_FontLineSkip(font_.getFont())
|
||||||
|
+ padTop_ + padBottom_;
|
||||||
|
} else {
|
||||||
|
width = textWidth + padLeft_ + padRight_;
|
||||||
|
height = textHeight + padTop_ + padBottom_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == 0) {
|
||||||
|
width = textWidth + padLeft_ + padRight_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((width != realWidth_) || (height != realHeight_) || !textSurf_) {
|
||||||
|
if (!multiline) {
|
||||||
|
textSurf_.reset(TTF_RenderUTF8_Blended(font_, text_.c_str(), fg_));
|
||||||
|
|
||||||
|
if (!textSurf_)
|
||||||
|
throw TTFException{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((width != realWidth_) || (height != realHeight_)) {
|
||||||
|
bool parentLayout = false;
|
||||||
|
if (parent_ && (caller != parent_) &&
|
||||||
|
(((width != realWidth_) && ((parent_->getWidth() == WRAP_CONTENT) ||
|
||||||
|
(parent_->getWidth() == WRAP_CONTENT_FILL))) ||
|
||||||
|
((height != realHeight_) && ((parent_->getHeight() == WRAP_CONTENT) ||
|
||||||
|
(parent_->getHeight() == WRAP_CONTENT_FILL)))))
|
||||||
|
parentLayout = true;
|
||||||
|
|
||||||
|
realWidth_ = width;
|
||||||
|
realHeight_ = height;
|
||||||
|
if (parentLayout)
|
||||||
|
_layout(*parent_);
|
||||||
|
|
||||||
|
printf("TextWidget %s now %d, %d\n", name_.c_str(), realWidth_, realHeight_);
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateGL();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWidget::render(SDL_Surface *dst, SDL_Rect *dstRect) const
|
void TextWidget::render(SDL_Surface *dst, SDL_Rect *dstRect) const
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ class Font;
|
|||||||
|
|
||||||
class TextWidget final : public Widget {
|
class TextWidget final : public Widget {
|
||||||
public:
|
public:
|
||||||
TextWidget(Font& font, std::string text = "",
|
TextWidget(TTF_Font *font, std::string text = "",
|
||||||
int width = WRAP_CONTENT, int height = WRAP_CONTENT);
|
int width = WRAP_CONTENT, int height = WRAP_CONTENT,
|
||||||
|
std::string name = "");
|
||||||
|
|
||||||
TextWidget(TextWidget const& copy) = delete;
|
TextWidget(TextWidget const& copy) = delete;
|
||||||
TextWidget& operator=(TextWidget const& copy) = delete;
|
TextWidget& operator=(TextWidget const& copy) = delete;
|
||||||
@@ -26,11 +27,11 @@ public:
|
|||||||
void render(SDL_Surface *dst, SDL_Rect *dstRect) const override;
|
void render(SDL_Surface *dst, SDL_Rect *dstRect) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void layout() override;
|
void layout(Widget* caller = nullptr) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string text_;
|
std::string text_;
|
||||||
Font& font_;
|
TTF_Font *font_;
|
||||||
SDLSurfaceUPtr textSurf_;
|
SDLSurfaceUPtr textSurf_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,19 +7,10 @@
|
|||||||
|
|
||||||
using namespace gl;
|
using namespace gl;
|
||||||
|
|
||||||
std::unique_ptr<VBOManager> VBOManager::instance{nullptr};
|
template<>
|
||||||
static std::once_flag instance_flag;
|
std::unique_ptr<VBOManager> Singleton<VBOManager>::instance{nullptr};
|
||||||
|
template<>
|
||||||
VBOManager& VBOManager::getInstance()
|
std::once_flag Singleton<VBOManager>::instance_flag{};
|
||||||
{
|
|
||||||
std::call_once(instance_flag, init);
|
|
||||||
return *instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VBOManager::init()
|
|
||||||
{
|
|
||||||
instance.reset(new VBOManager{});
|
|
||||||
}
|
|
||||||
|
|
||||||
VBOManager::VBOManager()
|
VBOManager::VBOManager()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
#include <glbinding/gl/types.h>
|
#include <glbinding/gl/types.h>
|
||||||
|
|
||||||
class VBOManager {
|
#include "Singleton.hh"
|
||||||
|
|
||||||
|
class VBOManager : public Singleton<VBOManager> {
|
||||||
public:
|
public:
|
||||||
static VBOManager& getInstance();
|
|
||||||
|
|
||||||
~VBOManager();
|
~VBOManager();
|
||||||
|
|
||||||
VBOManager(VBOManager const& copy) = delete;
|
VBOManager(VBOManager const& copy) = delete;
|
||||||
@@ -19,9 +19,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
VBOManager();
|
VBOManager();
|
||||||
|
friend class Singleton<VBOManager>;
|
||||||
static void init();
|
|
||||||
static std::unique_ptr<VBOManager> instance;
|
|
||||||
|
|
||||||
class VBO;
|
class VBO;
|
||||||
|
|
||||||
|
|||||||
44
View.cc
44
View.cc
@@ -17,7 +17,7 @@ void View::addChild(std::unique_ptr<Widget> child)
|
|||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget& View::getChildByName(std::string name)
|
Widget& View::getChildByName(std::string const& name)
|
||||||
{
|
{
|
||||||
if (name == "")
|
if (name == "")
|
||||||
throw ChildNotFoundException{};
|
throw ChildNotFoundException{};
|
||||||
@@ -30,7 +30,7 @@ Widget& View::getChildByName(std::string name)
|
|||||||
throw ChildNotFoundException{};
|
throw ChildNotFoundException{};
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget const& View::getChildByName(std::string name) const
|
Widget const& View::getChildByName(std::string const& name) const
|
||||||
{
|
{
|
||||||
if (name == "")
|
if (name == "")
|
||||||
throw ChildNotFoundException{};
|
throw ChildNotFoundException{};
|
||||||
@@ -43,7 +43,45 @@ Widget const& View::getChildByName(std::string name) const
|
|||||||
throw ChildNotFoundException{};
|
throw ChildNotFoundException{};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Widget> View::removeChild(std::string name)
|
Widget& View::getPath(std::string const& path)
|
||||||
|
{
|
||||||
|
auto slash = path.find('/');
|
||||||
|
|
||||||
|
// Last path element
|
||||||
|
if (slash == std::string::npos)
|
||||||
|
return getChildByName(path);
|
||||||
|
|
||||||
|
auto first = path.substr(0, slash);
|
||||||
|
auto& child = getChildByName(first);
|
||||||
|
try {
|
||||||
|
View& next = dynamic_cast<View&>(child);
|
||||||
|
|
||||||
|
return next.getPath(path.substr(slash+1));
|
||||||
|
} catch(std::bad_cast &ex) {
|
||||||
|
throw ChildNotFoundException{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget const& View::getPath(std::string const& path) const
|
||||||
|
{
|
||||||
|
auto slash = path.find('/');
|
||||||
|
|
||||||
|
// Last path element
|
||||||
|
if (slash == std::string::npos)
|
||||||
|
return getChildByName(path);
|
||||||
|
|
||||||
|
auto first = path.substr(0, slash);
|
||||||
|
auto& child = getChildByName(first);
|
||||||
|
try {
|
||||||
|
View const& next = dynamic_cast<View const&>(child);
|
||||||
|
|
||||||
|
return next.getPath(path.substr(slash+1));
|
||||||
|
} catch(std::bad_cast &ex) {
|
||||||
|
throw ChildNotFoundException{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> View::removeChild(std::string const& name)
|
||||||
{
|
{
|
||||||
if (name == "")
|
if (name == "")
|
||||||
throw ChildNotFoundException{};
|
throw ChildNotFoundException{};
|
||||||
|
|||||||
9
View.hh
9
View.hh
@@ -33,11 +33,14 @@ public:
|
|||||||
|
|
||||||
void addChild(std::unique_ptr<Widget> child);
|
void addChild(std::unique_ptr<Widget> child);
|
||||||
|
|
||||||
Widget& getChildByName(std::string name);
|
Widget& getChildByName(std::string const& name);
|
||||||
|
|
||||||
Widget const& getChildByName(std::string name) const;
|
Widget const& getChildByName(std::string const& name) const;
|
||||||
|
|
||||||
std::unique_ptr<Widget> removeChild(std::string name);
|
Widget& getPath(std::string const& path);
|
||||||
|
Widget const& getPath(std::string const& path) const;
|
||||||
|
|
||||||
|
std::unique_ptr<Widget> removeChild(std::string const& name);
|
||||||
std::unique_ptr<Widget> removeChild(Widget& child);
|
std::unique_ptr<Widget> removeChild(Widget& child);
|
||||||
|
|
||||||
void render(SDL_Surface *dst, SDL_Rect *dstRect) const override;
|
void render(SDL_Surface *dst, SDL_Rect *dstRect) const override;
|
||||||
|
|||||||
107
Widget.cc
107
Widget.cc
@@ -13,16 +13,22 @@ Widget::Widget(int width, int height, std::string name)
|
|||||||
bg_(SDL_Color{0, 0, 0, 0}), fg_(SDL_Color{255, 255, 255, 255}),
|
bg_(SDL_Color{0, 0, 0, 0}), fg_(SDL_Color{255, 255, 255, 255}),
|
||||||
padLeft_(0), padTop_(0), padRight_(0), padBottom_(0),
|
padLeft_(0), padTop_(0), padRight_(0), padBottom_(0),
|
||||||
alignHoriz_(ALIGN_LEFT), alignVert_(ALIGN_TOP),
|
alignHoriz_(ALIGN_LEFT), alignVert_(ALIGN_TOP),
|
||||||
|
alignContHoriz_(ALIGN_LEFT), alignContVert_(ALIGN_TOP),
|
||||||
|
parent_(nullptr),
|
||||||
renderTop_(0), renderLeft_(0),
|
renderTop_(0), renderLeft_(0),
|
||||||
renderTexValid_(false), renderAttribsValid_(false),
|
renderTexValid_(false), renderAttribsValid_(false),
|
||||||
renderTex_(nullptr), vaID_(0)
|
renderTex_(nullptr), vaID_(0)
|
||||||
{
|
{
|
||||||
if (width_ == WRAP_CONTENT)
|
if ((width_ == WRAP_CONTENT) ||
|
||||||
|
(width_ == MATCH_PARENT) ||
|
||||||
|
(width_ == WRAP_CONTENT_FILL))
|
||||||
realWidth_ = 0;
|
realWidth_ = 0;
|
||||||
else
|
else
|
||||||
realWidth_ = width_;
|
realWidth_ = width_;
|
||||||
|
|
||||||
if (height_ == WRAP_CONTENT)
|
if ((height_ == WRAP_CONTENT) ||
|
||||||
|
(height_ == MATCH_PARENT) ||
|
||||||
|
(height_ == WRAP_CONTENT_FILL))
|
||||||
realHeight_ = 0;
|
realHeight_ = 0;
|
||||||
else
|
else
|
||||||
realHeight_ = height_;
|
realHeight_ = height_;
|
||||||
@@ -38,18 +44,33 @@ void Widget::setSize(int width, int height)
|
|||||||
if ((width_ != width) || (height_ != height)) {
|
if ((width_ != width) || (height_ != height)) {
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
|
|
||||||
if (width_ != WRAP_CONTENT)
|
|
||||||
if (realWidth_ != width_) {
|
|
||||||
realWidth_ = width_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height_ != WRAP_CONTENT)
|
if (width_ == MATCH_PARENT) {
|
||||||
if (realHeight_ != height_) {
|
if (parent_)
|
||||||
realHeight_ = height_;
|
realWidth_ = parent_->realWidth_ - parent_->padLeft_ - parent_->padRight_;
|
||||||
}
|
else
|
||||||
|
realWidth_ = 0;
|
||||||
|
} else if ((width_ == WRAP_CONTENT) ||
|
||||||
|
(width_ == WRAP_CONTENT_FILL))
|
||||||
|
realWidth_ = 0;
|
||||||
|
else
|
||||||
|
realWidth_ = width_;
|
||||||
|
|
||||||
layout();
|
if (height_ == MATCH_PARENT) {
|
||||||
|
if (parent_)
|
||||||
|
realHeight_ = parent_->realHeight_ - parent_->padTop_ - parent_->padBottom_;
|
||||||
|
else
|
||||||
|
realHeight_ = 0;
|
||||||
|
} else if ((height_ == WRAP_CONTENT) ||
|
||||||
|
(height_ == WRAP_CONTENT_FILL))
|
||||||
|
realHeight_ = 0;
|
||||||
|
else
|
||||||
|
realHeight_ = height_;
|
||||||
|
|
||||||
|
if (parent_)
|
||||||
|
parent_->layout();
|
||||||
|
else
|
||||||
|
layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,9 +123,32 @@ void Widget::setAlignment(int horiz, int vert) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::setContainerAlignment(int horiz, int vert) {
|
||||||
|
if ((alignContHoriz_ != horiz) ||
|
||||||
|
(alignContVert_ != vert)) {
|
||||||
|
alignContHoriz_ = horiz;
|
||||||
|
alignContVert_ = vert;
|
||||||
|
|
||||||
|
if (parent_)
|
||||||
|
parent_->layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::setParent(Widget *parent)
|
void Widget::setParent(Widget *parent)
|
||||||
{
|
{
|
||||||
parent_ = parent;
|
parent_ = parent;
|
||||||
|
if (width_ == MATCH_PARENT) {
|
||||||
|
if (parent_ && (parent_->realWidth_ > 0))
|
||||||
|
realWidth_ = parent_->realWidth_ - parent_->padLeft_ - parent_->padRight_;
|
||||||
|
else
|
||||||
|
realWidth_ = 0;
|
||||||
|
}
|
||||||
|
if (height_ == MATCH_PARENT) {
|
||||||
|
if (parent_ && (parent_->realHeight_ > 0))
|
||||||
|
realHeight_ = parent_->realHeight_ - parent_->padTop_ - parent_->padBottom_;
|
||||||
|
else
|
||||||
|
realHeight_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::setGLRenderPos(int left, int top)
|
void Widget::setGLRenderPos(int left, int top)
|
||||||
@@ -205,11 +249,47 @@ SDL_Rect Widget::calcContentRect(SDL_Surface *dst, SDL_Rect *dstRect, SDL_Rect c
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<int, int> Widget::baseLayout(Widget *caller)
|
||||||
|
{
|
||||||
|
int width = realWidth_, height = realHeight_;
|
||||||
|
if ((width_ == MATCH_PARENT) && parent_) {
|
||||||
|
if ((parent_->getRealWidth() <= 0) && (parent_ != caller))
|
||||||
|
parent_->layout();
|
||||||
|
width = parent_->getRealWidth() - parent_->getLeftPadding() - parent_->getRightPadding();
|
||||||
|
} else if ((width_ == WRAP_CONTENT_FILL) && parent_ && (parent_->getRealWidth() > 0)) {
|
||||||
|
width = parent_->getRealWidth() - parent_->getLeftPadding() - parent_->getRightPadding();
|
||||||
|
} else if ((width_ == WRAP_CONTENT) || (width_ == WRAP_CONTENT_FILL)) {
|
||||||
|
width = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((height_ == MATCH_PARENT) && parent_) {
|
||||||
|
if ((parent_->getRealHeight() <= 0) && (parent_ != caller))
|
||||||
|
parent_->layout();
|
||||||
|
height = parent_->getRealHeight() - parent_->getTopPadding() - parent_->getBottomPadding();
|
||||||
|
} else if ((height_ == WRAP_CONTENT_FILL) && parent_ && (parent_->getRealHeight() > 0)) {
|
||||||
|
height = parent_->getRealHeight() - parent_->getTopPadding() - parent_->getBottomPadding();
|
||||||
|
} else if ((height_ == WRAP_CONTENT) || (height_ == WRAP_CONTENT_FILL)) {
|
||||||
|
height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("baseLayout: %s: %d, %d\n", name_.c_str(), width, height);
|
||||||
|
|
||||||
|
return std::make_tuple(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::invalidateGL()
|
||||||
|
{
|
||||||
|
renderTexValid_ = false;
|
||||||
|
if (parent_)
|
||||||
|
parent_->invalidateGL();
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::renderToGL() const
|
void Widget::renderToGL() const
|
||||||
{
|
{
|
||||||
const size_t NUM_TRIANGLES = 6;
|
const size_t NUM_TRIANGLES = 6;
|
||||||
|
|
||||||
if (!renderTexValid_) {
|
if (!renderTexValid_) {
|
||||||
|
printf("Widget::renderToGL: redraw texture for %s\n", name_.c_str());
|
||||||
uint32_t rmask, gmask, bmask, amask;
|
uint32_t rmask, gmask, bmask, amask;
|
||||||
int bpp;
|
int bpp;
|
||||||
if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888,
|
if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888,
|
||||||
@@ -235,7 +315,8 @@ void Widget::renderToGL() const
|
|||||||
renderTex_->bind();
|
renderTex_->bind();
|
||||||
|
|
||||||
if (!renderAttribsValid_) {
|
if (!renderAttribsValid_) {
|
||||||
int t = renderTop_, l = renderLeft_, b = renderTop_+realHeight_,
|
printf("Widget::renderToGL: regen attribs for %s\n", name_.c_str());
|
||||||
|
short int t = renderTop_, l = renderLeft_, b = renderTop_+realHeight_,
|
||||||
r = renderLeft_+realWidth_;
|
r = renderLeft_+realWidth_;
|
||||||
std::vector<VertexAttribs> vertexAttribs{
|
std::vector<VertexAttribs> vertexAttribs{
|
||||||
{{l, t}, {0, 0}},
|
{{l, t}, {0, 0}},
|
||||||
|
|||||||
28
Widget.hh
28
Widget.hh
@@ -15,6 +15,9 @@ static const int WRAP_CONTENT = -1;
|
|||||||
WRAP_CONTENT */
|
WRAP_CONTENT */
|
||||||
static const int MATCH_PARENT = -2;
|
static const int MATCH_PARENT = -2;
|
||||||
|
|
||||||
|
/* Size the widget to wrap the content size, expanding to fill parent when
|
||||||
|
parent is larger */
|
||||||
|
static const int WRAP_CONTENT_FILL = -3;
|
||||||
|
|
||||||
static const int ALIGN_TOP = 0;
|
static const int ALIGN_TOP = 0;
|
||||||
static const int ALIGN_BOTTOM = 1;
|
static const int ALIGN_BOTTOM = 1;
|
||||||
@@ -56,8 +59,16 @@ public:
|
|||||||
the background is applied to the entire dstRect, and the content is
|
the background is applied to the entire dstRect, and the content is
|
||||||
rendered with padding*/
|
rendered with padding*/
|
||||||
void setPadding(int left, int top, int right, int bottom);
|
void setPadding(int left, int top, int right, int bottom);
|
||||||
|
int getLeftPadding() const { return padLeft_; }
|
||||||
|
int getTopPadding() const { return padTop_; }
|
||||||
|
int getRightPadding() const { return padRight_; }
|
||||||
|
int getBottomPadding() const { return padBottom_; }
|
||||||
|
|
||||||
virtual void setAlignment(int horiz, int vert);
|
virtual void setAlignment(int horiz, int vert);
|
||||||
|
|
||||||
|
void setContainerAlignment(int horiz, int vert);
|
||||||
|
int getContainerAlignmentHoriz() const { return alignContHoriz_; }
|
||||||
|
int getContainerAlignmentVert() const { return alignContVert_; }
|
||||||
|
|
||||||
void setGLRenderPos(int left, int top);
|
void setGLRenderPos(int left, int top);
|
||||||
|
|
||||||
@@ -66,23 +77,29 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* Update realWidth_ and realHeight_ where necessary
|
/* Update realWidth_ and realHeight_ where necessary
|
||||||
(width_ or height_ == WRAP_CONTENT) */
|
(width_ or height_ == WRAP_CONTENT). Check caller to prevent recursion */
|
||||||
virtual void layout() = 0;
|
virtual void layout(Widget *caller = nullptr) = 0;
|
||||||
|
|
||||||
/* Inform the renderToGL() code that any cached presentations are now invalid */
|
/* Inform the renderToGL() code that any cached presentations are now invalid */
|
||||||
void invalidateGL() { renderTexValid_ = false; }
|
void invalidateGL();
|
||||||
|
|
||||||
/* Helper to calculate the SDL_Rect with which to render the content,
|
/* Helper to calculate the SDL_Rect with which to render the content,
|
||||||
respecting alignment and padding */
|
respecting alignment and padding */
|
||||||
SDL_Rect calcContentRect(SDL_Surface *dst, SDL_Rect *dstRect, SDL_Rect contentRect) const;
|
SDL_Rect calcContentRect(SDL_Surface *dst, SDL_Rect *dstRect, SDL_Rect contentRect) const;
|
||||||
|
|
||||||
|
/* Helper to calculate layout height, width constraints
|
||||||
|
Returns width, height tuple with with width/height > 0 if fixed or
|
||||||
|
determined by parent layout, 0 if it should be based on widget contents.
|
||||||
|
Checks caller to prevent recursion */
|
||||||
|
std::tuple<int, int> baseLayout(Widget *caller);
|
||||||
|
|
||||||
void setParent(Widget *parent);
|
void setParent(Widget *parent);
|
||||||
|
|
||||||
/* Helpers to allow Views to access protected methods of other Widgets */
|
/* Helpers to allow Views to access protected methods of other Widgets */
|
||||||
void _setParent(Widget& child) { child.setParent(this); }
|
void _setParent(Widget& child) { child.setParent(this); }
|
||||||
void _clearParent(Widget& child) { child.setParent(nullptr); }
|
void _clearParent(Widget& child) { child.setParent(nullptr); }
|
||||||
void _layout(Widget& child) { child.layout(); }
|
void _layout(Widget& child) { child.layout(this); }
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
|
||||||
int width_, height_;
|
int width_, height_;
|
||||||
@@ -94,6 +111,7 @@ protected:
|
|||||||
int padLeft_, padTop_, padRight_, padBottom_;
|
int padLeft_, padTop_, padRight_, padBottom_;
|
||||||
|
|
||||||
int alignHoriz_, alignVert_;
|
int alignHoriz_, alignVert_;
|
||||||
|
int alignContHoriz_, alignContVert_;
|
||||||
|
|
||||||
Widget *parent_;
|
Widget *parent_;
|
||||||
|
|
||||||
|
|||||||
11
common.hh
11
common.hh
@@ -141,14 +141,21 @@ enum class VAFormats {
|
|||||||
// Some helpers to C++11-ify SDL
|
// Some helpers to C++11-ify SDL
|
||||||
|
|
||||||
struct SDLSurfaceDeleter {
|
struct SDLSurfaceDeleter {
|
||||||
void operator()(SDL_Surface* ptr) const
|
void operator()(SDL_Surface* ptr) const {
|
||||||
{
|
|
||||||
SDL_FreeSurface(ptr);
|
SDL_FreeSurface(ptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using SDLSurfaceUPtr = std::unique_ptr<SDL_Surface, SDLSurfaceDeleter>;
|
using SDLSurfaceUPtr = std::unique_ptr<SDL_Surface, SDLSurfaceDeleter>;
|
||||||
|
|
||||||
|
struct TTFFontDeleter {
|
||||||
|
void operator()(TTF_Font* font) const {
|
||||||
|
TTF_CloseFont(font);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using TTFFontUPtr = std::unique_ptr<TTF_Font, TTFFontDeleter>;
|
||||||
|
|
||||||
class SDLSurfaceScopedLock {
|
class SDLSurfaceScopedLock {
|
||||||
public:
|
public:
|
||||||
SDLSurfaceScopedLock(SDL_Surface *surf) : surf_(surf) {
|
SDLSurfaceScopedLock(SDL_Surface *surf) : surf_(surf) {
|
||||||
|
|||||||
19
layouts/test.xml
Normal file
19
layouts/test.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<gui version="1">
|
||||||
|
<style name="ButtonStyle100x30" font="DejaVuSans.ttf" width="100" height="30"
|
||||||
|
paddingLeft="6" paddingTop="6" paddingRight="6" paddingBottom="6"
|
||||||
|
background="textures/button_100x30.png" color="0,0,0,255"
|
||||||
|
alignHorizontal="center" alignVertical="center"/>
|
||||||
|
|
||||||
|
<LinearLayout direction="vertical" name="layout" backgroundColor="0,255,255,128"
|
||||||
|
childPadding="4" paddingLeft="4" paddingTop="4" paddingRight="4"
|
||||||
|
paddingBottom="4">
|
||||||
|
<TextWidget font="DejaVuSans.ttf" width="204" name="laber"
|
||||||
|
backgroundColor="255,0,0,128"
|
||||||
|
text="Dies ist ein längerer Text, der umgebrochen werden sollte. Bla laber schwafl, laber bullshit bingo" />
|
||||||
|
<LinearLayout direction="horizontal" name="layout2" width="match_parent"
|
||||||
|
childPadding="4" backgroundColor="0,255,0,128">
|
||||||
|
<TextWidget name="button2" text="Cancel" style="ButtonStyle100x30" />
|
||||||
|
<TextWidget name="button" text="Do stuff!" style="ButtonStyle100x30" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</gui>
|
||||||
49
main.cc
49
main.cc
@@ -27,6 +27,9 @@
|
|||||||
#include "Overlay.hh"
|
#include "Overlay.hh"
|
||||||
#include "TextWidget.hh"
|
#include "TextWidget.hh"
|
||||||
#include "LinearLayout.hh"
|
#include "LinearLayout.hh"
|
||||||
|
#include "GUILoader.hh"
|
||||||
|
#include "FontProvider.hh"
|
||||||
|
#include "ImageProvider.hh"
|
||||||
|
|
||||||
using namespace gl;
|
using namespace gl;
|
||||||
|
|
||||||
@@ -125,7 +128,7 @@ int main(int argc, char *argv[])
|
|||||||
glm::vec3(0.0f, 0.0f, 0),
|
glm::vec3(0.0f, 0.0f, 0),
|
||||||
glm::vec3(0, 1, 0));
|
glm::vec3(0, 1, 0));
|
||||||
|
|
||||||
Font font{"DejaVuSans.ttf", 12};
|
//Font font{"DejaVuSans.ttf", 12};
|
||||||
|
|
||||||
// sf::Font font;
|
// sf::Font font;
|
||||||
// if (!font.loadFromFile("DejaVuSans.ttf")) {
|
// if (!font.loadFromFile("DejaVuSans.ttf")) {
|
||||||
@@ -212,34 +215,55 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
bool close = false;
|
bool close = false;
|
||||||
|
|
||||||
|
TTF_Font * font = FontProvider::getInstance().getFont("DejaVuSans.ttf", 12);
|
||||||
unsigned fpsTime = 0, fpsCount = 0;
|
unsigned fpsTime = 0, fpsCount = 0;
|
||||||
TextWidget fpsText{font, "0 FPS"};
|
TextWidget fpsText{font, "0 FPS", WRAP_CONTENT, WRAP_CONTENT, "fpsText"};
|
||||||
fpsText.setBackgroundColor(SDL_Color{0, 255, 0, 128});
|
fpsText.setBackgroundColor(SDL_Color{0, 255, 0, 128});
|
||||||
fpsText.setPadding(8, 8, 8, 8);
|
fpsText.setPadding(8, 8, 8, 8);
|
||||||
|
|
||||||
auto laber = make_unique<TextWidget>(font, "Dies ist ein längerer\nText, der umgebrochen werden sollte. Bla laber schwafl, laber bullshit bingo",
|
auto laber = make_unique<TextWidget>(font, "Dies ist ein längerer\nText, der umgebrochen werden sollte. Bla laber schwafl, laber bullshit bingo",
|
||||||
200, WRAP_CONTENT);
|
204, WRAP_CONTENT, "laber");
|
||||||
laber->setBackgroundColor(SDL_Color{255, 0, 0, 128});
|
laber->setBackgroundColor(SDL_Color{255, 0, 0, 128});
|
||||||
laber->setPadding(4, 0, 4, 0);
|
|
||||||
|
|
||||||
SDLSurfaceUPtr buttonImg(IMG_Load("textures/button_100x30.png"));
|
SDLSurfaceUPtr buttonImg(IMG_Load("textures/button_100x30.png"));
|
||||||
|
|
||||||
if (!buttonImg)
|
if (!buttonImg)
|
||||||
throw SDLException();
|
throw SDLException();
|
||||||
|
|
||||||
auto button = make_unique<TextWidget>(font, "Do stuff!", 100, 30);
|
auto button = make_unique<TextWidget>(font, "Do stuff!", 100, 30, "button");
|
||||||
button->setPadding(6, 6, 6, 6);
|
button->setPadding(6, 6, 6, 6);
|
||||||
button->setBackground(buttonImg.get());
|
button->setBackground(buttonImg.get());
|
||||||
button->setForegroundColor(SDL_Color{0, 0, 0, 255});
|
button->setForegroundColor(SDL_Color{0, 0, 0, 255});
|
||||||
button->setAlignment(ALIGN_CENTER, ALIGN_CENTER);
|
button->setAlignment(ALIGN_CENTER, ALIGN_CENTER);
|
||||||
|
auto button2 = make_unique<TextWidget>(font, "Cancel", 100, 30, "button2");
|
||||||
|
button2->setPadding(6, 6, 6, 6);
|
||||||
|
button2->setBackground(buttonImg.get());
|
||||||
|
button2->setForegroundColor(SDL_Color{0, 0, 0, 255});
|
||||||
|
button2->setAlignment(ALIGN_CENTER, ALIGN_CENTER);
|
||||||
|
|
||||||
auto layout = make_unique<LinearLayout>();
|
auto layout2 = make_unique<LinearLayout>(DIR_HORIZONTAL, MATCH_PARENT, WRAP_CONTENT, "layout2");
|
||||||
|
layout2->setBackgroundColor(SDL_Color{0, 255, 0, 128});
|
||||||
|
layout2->setChildPadding(4);
|
||||||
|
layout2->addChild(std::move(button2));
|
||||||
|
layout2->addChild(std::move(button));
|
||||||
|
|
||||||
|
auto layout = make_unique<LinearLayout>(DIR_VERTICAL, WRAP_CONTENT, WRAP_CONTENT, "layout");
|
||||||
layout->setBackgroundColor(SDL_Color{0, 255, 255, 128});
|
layout->setBackgroundColor(SDL_Color{0, 255, 255, 128});
|
||||||
layout->addChild(std::move(laber));
|
layout->addChild(std::move(laber));
|
||||||
layout->addChild(std::move(button));
|
layout->addChild(std::move(layout2));
|
||||||
layout->setChildPadding(4);
|
layout->setChildPadding(4);
|
||||||
layout->setPadding(4, 4, 4, 4);
|
layout->setPadding(4, 4, 4, 4);
|
||||||
layout->setGLRenderPos(200, 200);
|
layout->setGLRenderPos(200, 200);
|
||||||
|
|
||||||
|
auto gui = loadGUIFromFile("layouts/test.xml");
|
||||||
|
if (!gui)
|
||||||
|
throw Exception{"GUI load failed"};
|
||||||
|
View& guiRoot = dynamic_cast<View&>(*gui);
|
||||||
|
gui->setGLRenderPos(200, 500);
|
||||||
|
|
||||||
|
TextWidget& xmlButton = dynamic_cast<TextWidget&>(guiRoot.getPath("layout2/button"));
|
||||||
|
int countdown = 5;
|
||||||
|
xmlButton.setText(std::to_string(countdown));
|
||||||
|
|
||||||
while (!close) {
|
while (!close) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
@@ -368,6 +392,13 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
fpsCount = 0;
|
fpsCount = 0;
|
||||||
fpsTime = 0;
|
fpsTime = 0;
|
||||||
|
if(countdown > 0) {
|
||||||
|
--countdown;
|
||||||
|
if (countdown > 0)
|
||||||
|
xmlButton.setText(std::to_string(countdown));
|
||||||
|
else
|
||||||
|
xmlButton.setText("Do stuff!");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
++fpsCount;
|
++fpsCount;
|
||||||
fpsTime += elapsed;
|
fpsTime += elapsed;
|
||||||
@@ -380,6 +411,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
fpsText.renderToGL();
|
fpsText.renderToGL();
|
||||||
layout->renderToGL();
|
layout->renderToGL();
|
||||||
|
gui->renderToGL();
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
SDL_GL_SwapWindow(window);
|
SDL_GL_SwapWindow(window);
|
||||||
@@ -393,6 +425,9 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
|
|
||||||
|
FontProvider::getInstance().cleanup();
|
||||||
|
ImageProvider::getInstance().cleanup();
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
TTF_Quit();
|
TTF_Quit();
|
||||||
IMG_Quit();
|
IMG_Quit();
|
||||||
|
|||||||
Reference in New Issue
Block a user