diff --git a/CMakeLists.txt b/CMakeLists.txt index 159b5281..7692392a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ SET(SOURCES src/framework/core/engine.cpp # framework utilities + src/framework/util/color.cpp src/framework/util/util.cpp src/framework/util/logger.cpp src/framework/util/rsa.cpp @@ -84,12 +85,13 @@ SET(SOURCES src/framework/graphics/graphics.cpp # framework ui - src/framework/ui/anchorlayout.cpp + src/framework/ui/uilayout.cpp src/framework/ui/uielement.cpp src/framework/ui/uielementskin.cpp src/framework/ui/uibuttonskin.cpp src/framework/ui/uicontainer.cpp src/framework/ui/uiskins.cpp + src/framework/ui/uiloader.cpp src/framework/ui/uipanel.cpp src/framework/ui/uibutton.cpp src/framework/ui/uilabel.cpp diff --git a/src/framework/core/configs.cpp b/src/framework/core/configs.cpp index 438c26df..466392b3 100644 --- a/src/framework/core/configs.cpp +++ b/src/framework/core/configs.cpp @@ -35,7 +35,7 @@ bool Configs::load(const std::string& fileName) return false; std::string fileContents = g_resources.loadTextFile(fileName); - if(fileContents.size() == 0) + if(fileContents.size()) return false; std::istringstream fin(fileContents); @@ -52,8 +52,8 @@ bool Configs::load(const std::string& fileName) it.second() >> value; m_confsMap[key] = value; } - } catch (YAML::ParserException& e) { - logError("Malformed configuration file!"); + } catch (YAML::Exception& e) { + logError("Malformed config file: %s", e.what()); return false; } diff --git a/src/framework/core/engine.cpp b/src/framework/core/engine.cpp index e992b392..2db5edc4 100644 --- a/src/framework/core/engine.cpp +++ b/src/framework/core/engine.cpp @@ -37,7 +37,7 @@ void Engine::init() { // initialize stuff g_graphics.init(); - g_fonts.init(); + g_fonts.init("tibia-12px-rounded"); } void Engine::terminate() @@ -52,6 +52,7 @@ void Engine::terminate() void Engine::run() { + Font *defaultFont = g_fonts.getDefaultFont(); int ticks = Platform::getTicks(); int lastFpsTicks = ticks; int frameCount = 0; @@ -87,8 +88,8 @@ void Engine::run() // render fps if(m_calculateFps) { std::string fpsText = format("FPS: %d", fps); - Size textSize = g_defaultFont->calculateTextRectSize(fpsText); - g_defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - textSize.width() - 10, 10)); + Size textSize = defaultFont->calculateTextRectSize(fpsText); + defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - textSize.width() - 10, 10)); } // swap buffers @@ -119,7 +120,7 @@ void Engine::render() g_graphics.beginRender(); if(m_currentState) m_currentState->render(); - g_ui->render(); + UIContainer::getRootContainer()->render(); g_graphics.endRender(); } @@ -132,7 +133,7 @@ void Engine::onClose() void Engine::onResize(const Size& size) { g_graphics.resize(size); - g_ui->setSize(size); + UIContainer::getRootContainer()->setSize(size); if(m_currentState) m_currentState->onResize(size); @@ -141,7 +142,7 @@ void Engine::onResize(const Size& size) void Engine::onInputEvent(const InputEvent& event) { // inputs goest to gui first - if(!g_ui->onInputEvent(event)) { + if(!UIContainer::getRootContainer()->onInputEvent(event)) { // if gui didnt capture the input then goes to the state if(m_currentState) m_currentState->onInputEvent(event); diff --git a/src/framework/core/resources.cpp b/src/framework/core/resources.cpp index 73b3da5d..7f4cb730 100644 --- a/src/framework/core/resources.cpp +++ b/src/framework/core/resources.cpp @@ -43,7 +43,7 @@ bool Resources::setWriteDir(const std::string& path) bool ret = (bool)PHYSFS_setWriteDir(path.c_str()); if(!ret) - logError("Could not set the path \"%s\" as write directory, file write will not work.", path.c_str()); + logError("Could not set the path \"%s\" as write directory, file write will not work correctly.", path.c_str()); return ret; } diff --git a/src/framework/graphics/borderedimage.cpp b/src/framework/graphics/borderedimage.cpp index 73fe7334..225ef356 100644 --- a/src/framework/graphics/borderedimage.cpp +++ b/src/framework/graphics/borderedimage.cpp @@ -84,70 +84,64 @@ void BorderedImage::draw(const Rect& screenCoords) if(screenCoords.size() <= m_cornersSize) return; - const Size& textureSize = m_texture->getSize(); Rect rectCoords; Size centerSize = screenCoords.size() - m_cornersSize; - g_graphics._beginTextureRender(m_texture.get()); - // first the center rectCoords = Rect(screenCoords.left() + m_leftBorderTexCoords.width(), screenCoords.top() + m_topBorderTexCoords.height(), centerSize); - g_graphics._drawRepeatedTexturedRect(rectCoords, m_centerTexCoords, textureSize); + g_graphics.drawRepeatedTexturedRect(rectCoords, m_texture, m_centerTexCoords); // top left corner rectCoords = Rect(screenCoords.topLeft(), m_topLeftCornerTexCoords.size()); - g_graphics._drawTexturedRect(rectCoords, m_topLeftCornerTexCoords, textureSize); + g_graphics.drawTexturedRect(rectCoords, m_texture, m_topLeftCornerTexCoords); // top rectCoords = Rect(screenCoords.left() + m_topLeftCornerTexCoords.width(), screenCoords.topLeft().y, centerSize.width(), m_topBorderTexCoords.height()); - g_graphics._drawRepeatedTexturedRect(rectCoords, m_topBorderTexCoords, textureSize); + g_graphics.drawRepeatedTexturedRect(rectCoords, m_texture, m_topBorderTexCoords); // top right corner rectCoords = Rect(screenCoords.left() + m_topLeftCornerTexCoords.width() + centerSize.width(), screenCoords.top(), m_topRightCornerTexCoords.size()); - g_graphics._drawTexturedRect(rectCoords, m_topRightCornerTexCoords, textureSize); + g_graphics.drawTexturedRect(rectCoords, m_texture, m_topRightCornerTexCoords); // left rectCoords = Rect(screenCoords.left(), screenCoords.top() + m_topLeftCornerTexCoords.height(), m_leftBorderTexCoords.width(), centerSize.height()); - g_graphics._drawRepeatedTexturedRect(rectCoords, m_leftBorderTexCoords, textureSize); + g_graphics.drawRepeatedTexturedRect(rectCoords, m_texture, m_leftBorderTexCoords); // right rectCoords = Rect(screenCoords.left() + m_leftBorderTexCoords.width() + centerSize.width(), screenCoords.top() + m_topRightCornerTexCoords.height(), m_rightBorderTexCoords.width(), centerSize.height()); - g_graphics._drawRepeatedTexturedRect(rectCoords, m_rightBorderTexCoords, textureSize); + g_graphics.drawRepeatedTexturedRect(rectCoords, m_texture, m_rightBorderTexCoords); // bottom left corner rectCoords = Rect(screenCoords.left(), screenCoords.top() + m_topBorderTexCoords.height() + centerSize.height(), m_bottomLeftCornerTexCoords.size()); - g_graphics._drawTexturedRect(rectCoords, m_bottomLeftCornerTexCoords, textureSize); + g_graphics.drawTexturedRect(rectCoords, m_texture, m_bottomLeftCornerTexCoords); // bottom rectCoords = Rect(screenCoords.left() + m_bottomLeftCornerTexCoords.width(), screenCoords.top() + m_topBorderTexCoords.height() + centerSize.height(), centerSize.width(), m_bottomBorderTexCoords.height()); - g_graphics._drawRepeatedTexturedRect(rectCoords, m_bottomBorderTexCoords, textureSize); + g_graphics.drawRepeatedTexturedRect(rectCoords, m_texture, m_bottomBorderTexCoords); // bottom right corner rectCoords = Rect(screenCoords.left() + m_bottomLeftCornerTexCoords.width() + centerSize.width(), screenCoords.top() + m_topRightCornerTexCoords.height() + centerSize.height(), m_bottomRightCornerTexCoords.size()); - g_graphics._drawTexturedRect(rectCoords, m_bottomRightCornerTexCoords, textureSize); - - //g_graphics._drawBoundingRect(screenCoords, Color(0xFF00FF00), 1); - g_graphics._endTextureRender(); + g_graphics.drawTexturedRect(rectCoords, m_texture, m_bottomRightCornerTexCoords); } diff --git a/src/framework/graphics/font.cpp b/src/framework/graphics/font.cpp index 56825a58..836b1cf3 100644 --- a/src/framework/graphics/font.cpp +++ b/src/framework/graphics/font.cpp @@ -27,11 +27,7 @@ #include "textures.h" #include "graphics.h" -Font::Font() : - m_glyphHeight(10), - m_topMargin(0) -{ -} + void Font::calculateGlyphsWidthsAutomatically(const Size& glyphSize) { @@ -73,12 +69,11 @@ bool Font::load(const std::string& file) { std::string fileContents = g_resources.loadTextFile(file); if(!fileContents.size()) { - logError("Empty font file \"%s", file.c_str()); + logError("Coult not load font file \"%s", file.c_str()); return false; } std::istringstream fin(fileContents); - std::string textureName; Size glyphSize; @@ -88,22 +83,25 @@ bool Font::load(const std::string& file) YAML::Node doc; parser.GetNextDocument(doc); + // required values doc["glyph height"] >> m_glyphHeight; - doc["glyph spacing"] >> m_glyphSpacing; - doc["top margin"] >> m_topMargin; doc["image glyph size"] >> glyphSize; doc["image"] >> textureName; + // optional values + if(doc.FindValue("glyph spacing")) + doc["glyph spacing"] >> m_glyphSpacing; + if(doc.FindValue("top margin")) + doc["top margin"] >> m_topMargin; + + // load texture m_texture = g_textures.get("fonts/" + textureName); if(!m_texture) { - logError("Failed to load image for font \"%s\"", file.c_str()); + logError("Failed to load image for font file \"%s\"", file.c_str()); return false; } - // set glyphs height - for(int glyph = 32; glyph < 256; ++glyph) { - } - + // auto calculate widths calculateGlyphsWidthsAutomatically(glyphSize); // read custom widths @@ -121,13 +119,12 @@ bool Font::load(const std::string& file) int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width(); for(int glyph = 32; glyph< 256; ++glyph) { m_glyphsTextureCoords[glyph].setRect(((glyph - 32) % numHorizontalGlyphs) * glyphSize.width(), - ((glyph - 32) / numHorizontalGlyphs) * glyphSize.height(), - m_glyphsSize[glyph].width(), - m_glyphHeight); + ((glyph - 32) / numHorizontalGlyphs) * glyphSize.height(), + m_glyphsSize[glyph].width(), + m_glyphHeight); } - - } catch (YAML::ParserException& e) { - logError("Malformed font file \"%s\"", file.c_str()); + } catch (YAML::Exception& e) { + logError("Malformed font file \"%s\":\n %s", file.c_str(), e.what()); return false; } @@ -146,18 +143,12 @@ void Font::renderText(const std::string& text, const Rect& screenCoords, int align, const Color& color, - const Point& startInternalPos, - bool debug) + const Point& startInternalPos) { // prevent glitches from invalid rects if(!screenCoords.isValid()) return; - // begin texture rendering - g_graphics.setColor(color); - g_graphics._beginTextureRender(m_texture.get()); - - const Size& textureSize = m_texture->getSize(); int textLenght = text.length(); // map glyphs positions @@ -227,17 +218,8 @@ void Font::renderText(const std::string& text, } // render glyph - g_graphics._drawTexturedRect(glyphScreenCoords, glyphTextureCoords, textureSize); - - //g_graphics._drawBoundingRect(glyphScreenCoords, Color(0xFF0000FF)); + g_graphics.drawTexturedRect(glyphScreenCoords, m_texture, glyphTextureCoords, color); } - - // end texture redering - g_graphics._endTextureRender(); - g_graphics.resetColor(); - - if(debug) - g_graphics.drawBoundingRect(screenCoords.expanded(1), Color(0xFF00FF00)); } Point* Font::calculateGlyphsPositions(const std::string& text, int align, Size *textBoxSize) diff --git a/src/framework/graphics/font.h b/src/framework/graphics/font.h index 82382676..1a5b1c26 100644 --- a/src/framework/graphics/font.h +++ b/src/framework/graphics/font.h @@ -45,8 +45,10 @@ enum EAlign { class Font { public: - Font(); - virtual ~Font() { } + Font(const std::string& name) : + m_name(name), + m_glyphHeight(10), + m_topMargin(0) { } /// Load font from file bool load(const std::string &file); @@ -62,9 +64,8 @@ public: void renderText(const std::string& text, const Rect& screenCoords, int align = ALIGN_TOP_LEFT, - const Color& color = Color(0xFFFFFFFF), - const Point& startInternalPos = Point(), - bool debug = false); + const Color& color = Color::white, + const Point& startInternalPos = Point()); /// Calculate glyphs positions to use on render, also calculates textBoxSize if wanted Point *calculateGlyphsPositions(const std::string& text, int align = ALIGN_TOP_LEFT, Size *textBoxSize = NULL); @@ -72,9 +73,12 @@ public: /// Simulate render and calculate text size Size calculateTextRectSize(const std::string& text); + const std::string& getName() const { return m_name; } + private: void calculateGlyphsWidthsAutomatically(const Size& glyphSize); + std::string m_name; int m_glyphHeight; int m_topMargin; Size m_glyphSpacing; @@ -83,4 +87,6 @@ private: Size m_glyphsSize[256]; }; +typedef std::shared_ptr FontPtr; + #endif // FONT_H diff --git a/src/framework/graphics/fonts.cpp b/src/framework/graphics/fonts.cpp index 0966493a..acb03e9c 100644 --- a/src/framework/graphics/fonts.cpp +++ b/src/framework/graphics/fonts.cpp @@ -23,13 +23,11 @@ #include "fonts.h" -#include "font.h" #include "core/resources.h" Fonts g_fonts; -Font *g_defaultFont = NULL; -void Fonts::init() +void Fonts::init(const std::string& defaultFontName) { // load all fonts std::list files = g_resources.getDirectoryFiles("fonts"); @@ -37,27 +35,28 @@ void Fonts::init() if(boost::ends_with(file, ".yml")) { std::string name = file; boost::erase_first(name, ".yml"); - std::shared_ptr font(new Font); - font->load("fonts/" + file); - m_fonts[name] = font; + FontPtr font(new Font(name)); + if(font->load("fonts/" + file)) { + m_fonts.push_back(font); + + if(name == defaultFontName) + m_defaultFont = font; + } } } - // set default font - g_defaultFont = get("tibia-12px-rounded"); - if(!g_defaultFont) - logFatal("Default font not found!"); + if(!m_defaultFont) + logFatal("Could not load the default font \"%s\"\n", defaultFontName.c_str()); } Font* Fonts::get(const std::string& fontName) { // find font by name - auto it = m_fonts.find(fontName); - if(it != m_fonts.end()) { - return it->second.get(); + for(auto it = m_fonts.begin(); it != m_fonts.end(); ++it) { + if((*it)->getName() == fontName) + return (*it).get(); } - logError("Font \"%s\" not found", fontName.c_str()); - return NULL; + logError("Font \"%s\" not found, returing the default one", fontName.c_str()); + return m_defaultFont.get(); } - diff --git a/src/framework/graphics/fonts.h b/src/framework/graphics/fonts.h index 0475d6a9..f47a6103 100644 --- a/src/framework/graphics/fonts.h +++ b/src/framework/graphics/fonts.h @@ -34,19 +34,22 @@ public: Fonts() { } /// Initialize all fonts - void init(); + void init(const std::string& defaultFontName); + + /// Terminate all fonts + void terminate() { } /// Get a font by name Font *get(const std::string& fontName); - /// Terminate all fonts - void terminate() { } + /// Get the default font + Font *getDefaultFont() { return m_defaultFont.get(); }; private: - std::map > m_fonts; + std::vector m_fonts; + FontPtr m_defaultFont; }; extern Fonts g_fonts; -extern Font *g_defaultFont; #endif // FONTS_H diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index 8f737171..8684929f 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -123,49 +123,19 @@ void Graphics::endRender() } - -void Graphics::setColor(const Color& color) -{ - glColor4ubv(color.rgbaPtr()); -} - -void Graphics::resetColor() -{ - glColor4ub(0xFF, 0xFF, 0xFF, 0xFF); -} - -void Graphics::_beginTextureRender(const Texture *texture) -{ - glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); - glBegin(GL_QUADS); -} - -void Graphics::_endTextureRender() -{ - glEnd(); -} - -void Graphics::drawTexturedRect(const Rect& screenCoords, const Texture *texture, const Rect& textureCoords) +void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color) { if(screenCoords.size().isEmpty()) return; - glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); - glBegin(GL_QUADS); - _drawTexturedRect(screenCoords, textureCoords, texture->getSize()); - glEnd(); -} - -void Graphics::_drawTexturedRect(const Rect& screenCoords, const Rect& textureCoords, const Size& textureSize) -{ - if(screenCoords.size().isEmpty()) - return; + glColor4ubv(color.rgbaPtr()); // rect correction for opengl int right = screenCoords.right() + 1; int bottom = screenCoords.bottom() + 1; int top = screenCoords.top(); int left = screenCoords.left(); + const Size& textureSize = texture->getSize(); float textureRight = 0.0f; float textureBottom = 1.0f; @@ -179,24 +149,18 @@ void Graphics::_drawTexturedRect(const Rect& screenCoords, const Rect& textureCo textureLeft = (float)textureCoords.left() / textureSize.width(); } + glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); + glBegin(GL_QUADS); + glTexCoord2f(textureLeft, textureTop); glVertex2i(left, top); glTexCoord2f(textureLeft, textureBottom); glVertex2i(left, bottom); glTexCoord2f(textureRight, textureBottom); glVertex2i(right, bottom); glTexCoord2f(textureRight, textureTop); glVertex2i(right, top); -} - -void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const Texture* texture, const Rect& texCoords) -{ - if(screenCoords.size().isEmpty()) - return; - glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); - glBegin(GL_QUADS); - _drawRepeatedTexturedRect(screenCoords, texCoords, texture->getSize()); glEnd(); } -void Graphics::_drawRepeatedTexturedRect(const Rect& screenCoords, const Rect& textureCoords, const Size& textureSize) +void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color) { if(screenCoords.size().isEmpty()) return; @@ -219,20 +183,19 @@ void Graphics::_drawRepeatedTexturedRect(const Rect& screenCoords, const Rect& t } partialCoords.translate(screenCoords.topLeft()); - _drawTexturedRect(partialCoords, partialTextureCoords, textureSize); + drawTexturedRect(partialCoords, texture, partialTextureCoords, color); } } } - -void Graphics::drawColoredRect(const Rect& screenCoords, const Color& color) +void Graphics::drawFilledRect(const Rect& screenCoords, const Color& color) { if(screenCoords.size().isEmpty()) return; glDisable(GL_TEXTURE_2D); - setColor(color); + glColor4ubv(color.rgbaPtr()); // rect correction for opengl int right = screenCoords.right() + 1; @@ -248,8 +211,6 @@ void Graphics::drawColoredRect(const Rect& screenCoords, const Color& color) glEnd(); glEnable(GL_TEXTURE_2D); - - resetColor(); } @@ -260,7 +221,7 @@ void Graphics::drawBoundingRect(const Rect& screenCoords, const Color& color, in glDisable(GL_TEXTURE_2D); - setColor(color); + glColor4ubv(color.rgbaPtr()); // rect correction for opengl int right = screenCoords.right()+1; @@ -295,13 +256,4 @@ void Graphics::drawBoundingRect(const Rect& screenCoords, const Color& color, in glEnd(); glEnable(GL_TEXTURE_2D); - - resetColor(); -} - -void Graphics::_drawBoundingRect(const Rect& screenCoords, const Color& color, int innerLineWidth) -{ - glEnd(); - drawBoundingRect(screenCoords, color, innerLineWidth); - glBegin(GL_QUADS); } diff --git a/src/framework/graphics/graphics.h b/src/framework/graphics/graphics.h index 697d2149..7104457f 100644 --- a/src/framework/graphics/graphics.h +++ b/src/framework/graphics/graphics.h @@ -26,15 +26,17 @@ #define GRAPHICS_H #include "prerequisites.h" - -class Texture; +#include "texture.h" class Graphics { public: Graphics() { } + /// Initialize graphics void init(); + + /// Termiante graphics void terminate(); /// Check if a GL extension is supported @@ -54,21 +56,10 @@ public: const Size& getScreenSize() const { return m_screenSize; } - void setColor(const Color& color); - void resetColor(); - - // high level rendering - void drawTexturedRect(const Rect& screenCoords, const Texture *texture, const Rect& texCoords = Rect()); - void drawRepeatedTexturedRect(const Rect& screenCoords, const Texture *texture, const Rect& texCoords); - void drawColoredRect(const Rect& screenCoords, const Color& color); - void drawBoundingRect(const Rect& screenCoords, const Color& color, int innerLineWidth = 1); - - // lower level rendering - void _beginTextureRender(const Texture *texture); - void _drawTexturedRect(const Rect& screenCoords, const Rect& textureCoords, const Size& textureSize); - void _drawRepeatedTexturedRect(const Rect& screenCoords, const Rect& textureCoords, const Size& textureSize); - void _drawBoundingRect(const Rect& screenCoords, const Color& color, int innerLineWidth = 1); - void _endTextureRender(); + void drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords = Rect(), const Color& color = Color::white); + void drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color = Color::white); + void drawFilledRect(const Rect& screenCoords, const Color& color); + void drawBoundingRect(const Rect& screenCoords, const Color& color = Color::green, int innerLineWidth = 1); private: Size m_screenSize; diff --git a/src/framework/graphics/image.cpp b/src/framework/graphics/image.cpp index d41198ce..2c3b3c7d 100644 --- a/src/framework/graphics/image.cpp +++ b/src/framework/graphics/image.cpp @@ -37,14 +37,9 @@ Image::Image(const std::string& texture, Rect textureCoords) : m_texture = g_textures.get(texture); } -void Image::enableBilinearFilter() -{ - m_texture->enableBilinearFilter(); -} - void Image::draw(const Rect& screenCoords) { - g_graphics.drawTexturedRect(screenCoords, m_texture.get(), m_textureCoords); + g_graphics.drawTexturedRect(screenCoords, m_texture, m_textureCoords); } diff --git a/src/framework/graphics/image.h b/src/framework/graphics/image.h index e7fae308..62b98fa8 100644 --- a/src/framework/graphics/image.h +++ b/src/framework/graphics/image.h @@ -36,7 +36,7 @@ public: Image(const std::string& texture); Image(const std::string& texture, Rect textureCoords); - void enableBilinearFilter(); + /// Draw image on screen virtual void draw(const Rect& screenCoords); protected: diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index ea0bf577..4972584b 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -29,9 +29,9 @@ Texture::Texture(int width, int height, int components, uchar *pixels) { - m_size.setWidth(width); - m_size.setHeight(height); + m_size.setSize(width, height); + // generate opengl texture glGenTextures(1, &m_textureId); glBindTexture(GL_TEXTURE_2D, m_textureId); @@ -51,11 +51,14 @@ Texture::Texture(int width, int height, int components, uchar *pixels) break; } + // load the pixels into opengl memory glTexImage2D(GL_TEXTURE_2D, 0, components, width, height, 0, format, GL_UNSIGNED_BYTE, pixels); + // disable texture border glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // nearest filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } @@ -75,6 +78,7 @@ void Texture::enableBilinearFilter() uchar *Texture::getPixels() { + // copy pixels from opengl memory uchar *pixels = new uchar[m_size.area()*4]; glBindTexture(GL_TEXTURE_2D, m_textureId); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); diff --git a/src/framework/graphics/textures.cpp b/src/framework/graphics/textures.cpp index 77d919cb..be43dd44 100644 --- a/src/framework/graphics/textures.cpp +++ b/src/framework/graphics/textures.cpp @@ -41,23 +41,22 @@ TexturePtr Textures::get(const std::string& textureFile) texture = it->second.lock(); } - if(!texture) { // load texture + // texture not found, load it + if(!texture) { // currently only png textures are supported - if(!boost::ends_with(textureFile, ".png")) { + if(!boost::ends_with(textureFile, ".png")) logFatal("Unable to load texture %s, file format no supported.", textureFile.c_str()); - return texture; - } + // load texture file data uint fileSize; uchar *textureFileData = g_resources.loadFile(textureFile, &fileSize); - if(!textureFileData) { + if(!textureFileData) logFatal("Unable to load texture %s, file could not be read.", textureFile.c_str()); - return texture; - } + // load the texture texture = TexturePtr(TextureLoader::loadPNG(textureFileData)); if(!texture) - logFatal("Unable to load texture %s, loading error.", textureFile.c_str()); + logFatal("Unable to load texture %s", textureFile.c_str()); delete[] textureFileData; } diff --git a/src/framework/ui/ui.h b/src/framework/ui/ui.h index 44a4c351..fad828b6 100644 --- a/src/framework/ui/ui.h +++ b/src/framework/ui/ui.h @@ -36,5 +36,6 @@ #include "uiskins.h" #include "uiwindow.h" #include "uitextedit.h" +#include "uiloader.h" #endif // UI_H diff --git a/src/framework/ui/uibutton.cpp b/src/framework/ui/uibutton.cpp index 935fe3df..093037fa 100644 --- a/src/framework/ui/uibutton.cpp +++ b/src/framework/ui/uibutton.cpp @@ -26,12 +26,6 @@ #include "graphics/fonts.h" #include "graphics/font.h" -void UIButton::load(const YAML::Node& node) -{ - UIElement::load(node); - node["text"] >> m_text; -} - void UIButton::render() { UIElement::render(); diff --git a/src/framework/ui/uibutton.h b/src/framework/ui/uibutton.h index fa4dd59b..03141474 100644 --- a/src/framework/ui/uibutton.h +++ b/src/framework/ui/uibutton.h @@ -40,11 +40,12 @@ public: UIElement(); } - void load(const YAML::Node& node); - virtual void render(); bool onInputEvent(const InputEvent& event); + void setText(const std::string& text) { m_text = text; } + const std::string& getText() const { return m_text; } + UI::EButtonState getState() { return m_state; } void onClick(const Callback& callback) { m_buttonClickCallback = callback; } diff --git a/src/framework/ui/uicontainer.cpp b/src/framework/ui/uicontainer.cpp index 6ed80dc5..2f2d61d2 100644 --- a/src/framework/ui/uicontainer.cpp +++ b/src/framework/ui/uicontainer.cpp @@ -30,90 +30,11 @@ #include "uitextedit.h" #include "uiwindow.h" -UIContainerPtr g_ui(new UIContainer); +UIContainerPtr rootContainer(new UIContainer); -UIElementPtr createElementFromDescription(std::string elementId) +UIContainerPtr& UIContainer::getRootContainer() { - UIElementPtr element; - - std::vector split; - boost::split(split, elementId, boost::is_any_of("-")); - if(split.size() != 2) { - logError("incorrect element id format: %s", elementId.c_str()); - return element; - } - - std::string elementType = split[1]; - if(elementType == "panel") { - element = UIElementPtr(new UIPanel); - } else if(elementType == "button") { - element = UIElementPtr(new UIButton); - } else if(elementType == "label") { - element = UIElementPtr(new UILabel); - } else if(elementType == "window") { - element = UIElementPtr(new UIWindow); - } else if(elementType == "textEdit") { - element = UIElementPtr(new UITextEdit); - } - - if(element) - element->setId(elementId); - - return element; -} - -void UIContainer::load(const YAML::Node& node) -{ - UIElement::load(node); - - for(auto it = node.begin(); it != node.end(); ++it) { - std::string elementDesc; - it.first() >> elementDesc; - - if(elementDesc.find("-") != std::string::npos) { - UIElementPtr element = createElementFromDescription(elementDesc); - if(element) { - addChild(element); - element->load(it.second()); - } - } - } -} - -UIContainerPtr UIContainer::load(const std::string& file) -{ - //TODO: handle errors - //TODO: display errors in which file and line - UIContainerPtr container; - - std::string fileContents = g_resources.loadTextFile(file); - if(!fileContents.size()) { - logFatal("could not load ui file \"%s", file.c_str()); - return UIContainerPtr(); - } - - std::istringstream fin(fileContents); - - try { - YAML::Parser parser(fin); - - YAML::Node doc; - parser.GetNextDocument(doc); - - std::string elementDesc; - doc.begin().first() >> elementDesc; - UIElementPtr element = createElementFromDescription(elementDesc); - if(element) { - g_ui->addChild(element); - element->load(doc.begin().second()); - return element->asUIContainer(); - } - } catch (YAML::ParserException& e) { - logError("Malformed ui file \"%s\": %s", file.c_str(), e.what()); - return UIContainerPtr(); - } - - return container; + return rootContainer; } void UIContainer::addChild(UIElementPtr child) diff --git a/src/framework/ui/uicontainer.h b/src/framework/ui/uicontainer.h index 8b1f5c43..66c3b0a5 100644 --- a/src/framework/ui/uicontainer.h +++ b/src/framework/ui/uicontainer.h @@ -34,11 +34,6 @@ public: UIContainer(UI::EElementType type = UI::Container) : UIElement(type) { } virtual ~UIContainer() { } - virtual void load(const YAML::Node& node); - - //TODO: move this shit - static UIContainerPtr load(const std::string& file); - void addChild(UIElementPtr child); void removeChild(UIElementPtr child); UIElementPtr getChildById(const std::string& id); @@ -56,11 +51,11 @@ public: UIContainerPtr asUIContainer() { return std::static_pointer_cast(shared_from_this()); } + static UIContainerPtr& getRootContainer(); + protected: std::list m_children; UIElementPtr m_activeElement; }; -extern UIContainerPtr g_ui; - #endif // UICONTAINER_H diff --git a/src/framework/ui/uielement.cpp b/src/framework/ui/uielement.cpp index 909231c6..dfbe0c6e 100644 --- a/src/framework/ui/uielement.cpp +++ b/src/framework/ui/uielement.cpp @@ -27,7 +27,7 @@ #include "uielementskin.h" UIElement::UIElement(UI::EElementType type) : - AnchorLayout(), + UILayout(), m_type(type), m_skin(NULL), m_visible(true), @@ -38,107 +38,6 @@ UIElement::UIElement(UI::EElementType type) : setSkin(g_uiSkins.getElementSkin(type)); } -void UIElement::load(const YAML::Node& node) -{ - if(node.FindValue("skin")) - setSkin(g_uiSkins.getElementSkin(m_type, node["skin"])); - - if(node.FindValue("size")) { - Size size; - node["size"] >> size; - setSize(size); - } - - int margin; - if(node.FindValue("margin.left")) { - node["margin.left"] >> margin; - setMarginLeft(margin); - } - - if(node.FindValue("margin.right")) { - node["margin.right"] >> margin; - setMarginRight(margin); - } - - if(node.FindValue("margin.top")) { - node["margin.top"] >> margin; - setMarginTop(margin); - } - - if(node.FindValue("margin.bottom")) { - node["margin.bottom"] >> margin; - setMarginBottom(margin); - } - - if(node.FindValue("anchors.left")) - loadAnchor(ANCHOR_LEFT, node["anchors.left"]); - - if(node.FindValue("anchors.right")) - loadAnchor(ANCHOR_RIGHT, node["anchors.right"]); - - if(node.FindValue("anchors.top")) - loadAnchor(ANCHOR_TOP, node["anchors.top"]); - - if(node.FindValue("anchors.bottom")) - loadAnchor(ANCHOR_BOTTOM, node["anchors.bottom"]); - - if(node.FindValue("anchors.horizontalCenter")) - loadAnchor(ANCHOR_HORIZONTAL_CENTER, node["anchors.horizontalCenter"]); - - if(node.FindValue("anchors.verticalCenter")) - loadAnchor(ANCHOR_VERTICAL_CENTER, node["anchors.verticalCenter"]); -} - -void UIElement::loadAnchor(EAnchorType type, const YAML::Node& node) -{ - std::string anchorDescription; - node >> anchorDescription; - - std::vector split; - boost::split(split, anchorDescription, boost::is_any_of(".")); - if(split.size() != 2) { - logError("wrong anchors description: %s", anchorDescription.c_str()); - return; - } - - std::string relativeElementId = split[0]; - std::string relativeAnchorTypeId = split[1]; - EAnchorType relativeAnchorType; - - if(relativeAnchorTypeId == "left") - relativeAnchorType = ANCHOR_LEFT; - else if(relativeAnchorTypeId == "right") - relativeAnchorType = ANCHOR_RIGHT; - else if(relativeAnchorTypeId == "top") - relativeAnchorType = ANCHOR_TOP; - else if(relativeAnchorTypeId == "bottom") - relativeAnchorType = ANCHOR_BOTTOM; - else if(relativeAnchorTypeId == "horizontalCenter") - relativeAnchorType = ANCHOR_HORIZONTAL_CENTER; - else if(relativeAnchorTypeId == "verticalCenter") - relativeAnchorType = ANCHOR_VERTICAL_CENTER; - else { - logError("wrong anchors description: %s", anchorDescription.c_str()); - return; - } - - AnchorLayoutPtr relativeElement; - if(relativeElementId == "parent" && getParent()) { - relativeElement = getParent()->asAnchorLayout(); - } else { - UIElementPtr element = g_ui->recursiveGetChildById(relativeElementId); - if(element) - relativeElement = element->asAnchorLayout(); - } - - if(relativeElement) { - addAnchor(type, AnchorLine(relativeElement, relativeAnchorType)); - } else { - logError("anchoring has failed: %s", anchorDescription.c_str()); - return; - } -} - bool UIElement::setSkin(const std::string& skinName) { setSkin(g_uiSkins.getElementSkin(m_type, skinName)); diff --git a/src/framework/ui/uielement.h b/src/framework/ui/uielement.h index 2c9c7e61..8b4efb34 100644 --- a/src/framework/ui/uielement.h +++ b/src/framework/ui/uielement.h @@ -28,7 +28,7 @@ #include "prerequisites.h" #include "core/input.h" #include "uiconstants.h" -#include "anchorlayout.h" +#include "uilayout.h" class UIElementSkin; @@ -40,7 +40,7 @@ class UIElement; typedef std::shared_ptr UIElementPtr; typedef std::weak_ptr UIElementWeakPtr; -class UIElement : public AnchorLayout +class UIElement : public UILayout { public: UIElement(UI::EElementType type = UI::Element); @@ -49,9 +49,6 @@ public: virtual void render(); virtual bool onInputEvent(const InputEvent& event) { return false; } - virtual void load(const YAML::Node& node); - void loadAnchor(EAnchorType type, const YAML::Node& node); - bool setSkin(const std::string& skinName); void setSkin(UIElementSkin *skin); UIElementSkin *getSkin() { return m_skin; } diff --git a/src/framework/ui/uilabel.cpp b/src/framework/ui/uilabel.cpp index 89c7c6a3..27e18d46 100644 --- a/src/framework/ui/uilabel.cpp +++ b/src/framework/ui/uilabel.cpp @@ -35,15 +35,6 @@ UILabel::UILabel(const std::string& text, Font* font) : setSize(m_font->calculateTextRectSize(text)); } -void UILabel::load(const YAML::Node& node) -{ - UIElement::load(node); - - std::string text; - node["text"] >> text; - setText(text); -} - void UILabel::render() { m_font->renderText(m_text, getRect(), ALIGN_LEFT, Color(0xFFBFBFBF)); diff --git a/src/framework/ui/uilabel.h b/src/framework/ui/uilabel.h index 6b8bd9a6..5d45f6ef 100644 --- a/src/framework/ui/uilabel.h +++ b/src/framework/ui/uilabel.h @@ -35,8 +35,6 @@ class UILabel : public UIElement public: UILabel(const std::string& text = std::string(), Font *font = NULL); - void load(const YAML::Node& node); - void render(); void setText(const std::string& text); diff --git a/src/framework/ui/anchorlayout.cpp b/src/framework/ui/uilayout.cpp similarity index 89% rename from src/framework/ui/anchorlayout.cpp rename to src/framework/ui/uilayout.cpp index f9d7ae7f..a5689b4a 100644 --- a/src/framework/ui/anchorlayout.cpp +++ b/src/framework/ui/uilayout.cpp @@ -22,12 +22,12 @@ */ -#include "anchorlayout.h" +#include "uilayout.h" #include "uielement.h" int AnchorLine::getPos() const { - AnchorLayoutPtr element = m_relativeElement.lock(); + UILayoutPtr element = m_relativeElement.lock(); if(element) { switch(m_anchorType) { case ANCHOR_LEFT: @@ -50,30 +50,30 @@ int AnchorLine::getPos() const return 0; } -void AnchorLayout::setSize(const Size& size) +void UILayout::setSize(const Size& size) { m_rect.setSize(size); recalculateAnchors(); } -void AnchorLayout::setRect(const Rect& rect) +void UILayout::setRect(const Rect& rect) { m_rect = rect; recalculateAnchors(); } -void AnchorLayout::addAnchor(EAnchorType type, const AnchorLine& anchorLine) +void UILayout::addAnchor(EAnchorType type, const AnchorLine& anchorLine) { if(!anchorLine.isValid()) { logError("anchoring for an element has failed, got an invalid anchor line"); return; } m_anchors[type] = anchorLine; - anchorLine.getRelativeElement()->addAnchoredElement(asAnchorLayout()); + anchorLine.getRelativeElement()->addAnchoredElement(asUILayout()); recalculateAnchors(); } -void AnchorLayout::addAnchoredElement(AnchorLayoutPtr anchoredElement) +void UILayout::addAnchoredElement(UILayoutPtr anchoredElement) { bool found = false; for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) { @@ -86,7 +86,7 @@ void AnchorLayout::addAnchoredElement(AnchorLayoutPtr anchoredElement) m_anchoredElements.push_back(anchoredElement); } -void AnchorLayout::recalculateAnchors() +void UILayout::recalculateAnchors() { // horizontal if(m_anchors[ANCHOR_HORIZONTAL_CENTER].isValid()) { @@ -117,7 +117,7 @@ void AnchorLayout::recalculateAnchors() } for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) { - AnchorLayoutPtr element = (*it).lock(); + UILayoutPtr element = (*it).lock(); if(element) element->recalculateAnchors(); } diff --git a/src/framework/ui/uilayout.h b/src/framework/ui/uilayout.h new file mode 100644 index 00000000..757fe1f6 --- /dev/null +++ b/src/framework/ui/uilayout.h @@ -0,0 +1,120 @@ +/* The MIT License + * + * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef UILAYOUT_H +#define UILAYOUT_H + +#include "prerequisites.h" +#include "uiconstants.h" + +enum EAnchorType { + ANCHOR_LEFT = 0, + ANCHOR_RIGHT, + ANCHOR_TOP, + ANCHOR_BOTTOM, + ANCHOR_HORIZONTAL_CENTER, + ANCHOR_VERTICAL_CENTER, + ANCHOR_NONE +}; + +class UILayout; +typedef std::shared_ptr UILayoutPtr; +typedef std::weak_ptr UILayoutWeakPtr; + +class AnchorLine +{ +public: + AnchorLine() : m_anchorType(ANCHOR_NONE) { } + AnchorLine(const AnchorLine& other) : + m_relativeElement(other.m_relativeElement), m_anchorType(other.m_anchorType) { } + AnchorLine(UILayoutPtr relativeElement, EAnchorType anchorType) : + m_relativeElement(relativeElement), m_anchorType(anchorType) { } + bool isValid() const { return (m_anchorType != ANCHOR_NONE && !m_relativeElement.expired()); } + + int getPos() const; + EAnchorType getAnchorType() const { return m_anchorType; } + UILayoutPtr getRelativeElement() const { return m_relativeElement.lock(); } + +private: + UILayoutWeakPtr m_relativeElement; + EAnchorType m_anchorType; +}; + +class UILayout : public std::enable_shared_from_this +{ +public: + UILayout() : + m_marginLeft(0), + m_marginRight(0), + m_marginTop(0), + m_marginBottom(0) { } + virtual ~UILayout() { } + + void setSize(const Size& size); + Size getSize() { return m_rect.size(); } + + void setRect(const Rect& rect); + const Rect& getRect() const{ return m_rect; } + + void addAnchor(EAnchorType type, const AnchorLine& anchorLine); + void anchorLeft(const AnchorLine& anchorLine) { addAnchor(ANCHOR_LEFT, anchorLine); } + void anchorRight(const AnchorLine& anchorLine) { addAnchor(ANCHOR_RIGHT, anchorLine); } + void anchorTop(const AnchorLine& anchorLine) { addAnchor(ANCHOR_TOP, anchorLine); } + void anchorBottom(const AnchorLine& anchorLine) { addAnchor(ANCHOR_BOTTOM, anchorLine); } + void anchorHorizontalCenter(const AnchorLine& anchorLine) { addAnchor(ANCHOR_HORIZONTAL_CENTER, anchorLine); } + void anchorVerticalCenter(const AnchorLine& anchorLine) { addAnchor(ANCHOR_VERTICAL_CENTER, anchorLine); } + + AnchorLine left() { return AnchorLine(asUILayout(), ANCHOR_LEFT); } + AnchorLine right() { return AnchorLine(asUILayout(), ANCHOR_RIGHT); } + AnchorLine top() { return AnchorLine(asUILayout(), ANCHOR_TOP); } + AnchorLine bottom() { return AnchorLine(asUILayout(), ANCHOR_BOTTOM); } + AnchorLine horizontalCenter() { return AnchorLine(asUILayout(), ANCHOR_HORIZONTAL_CENTER); } + AnchorLine verticalCenter() { return AnchorLine(asUILayout(), ANCHOR_VERTICAL_CENTER); } + + void setMargin(int top, int left, int bottom, int right) { m_marginLeft = left; m_marginRight = right; m_marginTop = top; m_marginBottom = bottom; recalculateAnchors(); } + void setMargin(int horizontal, int vertical) { m_marginLeft = m_marginRight = horizontal; m_marginTop = m_marginBottom = vertical; recalculateAnchors(); } + void setMargin(int margin) { m_marginLeft = m_marginRight = m_marginTop = m_marginBottom = margin; recalculateAnchors(); } + + void setMarginLeft(int margin) { m_marginLeft = margin; recalculateAnchors(); } + void setMarginRight(int margin) { m_marginRight = margin; recalculateAnchors(); } + void setMarginTop(int margin) { m_marginTop = margin; recalculateAnchors(); } + void setMarginBottom(int margin) { m_marginBottom = margin; recalculateAnchors(); } + + UILayoutPtr asUILayout() { return shared_from_this(); } + +private: + void recalculateAnchors(); + void addAnchoredElement(UILayoutPtr anchoredElement); + + AnchorLine m_anchors[6]; + + Rect m_rect; + int m_marginLeft; + int m_marginRight; + int m_marginTop; + int m_marginBottom; + std::list m_anchoredElements; +}; + +#endif // UILAYOUT_H diff --git a/src/framework/ui/uiloader.cpp b/src/framework/ui/uiloader.cpp new file mode 100644 index 00000000..446d116e --- /dev/null +++ b/src/framework/ui/uiloader.cpp @@ -0,0 +1,254 @@ +/* The MIT License + * + * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "uiloader.h" +#include "core/resources.h" +#include "ui.h" + +UIElementPtr UILoader::createElementFromId(const std::string& id) +{ + UIElementPtr element; + + std::vector split; + boost::split(split, id, boost::is_any_of("#")); + if(split.size() != 2) + return element; + + std::string elementType = split[0]; + std::string elementId = split[1]; + + if(elementType == "panel") { + element = UIElementPtr(new UIPanel); + } else if(elementType == "button") { + element = UIElementPtr(new UIButton); + } else if(elementType == "label") { + element = UIElementPtr(new UILabel); + } else if(elementType == "window") { + element = UIElementPtr(new UIWindow); + } else if(elementType == "textEdit") { + element = UIElementPtr(new UITextEdit); + } + + if(element) + element->setId(elementId); + + return element; +} + +UIElementPtr UILoader::loadFile(const std::string& file, const UIContainerPtr& parent) +{ + std::string fileContents = g_resources.loadTextFile(file); + if(!fileContents.size()) { + logFatal("Could not load ui file \"%s", file.c_str()); + return UIElementPtr(); + } + + std::istringstream fin(fileContents); + + try { + YAML::Parser parser(fin); + + YAML::Node doc; + parser.GetNextDocument(doc); + + // get element id + std::string elementId; + doc.begin().first() >> elementId; + + // first we should populate all elements + // only after that we can load anchors + + // create element interpreting it's id + UIElementPtr element = createElementFromId(elementId); + if(!element) + throw YAML::Exception(doc.begin().first().GetMark(), "invalid element type"); + parent->addChild(element); + + // populete it + if(element->asUIContainer()) + populateContainer(element->asUIContainer(), doc.begin().second()); + + // now do the real load + loadElements(element, doc.begin().second()); + } catch (YAML::Exception& e) { + logFatal("Failed to load ui file \"%s\":\n %s", file.c_str(), e.what()); + } + + return UIElementPtr(); +} + +void UILoader::populateContainer(const UIContainerPtr& parent, const YAML::Node& node) +{ + for(auto it = node.begin(); it != node.end(); ++it) { + std::string id; + it.first() >> id; + + // check if it's and element id + if(id.find("#") != std::string::npos) { + UIElementPtr element = createElementFromId(id); + if(!element) + throw YAML::Exception(it.first().GetMark(), "invalid element type"); + parent->addChild(element); + + // also populate this element if it's a parent + if(element->asUIContainer()) + populateContainer(element->asUIContainer(), it.second()); + } + } +} + +void UILoader::loadElements(const UIElementPtr& parent, const YAML::Node& node) +{ + loadElement(parent, node); + + if(parent->asUIContainer()) { + UIContainerPtr container = parent->asUIContainer(); + for(auto it = node.begin(); it != node.end(); ++it) { + std::string id; + it.first() >> id; + + // check if it's and element id + if(id.find("#") != std::string::npos) { + std::vector split; + boost::split(split, id, boost::is_any_of("#")); + loadElements(container->getChildById(split[1]), it.second()); + } + } + } +} + +void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node) +{ + std::string tmp; + + if(node.FindValue("skin")) + element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), node["skin"])); + + if(node.FindValue("size")) { + Size size; + node["size"] >> size; + element->setSize(size); + } + + int margin; + if(node.FindValue("margin.left")) { + node["margin.left"] >> margin; + element->setMarginLeft(margin); + } + + if(node.FindValue("margin.right")) { + node["margin.right"] >> margin; + element->setMarginRight(margin); + } + + if(node.FindValue("margin.top")) { + node["margin.top"] >> margin; + element->setMarginTop(margin); + } + + if(node.FindValue("margin.bottom")) { + node["margin.bottom"] >> margin; + element->setMarginBottom(margin); + } + + if(node.FindValue("anchors.left")) + loadElementAnchor(element, ANCHOR_LEFT, node["anchors.left"]); + + if(node.FindValue("anchors.right")) + loadElementAnchor(element, ANCHOR_RIGHT, node["anchors.right"]); + + if(node.FindValue("anchors.top")) + loadElementAnchor(element, ANCHOR_TOP, node["anchors.top"]); + + if(node.FindValue("anchors.bottom")) + loadElementAnchor(element, ANCHOR_BOTTOM, node["anchors.bottom"]); + + if(node.FindValue("anchors.horizontalCenter")) + loadElementAnchor(element, ANCHOR_HORIZONTAL_CENTER, node["anchors.horizontalCenter"]); + + if(node.FindValue("anchors.verticalCenter")) + loadElementAnchor(element, ANCHOR_VERTICAL_CENTER, node["anchors.verticalCenter"]); + + // load specific element type + if(element->getElementType() == UI::Button) { + UIButtonPtr button = std::static_pointer_cast(element); + node["text"] >> tmp; + button->setText(tmp); + } + else if(element->getElementType() == UI::Window) { + UIWindowPtr window = std::static_pointer_cast(element); + node["title"] >> tmp; + window->setTitle(tmp); + } + else if(element->getElementType() == UI::Label) { + UILabelPtr label = std::static_pointer_cast(element); + node["text"] >> tmp; + label->setText(tmp); + } +} + +void UILoader::loadElementAnchor(const UIElementPtr& element, EAnchorType type, const YAML::Node& node) +{ + std::string anchorDescription; + node >> anchorDescription; + + std::vector split; + boost::split(split, anchorDescription, boost::is_any_of(".")); + if(split.size() != 2) + throw YAML::Exception(node.GetMark(), "invalid anchors description"); + + std::string relativeElementId = split[0]; + std::string relativeAnchorTypeId = split[1]; + EAnchorType relativeAnchorType; + + if(relativeAnchorTypeId == "left") + relativeAnchorType = ANCHOR_LEFT; + else if(relativeAnchorTypeId == "right") + relativeAnchorType = ANCHOR_RIGHT; + else if(relativeAnchorTypeId == "top") + relativeAnchorType = ANCHOR_TOP; + else if(relativeAnchorTypeId == "bottom") + relativeAnchorType = ANCHOR_BOTTOM; + else if(relativeAnchorTypeId == "horizontalCenter") + relativeAnchorType = ANCHOR_HORIZONTAL_CENTER; + else if(relativeAnchorTypeId == "verticalCenter") + relativeAnchorType = ANCHOR_VERTICAL_CENTER; + else + throw YAML::Exception(node.GetMark(), "invalid anchors description"); + + UILayoutPtr relativeElement; + if(relativeElementId == "parent" && element->getParent()) { + relativeElement = element->getParent()->asUILayout(); + } else { + UIElementPtr tmp = UIContainer::getRootContainer()->recursiveGetChildById(relativeElementId); + if(tmp) + relativeElement = tmp->asUILayout(); + } + + if(relativeElement) { + element->addAnchor(type, AnchorLine(relativeElement, relativeAnchorType)); + } else { + throw YAML::Exception(node.GetMark(), "anchoring failed, does the relative element really exists?"); + } +} diff --git a/src/framework/ui/uiloader.h b/src/framework/ui/uiloader.h new file mode 100644 index 00000000..83a2e9dc --- /dev/null +++ b/src/framework/ui/uiloader.h @@ -0,0 +1,53 @@ +/* The MIT License + * + * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef UILOADER_H +#define UILOADER_H + +#include "prerequisites.h" +#include "uiconstants.h" +#include "uicontainer.h" + +namespace UILoader +{ + /// Detect element type and create it + UIElementPtr createElementFromId(const std::string& id); + + /// Loads an UIElement and it's children from a YAML file + UIElementPtr loadFile(const std::string& file, const UIContainerPtr& parent); + + /// Populate container children from a YAML node + void populateContainer(const UIContainerPtr& parent, const YAML::Node& node); + + /// Load element and its children from a YAML node + void loadElements(const UIElementPtr& parent, const YAML::Node& node); + + /// Load element proprieties from a YAML node + void loadElement(const UIElementPtr& element, const YAML::Node& node); + + /// Load anchor from a YAML node + void loadElementAnchor(const UIElementPtr& element, EAnchorType type, const YAML::Node& node); +}; + +#endif // UILOADER_H diff --git a/src/framework/ui/uiwindow.cpp b/src/framework/ui/uiwindow.cpp index 049233e6..2fad0bfc 100644 --- a/src/framework/ui/uiwindow.cpp +++ b/src/framework/ui/uiwindow.cpp @@ -23,9 +23,3 @@ #include "uiwindow.h" - -void UIWindow::load(const YAML::Node& node) -{ - UIContainer::load(node); - node["title"] >> m_title; -} diff --git a/src/framework/ui/uiwindow.h b/src/framework/ui/uiwindow.h index a3f8a84e..23bbad8f 100644 --- a/src/framework/ui/uiwindow.h +++ b/src/framework/ui/uiwindow.h @@ -35,8 +35,7 @@ public: UIContainer(UI::Window), m_title(title) { } - void load(const YAML::Node& node); - + void setTitle(const std::string& title) { m_title = title; } const std::string& getTitle() const { return m_title; } private: diff --git a/src/framework/util/color.cpp b/src/framework/util/color.cpp new file mode 100644 index 00000000..daf2a9a6 --- /dev/null +++ b/src/framework/util/color.cpp @@ -0,0 +1,33 @@ +/* The MIT License + * + * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "color.h" + +Color Color::white(0xFF, 0xFF, 0xFF, 0xFF); +Color Color::black(0x00, 0x00, 0x00, 0xFF); +Color Color::alpha(0x00, 0x00, 0x00, 0x00); +Color Color::red (0xFF, 0x00, 0x00, 0xFF); +Color Color::green(0x00, 0xFF, 0x00, 0xFF); +Color Color::blue (0x00, 0x00, 0xFF, 0xFF); +Color Color::pink (0xFF, 0x00, 0xFF, 0xFF); diff --git a/src/framework/util/color.h b/src/framework/util/color.h index 77cf2996..4bf4e270 100644 --- a/src/framework/util/color.h +++ b/src/framework/util/color.h @@ -37,10 +37,10 @@ public: inline Color(const Color& other) : color(other.color) { } inline Color(RGBA rgba) : color(rgba) { } - inline uint8 red() const { return (color >> 24) & 0xFF; } - inline uint8 green() const { return (color >> 16) & 0xFF; } - inline uint8 blue() const { return (color >> 8) & 0xFF; } - inline uint8 alpha() const { return color & 0xFF; } + inline uint8 r() const { return (color >> 24) & 0xFF; } + inline uint8 g() const { return (color >> 16) & 0xFF; } + inline uint8 b() const { return (color >> 8) & 0xFF; } + inline uint8 a() const { return color & 0xFF; } inline RGBA rgba() const { return color; } inline const uint8* rgbaPtr() const { return (const uint8*)&color; } @@ -55,6 +55,14 @@ public: inline bool operator==(const Color& other) const { return other.color == color; } inline bool operator!=(const Color& other) const { return other.color != color; } + static Color white; + static Color black; + static Color alpha; + static Color red; + static Color green; + static Color blue; + static Color pink; + private: RGBA color; }; @@ -69,4 +77,13 @@ inline void operator>>(const YAML::Node& node, Color& color) color.setRGBA(r,g,b,a); } +inline std::ostream& operator<<(std::ostream& out, const Color& color) +{ + out << "Color(" << (int)color.r() << "," + << (int)color.g() << "," + << (int)color.b() << "," + << (int)color.a() << ")"; + return out; +} + #endif // COLOR_H diff --git a/src/framework/util/point.h b/src/framework/util/point.h index 37a21ffd..baa4f93b 100644 --- a/src/framework/util/point.h +++ b/src/framework/util/point.h @@ -85,4 +85,12 @@ inline void operator>>(const YAML::Node& node, TPoint& point) node[1] >> point.y; } +template +inline std::ostream& operator<<(std::ostream& out, const TPoint& point) +{ + out << "Point(" << point.x << "," + << point.y << ")"; + return out; +} + #endif diff --git a/src/framework/util/rect.h b/src/framework/util/rect.h index ce574d61..44220799 100644 --- a/src/framework/util/rect.h +++ b/src/framework/util/rect.h @@ -304,4 +304,14 @@ inline void operator>>(const YAML::Node& node, TRect& rect) rect.setRect(x, y, width, height); } +template +inline std::ostream& operator<<(std::ostream& out, const TRect& rect) +{ + out << "Rect(" << rect.left() << "," + << rect.top() << "," + << rect.width() << "," + << rect.height() << ")"; + return out; +} + #endif // RECT_H diff --git a/src/framework/util/size.h b/src/framework/util/size.h index 7ea11b74..a7e68c93 100644 --- a/src/framework/util/size.h +++ b/src/framework/util/size.h @@ -120,4 +120,12 @@ inline void operator>>(const YAML::Node& node, TSize& size) size.setSize(w, h); } +template +inline std::ostream& operator<<(std::ostream& out, const TSize& size) +{ + out << "Size(" << size.width() << "," + << size.height() << ")"; + return out; +} + #endif diff --git a/src/menustate.cpp b/src/menustate.cpp index a1c4a462..b668d2fe 100644 --- a/src/menustate.cpp +++ b/src/menustate.cpp @@ -39,15 +39,15 @@ void MenuState::onEnter() m_background = g_textures.get("background.png"); m_background->enableBilinearFilter(); - UIContainerPtr mainMenuPanel = UIContainer::load("ui/mainMenu-panel.yml"); - + UIElementPtr mainMenuPanel = UILoader::loadFile("ui/mainMenu.yml", UIContainer::getRootContainer()); +/* UIButtonPtr button = std::static_pointer_cast(mainMenuPanel->getChildById("exitGame-button")); button->onClick([]{ g_engine.stop(); }); button = std::static_pointer_cast(mainMenuPanel->getChildById("enterGame-button")); button->onClick([]{ UIContainer::load("ui/enterGame-window.yml"); - }); + });*/ } void MenuState::onLeave() @@ -82,7 +82,7 @@ void MenuState::render() texCoordsSize = texCoordsSize.boundedTo(texSize); Rect texCoords(0, 0, texCoordsSize); texCoords.moveBottomRight(texSize.toPoint()); - g_graphics.drawTexturedRect(Rect(0, 0, screenSize), m_background.get(), texCoords); + g_graphics.drawTexturedRect(Rect(0, 0, screenSize), m_background, texCoords); } void MenuState::createMainMenu()