275 lines
6.6 KiB
C++
275 lines
6.6 KiB
C++
#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);
|
|
}
|