diff --git a/CMakeLists.txt b/CMakeLists.txt index 822abe3f..4eea742e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,13 +78,19 @@ SET(SOURCES src/framework/util.cpp # ui + src/framework/ui/anchorlayout.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/uipanel.cpp src/framework/ui/uibutton.cpp src/framework/ui/uilabel.cpp + src/framework/ui/uiwindow.cpp + src/framework/ui/uiwindowskin.cpp + src/framework/ui/uitextedit.cpp + src/framework/ui/uitexteditskin.cpp # network src/framework/net/connection.cpp diff --git a/src/framework/borderedimage.cpp b/src/framework/borderedimage.cpp index c43b272e..8fcd2d0b 100644 --- a/src/framework/borderedimage.cpp +++ b/src/framework/borderedimage.cpp @@ -76,8 +76,8 @@ void BorderedImage::setTexCoords(const Rect& left, m_bottomRightCornerTexCoords = bottomRight; m_centerTexCoords = center; - m_cornersSize = Size(topLeft.width() + topRight.width(), - topLeft.height() + bottomLeft.height()); + m_cornersSize = Size(left.width() + right.width(), + top.height() + bottom.height()); } void BorderedImage::draw(const Rect& screenCoords) @@ -93,7 +93,8 @@ void BorderedImage::draw(const Rect& screenCoords) g_graphics._beginTextureRender(m_texture.get()); // first the center - rectCoords = Rect(screenCoords.topLeft() + m_topLeftCornerTexCoords.size().toPoint(), + rectCoords = Rect(screenCoords.left() + m_leftBorderTexCoords.width(), + screenCoords.top() + m_topBorderTexCoords.height(), centerSize); g_graphics._drawRepeatedTexturedRect(rectCoords, m_centerTexCoords, textureSize); @@ -124,7 +125,7 @@ void BorderedImage::draw(const Rect& screenCoords) g_graphics._drawRepeatedTexturedRect(rectCoords, m_leftBorderTexCoords, textureSize); // right - rectCoords = Rect(screenCoords.left() + m_topLeftCornerTexCoords.width() + centerSize.width(), + rectCoords = Rect(screenCoords.left() + m_leftBorderTexCoords.width() + centerSize.width(), screenCoords.top() + m_topRightCornerTexCoords.height(), m_rightBorderTexCoords.width(), centerSize.height()); @@ -132,7 +133,7 @@ void BorderedImage::draw(const Rect& screenCoords) // bottom left corner rectCoords = Rect(screenCoords.left(), - screenCoords.top() + m_topLeftCornerTexCoords.height() + centerSize.height(), + screenCoords.top() + m_topBorderTexCoords.height() + centerSize.height(), m_bottomLeftCornerTexCoords.size()); g_graphics._drawTexturedRect(rectCoords, m_bottomLeftCornerTexCoords, textureSize); @@ -149,5 +150,6 @@ void BorderedImage::draw(const Rect& screenCoords) m_bottomRightCornerTexCoords.size()); g_graphics._drawTexturedRect(rectCoords, m_bottomRightCornerTexCoords, textureSize); + //g_graphics._drawBoundingRect(screenCoords, Color(0xFF00FF00), 1); g_graphics._endTextureRender(); } diff --git a/src/framework/engine.cpp b/src/framework/engine.cpp index 87ac4cbb..c89229a1 100644 --- a/src/framework/engine.cpp +++ b/src/framework/engine.cpp @@ -33,8 +33,6 @@ #include "net/connections.h" #include "ui/uicontainer.h" -#define MINIMUN_UPDATE_DELAY 50 - Engine g_engine; void Engine::init() @@ -57,17 +55,11 @@ void Engine::terminate() void Engine::run() { int ticks = Platform::getTicks(); - int lastUpdateTicks = ticks; int lastFpsTicks = ticks; - int updateElapsedTicks = ticks; int frameCount = 0; int fps = 0; m_running = true; - // before redering do the first update - update(ticks, 0); - lastUpdateTicks = ticks; - while(!m_stopping) { // poll platform events Platform::poll(); @@ -75,18 +67,11 @@ void Engine::run() // poll network events g_connections.poll(); - // update before redering ticks = Platform::getTicks(); // poll diaptcher tasks g_dispatcher.poll(ticks); - updateElapsedTicks = ticks - lastUpdateTicks; - if(updateElapsedTicks >= MINIMUN_UPDATE_DELAY) { - update(ticks, updateElapsedTicks); - lastUpdateTicks = ticks; - } - // render only when visible if(Platform::isWindowVisible()) { // calculate and fps @@ -140,13 +125,6 @@ void Engine::render() g_graphics.endRender(); } -void Engine::update(int ticks, int elapsedTicks) -{ - if(m_currentState) - m_currentState->update(ticks, elapsedTicks); - g_ui->update(ticks, elapsedTicks); -} - void Engine::onClose() { if(m_currentState) @@ -156,13 +134,13 @@ void Engine::onClose() void Engine::onResize(const Size& size) { g_graphics.resize(size); - g_ui->resize(size); + g_ui->setSize(size); if(m_currentState) m_currentState->onResize(size); } -void Engine::onInputEvent(InputEvent *event) +void Engine::onInputEvent(const InputEvent& event) { // inputs goest to gui first if(!g_ui->onInputEvent(event)) { diff --git a/src/framework/engine.h b/src/framework/engine.h index 7f2063dd..5a85634f 100644 --- a/src/framework/engine.h +++ b/src/framework/engine.h @@ -60,7 +60,7 @@ public: /// Fired by platform on window resize void onResize(const Size& size); /// Fired by platform on mouse/keyboard input - void onInputEvent(InputEvent *event); + void onInputEvent(const InputEvent& event); /// Enable FPS counter on screen void enableFpsCounter(bool enable = true) { m_calculateFps = enable; }; @@ -68,8 +68,6 @@ public: private: /// Called to render every frame void render(); - /// Called between renders - void update(int ticks, int elapsedTicks); bool m_stopping; bool m_running; diff --git a/src/framework/font.cpp b/src/framework/font.cpp index cf4add32..4192fac6 100644 --- a/src/framework/font.cpp +++ b/src/framework/font.cpp @@ -28,12 +28,47 @@ #include "graphics.h" Font::Font() : - m_lineHeight(14), - m_cursorSize(14), - m_color(0xFFFFFFFF) + m_glyphHeight(10), + m_topMargin(0) { } +void Font::calculateGlyphsWidthsAutomatically(const Size& glyphSize) +{ + int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width(); + uchar *texturePixels = m_texture->getPixels(); + + // small AI to auto calculate pixels widths + for(int glyph = 32; glyph< 256; ++glyph) { + Rect glyphCoords(((glyph - 32) % numHorizontalGlyphs) * glyphSize.width(), + ((glyph - 32) / numHorizontalGlyphs) * glyphSize.height(), + glyphSize.width(), + m_glyphHeight); + int width = glyphSize.width(); + for(int x = glyphCoords.left() + 2; x <= glyphCoords.right(); ++x) { + bool allAlpha = true; + + // check if all vertical pixels are alpha + for(int y = glyphCoords.top(); y <= glyphCoords.bottom(); ++y) { + if(texturePixels[(y * m_texture->getSize().width() * 4) + (x*4) + 3] != 0) { + allAlpha = false; + break; + } + } + + // if all pixels were alpha we found the width + if(allAlpha) { + width = x - glyphCoords.left(); + break; + } + } + // store glyph size + m_glyphsSize[glyph].setSize(width, m_glyphHeight); + } + + delete[] texturePixels; +} + bool Font::load(const std::string& file) { std::string fileContents = g_resources.loadTextFile(file); @@ -45,10 +80,7 @@ bool Font::load(const std::string& file) std::istringstream fin(fileContents); std::string textureName; - int numHorizontalGlyphs; - int firstGlyph; Size glyphSize; - Size textureSize; try { YAML::Parser parser(fin); @@ -56,10 +88,9 @@ bool Font::load(const std::string& file) YAML::Node doc; parser.GetNextDocument(doc); - doc["line height"] >> m_lineHeight; - doc["cursor size"] >> m_cursorSize; - doc["color"] >> m_color; - doc["first glyph"] >> firstGlyph; + doc["glyph height"] >> m_glyphHeight; + doc["glyph spacing"] >> m_glyphSpacing; + doc["top margin"] >> m_topMargin; doc["image glyph size"] >> glyphSize; doc["image"] >> textureName; @@ -69,25 +100,32 @@ bool Font::load(const std::string& file) return false; } - textureSize = m_texture->getSize(); - numHorizontalGlyphs = textureSize.width() / glyphSize.width(); + // set glyphs height + for(int glyph = 32; glyph < 256; ++glyph) { + } - const YAML::Node& widthsNode = doc["glyph widths"]; - for(auto it = widthsNode.begin(); it != widthsNode.end(); ++it) { - int glyph, glyphWidth; - it.first() >> glyph; - it.second() >> glyphWidth; + calculateGlyphsWidthsAutomatically(glyphSize); - // calculate glyph texture coords - m_glyphsTextureCoords[glyph].setRect(((glyph - firstGlyph) % numHorizontalGlyphs) * glyphSize.width(), - ((glyph - firstGlyph) / numHorizontalGlyphs) * glyphSize.height(), - glyphWidth, - glyphSize.height()); + // read custom widths + if(doc.FindValue("glyph widths")) { + const YAML::Node& widthsNode = doc["glyph widths"]; + for(auto it = widthsNode.begin(); it != widthsNode.end(); ++it) { + int glyph, glyphWidth; + it.first() >> glyph; + it.second() >> glyphWidth; + m_glyphsSize[glyph].setWidth(glyphWidth); + } + } - // store glyph size - m_glyphsSize[glyph].setHeight(glyphSize.height()); - m_glyphsSize[glyph].setWidth(glyphWidth); + // calculate glyphs texture coords + 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); } + } catch (YAML::ParserException& e) { logError("Malformed font file \"%s\"", file.c_str()); return false; @@ -107,6 +145,7 @@ void Font::renderText(const std::string& text, void Font::renderText(const std::string& text, const Rect& screenCoords, int align, + const Color& color, const Point& startInternalPos, bool debug) { @@ -115,7 +154,7 @@ void Font::renderText(const std::string& text, return; // begin texture rendering - g_graphics.setColor(m_color); + g_graphics.setColor(color); g_graphics._beginTextureRender(m_texture.get()); const Size& textureSize = m_texture->getSize(); @@ -189,6 +228,8 @@ void Font::renderText(const std::string& text, // render glyph g_graphics._drawTexturedRect(glyphScreenCoords, glyphTextureCoords, textureSize); + + //g_graphics._drawBoundingRect(glyphScreenCoords, Color(0xFF0000FF)); } // end texture redering @@ -196,7 +237,7 @@ void Font::renderText(const std::string& text, g_graphics.resetColor(); if(debug) - g_graphics.drawBoundingRect(screenCoords.expanded(1), Color(0xFF00FF00), 1); + g_graphics.drawBoundingRect(screenCoords.expanded(1), Color(0xFF00FF00)); } Point* Font::calculateGlyphsPositions(const std::string& text, int align, Size *textBoxSize) @@ -211,7 +252,7 @@ Point* Font::calculateGlyphsPositions(const std::string& text, int align, Size * // protect buffer overflow on glyphsPostions int numGlyphs = text.length(); if(numGlyphs > 8192) - logFatal("A text was too long to render!"); + logFatal("could not calculate glyphs positions, text length is > 8192!"); // calculate lines width if((align & ALIGN_RIGHT || align & ALIGN_HORIZONTAL_CENTER) || textBoxSize) { @@ -222,13 +263,13 @@ Point* Font::calculateGlyphsPositions(const std::string& text, int align, Size * if(glyph == (uchar)'\n') { lineWidths[++lines] = 0; } else if(glyph >= 32) { - lineWidths[lines] += m_glyphsSize[glyph].width(); + lineWidths[lines] += m_glyphsSize[glyph].width() + m_glyphSpacing.width(); maxLineWidth = std::max(maxLineWidth, lineWidths[lines]); } } } - Point virtualPos; + Point virtualPos(0, m_topMargin); lines = 0; for(i = 0; i < numGlyphs; ++i) { glyph = (int)text[i]; @@ -239,7 +280,7 @@ Point* Font::calculateGlyphsPositions(const std::string& text, int align, Size * // new line or first glyph if(glyph == (uchar)'\n' || i == 0) { if(glyph == (uchar)'\n') { - virtualPos.y += m_lineHeight; + virtualPos.y += m_glyphHeight + m_glyphSpacing.height(); lines++; } @@ -255,13 +296,13 @@ Point* Font::calculateGlyphsPositions(const std::string& text, int align, Size * // render only if the glyph is valid if(glyph >= 32 && glyph != (uchar)'\n') { - virtualPos.x += m_glyphsSize[glyph].width(); + virtualPos.x += m_glyphsSize[glyph].width() + m_glyphSpacing.width(); } } if(textBoxSize) { textBoxSize->setWidth(maxLineWidth); - textBoxSize->setHeight(virtualPos.y + m_lineHeight); + textBoxSize->setHeight(virtualPos.y + m_glyphHeight); } return (Point *)glyphsPositions; diff --git a/src/framework/font.h b/src/framework/font.h index 66cc0e00..12d71ee7 100644 --- a/src/framework/font.h +++ b/src/framework/font.h @@ -64,6 +64,7 @@ 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); @@ -74,9 +75,11 @@ public: Size calculateTextRectSize(const std::string& text); private: - int m_lineHeight; - int m_cursorSize; - Color m_color; + void calculateGlyphsWidthsAutomatically(const Size& glyphSize); + + int m_glyphHeight; + int m_topMargin; + Size m_glyphSpacing; TexturePtr m_texture; Rect m_glyphsTextureCoords[256]; Size m_glyphsSize[256]; diff --git a/src/framework/gamestate.h b/src/framework/gamestate.h index ba0ff028..37fdd939 100644 --- a/src/framework/gamestate.h +++ b/src/framework/gamestate.h @@ -25,6 +25,7 @@ #ifndef GAMESTATE_H #define GAMESTATE_H +#include "input.h" #include "size.h" struct InputEvent; @@ -39,11 +40,10 @@ public: virtual void onLeave() = 0; virtual void onClose() = 0; - virtual void onInputEvent(InputEvent *event) = 0; + virtual void onInputEvent(const InputEvent& event) = 0; virtual void onResize(const Size& size) = 0; virtual void render() = 0; - virtual void update(int ticks, int elapsedTicks) = 0; }; #endif // GAMESTATE_H diff --git a/src/framework/graphics.cpp b/src/framework/graphics.cpp index 327eb00a..d44b1923 100644 --- a/src/framework/graphics.cpp +++ b/src/framework/graphics.cpp @@ -148,6 +148,9 @@ void Graphics::_endTextureRender() void Graphics::drawTexturedRect(const Rect& screenCoords, const Texture *texture, const Rect& textureCoords) { + if(screenCoords.size().isEmpty()) + return; + glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); glBegin(GL_QUADS); _drawTexturedRect(screenCoords, textureCoords, texture->getSize()); @@ -156,6 +159,9 @@ void Graphics::drawTexturedRect(const Rect& screenCoords, const Texture *texture void Graphics::_drawTexturedRect(const Rect& screenCoords, const Rect& textureCoords, const Size& textureSize) { + if(screenCoords.size().isEmpty()) + return; + // rect correction for opengl int right = screenCoords.right() + 1; int bottom = screenCoords.bottom() + 1; @@ -182,6 +188,9 @@ void Graphics::_drawTexturedRect(const Rect& screenCoords, const Rect& textureCo 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()); @@ -190,6 +199,9 @@ void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const Texture* void Graphics::_drawRepeatedTexturedRect(const Rect& screenCoords, const Rect& textureCoords, const Size& textureSize) { + if(screenCoords.size().isEmpty()) + return; + // render many repeated texture rects Rect virtualScreenCoords(0,0,screenCoords.size()); for(int y = 0; y <= virtualScreenCoords.height(); y += textureCoords.height()) { @@ -216,6 +228,9 @@ void Graphics::_drawRepeatedTexturedRect(const Rect& screenCoords, const Rect& t void Graphics::drawColoredRect(const Rect& screenCoords, const Color& color) { + if(screenCoords.size().isEmpty()) + return; + glDisable(GL_TEXTURE_2D); setColor(color); diff --git a/src/framework/input.h b/src/framework/input.h index 0090852d..4f74b765 100644 --- a/src/framework/input.h +++ b/src/framework/input.h @@ -25,6 +25,8 @@ #ifndef INPUT_H #define INPUT_H +#include "prerequisites.h" + enum EKeyCode { KC_UNKNOWN = 0x00, KC_ESCAPE = 0x01, diff --git a/src/framework/platform.h b/src/framework/platform.h index 2e2f1d5e..fb61d8af 100644 --- a/src/framework/platform.h +++ b/src/framework/platform.h @@ -72,7 +72,7 @@ namespace Platform void swapBuffers(); /// Get the app user directory, the place to save files configurations files - const char *getAppUserDir(); + std::string getAppUserDir(); } #endif // PLATFORM_H diff --git a/src/framework/rect.h b/src/framework/rect.h index 90857df0..44c12018 100644 --- a/src/framework/rect.h +++ b/src/framework/rect.h @@ -54,6 +54,8 @@ public: inline T top() const { return y1; } inline T right() const { return x2; } inline T bottom() const { return y2; } + inline T horizontalCenter() const { return x1 + (x2 - x1)/2; } + inline T verticalCenter() const { return y1 + (y2 - y1)/2; } inline T x() const { return x1; } inline T y() const { return y1; } inline TPoint topLeft() const { return TPoint(x1, y1); } @@ -108,6 +110,16 @@ public: x2 = x1 + w; y2 = y1 + h; } + inline void moveHorizontalCenter(T x) { + T w = x2 - x1; + x1 = x - w/2; + x2 = x1 + w; + } + inline void moveVerticalCenter(T y) { + T h = y2 - y1; + y1 = y - h/2; + y2 = y1 + h; + } inline bool contains(const TPoint &p, bool insideOnly = false) const { T l, r; diff --git a/src/framework/texture.cpp b/src/framework/texture.cpp index 3da954a3..ea0bf577 100644 --- a/src/framework/texture.cpp +++ b/src/framework/texture.cpp @@ -72,3 +72,11 @@ void Texture::enableBilinearFilter() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } + +uchar *Texture::getPixels() +{ + 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); + return pixels; +} diff --git a/src/framework/texture.h b/src/framework/texture.h index 495baedb..e80112af 100644 --- a/src/framework/texture.h +++ b/src/framework/texture.h @@ -42,6 +42,7 @@ public: const Size& getSize() const { return m_size; } uint getTextureId() const { return m_textureId; } + uchar *getPixels(); private: uint m_textureId; diff --git a/src/framework/ui/anchorlayout.cpp b/src/framework/ui/anchorlayout.cpp new file mode 100644 index 00000000..78739e64 --- /dev/null +++ b/src/framework/ui/anchorlayout.cpp @@ -0,0 +1,123 @@ +/* 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 "anchorlayout.h" + +int AnchorLine::getPos() const +{ + AnchorLayoutPtr element = m_relativeElement.lock(); + if(element) { + switch(m_anchorType) { + case ANCHOR_LEFT: + return element->getRect().left(); + case ANCHOR_RIGHT: + return element->getRect().right(); + case ANCHOR_TOP: + return element->getRect().top(); + case ANCHOR_BOTTOM: + return element->getRect().bottom(); + case ANCHOR_HORIZONTAL_CENTER: + return element->getRect().horizontalCenter(); + case ANCHOR_VERTICAL_CENTER: + return element->getRect().verticalCenter(); + default: + return 0; + } + } + logError("anchor line of an element have expired"); + return 0; +} + +void AnchorLayout::setSize(const Size& size) +{ + m_rect.setSize(size); + recalculateAnchors(); +} + +void AnchorLayout::setRect(const Rect& rect) +{ + m_rect = rect; + recalculateAnchors(); +} + +void AnchorLayout::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()); + recalculateAnchors(); +} + +void AnchorLayout::addAnchoredElement(AnchorLayoutPtr anchoredElement) +{ + bool found = false; + for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) { + if((*it).lock() == anchoredElement) { + found = true; + break; + } + } + if(!found) + m_anchoredElements.push_back(anchoredElement); +} + +void AnchorLayout::recalculateAnchors() +{ + // horizontal + if(m_anchors[ANCHOR_HORIZONTAL_CENTER].isValid()) { + m_rect.moveHorizontalCenter(m_anchors[ANCHOR_HORIZONTAL_CENTER].getPos() + m_marginLeft - m_marginRight); + } else { + if(m_anchors[ANCHOR_LEFT].isValid() && m_anchors[ANCHOR_RIGHT].isValid()) { + m_rect.setLeft(m_anchors[ANCHOR_LEFT].getPos() + m_marginLeft); + m_rect.setRight(m_anchors[ANCHOR_RIGHT].getPos() - m_marginRight); + } else if(m_anchors[ANCHOR_LEFT].isValid()) { + m_rect.moveLeft(m_anchors[ANCHOR_LEFT].getPos() + m_marginLeft); + } else if(m_anchors[ANCHOR_RIGHT].isValid()) { + m_rect.moveRight(m_anchors[ANCHOR_RIGHT].getPos() - m_marginRight); + } + } + + // vertical + if(m_anchors[ANCHOR_VERTICAL_CENTER].isValid()) { + m_rect.moveVerticalCenter(m_anchors[ANCHOR_VERTICAL_CENTER].getPos() + m_marginTop - m_marginBottom); + } else { + if(m_anchors[ANCHOR_TOP].isValid() && m_anchors[ANCHOR_BOTTOM].isValid()) { + m_rect.setLeft(m_anchors[ANCHOR_TOP].getPos() + m_marginTop); + m_rect.setRight(m_anchors[ANCHOR_BOTTOM].getPos() - m_marginBottom); + } else if(m_anchors[ANCHOR_TOP].isValid()) { + m_rect.moveTop(m_anchors[ANCHOR_TOP].getPos() + m_marginTop); + } else if(m_anchors[ANCHOR_BOTTOM].isValid()) { + m_rect.moveBottom(m_anchors[ANCHOR_BOTTOM].getPos() - m_marginBottom); + } + } + + for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) { + AnchorLayoutPtr element = (*it).lock(); + if(element) + element->recalculateAnchors(); + } +} diff --git a/src/framework/ui/anchorlayout.h b/src/framework/ui/anchorlayout.h new file mode 100644 index 00000000..96c944d6 --- /dev/null +++ b/src/framework/ui/anchorlayout.h @@ -0,0 +1,116 @@ +/* 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 ANCHORLAYOUT_H +#define ANCHORLAYOUT_H + +#include "../prerequisites.h" +#include "../rect.h" +#include "uiconstants.h" + +enum EAnchorType { + ANCHOR_LEFT = 0, + ANCHOR_RIGHT, + ANCHOR_TOP, + ANCHOR_BOTTOM, + ANCHOR_HORIZONTAL_CENTER, + ANCHOR_VERTICAL_CENTER, + ANCHOR_NONE +}; + +class AnchorLayout; +typedef std::shared_ptr AnchorLayoutPtr; +typedef std::weak_ptr AnchorLayoutWeakPtr; + +class AnchorLine +{ +public: + AnchorLine() : m_anchorType(ANCHOR_NONE) { } + AnchorLine(const AnchorLine& other) : + m_relativeElement(other.m_relativeElement), m_anchorType(other.m_anchorType) { } + AnchorLine(AnchorLayoutPtr 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; } + AnchorLayoutPtr getRelativeElement() const { return m_relativeElement.lock(); } + +private: + AnchorLayoutWeakPtr m_relativeElement; + EAnchorType m_anchorType; +}; + +class AnchorLayout : public std::enable_shared_from_this +{ +public: + AnchorLayout() : + m_marginLeft(0), + m_marginRight(0), + m_marginTop(0), + m_marginBottom(0) { } + virtual ~AnchorLayout() { } + + 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(asAnchorLayout(), ANCHOR_LEFT); } + AnchorLine right() { return AnchorLine(asAnchorLayout(), ANCHOR_RIGHT); } + AnchorLine top() { return AnchorLine(asAnchorLayout(), ANCHOR_TOP); } + AnchorLine bottom() { return AnchorLine(asAnchorLayout(), ANCHOR_BOTTOM); } + AnchorLine horizontalCenter() { return AnchorLine(asAnchorLayout(), ANCHOR_HORIZONTAL_CENTER); } + AnchorLine verticalCenter() { return AnchorLine(asAnchorLayout(), 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(); } + + AnchorLayoutPtr asAnchorLayout() { return shared_from_this(); } + +private: + void recalculateAnchors(); + void addAnchoredElement(AnchorLayoutPtr 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 // ANCHORLAYOUT_H diff --git a/src/framework/ui/ui.h b/src/framework/ui/ui.h index 2dcb8528..27d4d11c 100644 --- a/src/framework/ui/ui.h +++ b/src/framework/ui/ui.h @@ -29,11 +29,12 @@ #include "uiconstants.h" #include "uielement.h" -#include "uielementskin.h" #include "uicontainer.h" #include "uipanel.h" #include "uibutton.h" #include "uilabel.h" #include "uiskins.h" +#include "uiwindow.h" +#include "uitextedit.h" #endif // UI_H diff --git a/src/framework/ui/uibutton.cpp b/src/framework/ui/uibutton.cpp index b246ea28..25c87a1e 100644 --- a/src/framework/ui/uibutton.cpp +++ b/src/framework/ui/uibutton.cpp @@ -30,5 +30,20 @@ void UIButton::render() { UIElement::render(); - g_fonts.get("tibia-8px-antialised")->renderText(m_text, m_rect, ALIGN_CENTER); + g_fonts.get("tibia-8px-antialised")->renderText(m_text, getRect(), ALIGN_CENTER, Color(0xFFEEEEEE)); +} + +bool UIButton::onInputEvent(const InputEvent& event) +{ + if(event.type == EV_MOUSE_LDOWN && + getRect().contains(Point(event.mouse.x, event.mouse.y))) { + m_state = UI::ButtonDown; + } else if(m_state == UI::ButtonDown && event.type == EV_MOUSE_LUP) { + m_state = UI::ButtonUp; + if(getRect().contains(Point(event.mouse.x, event.mouse.y))) { + if(m_buttonClickCallback) + m_buttonClickCallback(); + } + } + return false; } diff --git a/src/framework/ui/uibutton.h b/src/framework/ui/uibutton.h index 61e27222..6fc91a4a 100644 --- a/src/framework/ui/uibutton.h +++ b/src/framework/ui/uibutton.h @@ -29,6 +29,8 @@ #include "uielement.h" #include "../borderedimage.h" +typedef std::function Callback; + class UIButton : public UIElement { public: @@ -39,10 +41,16 @@ public: } virtual void render(); + bool onInputEvent(const InputEvent& event); + + UI::EButtonState getState() { return m_state; } + + void onClick(const Callback& callback) { m_buttonClickCallback = callback; } private: std::string m_text; UI::EButtonState m_state; + Callback m_buttonClickCallback; }; typedef std::shared_ptr UIButtonPtr; diff --git a/src/framework/ui/uibuttonskin.cpp b/src/framework/ui/uibuttonskin.cpp new file mode 100644 index 00000000..52d48df4 --- /dev/null +++ b/src/framework/ui/uibuttonskin.cpp @@ -0,0 +1,50 @@ +/* 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 "uibuttonskin.h" +#include "uibutton.h" + +void UIButtonSkin::draw(UIElement *element) +{ + UIButton *button = static_cast(element); + + if(button->getState() == UI::ButtonDown && m_buttonDownImage) { + m_buttonDownImage->draw(element->getRect()); + } else if(button->getState() == UI::ButtonMouseOver && m_buttonHoverImage) { + m_buttonHoverImage->draw(element->getRect()); + } else { + UIElementSkin::draw(element); + } +} + +void UIButtonSkin::load(const YAML::Node& node) +{ + UIElementSkin::load(node); + + if(node.FindValue("down state")) + m_buttonDownImage = loadImage(node["down state"]); + + if(node.FindValue("mouse over state")) + m_buttonHoverImage = loadImage(node["mouse over state"]); +} diff --git a/src/framework/ui/uibuttonskin.h b/src/framework/ui/uibuttonskin.h new file mode 100644 index 00000000..8caaa0d3 --- /dev/null +++ b/src/framework/ui/uibuttonskin.h @@ -0,0 +1,46 @@ +/* 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 UIBUTTONSKIN_H +#define UIBUTTONSKIN_H + +#include "../prerequisites.h" +#include "uiconstants.h" +#include "uielementskin.h" + +class UIButtonSkin : public UIElementSkin +{ +public: + UIButtonSkin(const std::string& name) : + UIElementSkin(name, UI::Button) { } + + void load(const YAML::Node& node); + void draw(UIElement *element); + +private: + ImagePtr m_buttonDownImage; + ImagePtr m_buttonHoverImage; +}; + +#endif // UIBUTTONSKIN_H diff --git a/src/framework/ui/uiconstants.h b/src/framework/ui/uiconstants.h index 4239240f..66d34923 100644 --- a/src/framework/ui/uiconstants.h +++ b/src/framework/ui/uiconstants.h @@ -53,12 +53,12 @@ namespace UI { enum EElementType { - Element, + Element = 0, Container, Panel, Window, Label, - TextBox, + TextEdit, Button, CheckBox }; diff --git a/src/framework/ui/uicontainer.cpp b/src/framework/ui/uicontainer.cpp index 34de5c7e..19821415 100644 --- a/src/framework/ui/uicontainer.cpp +++ b/src/framework/ui/uicontainer.cpp @@ -30,11 +30,6 @@ void UIContainer::addChild(UIElementPtr child) { m_children.push_back(child); child->setParent(asUIContainer()); - - // adjust child rect - Rect childRect = child->getRect(); - childRect.translate(m_rect.topLeft()); - child->setRect(childRect); } void UIContainer::removeChild(UIElementPtr child) @@ -42,12 +37,14 @@ void UIContainer::removeChild(UIElementPtr child) if(m_activeElement == child) setActiveElement(UIElementPtr()); m_children.remove(child); + if(child->getParent() == shared_from_this()) + child->setParent(UIContainerPtr()); } -UIElementPtr UIContainer::getChildByName(const std::string& name) const +UIElementPtr UIContainer::getChildById(const std::string& id) const { for(auto it = m_children.begin(); it != m_children.end(); ++it) { - if((*it)->getName() == name) { + if((*it)->getId() == id) { return (*it); break; } @@ -55,62 +52,25 @@ UIElementPtr UIContainer::getChildByName(const std::string& name) const return UIElementPtr(); } - -void UIContainer::setRect(const Rect& rect) +void UIContainer::render() { - // update children rect + UIElement::render(); for(auto it = m_children.begin(); it != m_children.end(); ++it) { const UIElementPtr& child = (*it); - - // transforme child rect - Rect childRect = child->getRect(); - childRect.translate(-m_rect.topLeft()); - childRect.translate(rect.topLeft()); - child->setRect(childRect); + if(child->isVisible()) + child->render(); } - - m_rect = rect; } -void UIContainer::resize(const Size& size) +bool UIContainer::onInputEvent(const InputEvent& event) { - Rect newRect = m_rect; - newRect.setSize(size); - setRect(newRect); -} - -void UIContainer::move(const Point& trans) -{ - Rect newRect = m_rect; - newRect.translate(trans); - setRect(newRect); -} - -void UIContainer::moveTo(const Point& pos) -{ - Rect newRect = m_rect; - newRect.moveTo(pos); - setRect(newRect); - -} - -void UIContainer::render() -{ - UIElement::render(); + bool ret = false; for(auto it = m_children.begin(); it != m_children.end(); ++it) { const UIElementPtr& child = (*it); - child->render(); + if(child->isEnabled() && child->isVisible()) + ret = child->onInputEvent(event) || ret; } -} - -void UIContainer::update(int ticks, int elapsedTicks) -{ - -} - -bool UIContainer::onInputEvent(InputEvent* event) -{ - return false; + return ret; } void UIContainer::setActiveElement(UIElementPtr activeElement) diff --git a/src/framework/ui/uicontainer.h b/src/framework/ui/uicontainer.h index 5629a51d..0ef14f6f 100644 --- a/src/framework/ui/uicontainer.h +++ b/src/framework/ui/uicontainer.h @@ -36,20 +36,14 @@ public: UIContainer(UI::EElementType type = UI::Container) : UIElement(type) { } virtual ~UIContainer() { } - virtual void addChild(UIElementPtr child); - virtual void removeChild(UIElementPtr child); - virtual UIElementPtr getChildByName(const std::string& name) const; - - virtual void setRect(const Rect& rect); - virtual void resize(const Size& size); - virtual void move(const Point& trans); - virtual void moveTo(const Point& pos); + void addChild(UIElementPtr child); + void removeChild(UIElementPtr child); + UIElementPtr getChildById(const std::string& id) const; virtual void render(); - virtual void update(int ticks, int elapsedTicks); - virtual bool onInputEvent(InputEvent *event); + virtual bool onInputEvent(const InputEvent& event); - virtual void setActiveElement(UIElementPtr activeElement); + void setActiveElement(UIElementPtr activeElement); UIElementPtr getActiveElement() const { return m_activeElement; } virtual UI::EElementType getElementType() const { return UI::Container; } diff --git a/src/framework/ui/uielement.cpp b/src/framework/ui/uielement.cpp index 24ff0cdb..052ee849 100644 --- a/src/framework/ui/uielement.cpp +++ b/src/framework/ui/uielement.cpp @@ -24,12 +24,18 @@ #include "uielement.h" #include "uiskins.h" +#include "uielementskin.h" UIElement::UIElement(UI::EElementType type) : - m_type(type) + AnchorLayout(), + m_type(type), + m_skin(NULL), + m_visible(true), + m_enabled(true) { // set default skin - setSkin(g_uiSkins.getElementSkin(type)); + if(type > UI::Container) + setSkin(g_uiSkins.getElementSkin(type)); } @@ -41,8 +47,8 @@ bool UIElement::setSkin(const std::string& skinName) void UIElement::setSkin(UIElementSkin* skin) { - if(skin && !m_rect.isValid()) { - m_rect.setSize(skin->getDefaultSize()); + if(skin && !getRect().isValid()) { + setSize(skin->getDefaultSize()); } m_skin = skin; } @@ -52,4 +58,3 @@ void UIElement::render() if(m_skin) m_skin->draw(this); } - diff --git a/src/framework/ui/uielement.h b/src/framework/ui/uielement.h index 37997c3b..ae3c6014 100644 --- a/src/framework/ui/uielement.h +++ b/src/framework/ui/uielement.h @@ -29,55 +29,54 @@ #include "../input.h" #include "../rect.h" #include "uiconstants.h" -#include "uielementskin.h" +#include "anchorlayout.h" + +class UIElementSkin; class UIContainer; typedef std::shared_ptr UIContainerPtr; +typedef std::weak_ptr UIContainerWeakPtr; class UIElement; typedef std::shared_ptr UIElementPtr; +typedef std::weak_ptr UIElementWeakPtr; -class UIElement : public std::enable_shared_from_this +class UIElement : public AnchorLayout { public: UIElement(UI::EElementType type = UI::Element); virtual ~UIElement() { } virtual void render(); - virtual void update(int ticks, int elapsedTicks) { } - virtual bool onInputEvent(InputEvent *event) { return false; } + virtual bool onInputEvent(const InputEvent& event) { return false; } bool setSkin(const std::string& skinName); void setSkin(UIElementSkin *skin); UIElementSkin *getSkin() { return m_skin; } virtual void setParent(UIContainerPtr parent) { m_parent = parent; } - UIContainerPtr getParent() const { return m_parent; } - - virtual void setName(const std::string& name) { m_name = name; } - const std::string& getName() const { return m_name; } + UIContainerPtr getParent() const { return m_parent.lock(); } - virtual void setRect(const Rect& rect) { m_rect = rect; } - const Rect& getRect() const{ return m_rect; } + void setId(const std::string& id) { m_id = id; } + const std::string& getId() const { return m_id; } - virtual void setActive(bool active) { m_active = active; } - bool isActive() const { return m_active; } + void setEnabled(bool enabled) { m_enabled = enabled; } + bool isEnabled() const { return m_enabled; } - virtual void setVisible(bool visible) { m_visible = visible; } + void setVisible(bool visible) { m_visible = visible; } bool isVisible() const { return m_visible; } UI::EElementType getElementType() const { return m_type; } - UIElementPtr asUIElement() { return shared_from_this(); } + UIElementPtr asUIElement() { return std::static_pointer_cast(shared_from_this()); } -protected: +private: UI::EElementType m_type; - UIContainerPtr m_parent; + UIContainerWeakPtr m_parent; UIElementSkin *m_skin; - Rect m_rect; - std::string m_name; + std::string m_id; bool m_visible; - bool m_active; + bool m_enabled; }; #endif // UIELEMENT_H diff --git a/src/framework/ui/uielementskin.cpp b/src/framework/ui/uielementskin.cpp index 2698f0ef..f5b5928b 100644 --- a/src/framework/ui/uielementskin.cpp +++ b/src/framework/ui/uielementskin.cpp @@ -30,29 +30,41 @@ void UIElementSkin::draw(UIElement *element) { - const ImagePtr& image = m_stateImages.front(); - if(image) { - image->draw(element->getRect()); - } + if(m_defaultImage) + m_defaultImage->draw(element->getRect()); } void UIElementSkin::load(const YAML::Node& node) { if(node.FindValue("default size")) node["default size"] >> m_defaultSize; + m_defaultImage = loadImage(node); +} +ImagePtr UIElementSkin::loadImage(const YAML::Node& node) +{ + ImagePtr image; if(node.FindValue("bordered image")) { const YAML::Node& child = node["bordered image"]; Rect left, right, top, bottom, topLeft, topRight, bottomLeft, bottomRight, center; - child["left border"] >> left; - child["right border"] >> right; - child["top border"] >> top; - child["bottom border"] >> bottom; - child["top left corner"] >> topLeft; - child["top right corner"] >> topRight; - child["bottom left corner"] >> bottomLeft; - child["bottom right corner"] >> bottomRight; - child["center"] >> center; + if(child.FindValue("left border")) + child["left border"] >> left; + if(child.FindValue("right border")) + child["right border"] >> right; + if(child.FindValue("top border")) + child["top border"] >> top; + if(child.FindValue("bottom border")) + child["bottom border"] >> bottom; + if(child.FindValue("top left corner")) + child["top left corner"] >> topLeft; + if(child.FindValue("top right corner")) + child["top right corner"] >> topRight; + if(child.FindValue("bottom left corner")) + child["bottom left corner"] >> bottomLeft; + if(child.FindValue("bottom right corner")) + child["bottom right corner"] >> bottomRight; + if(child.FindValue("center")) + child["center"] >> center; TexturePtr texture; if(child.FindValue("image")) { @@ -63,7 +75,7 @@ void UIElementSkin::load(const YAML::Node& node) texture = g_uiSkins.getDefaultTexture(); } - ImagePtr image = ImagePtr(new BorderedImage(texture, + image = ImagePtr(new BorderedImage(texture, left, right, top, @@ -73,6 +85,7 @@ void UIElementSkin::load(const YAML::Node& node) bottomLeft, bottomRight, center)); - m_stateImages.push_back(image); } + return image; } + diff --git a/src/framework/ui/uielementskin.h b/src/framework/ui/uielementskin.h index 3d456fc5..cd24f39a 100644 --- a/src/framework/ui/uielementskin.h +++ b/src/framework/ui/uielementskin.h @@ -38,19 +38,24 @@ public: UIElementSkin(const std::string& name, UI::EElementType elementType) : m_name(name), m_elementType(elementType) { } + virtual ~UIElementSkin() { } - void load(const YAML::Node& node); - void draw(UIElement *element); + virtual void load(const YAML::Node& node); + virtual void draw(UIElement *element); const std::string& getName() const { return m_name; } const Size& getDefaultSize() const { return m_defaultSize; } UI::EElementType getElementType() const { return m_elementType; } + ImagePtr getDefaultImage() const { return m_defaultImage; } + +protected: + ImagePtr loadImage(const YAML::Node& node); private: std::string m_name; UI::EElementType m_elementType; Size m_defaultSize; - std::vector m_stateImages; + ImagePtr m_defaultImage; }; #endif // UIELEMENTSKIN_H diff --git a/src/framework/ui/uilabel.cpp b/src/framework/ui/uilabel.cpp index e1cc5048..af7d6dc7 100644 --- a/src/framework/ui/uilabel.cpp +++ b/src/framework/ui/uilabel.cpp @@ -23,4 +23,26 @@ #include "uilabel.h" +#include "../fonts.h" + +UILabel::UILabel(const std::string& text, Font* font) : + UIElement(UI::Label), + m_text(text), + m_font(font) +{ + if(!font) + m_font = g_fonts.get("tibia-10px-antialised"); + setSize(m_font->calculateTextRectSize(text)); +} + +void UILabel::render() +{ + m_font->renderText(m_text, getRect(), ALIGN_LEFT, Color(0xFFBFBFBF)); +} + +void UILabel::setText(const std::string& text) +{ + setSize(m_font->calculateTextRectSize(text)); + m_text = text; +} diff --git a/src/framework/ui/uilabel.h b/src/framework/ui/uilabel.h index d0deb919..4b2ab7f9 100644 --- a/src/framework/ui/uilabel.h +++ b/src/framework/ui/uilabel.h @@ -28,10 +28,23 @@ #include "../prerequisites.h" #include "uielement.h" +class Font; + class UILabel : public UIElement { public: - UILabel() : UIElement(UI::Label) { } + UILabel(const std::string& text, Font *font = NULL); + + void render(); + + void setText(const std::string& text); + const std::string& getText() const { return m_text; } + +private: + std::string m_text; + Font *m_font; }; +typedef std::shared_ptr UILabelPtr; + #endif // UILABEL_H diff --git a/src/framework/ui/uiskins.cpp b/src/framework/ui/uiskins.cpp index b58d39d5..ee296b0d 100644 --- a/src/framework/ui/uiskins.cpp +++ b/src/framework/ui/uiskins.cpp @@ -26,9 +26,26 @@ #include "../resources.h" #include "../textures.h" #include "uielementskin.h" +#include "uibuttonskin.h" +#include "uiwindowskin.h" +#include "uitexteditskin.h" UISkins g_uiSkins; +void UISkins::init() +{ + // load default skin + g_uiSkins.load("skins/tibiaskin.yml"); + +} + +void UISkins::terminate() +{ + for(auto it = m_elementSkins.begin(); it != m_elementSkins.end(); ++it) + delete (*it); + m_elementSkins.clear(); +} + bool UISkins::load(const std::string& file) { std::string fileContents = g_resources.loadTextFile(file); @@ -55,9 +72,9 @@ bool UISkins::load(const std::string& file) std::string name; it.first() >> name; - UIElementSkin *elementSkin = new UIElementSkin(name, UI::Button); - elementSkin->load(it.second()); - m_elementSkins.push_back(elementSkin); + UIElementSkin *skin = new UIButtonSkin(name); + skin->load(it.second()); + m_elementSkins.push_back(skin); } } @@ -67,11 +84,49 @@ bool UISkins::load(const std::string& file) std::string name; it.first() >> name; - UIElementSkin *elementSkin = new UIElementSkin(name, UI::Panel); - elementSkin->load(it.second()); - m_elementSkins.push_back(elementSkin); + UIElementSkin *skin = new UIElementSkin(name, UI::Panel); + skin->load(it.second()); + m_elementSkins.push_back(skin); + } + } + + { + const YAML::Node& node = doc["windows"]; + for(auto it = node.begin(); it != node.end(); ++it) { + std::string name; + it.first() >> name; + + UIElementSkin *skin = new UIWindowSkin(name); + skin->load(it.second()); + m_elementSkins.push_back(skin); + } + } + + { + const YAML::Node& node = doc["labels"]; + for(auto it = node.begin(); it != node.end(); ++it) { + std::string name; + it.first() >> name; + + UIElementSkin *skin = new UIElementSkin(name, UI::Label); + skin->load(it.second()); + m_elementSkins.push_back(skin); + } + } + + { + const YAML::Node& node = doc["text edits"]; + for(auto it = node.begin(); it != node.end(); ++it) { + std::string name; + it.first() >> name; + + UIElementSkin *skin = new UITextEditSkin(name); + skin->load(it.second()); + m_elementSkins.push_back(skin); } } + + } catch (YAML::ParserException& e) { logError("Malformed font file \"%s\"", file.c_str()); return false; diff --git a/src/framework/ui/uiskins.h b/src/framework/ui/uiskins.h index 6260c6b9..d240e133 100644 --- a/src/framework/ui/uiskins.h +++ b/src/framework/ui/uiskins.h @@ -36,6 +36,9 @@ class UISkins public: UISkins() { } + void init(); + void terminate(); + bool load(const std::string& file); UIElementSkin *getElementSkin(UI::EElementType elementType, const std::string& name = "default"); diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp new file mode 100644 index 00000000..9da68902 --- /dev/null +++ b/src/framework/ui/uitextedit.cpp @@ -0,0 +1,45 @@ +/* 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 "uitextedit.h" +#include "../fonts.h" + +UITextEdit::UITextEdit(Font* font) : + UIElement(UI::TextEdit), + m_font(font) +{ + if(!font) + m_font = g_fonts.get("tibia-10px-antialised"); +} + +void UITextEdit::render() +{ + UIElement::render(); + m_font->renderText(m_text, getRect(), ALIGN_LEFT, Color(0xFFBFBFBF)); +} + +void UITextEdit::setText(const std::string& text) +{ + m_text = text; +} diff --git a/src/framework/ui/uitextedit.h b/src/framework/ui/uitextedit.h new file mode 100644 index 00000000..7ada3721 --- /dev/null +++ b/src/framework/ui/uitextedit.h @@ -0,0 +1,50 @@ +/* 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 UITEXTEDIT_H +#define UITEXTEDIT_H + +#include "../prerequisites.h" +#include "uielement.h" + +class Font; + +class UITextEdit : public UIElement +{ +public: + UITextEdit(Font *font = NULL); + + void render(); + + void setText(const std::string& text); + const std::string& getText() const { return m_text; } + +private: + std::string m_text; + Font *m_font; +}; + +typedef std::shared_ptr UITextEditPtr; + +#endif // UITEXTEDIT_H diff --git a/src/framework/ui/uitexteditskin.cpp b/src/framework/ui/uitexteditskin.cpp new file mode 100644 index 00000000..33d422db --- /dev/null +++ b/src/framework/ui/uitexteditskin.cpp @@ -0,0 +1,26 @@ +/* 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 "uitexteditskin.h" + diff --git a/src/framework/ui/uitexteditskin.h b/src/framework/ui/uitexteditskin.h new file mode 100644 index 00000000..7e79e2cf --- /dev/null +++ b/src/framework/ui/uitexteditskin.h @@ -0,0 +1,40 @@ +/* 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 UITEXTEDITSKIN_H +#define UITEXTEDITSKIN_H + +#include "../prerequisites.h" +#include "uiconstants.h" +#include "uielementskin.h" + +class UITextEditSkin : public UIElementSkin +{ +public: + UITextEditSkin(const std::string& name) : + UIElementSkin(name, UI::TextEdit) { } + +}; + +#endif // UITEXTEDITSKIN_H diff --git a/src/framework/ui/uiwindow.cpp b/src/framework/ui/uiwindow.cpp new file mode 100644 index 00000000..9648e165 --- /dev/null +++ b/src/framework/ui/uiwindow.cpp @@ -0,0 +1,26 @@ +/* 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 "uiwindow.h" + diff --git a/src/framework/ui/uiwindow.h b/src/framework/ui/uiwindow.h new file mode 100644 index 00000000..72ac014a --- /dev/null +++ b/src/framework/ui/uiwindow.h @@ -0,0 +1,46 @@ +/* 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 UIWINDOW_H +#define UIWINDOW_H + +#include "../prerequisites.h" +#include "uicontainer.h" + +class UIWindow : public UIContainer +{ +public: + UIWindow(const std::string& title) : + UIContainer(UI::Window), + m_title(title) { } + + const std::string& getTitle() const { return m_title; } + +private: + std::string m_title; +}; + +typedef std::shared_ptr UIWindowPtr; + +#endif // UIWINDOW_H diff --git a/src/framework/ui/uiwindowskin.cpp b/src/framework/ui/uiwindowskin.cpp new file mode 100644 index 00000000..e2cfe348 --- /dev/null +++ b/src/framework/ui/uiwindowskin.cpp @@ -0,0 +1,60 @@ +/* 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 "uiwindowskin.h" +#include "uiwindow.h" +#include "../fonts.h" + +void UIWindowSkin::draw(UIElement* element) +{ + UIElementSkin::draw(element); + + UIWindow *window = static_cast(element); + + Rect headRect = window->getRect(); + Rect bodyRect = window->getRect(); + + headRect.setHeight(m_headHeight); + bodyRect.setTop(headRect.bottom() + 1); + + m_headImage->draw(headRect); + m_titleFont->renderText(window->getTitle(), + headRect, + ALIGN_CENTER, + Color(0xFF8F8F8F)); + + m_bodyImage->draw(bodyRect); +} + +void UIWindowSkin::load(const YAML::Node& node) +{ + UIElementSkin::load(node); + + node["head"]["height"] >> m_headHeight; + m_headImage = loadImage(node["head"]); + m_bodyImage = loadImage(node["body"]); + + std::string fontName; + node["head"]["font"] >> fontName; + m_titleFont = g_fonts.get(fontName); +} diff --git a/src/framework/ui/uiwindowskin.h b/src/framework/ui/uiwindowskin.h new file mode 100644 index 00000000..09f917db --- /dev/null +++ b/src/framework/ui/uiwindowskin.h @@ -0,0 +1,48 @@ +/* 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 UIWINDOWSKIN_H +#define UIWINDOWSKIN_H + +#include "../prerequisites.h" +#include "uiconstants.h" +#include "uielementskin.h" +#include "../font.h" + +class UIWindowSkin : public UIElementSkin +{ +public: + UIWindowSkin(const std::string& name) : + UIElementSkin(name, UI::Window) { } + + void load(const YAML::Node& node); + void draw(UIElement *element); + +private: + ImagePtr m_headImage; + ImagePtr m_bodyImage; + Font *m_titleFont; + int m_headHeight; +}; + +#endif // UIWINDOWSKIN_H diff --git a/src/framework/win32platform.cpp b/src/framework/win32platform.cpp index 3606ddcb..a799983c 100644 --- a/src/framework/win32platform.cpp +++ b/src/framework/win32platform.cpp @@ -351,13 +351,13 @@ bool Platform::isWindowMaximized() return win32.maximized; } -const char *Platform::getAppUserDir() +std::string Platform::getAppUserDir() { std::stringstream sdir; sdir << PHYSFS_getUserDir() << "/." << win32.appName << "/"; if((mkdir(sdir.str().c_str()) != 0) && (errno != EEXIST)) logError("Couldn't create directory for saving configuration file. (%s)", sdir.str().c_str()); - return sdir.str().c_str(); + return sdir.str(); } LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) diff --git a/src/framework/x11platform.cpp b/src/framework/x11platform.cpp index 6887249f..a4021596 100644 --- a/src/framework/x11platform.cpp +++ b/src/framework/x11platform.cpp @@ -351,7 +351,7 @@ void Platform::poll() inputEvent.type = EV_TEXT_ENTER; inputEvent.key.keychar = buf[0]; inputEvent.key.keycode = KC_UNKNOWN; - g_engine.onInputEvent(&inputEvent); + g_engine.onInputEvent(inputEvent); } } @@ -364,7 +364,7 @@ void Platform::poll() inputEvent.key.keycode = x11.keyMap[keysym]; inputEvent.type = (event.type == KeyPress) ? EV_KEY_DOWN : EV_KEY_UP; inputEvent.key.keychar = (len > 0) ? buf[0] : 0; - g_engine.onInputEvent(&inputEvent); + g_engine.onInputEvent(inputEvent); } break; } @@ -387,14 +387,14 @@ void Platform::poll() inputEvent.type = EV_MOUSE_WHEEL_DOWN; break; } - g_engine.onInputEvent(&inputEvent); + g_engine.onInputEvent(inputEvent); break; case MotionNotify: inputEvent.type = EV_MOUSE_MOVE; inputEvent.mouse.x = event.xbutton.x; inputEvent.mouse.y = event.xbutton.y; - g_engine.onInputEvent(&inputEvent); + g_engine.onInputEvent(inputEvent); break; case MapNotify: @@ -826,11 +826,11 @@ bool Platform::isWindowMaximized() return ret; } -const char *Platform::getAppUserDir() +std::string Platform::getAppUserDir() { std::stringstream sdir; sdir << PHYSFS_getUserDir() << "/." << x11.appName << "/"; if((mkdir(sdir.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST)) logError("Couldn't create directory for saving configuration file. (%s)", sdir.str().c_str()); - return sdir.str().c_str(); + return sdir.str(); } diff --git a/src/main.cpp b/src/main.cpp index 1aae7c52..0badcbe1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,8 +52,8 @@ void signal_handler(int sig) void setDefaultConfigs() { // default size - int defWidth = 640; - int defHeight = 480; + int defWidth = 550; + int defHeight = 450; // init on screen center g_configs.setValue("window x", (Platform::getDisplayWidth() - defWidth)/2); @@ -101,7 +101,7 @@ int main(int argc, const char *argv[]) // create the window Platform::createWindow(g_configs.getInteger("window x"), g_configs.getInteger("window y"), g_configs.getInteger("window width"), g_configs.getInteger("window height"), - 640, 480, + 550, 450, g_configs.getBoolean("window maximized")); Platform::setWindowTitle("OTClient"); //Platform::setVsync(); @@ -109,8 +109,7 @@ int main(int argc, const char *argv[]) // init engine g_engine.init(); - // load UI skin - g_uiSkins.load("skins/tibiaskin.yml"); + g_uiSkins.init(); // state scope { @@ -129,6 +128,8 @@ int main(int argc, const char *argv[]) // terminate stuff g_engine.terminate(); + + g_uiSkins.terminate(); } // save configurations before exiting diff --git a/src/menustate.cpp b/src/menustate.cpp index c88eac3f..a89373d0 100644 --- a/src/menustate.cpp +++ b/src/menustate.cpp @@ -55,14 +55,14 @@ void MenuState::onClose() g_engine.stop(); } -void MenuState::onInputEvent(InputEvent* event) +void MenuState::onInputEvent(const InputEvent& event) { } void MenuState::onResize(const Size& size) { - recalculateMenuPanelPosition(); + } void MenuState::render() @@ -80,55 +80,123 @@ void MenuState::render() g_graphics.drawTexturedRect(Rect(0, 0, screenSize), m_background.get(), texCoords); } -void MenuState::update(int ticks, int elapsedTicks) -{ - -} - void MenuState::createMainMenu() { - UIButtonPtr button; - int y = 16; + int y = 0; m_menuPanel = UIPanelPtr(new UIPanel); m_menuPanel->setSkin("roundedGridPanel"); - recalculateMenuPanelPosition(); - - button = UIButtonPtr(new UIButton("Enter Game")); - button->setRect(Rect(16, y, 86, 20)); - m_menuPanel->addChild(button); - y += 30; + m_menuPanel->anchorLeft(g_ui->left()); + m_menuPanel->anchorBottom(g_ui->bottom()); + m_menuPanel->setSize(Size(118, 172)); + m_menuPanel->setMargin(0, 60, 70, 0); + g_ui->addChild(m_menuPanel); - button = UIButtonPtr(new UIButton("Access Account")); - button->setRect(Rect(16, y, 86, 20)); + // main menu + UIButtonPtr enterGameButton = UIButtonPtr(new UIButton("Enter Game")); + enterGameButton->anchorTop(m_menuPanel->top()); + enterGameButton->anchorHorizontalCenter(m_menuPanel->horizontalCenter()); + enterGameButton->setMargin(y += 16); + m_menuPanel->addChild(enterGameButton); + + UIButtonPtr button = UIButtonPtr(new UIButton("Access Account")); + button->anchorTop(m_menuPanel->top()); + button->anchorHorizontalCenter(m_menuPanel->horizontalCenter()); + button->setMargin(y += 30); m_menuPanel->addChild(button); - y += 30; button = UIButtonPtr(new UIButton("Options")); - button->setRect(Rect(16, y, 86, 20)); + button->anchorTop(m_menuPanel->top()); + button->anchorHorizontalCenter(m_menuPanel->horizontalCenter()); + button->setMargin(y += 30); m_menuPanel->addChild(button); - y += 30; button = UIButtonPtr(new UIButton("Info")); - button->setRect(Rect(16, y, 86, 20)); + button->anchorTop(m_menuPanel->top()); + button->anchorHorizontalCenter(m_menuPanel->horizontalCenter()); + button->setMargin(y += 30); m_menuPanel->addChild(button); - y += 30; button = UIButtonPtr(new UIButton("Exit Game")); - button->setRect(Rect(16, y, 86, 20)); + button->anchorLeft(m_menuPanel->left()); + button->anchorTop(m_menuPanel->top()); + button->anchorHorizontalCenter(m_menuPanel->horizontalCenter()); + button->setMargin(y += 30); + button->onClick([]{ g_engine.stop(); }); m_menuPanel->addChild(button); - y += 30; - g_ui->addChild(m_menuPanel); + // login window + UIWindowPtr window(new UIWindow("Enter Game")); + UIElementWeakPtr weakWindow(window); + window->setSize(Size(236, 178)); + window->anchorHorizontalCenter(g_ui->horizontalCenter()); + window->anchorVerticalCenter(g_ui->verticalCenter()); + window->setVisible(false); + g_ui->addChild(window); + + UILabelPtr label(new UILabel("Account name:")); + label->anchorLeft(window->left()); + label->anchorTop(window->top()); + label->setMargin(18, 33); + window->addChild(label); + + label = UILabelPtr(new UILabel("Password:")); + label->anchorLeft(window->left()); + label->anchorTop(window->top()); + label->setMargin(18, 62); + window->addChild(label); + + label = UILabelPtr(new UILabel("If you don't have\nan account yet:")); + label->anchorLeft(window->left()); + label->anchorTop(window->top()); + label->setMargin(18, 87); + window->addChild(label); + + button = UIButtonPtr(new UIButton("Create Account")); + button->anchorLeft(window->left()); + button->anchorTop(window->top()); + button->setMargin(132, 94); + window->addChild(button); + + button = UIButtonPtr(new UIButton("Ok")); + button->setSize(Size(43, 20)); + button->anchorRight(window->right()); + button->anchorBottom(window->bottom()); + button->setMargin(0, 0, 10, 66); + button->onClick([weakWindow]{ + UIElementPtr window = weakWindow.lock(); + if(window) + window->setVisible(false); + }); + window->addChild(button); + + button = UIButtonPtr(new UIButton("Cancel")); + button->setSize(Size(43, 20)); + button->anchorRight(window->right()); + button->anchorBottom(window->bottom()); + button->setMargin(0, 0, 10, 13); + button->onClick([weakWindow]{ + UIElementPtr window = weakWindow.lock(); + if(window) + window->setVisible(false); + }); + window->addChild(button); + + UITextEditPtr textEdit(new UITextEdit); + textEdit->anchorRight(window->right()); + textEdit->anchorTop(window->top()); + textEdit->setMargin(32, 0, 0, 18); + window->addChild(textEdit); + + textEdit = UITextEditPtr(new UITextEdit); + textEdit->anchorRight(window->right()); + textEdit->anchorTop(window->top()); + textEdit->setMargin(61, 0, 0, 18); + window->addChild(textEdit); + + enterGameButton->onClick([weakWindow]{ + UIElementPtr window = weakWindow.lock(); + if(window) + window->setVisible(true); + }); } - -void MenuState::recalculateMenuPanelPosition() -{ - if(m_menuPanel) { - // calculate panel rect - Size panelSize = Size(117, 171); - Rect panelRect = Rect(0, 0, panelSize); - panelRect.moveBottomLeft(Point(60, g_graphics.getScreenSize().height() - 70)); - m_menuPanel->setRect(panelRect); - } -} \ No newline at end of file diff --git a/src/menustate.h b/src/menustate.h index 74b1ddec..686e8569 100644 --- a/src/menustate.h +++ b/src/menustate.h @@ -40,15 +40,13 @@ public: void onLeave(); void onClose(); - void onInputEvent(InputEvent *event); + void onInputEvent(const InputEvent& event); void onResize(const Size& size); void render(); - void update(int ticks, int elapsedTicks); private: void createMainMenu(); - void recalculateMenuPanelPosition(); UIPanelPtr m_menuPanel; TexturePtr m_background; diff --git a/src/teststate.cpp b/src/teststate.cpp index 9335135b..fe4ac451 100644 --- a/src/teststate.cpp +++ b/src/teststate.cpp @@ -48,7 +48,7 @@ void TestState::onClose() g_engine.stop(); } -void TestState::onInputEvent(InputEvent* event) +void TestState::onInputEvent(const InputEvent& event) { } diff --git a/src/teststate.h b/src/teststate.h index 763941aa..9cf69a92 100644 --- a/src/teststate.h +++ b/src/teststate.h @@ -37,7 +37,7 @@ public: void onLeave(); void onClose(); - void onInputEvent(InputEvent *event); + void onInputEvent(const InputEvent& event); void onResize(const Size& size); virtual void render();