410 lines
17 KiB
C++
410 lines
17 KiB
C++
/*
|
|
* Copyright (c) 2010-2013 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 "uiwidget.h"
|
|
#include "uihorizontallayout.h"
|
|
#include "uiverticallayout.h"
|
|
#include "uigridlayout.h"
|
|
#include "uianchorlayout.h"
|
|
#include "uitranslator.h"
|
|
|
|
#include <framework/graphics/painter.h>
|
|
#include <framework/graphics/texture.h>
|
|
#include <framework/graphics/texturemanager.h>
|
|
|
|
void UIWidget::initBaseStyle()
|
|
{
|
|
m_backgroundColor = Color::alpha;
|
|
m_borderColor.set(Color::black);
|
|
m_iconColor = Color::white;
|
|
m_color = Color::white;
|
|
m_opacity = 1.0f;
|
|
m_rotation = 0.0f;
|
|
m_iconAlign = Fw::AlignNone;
|
|
|
|
// generate an unique id, this is need because anchored layouts find widgets by id
|
|
static unsigned long id = 1;
|
|
m_id = stdext::format("widget%d", id++);
|
|
}
|
|
|
|
void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode)
|
|
{
|
|
// parse lua variables and callbacks first
|
|
for(const OTMLNodePtr& node : styleNode->children()) {
|
|
// lua functions
|
|
if(stdext::starts_with(node->tag(), "@")) {
|
|
// load once
|
|
if(m_firstOnStyle) {
|
|
std::string funcName = node->tag().substr(1);
|
|
std::string funcOrigin = "@" + node->source() + ": [" + node->tag() + "]";
|
|
g_lua.loadFunction(node->value(), funcOrigin);
|
|
luaSetField(funcName);
|
|
}
|
|
// lua fields value
|
|
} else if(stdext::starts_with(node->tag(), "&")) {
|
|
std::string fieldName = node->tag().substr(1);
|
|
std::string fieldOrigin = "@" + node->source() + ": [" + node->tag() + "]";
|
|
|
|
g_lua.evaluateExpression(node->value(), fieldOrigin);
|
|
luaSetField(fieldName);
|
|
}
|
|
}
|
|
// load styles used by all widgets
|
|
for(const OTMLNodePtr& node : styleNode->children()) {
|
|
if(node->tag() == "color")
|
|
setColor(node->value<Color>());
|
|
else if(node->tag() == "x")
|
|
setX(node->value<int>());
|
|
else if(node->tag() == "y")
|
|
setY(node->value<int>());
|
|
else if(node->tag() == "pos")
|
|
setPosition(node->value<Point>());
|
|
else if(node->tag() == "width")
|
|
setWidth(node->value<int>());
|
|
else if(node->tag() == "height")
|
|
setHeight(node->value<int>());
|
|
else if(node->tag() == "rect")
|
|
setRect(node->value<Rect>());
|
|
else if(node->tag() == "background")
|
|
setBackgroundColor(node->value<Color>());
|
|
else if(node->tag() == "background-color")
|
|
setBackgroundColor(node->value<Color>());
|
|
else if(node->tag() == "background-offset-x")
|
|
setBackgroundOffsetX(node->value<int>());
|
|
else if(node->tag() == "background-offset-y")
|
|
setBackgroundOffsetY(node->value<int>());
|
|
else if(node->tag() == "background-offset")
|
|
setBackgroundOffset(node->value<Point>());
|
|
else if(node->tag() == "background-width")
|
|
setBackgroundWidth(node->value<int>());
|
|
else if(node->tag() == "background-height")
|
|
setBackgroundHeight(node->value<int>());
|
|
else if(node->tag() == "background-size")
|
|
setBackgroundSize(node->value<Size>());
|
|
else if(node->tag() == "background-rect")
|
|
setBackgroundRect(node->value<Rect>());
|
|
else if(node->tag() == "icon")
|
|
setIcon(stdext::resolve_path(node->value(), node->source()));
|
|
else if(node->tag() == "icon-source")
|
|
setIcon(stdext::resolve_path(node->value(), node->source()));
|
|
else if(node->tag() == "icon-color")
|
|
setIconColor(node->value<Color>());
|
|
else if(node->tag() == "icon-offset-x")
|
|
setIconOffsetX(node->value<int>());
|
|
else if(node->tag() == "icon-offset-y")
|
|
setIconOffsetY(node->value<int>());
|
|
else if(node->tag() == "icon-offset")
|
|
setIconOffset(node->value<Point>());
|
|
else if(node->tag() == "icon-width")
|
|
setIconWidth(node->value<int>());
|
|
else if(node->tag() == "icon-height")
|
|
setIconHeight(node->value<int>());
|
|
else if(node->tag() == "icon-size")
|
|
setIconSize(node->value<Size>());
|
|
else if(node->tag() == "icon-rect")
|
|
setIconRect(node->value<Rect>());
|
|
else if(node->tag() == "icon-clip")
|
|
setIconClip(node->value<Rect>());
|
|
else if(node->tag() == "icon-align")
|
|
setIconAlign(Fw::translateAlignment(node->value()));
|
|
else if(node->tag() == "opacity")
|
|
setOpacity(node->value<float>());
|
|
else if(node->tag() == "rotation")
|
|
setRotation(node->value<float>());
|
|
else if(node->tag() == "enabled")
|
|
setEnabled(node->value<bool>());
|
|
else if(node->tag() == "visible")
|
|
setVisible(node->value<bool>());
|
|
else if(node->tag() == "checked")
|
|
setChecked(node->value<bool>());
|
|
else if(node->tag() == "draggable")
|
|
setDraggable(node->value<bool>());
|
|
else if(node->tag() == "on")
|
|
setOn(node->value<bool>());
|
|
else if(node->tag() == "focusable")
|
|
setFocusable(node->value<bool>());
|
|
else if(node->tag() == "auto-focus")
|
|
setAutoFocusPolicy(Fw::translateAutoFocusPolicy(node->value()));
|
|
else if(node->tag() == "phantom")
|
|
setPhantom(node->value<bool>());
|
|
else if(node->tag() == "size")
|
|
setSize(node->value<Size>());
|
|
else if(node->tag() == "fixed-size")
|
|
setFixedSize(node->value<bool>());
|
|
else if(node->tag() == "clipping")
|
|
setClipping(node->value<bool>());
|
|
else if(node->tag() == "border") {
|
|
auto split = stdext::split(node->value(), " ");
|
|
if(split.size() == 2) {
|
|
setBorderWidth(stdext::safe_cast<int>(split[0]));
|
|
setBorderColor(stdext::safe_cast<Color>(split[1]));
|
|
} else
|
|
throw OTMLException(node, "border param must have its width followed by its color");
|
|
}
|
|
else if(node->tag() == "border-width")
|
|
setBorderWidth(node->value<int>());
|
|
else if(node->tag() == "border-width-top")
|
|
setBorderWidthTop(node->value<int>());
|
|
else if(node->tag() == "border-width-right")
|
|
setBorderWidthRight(node->value<int>());
|
|
else if(node->tag() == "border-width-bottom")
|
|
setBorderWidthBottom(node->value<int>());
|
|
else if(node->tag() == "border-width-left")
|
|
setBorderWidthLeft(node->value<int>());
|
|
else if(node->tag() == "border-color")
|
|
setBorderColor(node->value<Color>());
|
|
else if(node->tag() == "border-color-top")
|
|
setBorderColorTop(node->value<Color>());
|
|
else if(node->tag() == "border-color-right")
|
|
setBorderColorRight(node->value<Color>());
|
|
else if(node->tag() == "border-color-bottom")
|
|
setBorderColorBottom(node->value<Color>());
|
|
else if(node->tag() == "border-color-left")
|
|
setBorderColorLeft(node->value<Color>());
|
|
else if(node->tag() == "margin-top")
|
|
setMarginTop(node->value<int>());
|
|
else if(node->tag() == "margin-right")
|
|
setMarginRight(node->value<int>());
|
|
else if(node->tag() == "margin-bottom")
|
|
setMarginBottom(node->value<int>());
|
|
else if(node->tag() == "margin-left")
|
|
setMarginLeft(node->value<int>());
|
|
else if(node->tag() == "margin") {
|
|
std::string marginDesc = node->value();
|
|
std::vector<std::string> split = stdext::split(marginDesc, " ");
|
|
if(split.size() == 4) {
|
|
setMarginTop(stdext::safe_cast<int>(split[0]));
|
|
setMarginRight(stdext::safe_cast<int>(split[1]));
|
|
setMarginBottom(stdext::safe_cast<int>(split[2]));
|
|
setMarginLeft(stdext::safe_cast<int>(split[3]));
|
|
} else if(split.size() == 3) {
|
|
int marginTop = stdext::safe_cast<int>(split[0]);
|
|
int marginHorizontal = stdext::safe_cast<int>(split[1]);
|
|
int marginBottom = stdext::safe_cast<int>(split[2]);
|
|
setMarginTop(marginTop);
|
|
setMarginRight(marginHorizontal);
|
|
setMarginBottom(marginBottom);
|
|
setMarginLeft(marginHorizontal);
|
|
} else if(split.size() == 2) {
|
|
int marginVertical = stdext::safe_cast<int>(split[0]);
|
|
int marginHorizontal = stdext::safe_cast<int>(split[1]);
|
|
setMarginTop(marginVertical);
|
|
setMarginRight(marginHorizontal);
|
|
setMarginBottom(marginVertical);
|
|
setMarginLeft(marginHorizontal);
|
|
} else if(split.size() == 1) {
|
|
int margin = stdext::safe_cast<int>(split[0]);
|
|
setMarginTop(margin);
|
|
setMarginRight(margin);
|
|
setMarginBottom(margin);
|
|
setMarginLeft(margin);
|
|
}
|
|
}
|
|
else if(node->tag() == "padding-top")
|
|
setPaddingTop(node->value<int>());
|
|
else if(node->tag() == "padding-right")
|
|
setPaddingRight(node->value<int>());
|
|
else if(node->tag() == "padding-bottom")
|
|
setPaddingBottom(node->value<int>());
|
|
else if(node->tag() == "padding-left")
|
|
setPaddingLeft(node->value<int>());
|
|
else if(node->tag() == "padding") {
|
|
std::string paddingDesc = node->value();
|
|
std::vector<std::string> split = stdext::split(paddingDesc, " ");
|
|
if(split.size() == 4) {
|
|
setPaddingTop(stdext::safe_cast<int>(split[0]));
|
|
setPaddingRight(stdext::safe_cast<int>(split[1]));
|
|
setPaddingBottom(stdext::safe_cast<int>(split[2]));
|
|
setPaddingLeft(stdext::safe_cast<int>(split[3]));
|
|
} else if(split.size() == 3) {
|
|
int paddingTop = stdext::safe_cast<int>(split[0]);
|
|
int paddingHorizontal = stdext::safe_cast<int>(split[1]);
|
|
int paddingBottom = stdext::safe_cast<int>(split[2]);
|
|
setPaddingTop(paddingTop);
|
|
setPaddingRight(paddingHorizontal);
|
|
setPaddingBottom(paddingBottom);
|
|
setPaddingLeft(paddingHorizontal);
|
|
} else if(split.size() == 2) {
|
|
int paddingVertical = stdext::safe_cast<int>(split[0]);
|
|
int paddingHorizontal = stdext::safe_cast<int>(split[1]);
|
|
setPaddingTop(paddingVertical);
|
|
setPaddingRight(paddingHorizontal);
|
|
setPaddingBottom(paddingVertical);
|
|
setPaddingLeft(paddingHorizontal);
|
|
} else if(split.size() == 1) {
|
|
int padding = stdext::safe_cast<int>(split[0]);
|
|
setPaddingTop(padding);
|
|
setPaddingRight(padding);
|
|
setPaddingBottom(padding);
|
|
setPaddingLeft(padding);
|
|
}
|
|
}
|
|
// layouts
|
|
else if(node->tag() == "layout") {
|
|
std::string layoutType;
|
|
if(node->hasValue())
|
|
layoutType = node->value();
|
|
else
|
|
layoutType = node->valueAt<std::string>("type", "");
|
|
|
|
if(!layoutType.empty()) {
|
|
UILayoutPtr layout;
|
|
if(layoutType == "horizontalBox")
|
|
layout = UIHorizontalLayoutPtr(new UIHorizontalLayout(static_self_cast<UIWidget>()));
|
|
else if(layoutType == "verticalBox")
|
|
layout = UIVerticalLayoutPtr(new UIVerticalLayout(static_self_cast<UIWidget>()));
|
|
else if(layoutType == "grid")
|
|
layout = UIGridLayoutPtr(new UIGridLayout(static_self_cast<UIWidget>()));
|
|
else if(layoutType == "anchor")
|
|
layout = UIAnchorLayoutPtr(new UIAnchorLayout(static_self_cast<UIWidget>()));
|
|
else
|
|
throw OTMLException(node, "cannot determine layout type");
|
|
setLayout(layout);
|
|
}
|
|
|
|
if(node->hasChildren())
|
|
m_layout->applyStyle(node);
|
|
}
|
|
// anchors
|
|
else if(stdext::starts_with(node->tag(), "anchors.")) {
|
|
UIWidgetPtr parent = getParent();
|
|
if(!parent) {
|
|
if(m_firstOnStyle)
|
|
throw OTMLException(node, "cannot create anchor, there is no parent widget!");
|
|
else
|
|
continue;
|
|
}
|
|
|
|
UILayoutPtr layout = parent->getLayout();
|
|
UIAnchorLayoutPtr anchorLayout;
|
|
if(layout->isUIAnchorLayout())
|
|
anchorLayout = layout->static_self_cast<UIAnchorLayout>();
|
|
|
|
if(!anchorLayout)
|
|
throw OTMLException(node, "cannot create anchor, the parent widget doesn't use anchor layout!");
|
|
|
|
std::string what = node->tag().substr(8);
|
|
if(what == "fill") {
|
|
fill(node->value());
|
|
} else if(what == "centerIn") {
|
|
centerIn(node->value());
|
|
} else {
|
|
Fw::AnchorEdge anchoredEdge = Fw::translateAnchorEdge(what);
|
|
|
|
if(node->value() == "none") {
|
|
removeAnchor(anchoredEdge);
|
|
} else {
|
|
std::vector<std::string> split = stdext::split(node->value(), ".");
|
|
if(split.size() != 2)
|
|
throw OTMLException(node, "invalid anchor description");
|
|
|
|
std::string hookedWidgetId = split[0];
|
|
Fw::AnchorEdge hookedEdge = Fw::translateAnchorEdge(split[1]);
|
|
|
|
if(anchoredEdge == Fw::AnchorNone)
|
|
throw OTMLException(node, "invalid anchor edge");
|
|
|
|
if(hookedEdge == Fw::AnchorNone)
|
|
throw OTMLException(node, "invalid anchor target edge");
|
|
|
|
addAnchor(anchoredEdge, hookedWidgetId, hookedEdge);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UIWidget::drawBackground(const Rect& screenCoords)
|
|
{
|
|
if(m_backgroundColor.aF() > 0.0f) {
|
|
Rect drawRect = screenCoords;
|
|
drawRect.translate(m_backgroundRect.topLeft());
|
|
if(m_backgroundRect.isValid())
|
|
drawRect.resize(m_backgroundRect.size());
|
|
g_painter->setColor(m_backgroundColor);
|
|
g_painter->drawFilledRect(drawRect);
|
|
}
|
|
}
|
|
|
|
void UIWidget::drawBorder(const Rect& screenCoords)
|
|
{
|
|
// top
|
|
if(m_borderWidth.top > 0) {
|
|
g_painter->setColor(m_borderColor.top);
|
|
|
|
Rect borderRect(screenCoords.topLeft(), screenCoords.width(), m_borderWidth.top);
|
|
g_painter->drawFilledRect(borderRect);
|
|
}
|
|
// right
|
|
if(m_borderWidth.right > 0) {
|
|
g_painter->setColor(m_borderColor.right);
|
|
|
|
Rect borderRect(screenCoords.topRight() - Point(m_borderWidth.right - 1, 0), m_borderWidth.right, screenCoords.height());
|
|
g_painter->drawFilledRect(borderRect);
|
|
}
|
|
// bottom
|
|
if(m_borderWidth.bottom > 0) {
|
|
g_painter->setColor(m_borderColor.bottom);
|
|
|
|
Rect borderRect(screenCoords.bottomLeft() - Point(0, m_borderWidth.bottom - 1), screenCoords.width(), m_borderWidth.bottom);
|
|
g_painter->drawFilledRect(borderRect);
|
|
}
|
|
// left
|
|
if(m_borderWidth.left > 0) {
|
|
g_painter->setColor(m_borderColor.left);
|
|
|
|
Rect borderRect(screenCoords.topLeft(), m_borderWidth.left, screenCoords.height());
|
|
g_painter->drawFilledRect(borderRect);
|
|
}
|
|
}
|
|
|
|
void UIWidget::drawIcon(const Rect& screenCoords)
|
|
{
|
|
if(m_icon) {
|
|
Rect drawRect;
|
|
if(m_iconRect.isValid()) {
|
|
drawRect = screenCoords;
|
|
drawRect.translate(m_iconRect.topLeft());
|
|
drawRect.resize(m_iconRect.size());
|
|
} else {
|
|
drawRect.resize(m_iconClipRect.size());
|
|
|
|
if(m_iconAlign == Fw::AlignNone)
|
|
drawRect.moveCenter(screenCoords.center());
|
|
else
|
|
drawRect.alignIn(screenCoords, m_iconAlign);
|
|
}
|
|
drawRect.translate(m_iconOffset);
|
|
g_painter->setColor(m_iconColor);
|
|
g_painter->drawTexturedRect(drawRect, m_icon, m_iconClipRect);
|
|
}
|
|
}
|
|
|
|
void UIWidget::setIcon(const std::string& iconFile)
|
|
{
|
|
if(iconFile.empty())
|
|
m_icon = nullptr;
|
|
else
|
|
m_icon = g_textures.getTexture(iconFile);
|
|
if(m_icon && !m_iconClipRect.isValid())
|
|
m_iconClipRect = Rect(0, 0, m_icon->getSize());
|
|
} |