#include #define GLM_FORCE_RADIANS #include #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), alignContHoriz_(ALIGN_LEFT), alignContVert_(ALIGN_TOP), parent_(nullptr), renderTop_(0), renderLeft_(0), renderTexValid_(false), renderAttribsValid_(false), renderTex_(nullptr), vaID_(0) { if ((width_ == WRAP_CONTENT) || (width_ == MATCH_PARENT) || (width_ == WRAP_CONTENT_FILL)) realWidth_ = 0; else realWidth_ = width_; if ((height_ == WRAP_CONTENT) || (height_ == MATCH_PARENT) || (height_ == WRAP_CONTENT_FILL)) 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_ == MATCH_PARENT) { if (parent_) realWidth_ = parent_->realWidth_ - parent_->padLeft_ - parent_->padRight_; else realWidth_ = 0; } else if ((width_ == WRAP_CONTENT) || (width_ == WRAP_CONTENT_FILL)) realWidth_ = 0; else realWidth_ = width_; 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(); } } 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::setContainerAlignment(int horiz, int vert) { if ((alignContHoriz_ != horiz) || (alignContVert_ != vert)) { alignContHoriz_ = horiz; alignContVert_ = vert; if (parent_) parent_->layout(); } } void Widget::setParent(Widget *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) { 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; } std::tuple 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 { const size_t NUM_TRIANGLES = 6; if (!renderTexValid_) { printf("Widget::renderToGL: redraw texture for %s\n", name_.c_str()); 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(tmp.get()); // Texture resize invalidates vertex attributes renderAttribsValid_ = false; } else { renderTex_->copyFromSurface(tmp.get()); } renderTexValid_ = true; } else renderTex_->bind(); if (!renderAttribsValid_) { printf("Widget::renderToGL: regen attribs for %s\n", name_.c_str()); short int t = renderTop_, l = renderLeft_, b = renderTop_+realHeight_, r = renderLeft_+realWidth_; std::vector 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(vertexAttribs.data())); renderAttribsValid_ = true; } else glBindVertexArray(vaID_); glDrawArrays(GL_TRIANGLES, 0, NUM_TRIANGLES); }