WIP: GUI Toolkit
This commit is contained in:
274
Widget.cc
Normal file
274
Widget.cc
Normal file
@@ -0,0 +1,274 @@
|
||||
#include <vector>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
#include "Widget.hh"
|
||||
#include "texture.hh"
|
||||
#include "shaders.hh"
|
||||
|
||||
Widget::Widget(int width, int height, std::string name)
|
||||
: name_(std::move(name)), width_(width), height_(height),
|
||||
background_(nullptr),
|
||||
bg_(SDL_Color{0, 0, 0, 0}), fg_(SDL_Color{255, 255, 255, 255}),
|
||||
padLeft_(0), padTop_(0), padRight_(0), padBottom_(0),
|
||||
alignHoriz_(ALIGN_LEFT), alignVert_(ALIGN_TOP),
|
||||
renderTop_(0), renderLeft_(0),
|
||||
renderTexValid_(false), renderAttribsValid_(false),
|
||||
renderTex_(nullptr), vaID_(0)
|
||||
{
|
||||
if (width_ == WRAP_CONTENT)
|
||||
realWidth_ = 0;
|
||||
else
|
||||
realWidth_ = width_;
|
||||
|
||||
if (height_ == WRAP_CONTENT)
|
||||
realHeight_ = 0;
|
||||
else
|
||||
realHeight_ = height_;
|
||||
}
|
||||
|
||||
Widget::~Widget()
|
||||
{
|
||||
glDeleteVertexArrays(1, &vaID_);
|
||||
}
|
||||
|
||||
void Widget::setSize(int width, int height)
|
||||
{
|
||||
if ((width_ != width) || (height_ != height)) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
if (width_ != WRAP_CONTENT)
|
||||
if (realWidth_ != width_) {
|
||||
realWidth_ = width_;
|
||||
}
|
||||
|
||||
if (height_ != WRAP_CONTENT)
|
||||
if (realHeight_ != height_) {
|
||||
realHeight_ = height_;
|
||||
}
|
||||
|
||||
layout();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setBackground(SDL_Surface* surf)
|
||||
{
|
||||
if (background_ != surf) {
|
||||
background_ = surf;
|
||||
invalidateGL();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setBackgroundColor(SDL_Color bg)
|
||||
{
|
||||
if (bg_ != bg) {
|
||||
bg_ = bg;
|
||||
invalidateGL();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setForegroundColor(SDL_Color fg)
|
||||
{
|
||||
if (fg_ != fg) {
|
||||
fg_ = fg;
|
||||
invalidateGL();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setPadding(int left, int top, int right, int bottom)
|
||||
{
|
||||
if ((padLeft_ != left) ||
|
||||
(padTop_ != top) ||
|
||||
(padRight_ != right) ||
|
||||
(padBottom_ != bottom)) {
|
||||
padLeft_ = left;
|
||||
padTop_ = top;
|
||||
padRight_ = right;
|
||||
padBottom_ = bottom;
|
||||
|
||||
layout();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setAlignment(int horiz, int vert) {
|
||||
if ((alignHoriz_ != horiz) ||
|
||||
(alignVert_ != vert)) {
|
||||
alignHoriz_ = horiz;
|
||||
alignVert_ = vert;
|
||||
|
||||
invalidateGL();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setParent(Widget *parent)
|
||||
{
|
||||
parent_ = parent;
|
||||
}
|
||||
|
||||
void Widget::setGLRenderPos(int left, int top)
|
||||
{
|
||||
if ((top != renderTop_) ||
|
||||
(left != renderLeft_)) {
|
||||
renderAttribsValid_ = false;
|
||||
renderTop_ = top;
|
||||
renderLeft_ = left;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::render(SDL_Surface *dst, SDL_Rect *dstRect) const
|
||||
{
|
||||
if (background_) {
|
||||
if (SDL_BlitScaled(background_, nullptr, dst, dstRect) != 0)
|
||||
throw SDLException{};
|
||||
} else {
|
||||
// Skip completely transparent background
|
||||
if (bg_.a == 0)
|
||||
return;
|
||||
|
||||
// Simply write completely intransparent bg
|
||||
if (bg_.a == 255) {
|
||||
SDLSurfaceScopedLock lock{dst};
|
||||
if (SDL_FillRect(dst, dstRect,
|
||||
SDL_MapRGBA(dst->format, bg_.r, bg_.g, bg_.b, bg_.a)) != 0)
|
||||
throw SDLException{};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Do proper blending blit for all other bgs
|
||||
int w = dstRect?dstRect->w:dst->w;
|
||||
int h = dstRect?dstRect->h:dst->h;
|
||||
|
||||
uint32_t rmask, gmask, bmask, amask;
|
||||
int bpp;
|
||||
if (!SDL_PixelFormatEnumToMasks(dst->format->format,
|
||||
&bpp, &rmask, &gmask, &bmask, &amask))
|
||||
throw SDLException{};
|
||||
|
||||
SDLSurfaceUPtr tmp{SDL_CreateRGBSurface(0, w, h, bpp,
|
||||
rmask, gmask, bmask, amask)};
|
||||
if (!tmp)
|
||||
throw SDLException{};
|
||||
|
||||
{
|
||||
SDLSurfaceScopedLock lock{tmp};
|
||||
if (SDL_FillRect(tmp.get(), nullptr,
|
||||
SDL_MapRGBA(tmp->format, bg_.r, bg_.g, bg_.b, bg_.a)) != 0)
|
||||
throw SDLException{};
|
||||
}
|
||||
|
||||
if (SDL_BlitSurface(tmp.get(), nullptr, dst, dstRect) != 0)
|
||||
throw SDLException{};
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect Widget::calcContentRect(SDL_Surface *dst, SDL_Rect *dstRect, SDL_Rect contentRect) const
|
||||
{
|
||||
SDL_Rect rect;
|
||||
|
||||
if (dstRect) {
|
||||
rect.x = dstRect->x + padLeft_;
|
||||
rect.y = dstRect->y + padTop_;
|
||||
rect.w = dstRect->w - (padLeft_ + padRight_);
|
||||
rect.h = dstRect->h - (padTop_ + padBottom_);
|
||||
} else {
|
||||
rect.x = padLeft_;
|
||||
rect.y = padTop_;
|
||||
rect.w = dst->w - padLeft_ - padRight_;
|
||||
rect.h = dst->h - padTop_ - padBottom_;
|
||||
}
|
||||
|
||||
if (contentRect.w < rect.w) {
|
||||
auto space = rect.w - contentRect.w;
|
||||
if (alignHoriz_ == ALIGN_RIGHT) {
|
||||
rect.x += space;
|
||||
rect.w -= space;
|
||||
} else if (alignHoriz_ == ALIGN_CENTER) {
|
||||
rect.x += space/2;
|
||||
rect.w -= space/2;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentRect.h < rect.h) {
|
||||
auto space = rect.h - contentRect.h;
|
||||
if (alignHoriz_ == ALIGN_BOTTOM) {
|
||||
rect.y += space;
|
||||
rect.h -= space;
|
||||
} else if (alignHoriz_ == ALIGN_CENTER) {
|
||||
rect.y += space/2;
|
||||
rect.h -= space/2;
|
||||
}
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void Widget::renderToGL() const
|
||||
{
|
||||
const size_t NUM_TRIANGLES = 6;
|
||||
|
||||
if (!renderTexValid_) {
|
||||
uint32_t rmask, gmask, bmask, amask;
|
||||
int bpp;
|
||||
if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888,
|
||||
&bpp, &rmask, &gmask, &bmask, &amask))
|
||||
throw SDLException{};
|
||||
|
||||
SDLSurfaceUPtr tmp(SDL_CreateRGBSurface(0, realWidth_, realHeight_, bpp,
|
||||
rmask, gmask, bmask, amask));
|
||||
render(tmp.get(), nullptr);
|
||||
|
||||
if (!renderTex_ || (renderTex_->getWidth() != realWidth_) ||
|
||||
(renderTex_->getHeight() != realHeight_)) {
|
||||
|
||||
renderTex_ = make_unique<Texture2D>(tmp.get());
|
||||
|
||||
// Texture resize invalidates vertex attributes
|
||||
renderAttribsValid_ = false;
|
||||
} else {
|
||||
renderTex_->copyFromSurface(tmp.get());
|
||||
}
|
||||
renderTexValid_ = true;
|
||||
} else
|
||||
renderTex_->bind();
|
||||
|
||||
if (!renderAttribsValid_) {
|
||||
int t = renderTop_, l = renderLeft_, b = renderTop_+realHeight_,
|
||||
r = renderLeft_+realWidth_;
|
||||
std::vector<VertexAttribs> vertexAttribs{
|
||||
{{l, t}, {0, 0}},
|
||||
{{r, b}, {65535u, 65535u}},
|
||||
{{r, t}, {65535u, 0}},
|
||||
{{l, t}, {0, 0}},
|
||||
{{l, b}, {0, 65535u}},
|
||||
{{r, b}, {65535u, 65535u}}};
|
||||
|
||||
if (!vbo_) {
|
||||
vbo_ = VBOManager::getInstance().alloc(sizeof(VertexAttribs)*NUM_TRIANGLES);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
|
||||
glGenVertexArrays(1, &vaID_);
|
||||
glBindVertexArray(vaID_);
|
||||
|
||||
glEnableVertexAttribArray(ATTRIB_VERTEX_POS);
|
||||
glVertexAttribPointer(ATTRIB_VERTEX_POS, 2, GL_SHORT, GL_FALSE, sizeof(VertexAttribs),
|
||||
(void*)(vbo_.getOfs()+offsetof(VertexAttribs, vertex)));
|
||||
|
||||
glEnableVertexAttribArray(ATTRIB_VERTEX_TC);
|
||||
glVertexAttribPointer(ATTRIB_VERTEX_TC, 2, GL_UNSIGNED_SHORT, GL_TRUE,
|
||||
sizeof(VertexAttribs),
|
||||
(void*)(vbo_.getOfs()+offsetof(VertexAttribs, texCoords)));
|
||||
} else {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
|
||||
glBindVertexArray(vaID_);
|
||||
}
|
||||
glBufferSubData(GL_ARRAY_BUFFER, vbo_.getOfs(),
|
||||
sizeof(VertexAttribs)*NUM_TRIANGLES,
|
||||
static_cast<void const*>(vertexAttribs.data()));
|
||||
renderAttribsValid_ = true;
|
||||
} else
|
||||
glBindVertexArray(vaID_);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, NUM_TRIANGLES);
|
||||
}
|
||||
Reference in New Issue
Block a user