mirror of
https://github.com/edubart/otclient.git
synced 2025-10-19 22:13:27 +02:00
merge total remake
This commit is contained in:
@@ -1,17 +1,12 @@
|
||||
#ifndef UI_H
|
||||
#define UI_H
|
||||
|
||||
#include <global.h>
|
||||
#include "uimanager.h"
|
||||
#include "uiwidget.h"
|
||||
#include "uibutton.h"
|
||||
#include "uilabel.h"
|
||||
#include "uilayout.h"
|
||||
#include "uilineedit.h"
|
||||
#include "uiwindow.h"
|
||||
|
||||
#include <ui/uiskins.h>
|
||||
#include <ui/uiloader.h>
|
||||
#include <ui/uielement.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include <ui/uicontainer.h>
|
||||
#include <ui/uibutton.h>
|
||||
#include <ui/uilabel.h>
|
||||
#include <ui/uiwindow.h>
|
||||
#include <ui/uitextedit.h>
|
||||
#include <ui/uicheckbox.h>
|
||||
|
||||
#endif // UI_H
|
||||
#endif
|
||||
|
44
src/framework/ui/uianchor.cpp
Normal file
44
src/framework/ui/uianchor.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "uianchor.h"
|
||||
#include "uiwidget.h"
|
||||
|
||||
UIAnchor::UIAnchor(const UIWidgetPtr& anchoredWidget, AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
|
||||
: m_anchoredWidget(anchoredWidget), m_anchoredEdge(anchoredEdge), m_anchorLine(anchorLine) {
|
||||
}
|
||||
|
||||
UIWidgetPtr UIAnchor::getAnchorLineWidget() const {
|
||||
if(!m_anchoredWidget.expired() && !m_anchoredWidget.lock()->isDestroyed())
|
||||
return m_anchoredWidget.lock()->backwardsGetWidgetById(m_anchorLine.widgetId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
UIWidgetPtr UIAnchor::getAnchoredWidget() const {
|
||||
return m_anchoredWidget.lock();
|
||||
}
|
||||
|
||||
AnchorPoint UIAnchor::getAnchoredEdge() const {
|
||||
return m_anchoredEdge;
|
||||
}
|
||||
|
||||
int UIAnchor::getAnchorLinePoint() const {
|
||||
UIWidgetPtr anchorLineWidget = getAnchorLineWidget();
|
||||
if(anchorLineWidget) {
|
||||
switch(m_anchorLine.edge) {
|
||||
case AnchorLeft:
|
||||
return anchorLineWidget->getGeometry().left();
|
||||
case AnchorRight:
|
||||
return anchorLineWidget->getGeometry().right();
|
||||
case AnchorTop:
|
||||
return anchorLineWidget->getGeometry().top();
|
||||
case AnchorBottom:
|
||||
return anchorLineWidget->getGeometry().bottom();
|
||||
case AnchorHorizontalCenter:
|
||||
return anchorLineWidget->getGeometry().horizontalCenter();
|
||||
case AnchorVerticalCenter:
|
||||
return anchorLineWidget->getGeometry().verticalCenter();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -9999;
|
||||
}
|
27
src/framework/ui/uianchor.h
Normal file
27
src/framework/ui/uianchor.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef UIANCHOR_H
|
||||
#define UIANCHOR_H
|
||||
|
||||
#include "uideclarations.h"
|
||||
|
||||
struct AnchorLine {
|
||||
AnchorLine(std::string widgetId, AnchorPoint edge) : widgetId(widgetId), edge(edge) { }
|
||||
std::string widgetId;
|
||||
AnchorPoint edge;
|
||||
};
|
||||
|
||||
class UIAnchor
|
||||
{
|
||||
public:
|
||||
UIAnchor(const UIWidgetPtr& anchoredWidget, AnchorPoint anchoredEdge, const AnchorLine& anchorLine);
|
||||
UIWidgetPtr getAnchorLineWidget() const;
|
||||
UIWidgetPtr getAnchoredWidget() const;
|
||||
AnchorPoint getAnchoredEdge() const;
|
||||
int getAnchorLinePoint() const;
|
||||
|
||||
private:
|
||||
UIWidgetWeakPtr m_anchoredWidget;
|
||||
AnchorPoint m_anchoredEdge;
|
||||
AnchorLine m_anchorLine;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,112 +1,84 @@
|
||||
#include <global.h>
|
||||
#include <ui/uianchorlayout.h>
|
||||
#include <ui/uielement.h>
|
||||
#include "uianchorlayout.h"
|
||||
#include "uiwidget.h"
|
||||
|
||||
UIElementPtr Anchor::getAnchorLineElement() const
|
||||
bool UIAnchorLayout::addAnchor(const UIWidgetPtr& anchoredWidget, AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
|
||||
{
|
||||
if(!m_anchoredElement.expired())
|
||||
return m_anchoredElement.lock()->backwardsGetElementById(m_anchorLine.getElementId());
|
||||
return UIElementPtr();
|
||||
}
|
||||
UIAnchor anchor(anchoredWidget, anchoredEdge, anchorLine);
|
||||
UIWidgetPtr anchorLineWidget = anchor.getAnchorLineWidget();
|
||||
|
||||
int Anchor::getAnchorLinePoint() const
|
||||
{
|
||||
UIElementPtr anchorLineElement = getAnchorLineElement();
|
||||
if(anchorLineElement) {
|
||||
switch(m_anchorLine.getEdge()) {
|
||||
case AnchorLeft:
|
||||
return anchorLineElement->getRect().left();
|
||||
case AnchorRight:
|
||||
return anchorLineElement->getRect().right();
|
||||
case AnchorTop:
|
||||
return anchorLineElement->getRect().top();
|
||||
case AnchorBottom:
|
||||
return anchorLineElement->getRect().bottom();
|
||||
case AnchorHorizontalCenter:
|
||||
return anchorLineElement->getRect().horizontalCenter();
|
||||
case AnchorVerticalCenter:
|
||||
return anchorLineElement->getRect().verticalCenter();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -9999;
|
||||
}
|
||||
bool UIAnchorLayout::addAnchor(const UIElementPtr& anchoredElement, AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
|
||||
{
|
||||
Anchor anchor(anchoredElement, anchoredEdge, anchorLine);
|
||||
UIElementPtr anchorLineElement = anchor.getAnchorLineElement();
|
||||
|
||||
if(!anchorLineElement) {
|
||||
logError("ERROR: could not find the element to anchor on, wrong id?");
|
||||
/*
|
||||
if(!anchorLineWidget) {
|
||||
logError("ERROR: could not find the widget to anchor on, wrong id?");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// we can never anchor with itself
|
||||
if(anchoredElement == anchorLineElement) {
|
||||
if(anchoredWidget == anchorLineWidget) {
|
||||
logError("ERROR: anchoring with itself is not possible");
|
||||
return false;
|
||||
}
|
||||
|
||||
// we must never anchor to an anchor child
|
||||
if(hasElementInAnchorTree(anchorLineElement, anchoredElement)) {
|
||||
logError("ERROR: anchors is miss configurated, you must never make anchor chains in loops");
|
||||
if(anchoredWidget && hasWidgetInAnchorTree(anchorLineWidget, anchoredWidget)) {
|
||||
logError("ERROR: anchors is miss configured, you must never make an anchor chains in loops");
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup the anchor
|
||||
m_anchors.push_back(anchor);
|
||||
|
||||
// recalculate anchored element layout
|
||||
recalculateElementLayout(anchoredElement);
|
||||
// recalculate anchored widget layout
|
||||
updateWidget(anchoredWidget);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UIAnchorLayout::recalculateElementLayout(const UIElementPtr& element)
|
||||
void UIAnchorLayout::updateWidget(const UIWidgetPtr& widget)
|
||||
{
|
||||
Rect rect = element->getRect();
|
||||
Rect rect = widget->getGeometry();
|
||||
bool verticalMoved = false;
|
||||
bool horizontalMoved = false;
|
||||
|
||||
foreach(const Anchor& anchor, m_anchors) {
|
||||
if(anchor.getAnchoredElement() == element && anchor.getAnchorLineElement()) {
|
||||
// TODO: remove expired anchors
|
||||
for(const UIAnchor& anchor : m_anchors) {
|
||||
if(anchor.getAnchoredWidget() == widget && anchor.getAnchorLineWidget()) {
|
||||
int point = anchor.getAnchorLinePoint();
|
||||
switch(anchor.getAnchoredEdge()) {
|
||||
case AnchorHorizontalCenter:
|
||||
rect.moveHorizontalCenter(point + element->getMarginLeft() - element->getMarginRight());
|
||||
rect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight());
|
||||
horizontalMoved = true;
|
||||
break;
|
||||
case AnchorLeft:
|
||||
if(!horizontalMoved) {
|
||||
rect.moveLeft(point + element->getMarginLeft());
|
||||
rect.moveLeft(point + widget->getMarginLeft());
|
||||
horizontalMoved = true;
|
||||
} else
|
||||
rect.setLeft(point + element->getMarginLeft());
|
||||
rect.setLeft(point + widget->getMarginLeft());
|
||||
break;
|
||||
case AnchorRight:
|
||||
if(!horizontalMoved) {
|
||||
rect.moveRight(point - element->getMarginRight());
|
||||
rect.moveRight(point - widget->getMarginRight());
|
||||
horizontalMoved = true;
|
||||
} else
|
||||
rect.setRight(point - element->getMarginRight());
|
||||
rect.setRight(point - widget->getMarginRight());
|
||||
break;
|
||||
case AnchorVerticalCenter:
|
||||
rect.moveVerticalCenter(point + element->getMarginTop() - element->getMarginBottom());
|
||||
rect.moveVerticalCenter(point + widget->getMarginTop() - widget->getMarginBottom());
|
||||
verticalMoved = true;
|
||||
break;
|
||||
case AnchorTop:
|
||||
if(!verticalMoved) {
|
||||
rect.moveTop(point + element->getMarginTop());
|
||||
rect.moveTop(point + widget->getMarginTop());
|
||||
verticalMoved = true;
|
||||
} else
|
||||
rect.setTop(point + element->getMarginTop());
|
||||
rect.setTop(point + widget->getMarginTop());
|
||||
break;
|
||||
case AnchorBottom:
|
||||
if(!verticalMoved) {
|
||||
rect.moveBottom(point - element->getMarginBottom());
|
||||
rect.moveBottom(point - widget->getMarginBottom());
|
||||
verticalMoved = true;
|
||||
} else
|
||||
rect.setBottom(point - element->getMarginBottom());
|
||||
rect.setBottom(point - widget->getMarginBottom());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -114,45 +86,29 @@ void UIAnchorLayout::recalculateElementLayout(const UIElementPtr& element)
|
||||
}
|
||||
}
|
||||
|
||||
if(rect != element->getRect())
|
||||
element->setRect(rect);
|
||||
if(rect != widget->getGeometry())
|
||||
widget->setGeometry(rect);
|
||||
}
|
||||
|
||||
void UIAnchorLayout::recalculateChildrenLayout(const UIElementPtr& parent)
|
||||
void UIAnchorLayout::updateWidgetChildren(const UIWidgetPtr& parent)
|
||||
{
|
||||
foreach(const Anchor& anchor, m_anchors) {
|
||||
if(anchor.getAnchorLineElement() == parent) {
|
||||
UIElementPtr child = anchor.getAnchoredElement();
|
||||
for(const UIAnchor& anchor : m_anchors) {
|
||||
if(anchor.getAnchorLineWidget() == parent) {
|
||||
UIWidgetPtr child = anchor.getAnchoredWidget();
|
||||
if(child)
|
||||
recalculateElementLayout(child);
|
||||
updateWidget(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UIAnchorLayout::hasElementInAnchorTree(const UIElementPtr& element, const UIElementPtr& treeAnchor)
|
||||
bool UIAnchorLayout::hasWidgetInAnchorTree(const UIWidgetPtr& widget, const UIWidgetPtr& treeAnchor)
|
||||
{
|
||||
foreach(const Anchor& anchor, m_anchors) {
|
||||
if(anchor.getAnchorLineElement() == treeAnchor) {
|
||||
if(anchor.getAnchoredElement() == element || hasElementInAnchorTree(element, anchor.getAnchoredElement()))
|
||||
for(const UIAnchor& anchor : m_anchors) {
|
||||
if(anchor.getAnchorLineWidget() == treeAnchor) {
|
||||
if(anchor.getAnchoredWidget() == widget || hasWidgetInAnchorTree(widget, anchor.getAnchoredWidget()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AnchorPoint UIAnchorLayout::parseAnchorPoint(const std::string& anchorPointStr)
|
||||
{
|
||||
if(anchorPointStr == "left")
|
||||
return AnchorLeft;
|
||||
else if(anchorPointStr == "right")
|
||||
return AnchorRight;
|
||||
else if(anchorPointStr == "top")
|
||||
return AnchorTop;
|
||||
else if(anchorPointStr == "bottom")
|
||||
return AnchorBottom;
|
||||
else if(anchorPointStr == "horizontalCenter")
|
||||
return AnchorHorizontalCenter;
|
||||
else if(anchorPointStr == "verticalCenter")
|
||||
return AnchorVerticalCenter;
|
||||
return AnchorNone;
|
||||
}
|
||||
|
@@ -1,66 +1,20 @@
|
||||
#ifndef UIANCHORLAYOUT_H
|
||||
#define UIANCHORLAYOUT_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uilayout.h>
|
||||
|
||||
class UIElement;
|
||||
typedef std::shared_ptr<UIElement> UIElementPtr;
|
||||
typedef std::weak_ptr<UIElement> UIElementWeakPtr;
|
||||
|
||||
enum AnchorPoint {
|
||||
AnchorNone = 0,
|
||||
AnchorTop,
|
||||
AnchorBottom,
|
||||
AnchorLeft,
|
||||
AnchorRight,
|
||||
AnchorVerticalCenter,
|
||||
AnchorHorizontalCenter,
|
||||
};
|
||||
|
||||
class AnchorLine
|
||||
{
|
||||
public:
|
||||
AnchorLine(const std::string& elementId, AnchorPoint edge) : m_elementId(elementId), m_edge(edge) { }
|
||||
AnchorLine(const AnchorLine& other) : m_elementId(other.m_elementId), m_edge(other.m_edge) { }
|
||||
AnchorPoint getEdge() const { return m_edge; }
|
||||
const std::string& getElementId() const { return m_elementId; }
|
||||
|
||||
private:
|
||||
std::string m_elementId;
|
||||
AnchorPoint m_edge;
|
||||
};
|
||||
|
||||
class Anchor
|
||||
{
|
||||
public:
|
||||
Anchor(const UIElementPtr& anchoredElement, AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
|
||||
: m_anchoredElement(anchoredElement), m_anchoredEdge(anchoredEdge), m_anchorLine(anchorLine) { }
|
||||
UIElementPtr getAnchorLineElement() const ;
|
||||
UIElementPtr getAnchoredElement() const { return m_anchoredElement.lock(); }
|
||||
AnchorPoint getAnchoredEdge() const { return m_anchoredEdge; }
|
||||
int getAnchorLinePoint() const;
|
||||
|
||||
private:
|
||||
UIElementWeakPtr m_anchoredElement;
|
||||
AnchorPoint m_anchoredEdge;
|
||||
AnchorLine m_anchorLine;
|
||||
};
|
||||
#include "uilayout.h"
|
||||
#include "uianchor.h"
|
||||
|
||||
class UIAnchorLayout : public UILayout
|
||||
{
|
||||
public:
|
||||
bool addAnchor(const UIElementPtr& anchoredElement, AnchorPoint anchoredEdge, const AnchorLine& anchorLine);
|
||||
void recalculateElementLayout(const UIElementPtr& element);
|
||||
void recalculateChildrenLayout(const UIElementPtr& parent);
|
||||
bool addAnchor(const UIWidgetPtr& anchoredWidget, AnchorPoint anchoredEdge, const AnchorLine& anchorLine);
|
||||
void updateWidget(const UIWidgetPtr& widget);
|
||||
void updateWidgetChildren(const UIWidgetPtr& parent);
|
||||
|
||||
bool hasElementInAnchorTree(const UIElementPtr& element, const UIElementPtr& treeAnchor);
|
||||
static AnchorPoint parseAnchorPoint(const std::string& anchorPointStr);
|
||||
bool hasWidgetInAnchorTree(const UIWidgetPtr& widget, const UIWidgetPtr& treeAnchor);
|
||||
|
||||
private:
|
||||
std::vector<Anchor> m_anchors;
|
||||
std::list<UIAnchor> m_anchors;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<UIAnchorLayout> UIAnchorLayoutPtr;
|
||||
|
||||
#endif // UIANCHORLAYOUT_H
|
||||
#endif
|
||||
|
@@ -1,24 +1,90 @@
|
||||
#include <global.h>
|
||||
#include <core/dispatcher.h>
|
||||
#include <ui/uibutton.h>
|
||||
#include "uicontainer.h"
|
||||
#include "uibutton.h"
|
||||
#include <graphics/borderimage.h>
|
||||
#include <graphics/font.h>
|
||||
#include <otml/otmlnode.h>
|
||||
#include <luascript/luainterface.h>
|
||||
|
||||
void UIButton::onInputEvent(const InputEvent& event)
|
||||
UIButton::UIButton(): UIWidget(UITypeButton)
|
||||
{
|
||||
if(event.type == EV_MOUSE_LDOWN && getRect().contains(event.mousePos)) {
|
||||
m_state = ButtonDown;
|
||||
} else if(event.type == EV_MOUSE_LUP && m_state == ButtonDown) {
|
||||
m_state = ButtonUp;
|
||||
if(getRect().contains(event.mousePos)) {
|
||||
LuaObjectPtr me = asLuaObject();
|
||||
g_dispatcher.addTask([me] {
|
||||
me->callField("onClick");
|
||||
});
|
||||
}
|
||||
} else if(event.type == EV_MOUSE_MOVE && m_state != ButtonDown) {
|
||||
if(isMouseOver())
|
||||
m_state = ButtonMouseOver;
|
||||
else
|
||||
m_state = ButtonUp;
|
||||
m_state = ButtonUp;
|
||||
|
||||
// by default, all callbacks call lua fields
|
||||
m_onClick = [this]() { this->callLuaField("onClick"); };
|
||||
}
|
||||
|
||||
UIButtonPtr UIButton::create()
|
||||
{
|
||||
UIButtonPtr button(new UIButton);
|
||||
button->setStyle("Button");
|
||||
return button;
|
||||
}
|
||||
|
||||
void UIButton::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||
{
|
||||
UIWidget::loadStyleFromOTML(styleNode);
|
||||
|
||||
for(int i=0; i<3; ++i) {
|
||||
m_statesStyle[i].image = getImage();
|
||||
m_statesStyle[i].color = getColor();
|
||||
m_statesStyle[i].textTranslate = Point(0,0);
|
||||
}
|
||||
|
||||
if(OTMLNodePtr node = styleNode->get("state.up"))
|
||||
loadStateStyle(m_statesStyle[ButtonUp], node);
|
||||
if(OTMLNodePtr node = styleNode->get("state.hover"))
|
||||
loadStateStyle(m_statesStyle[ButtonHover], node);
|
||||
if(OTMLNodePtr node = styleNode->get("state.down"))
|
||||
loadStateStyle(m_statesStyle[ButtonDown], node);
|
||||
|
||||
m_text = styleNode->readAt("text", aux::empty_string);
|
||||
|
||||
if(OTMLNodePtr node = styleNode->get("onClick")) {
|
||||
g_lua.loadFunction(node->read<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
|
||||
luaSetField("onClick");
|
||||
}
|
||||
}
|
||||
|
||||
void UIButton::loadStateStyle(ButtonStateStyle& stateStyle, const OTMLNodePtr& stateStyleNode)
|
||||
{
|
||||
if(OTMLNodePtr node = stateStyleNode->get("border-image"))
|
||||
stateStyle.image = BorderImage::loadFromOTML(node);
|
||||
if(OTMLNodePtr node = stateStyleNode->get("image"))
|
||||
stateStyle.image = Image::loadFromOTML(node);
|
||||
stateStyle.textTranslate = stateStyleNode->readAt("text-translate", Point());
|
||||
stateStyle.color = stateStyleNode->readAt("color", getColor());
|
||||
}
|
||||
|
||||
void UIButton::render()
|
||||
{
|
||||
const ButtonStateStyle& currentStyle = m_statesStyle[m_state];
|
||||
Rect textRect = getGeometry();
|
||||
|
||||
if(currentStyle.image)
|
||||
currentStyle.image->draw(textRect);
|
||||
|
||||
textRect.translate(currentStyle.textTranslate);
|
||||
getFont()->renderText(m_text, textRect, AlignCenter, currentStyle.color);
|
||||
}
|
||||
|
||||
void UIButton::onHoverChange(bool hovered)
|
||||
{
|
||||
if(hovered && m_state == ButtonUp)
|
||||
m_state = ButtonHover;
|
||||
else if(m_state == ButtonHover)
|
||||
m_state = ButtonUp;
|
||||
}
|
||||
|
||||
void UIButton::onMousePress(const UIMouseEvent& event)
|
||||
{
|
||||
if(event.button == MouseLeftButton)
|
||||
m_state = ButtonDown;
|
||||
}
|
||||
|
||||
void UIButton::onMouseRelease(const UIMouseEvent& event)
|
||||
{
|
||||
if(m_state == ButtonDown) {
|
||||
if(m_onClick && getGeometry().contains(event.mousePos))
|
||||
m_onClick();
|
||||
m_state = isHovered() ? ButtonHover : ButtonUp;
|
||||
}
|
||||
}
|
||||
|
@@ -1,41 +1,43 @@
|
||||
#ifndef UIBUTTON_H
|
||||
#define UIBUTTON_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uielement.h>
|
||||
#include <graphics/borderedimage.h>
|
||||
#include "uiwidget.h"
|
||||
|
||||
class UIButton;
|
||||
typedef std::shared_ptr<UIButton> UIButtonPtr;
|
||||
|
||||
class UIButton : public UIElement
|
||||
class UIButton : public UIWidget
|
||||
{
|
||||
public:
|
||||
enum ButtonState
|
||||
{
|
||||
ButtonUp = 0,
|
||||
ButtonDown,
|
||||
ButtonMouseOver
|
||||
struct ButtonStateStyle {
|
||||
ImagePtr image;
|
||||
Point textTranslate;
|
||||
Color color;
|
||||
};
|
||||
|
||||
UIButton() :
|
||||
UIElement(UI::Button),
|
||||
m_state(ButtonUp) { }
|
||||
public:
|
||||
UIButton();
|
||||
|
||||
static UIButtonPtr create() { return UIButtonPtr(new UIButton); }
|
||||
static UIButtonPtr create();
|
||||
|
||||
void onInputEvent(const InputEvent& event);
|
||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||
void loadStateStyle(ButtonStateStyle& stateStyle, const OTMLNodePtr& stateStyleNode);
|
||||
virtual void render();
|
||||
|
||||
void setOnClick(const SimpleCallback& onClick) { m_onClick = onClick; }
|
||||
void setText(const std::string& text) { m_text = text; }
|
||||
|
||||
SimpleCallback getOnClick() const { return m_onClick; }
|
||||
std::string getText() const { return m_text; }
|
||||
ButtonState getState() const { return m_state; }
|
||||
|
||||
ButtonState getState() { return m_state; }
|
||||
UIButtonPtr asUIButton() { return std::static_pointer_cast<UIButton>(shared_from_this()); }
|
||||
|
||||
virtual const char *getLuaTypeName() const { return "UIButton"; }
|
||||
protected:
|
||||
virtual void onHoverChange(bool hovered);
|
||||
virtual void onMousePress(const UIMouseEvent& event);
|
||||
virtual void onMouseRelease(const UIMouseEvent& event);
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
ButtonState m_state;
|
||||
ButtonStateStyle m_statesStyle[3];
|
||||
SimpleCallback m_onClick;
|
||||
std::string m_text;
|
||||
};
|
||||
|
||||
#endif // UIBUTTON_H
|
||||
#endif
|
||||
|
@@ -1,41 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <graphics/fonts.h>
|
||||
#include <ui/uibuttonskin.h>
|
||||
#include <ui/uibutton.h>
|
||||
|
||||
void UIButtonSkin::load(OTMLNode* node)
|
||||
{
|
||||
UIElementSkin::load(node);
|
||||
|
||||
UIButtonStateSkinPtr defaultStateSkin = loadStateSkin(node);
|
||||
m_statesSkin[UIButton::ButtonUp] = defaultStateSkin;
|
||||
m_statesSkin[UIButton::ButtonDown] = defaultStateSkin;
|
||||
m_statesSkin[UIButton::ButtonMouseOver] = defaultStateSkin;
|
||||
|
||||
if(OTMLNode* cnode = node->at("down state"))
|
||||
m_statesSkin[UIButton::ButtonDown] = loadStateSkin(cnode);
|
||||
|
||||
if(OTMLNode* cnode = node->at("hover state"))
|
||||
m_statesSkin[UIButton::ButtonMouseOver] = loadStateSkin(cnode);
|
||||
}
|
||||
|
||||
UIButtonStateSkinPtr UIButtonSkin::loadStateSkin(OTMLNode* node)
|
||||
{
|
||||
UIButtonStateSkinPtr stateSkin = UIButtonStateSkinPtr(new UIButtonStateSkin);
|
||||
stateSkin->image = loadImage(node);
|
||||
stateSkin->textTranslate = node->readAt("text translate", Point());
|
||||
stateSkin->textColor = node->readAt("font color", getFontColor());
|
||||
return stateSkin;
|
||||
}
|
||||
|
||||
void UIButtonSkin::draw(UIElement *element)
|
||||
{
|
||||
UIButton *button = static_cast<UIButton*>(element);
|
||||
UIButtonStateSkinPtr stateSkin = m_statesSkin[button->getState()];
|
||||
Rect textRect = button->getRect();
|
||||
|
||||
stateSkin->image->draw(element->getRect());
|
||||
textRect.translate(stateSkin->textTranslate);
|
||||
|
||||
getFont()->renderText(button->getText(), textRect, AlignCenter, stateSkin->textColor);
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
#ifndef UIBUTTONSKIN_H
|
||||
#define UIBUTTONSKIN_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uielementskin.h>
|
||||
|
||||
class Font;
|
||||
|
||||
struct UIButtonStateSkin {
|
||||
ImagePtr image;
|
||||
Point textTranslate;
|
||||
Color textColor;
|
||||
};
|
||||
typedef std::shared_ptr<UIButtonStateSkin> UIButtonStateSkinPtr;
|
||||
|
||||
class UIButtonSkin : public UIElementSkin
|
||||
{
|
||||
public:
|
||||
UIButtonSkin(const std::string& name) :
|
||||
UIElementSkin(name, UI::Button) { }
|
||||
|
||||
void load(OTMLNode* node);
|
||||
UIButtonStateSkinPtr loadStateSkin(OTMLNode* node);
|
||||
|
||||
void draw(UIElement *element);
|
||||
|
||||
private:
|
||||
UIButtonStateSkinPtr m_statesSkin[3];
|
||||
};
|
||||
|
||||
#endif // UIBUTTONSKIN_H
|
@@ -1,8 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <ui/uicheckbox.h>
|
||||
|
||||
UICheckBox::UICheckBox(UI::ElementType type): UIElement(type)
|
||||
{
|
||||
|
||||
}
|
||||
|
@@ -1,15 +0,0 @@
|
||||
#ifndef UICHECKBOX_H
|
||||
#define UICHECKBOX_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uielement.h>
|
||||
|
||||
class UICheckBox : public UIElement
|
||||
{
|
||||
public:
|
||||
UICheckBox(UI::ElementType type = UI::Element);
|
||||
|
||||
virtual const char *getLuaTypeName() const { return "UICheckBox"; }
|
||||
};
|
||||
|
||||
#endif // UICHECKBOX_H
|
@@ -1,3 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <ui/uicheckboxskin.h>
|
||||
|
@@ -1,11 +0,0 @@
|
||||
#ifndef UICHECKBOXSKIN_H
|
||||
#define UICHECKBOXSKIN_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uielementskin.h>
|
||||
|
||||
class UICheckBoxSkin : public UIElementSkin
|
||||
{
|
||||
};
|
||||
|
||||
#endif // UICHECKBOXSKIN_H
|
@@ -1,316 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <core/resources.h>
|
||||
#include <ui/uicontainer.h>
|
||||
#include <ui/uianchorlayout.h>
|
||||
#include <core/dispatcher.h>
|
||||
|
||||
void UIContainer::destroy()
|
||||
{
|
||||
//logTraceDebug(getId());
|
||||
|
||||
// clear additional references
|
||||
m_lockedElements.clear();
|
||||
m_focusedElement.reset();
|
||||
|
||||
// destroy children
|
||||
while(m_children.size() > 0) {
|
||||
UIElementPtr element = m_children.front(); //hold reference
|
||||
element->destroy();
|
||||
}
|
||||
|
||||
UIElement::destroy();
|
||||
}
|
||||
|
||||
UIContainerPtr& UIContainer::getRoot()
|
||||
{
|
||||
static UIContainerPtr rootContainer;
|
||||
if(!rootContainer) {
|
||||
rootContainer = UIContainerPtr(new UIContainer);
|
||||
rootContainer->setId("root");
|
||||
rootContainer->setLayout(UILayoutPtr(new UIAnchorLayout));
|
||||
}
|
||||
return rootContainer;
|
||||
}
|
||||
|
||||
void UIContainer::addChild(const UIElementPtr& child)
|
||||
{
|
||||
m_children.push_back(child);
|
||||
if(child->getParent() != asUIContainer())
|
||||
child->setParent(asUIContainer());
|
||||
}
|
||||
|
||||
void UIContainer::removeChild(const UIElementPtr& child)
|
||||
{
|
||||
// defocus if needed
|
||||
if(m_focusedElement == child)
|
||||
setFocusedElement(UIElementPtr());
|
||||
|
||||
// try to unlock
|
||||
unlockElement(child);
|
||||
|
||||
// remove from children list
|
||||
m_children.remove(child);
|
||||
|
||||
if(child->getParent() == asUIContainer())
|
||||
child->setParent(UIContainerPtr());
|
||||
}
|
||||
|
||||
bool UIContainer::hasChild(const UIElementPtr& child)
|
||||
{
|
||||
auto it = std::find(m_children.begin(), m_children.end(), child);
|
||||
if(it != m_children.end())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
UIElementPtr UIContainer::getChildById(const std::string& id)
|
||||
{
|
||||
if(getId() == id || id == "self")
|
||||
return asUIElement();
|
||||
else if(id == "parent")
|
||||
return getParent();
|
||||
else if(id == "root")
|
||||
return getRoot();
|
||||
else if(id == "prev") {
|
||||
if(UIContainerPtr parent = getParent())
|
||||
return parent->getChildBefore(asUIElement());
|
||||
} else if(id == "next") {
|
||||
if(UIContainerPtr parent = getParent())
|
||||
return parent->getChildAfter(asUIElement());
|
||||
} else {
|
||||
foreach(const UIElementPtr& child, m_children) {
|
||||
if(child->getId() == id)
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return UIElementPtr();
|
||||
}
|
||||
|
||||
UIElementPtr UIContainer::getChildBefore(const UIElementPtr& reference)
|
||||
{
|
||||
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
|
||||
const UIElementPtr& element = (*it);
|
||||
if(element == reference) {
|
||||
if(++it != m_children.rend())
|
||||
return (*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return UIElementPtr();
|
||||
}
|
||||
|
||||
UIElementPtr UIContainer::getChildAfter(const UIElementPtr& reference)
|
||||
{
|
||||
for(auto it = m_children.begin(); it != m_children.end(); ++it) {
|
||||
const UIElementPtr& element = (*it);
|
||||
if(element == reference) {
|
||||
if(++it != m_children.end())
|
||||
return (*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return UIElementPtr();
|
||||
}
|
||||
|
||||
UIElementPtr UIContainer::recursiveGetChildById(const std::string& id)
|
||||
{
|
||||
if(getId() == id || id == "self")
|
||||
return asUIElement();
|
||||
else if(id == "parent")
|
||||
return getParent();
|
||||
else if(id == "root")
|
||||
return getRoot();
|
||||
else if(id == "prev") {
|
||||
if(UIContainerPtr parent = getParent())
|
||||
return parent->getChildBefore(asUIElement());
|
||||
} else if(id == "next") {
|
||||
if(UIContainerPtr parent = getParent())
|
||||
return parent->getChildAfter(asUIElement());
|
||||
} else {
|
||||
foreach(const UIElementPtr& element, m_children) {
|
||||
if(element->getId() == id)
|
||||
return element;
|
||||
else {
|
||||
UIContainerPtr container = element->asUIContainer();
|
||||
if(container) {
|
||||
UIElementPtr element2 = container->recursiveGetChildById(id);
|
||||
if(element2)
|
||||
return element2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return UIElementPtr();
|
||||
}
|
||||
|
||||
UIElementPtr UIContainer::getChildByPos(const Point& pos)
|
||||
{
|
||||
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
|
||||
const UIElementPtr& element = (*it);
|
||||
if(element->getRect().contains(pos))
|
||||
return element;
|
||||
}
|
||||
|
||||
return UIElementPtr();
|
||||
}
|
||||
|
||||
UIElementPtr UIContainer::recursiveGetChildByPos(const Point& pos)
|
||||
{
|
||||
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
|
||||
const UIElementPtr& element = (*it);
|
||||
if(element->getRect().contains(pos)) {
|
||||
if(UIContainerPtr container = element->asUIContainer()) {
|
||||
if(UIElementPtr containerChild = container->recursiveGetChildByPos(pos))
|
||||
return containerChild;
|
||||
else
|
||||
return container;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
return UIElementPtr();
|
||||
}
|
||||
|
||||
void UIContainer::pushChildToTop(const UIElementPtr& child)
|
||||
{
|
||||
auto it = std::find(m_children.begin(), m_children.end(), child);
|
||||
if(it != m_children.end()) {
|
||||
m_children.erase(it);
|
||||
m_children.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void UIContainer::onLoad()
|
||||
{
|
||||
foreach(const UIElementPtr& child, m_children)
|
||||
child->onLoad();
|
||||
UIElement::onLoad();
|
||||
}
|
||||
|
||||
void UIContainer::render()
|
||||
{
|
||||
UIElement::render();
|
||||
foreach(const UIElementPtr& child, m_children) {
|
||||
if(child->isVisible())
|
||||
child->render();
|
||||
}
|
||||
}
|
||||
|
||||
void UIContainer::onInputEvent(const InputEvent& event)
|
||||
{
|
||||
UIElementPtr focusedElement = m_focusedElement;
|
||||
foreach(const UIElementPtr& child, m_children) {
|
||||
bool shouldFire = false;
|
||||
|
||||
// events should pass only when element is visible and enabled
|
||||
if(child->isEnabled() && child->isVisible()) {
|
||||
if(event.type & EV_KEYBOARD) {
|
||||
// keyboard events only go to focused elements or containers
|
||||
if(child->asUIContainer() || child == focusedElement) {
|
||||
shouldFire = true;
|
||||
}
|
||||
// mouse events
|
||||
} else if(event.type & EV_MOUSE) {
|
||||
// mouse down and wheel events only go to elements that contains the mouse position
|
||||
if(event.type & EV_DOWN || event.type & EV_MOUSE_WHEEL) {
|
||||
// the child must contains the mouse position and be on top
|
||||
if(child->getRect().contains(event.mousePos) && child == getChildByPos(event.mousePos)) {
|
||||
// focus it
|
||||
if(event.type == EV_MOUSE_LDOWN && child->isFocusable())
|
||||
setFocusedElement(child);
|
||||
shouldFire = true;
|
||||
}
|
||||
} else {
|
||||
shouldFire = true;
|
||||
|
||||
if(event.type == EV_MOUSE_MOVE) {
|
||||
if(child->getRect().contains(event.mousePos) && UIContainer::getRoot()->recursiveGetChildByPos(event.mousePos) == child)
|
||||
child->setMouseOver(true);
|
||||
else
|
||||
child->setMouseOver(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(shouldFire)
|
||||
child->onInputEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void UIContainer::focusNextElement()
|
||||
{
|
||||
UIElementPtr element;
|
||||
std::list<UIElementPtr> rotatedChildren(m_children);
|
||||
auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedElement);
|
||||
if(focusedIt != rotatedChildren.end()) {
|
||||
std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end());
|
||||
rotatedChildren.pop_front();
|
||||
foreach(const UIElementPtr& child, rotatedChildren) {
|
||||
if(child->isFocusable()) {
|
||||
element = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(element)
|
||||
setFocusedElement(element);
|
||||
}
|
||||
|
||||
void UIContainer::setFocusedElement(const UIElementPtr& focusedElement)
|
||||
{
|
||||
if(focusedElement != m_focusedElement) {
|
||||
UIElementPtr oldFocused = m_focusedElement;
|
||||
m_focusedElement = focusedElement;
|
||||
|
||||
if(oldFocused)
|
||||
oldFocused->onFocusChange();
|
||||
if(focusedElement)
|
||||
focusedElement->onFocusChange();
|
||||
}
|
||||
|
||||
// when containers are focused they go to the top
|
||||
if(focusedElement && focusedElement->asUIContainer()) {
|
||||
g_dispatcher.addTask(std::bind(&UIContainer::pushChildToTop, asUIContainer(), m_focusedElement));
|
||||
}
|
||||
}
|
||||
|
||||
bool UIContainer::lockElement(const UIElementPtr& element)
|
||||
{
|
||||
if(std::find(m_children.begin(), m_children.end(), element) != m_children.end()) {
|
||||
m_lockedElements.remove(element);
|
||||
m_lockedElements.push_front(element);
|
||||
foreach(const UIElementPtr& child, m_children) {
|
||||
if(child != element)
|
||||
child->setEnabled(false);
|
||||
else
|
||||
child->setEnabled(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UIContainer::unlockElement(const UIElementPtr& element)
|
||||
{
|
||||
auto it = std::find(m_lockedElements.begin(), m_lockedElements.end(), element);
|
||||
if(it != m_lockedElements.end()) {
|
||||
m_lockedElements.erase(it);
|
||||
UIElementPtr newLockedElement;
|
||||
if(m_lockedElements.size() > 0)
|
||||
newLockedElement = m_lockedElements.front();
|
||||
foreach(const UIElementPtr& child, m_children) {
|
||||
if(newLockedElement) {
|
||||
if(child == newLockedElement)
|
||||
child->setEnabled(true);
|
||||
else
|
||||
child->setEnabled(false);
|
||||
} else
|
||||
child->setEnabled(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
#ifndef UICONTAINER_H
|
||||
#define UICONTAINER_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uielement.h>
|
||||
|
||||
class UIContainer : public UIElement
|
||||
{
|
||||
public:
|
||||
UIContainer(UI::ElementType type = UI::Container) :
|
||||
UIElement(type) { }
|
||||
virtual ~UIContainer() { }
|
||||
|
||||
static UIContainerPtr create() { return UIContainerPtr(new UIContainer); }
|
||||
virtual void destroy();
|
||||
virtual void onLoad();
|
||||
virtual void render();
|
||||
virtual void onInputEvent(const InputEvent& event);
|
||||
|
||||
/// Add an element, this must never be called from events loops
|
||||
void addChild(const UIElementPtr& child);
|
||||
/// Remove an element, this must never be called from events loops
|
||||
void removeChild(const UIElementPtr& child);
|
||||
/// Check if has child
|
||||
bool hasChild(const UIElementPtr& child);
|
||||
/// Find an element in this container by id
|
||||
UIElementPtr getChildById(const std::string& id);
|
||||
/// Find an element in this container and in its children by id
|
||||
UIElementPtr recursiveGetChildById(const std::string& id);
|
||||
/// Find an element by position
|
||||
UIElementPtr getChildByPos(const Point& pos);
|
||||
UIElementPtr getChildBefore(const UIElementPtr& reference);
|
||||
UIElementPtr getChildAfter(const UIElementPtr& reference);
|
||||
/// Find an element in this container and in its children by position
|
||||
UIElementPtr recursiveGetChildByPos(const Point& pos);
|
||||
/// Get children
|
||||
const std::list<UIElementPtr>& getChildren() const { return m_children; }
|
||||
/// Pushs a child to the top
|
||||
void pushChildToTop(const UIElementPtr& child);
|
||||
/// Return number of children
|
||||
int getChildCount() const { return m_children.size(); }
|
||||
|
||||
|
||||
/// Disable all children except the specified element
|
||||
bool lockElement(const UIElementPtr& element);
|
||||
/// Renable all children
|
||||
bool unlockElement(const UIElementPtr& element);
|
||||
|
||||
/// Focus next element
|
||||
void focusNextElement();
|
||||
/// Focus element
|
||||
void setFocusedElement(const UIElementPtr& focusedElement);
|
||||
/// Get focused element
|
||||
UIElementPtr getFocusedElement() const { return m_focusedElement; }
|
||||
|
||||
virtual UI::ElementType getElementType() const { return UI::Container; }
|
||||
UIContainerPtr asUIContainer() { return std::static_pointer_cast<UIContainer>(shared_from_this()); }
|
||||
|
||||
virtual const char *getLuaTypeName() const { return "UIContainer"; }
|
||||
|
||||
/// Get root container (the container that contains everything)
|
||||
static UIContainerPtr& getRoot();
|
||||
|
||||
private:
|
||||
std::list<UIElementPtr> m_children;
|
||||
std::list<UIElementPtr> m_lockedElements;
|
||||
UIElementPtr m_focusedElement;
|
||||
};
|
||||
|
||||
#endif // UICONTAINER_H
|
36
src/framework/ui/uideclarations.h
Normal file
36
src/framework/ui/uideclarations.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef UIDECLARATIONS_H
|
||||
#define UIDECLARATIONS_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
class UIManager;
|
||||
class UILayout;
|
||||
class UIAnchorLayout;
|
||||
class UIStyle;
|
||||
class UIWidget;
|
||||
class UILabel;
|
||||
class UIButton;
|
||||
class UILineEdit;
|
||||
class UIWindow;
|
||||
|
||||
typedef std::shared_ptr<UIWidget> UIWidgetPtr;
|
||||
typedef std::weak_ptr<UIWidget> UIWidgetWeakPtr;
|
||||
typedef std::deque<UIWidgetPtr> UIWidgetList;
|
||||
|
||||
typedef std::shared_ptr<UILayout> UILayoutPtr;
|
||||
typedef std::shared_ptr<UIAnchorLayout> UIAnchorLayoutPtr;
|
||||
typedef std::shared_ptr<UIStyle> UIStylePtr;
|
||||
typedef std::shared_ptr<UILabel> UILabelPtr;
|
||||
typedef std::shared_ptr<UIButton> UIButtonPtr;
|
||||
typedef std::shared_ptr<UILineEdit> UILineEditPtr;
|
||||
typedef std::shared_ptr<UIWindow> UIWindowPtr;
|
||||
|
||||
enum UIWidgetType {
|
||||
UITypeWidget = 0,
|
||||
UITypeLabel,
|
||||
UITypeButton,
|
||||
UITypeLineEdit,
|
||||
UITypeWindow
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,233 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <core/dispatcher.h>
|
||||
#include <graphics/graphics.h>
|
||||
#include <ui/uielement.h>
|
||||
#include <ui/uiskins.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include <ui/uicontainer.h>
|
||||
#include <ui/uianchorlayout.h>
|
||||
#include <script/luainterface.h>
|
||||
|
||||
UIElement::UIElement(UI::ElementType type) :
|
||||
LuaObject(),
|
||||
m_type(type),
|
||||
m_visible(true),
|
||||
m_enabled(true),
|
||||
m_mouseOver(false),
|
||||
m_destroyed(false),
|
||||
m_marginLeft(0),
|
||||
m_marginRight(0),
|
||||
m_marginTop(0),
|
||||
m_marginBottom(0)
|
||||
{
|
||||
// generate an unique id, this is need because anchoed layouts find elements by id
|
||||
static unsigned long id = 1;
|
||||
m_id = make_string("element", id++);
|
||||
}
|
||||
|
||||
UIElement::~UIElement()
|
||||
{
|
||||
//logTraceDebug(getId());
|
||||
}
|
||||
|
||||
void UIElement::destroyLater()
|
||||
{
|
||||
//logTraceDebug(getId());
|
||||
if(!m_destroyed)
|
||||
g_dispatcher.addTask(std::bind(&UIElement::destroy, asUIElement()));
|
||||
}
|
||||
|
||||
void UIElement::destroy()
|
||||
{
|
||||
//logTraceDebug(getId());
|
||||
if(!m_destroyed) {
|
||||
UIElementPtr me = asUIElement();
|
||||
|
||||
// remove from parent
|
||||
if(getParent())
|
||||
getParent()->removeChild(me);
|
||||
|
||||
g_dispatcher.addTask(std::bind(&UIElement::destroyCheck, me));
|
||||
|
||||
m_destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UIElement::destroyCheck()
|
||||
{
|
||||
//logTraceDebug(getId());
|
||||
|
||||
for(int i=0;i<2;++i)
|
||||
g_lua.collectGarbage();
|
||||
|
||||
UIElementPtr me = asUIElement();
|
||||
// check for leaks, the number of references must be always 2 here
|
||||
if(me.use_count() != 2 && me != UIContainer::getRoot()) {
|
||||
logWarning("destroyed element with id '",getId(),"', but it still have ",(me.use_count()-2)," references left");
|
||||
}
|
||||
}
|
||||
|
||||
void UIElement::setSize(const Size& size)
|
||||
{
|
||||
Rect rect = getRect();
|
||||
rect.setSize(size);
|
||||
setRect(rect);
|
||||
|
||||
if(UILayoutPtr layout = getLayout())
|
||||
layout->recalculateElementLayout(asUIElement());
|
||||
}
|
||||
|
||||
void UIElement::setRect(const Rect& rect)
|
||||
{
|
||||
if(rect != m_rect) {
|
||||
m_rect = rect;
|
||||
|
||||
// rect updated, recalculate children layout
|
||||
if(UILayoutPtr layout = getLayout())
|
||||
layout->recalculateChildrenLayout(asUIElement());
|
||||
|
||||
onRectUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void UIElement::applyDefaultSkin()
|
||||
{
|
||||
setSkin(g_uiSkins.getElementSkin(getElementType(), "default"));
|
||||
}
|
||||
|
||||
void UIElement::setSkin(const UIElementSkinPtr& skin)
|
||||
{
|
||||
m_skin = skin;
|
||||
if(skin)
|
||||
skin->apply(this);
|
||||
}
|
||||
|
||||
void UIElement::onLoad()
|
||||
{
|
||||
UIElementPtr me = asUIElement();
|
||||
g_dispatcher.addTask([me] {
|
||||
me->callField("onLoad");
|
||||
});
|
||||
}
|
||||
|
||||
void UIElement::render()
|
||||
{
|
||||
if(m_skin)
|
||||
m_skin->draw(this);
|
||||
//g_graphics.drawBoundingRect(getRect());
|
||||
}
|
||||
|
||||
UIElementPtr UIElement::backwardsGetElementById(const std::string& id)
|
||||
{
|
||||
UIElementPtr element;
|
||||
if(getId() == id || id == "self")
|
||||
element = asUIElement();
|
||||
else if(id == "parent")
|
||||
element = getParent();
|
||||
else if(id == "root")
|
||||
element = UIContainer::getRoot();
|
||||
else if(id == "prev") {
|
||||
if(UIContainerPtr parent = getParent())
|
||||
element = parent->getChildBefore(asUIElement());
|
||||
} else if(id == "next") {
|
||||
if(UIContainerPtr parent = getParent())
|
||||
element = parent->getChildAfter(asUIElement());
|
||||
} else {
|
||||
if(asUIContainer()) {
|
||||
element = asUIContainer()->recursiveGetChildById(id);
|
||||
if(element)
|
||||
return element;
|
||||
}
|
||||
|
||||
if(getParent())
|
||||
element = getParent()->backwardsGetElementById(id);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
void UIElement::moveTo(Point pos)
|
||||
{
|
||||
Rect newRect = getRect();
|
||||
newRect.moveTo(pos);
|
||||
|
||||
// bound newRect to parent rect
|
||||
UIContainerPtr parent = getParent();
|
||||
if(parent) {
|
||||
Rect parentRect = parent->getRect();
|
||||
if(newRect.left() < parentRect.left())
|
||||
newRect.moveLeft(parentRect.left());
|
||||
if(newRect.top() < parentRect.top())
|
||||
newRect.moveTop(parentRect.top());
|
||||
if(newRect.bottom() > parentRect.bottom())
|
||||
newRect.moveBottom(parentRect.bottom());
|
||||
if(newRect.right() > parentRect.right())
|
||||
newRect.moveRight(parentRect.right());
|
||||
}
|
||||
setRect(newRect);
|
||||
}
|
||||
|
||||
void UIElement::setLocked(bool locked)
|
||||
{
|
||||
UIContainerPtr parent = getParent();
|
||||
if(parent) {
|
||||
if(locked)
|
||||
parent->lockElement(asUIElement());
|
||||
else
|
||||
parent->unlockElement(asUIElement());
|
||||
}
|
||||
}
|
||||
|
||||
void UIElement::setParent(UIContainerPtr parent)
|
||||
{
|
||||
UIElementPtr me = asUIElement();
|
||||
UIContainerPtr oldParent = m_parent.lock();
|
||||
m_parent.reset();
|
||||
if(oldParent && oldParent->hasChild(me)) {
|
||||
oldParent->removeChild(me);
|
||||
}
|
||||
if(parent) {
|
||||
m_parent = UIContainerWeakPtr(parent);
|
||||
if(!parent->hasChild(me))
|
||||
parent->addChild(me);
|
||||
}
|
||||
}
|
||||
|
||||
bool UIElement::isFocused() const
|
||||
{
|
||||
if(UIContainerPtr parent = m_parent.lock())
|
||||
return (parent->getFocusedElement() == shared_from_this());
|
||||
return false;
|
||||
}
|
||||
|
||||
void UIElement::setFocused(bool focused)
|
||||
{
|
||||
if(UIContainerPtr parent = m_parent.lock()) {
|
||||
if(focused) {
|
||||
parent->setFocusedElement(asUIElement());
|
||||
} else if(parent->getFocusedElement() == asUIElement()) {
|
||||
parent->setFocusedElement(UIElementPtr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UILayoutPtr UIElement::getLayout() const
|
||||
{
|
||||
if(m_layout)
|
||||
return m_layout;
|
||||
else if(getParent())
|
||||
return getParent()->getLayout();
|
||||
return UILayoutPtr();
|
||||
}
|
||||
|
||||
void UIElement::centerIn(const std::string& targetId)
|
||||
{
|
||||
addAnchor(AnchorHorizontalCenter, targetId, AnchorHorizontalCenter);
|
||||
addAnchor(AnchorVerticalCenter, targetId, AnchorVerticalCenter);
|
||||
}
|
||||
|
||||
void UIElement::addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge)
|
||||
{
|
||||
UIAnchorLayoutPtr layout = std::dynamic_pointer_cast<UIAnchorLayout>(getLayout());
|
||||
if(layout)
|
||||
layout->addAnchor(asUIElement(), edge, AnchorLine(targetId, targetEdge));
|
||||
}
|
@@ -1,141 +0,0 @@
|
||||
#ifndef UIELEMENT_H
|
||||
#define UIELEMENT_H
|
||||
|
||||
#include <global.h>
|
||||
#include <core/input.h>
|
||||
#include <script/luaobject.h>
|
||||
#include <ui/uianchorlayout.h>
|
||||
|
||||
namespace UI {
|
||||
enum ElementType {
|
||||
Element = 0,
|
||||
Container,
|
||||
Panel,
|
||||
Window,
|
||||
Label,
|
||||
TextEdit,
|
||||
Button,
|
||||
CheckBox,
|
||||
LineDecoration
|
||||
};
|
||||
}
|
||||
|
||||
class UIElementSkin;
|
||||
typedef std::shared_ptr<UIElementSkin> UIElementSkinPtr;
|
||||
|
||||
class UIContainer;
|
||||
typedef std::shared_ptr<UIContainer> UIContainerPtr;
|
||||
typedef std::weak_ptr<UIContainer> UIContainerWeakPtr;
|
||||
|
||||
class UIElement;
|
||||
typedef std::shared_ptr<UIElement> UIElementPtr;
|
||||
typedef std::weak_ptr<UIElement> UIElementWeakPtr;
|
||||
|
||||
class UIElement : public LuaObject
|
||||
{
|
||||
public:
|
||||
UIElement(UI::ElementType type = UI::Element);
|
||||
virtual ~UIElement();
|
||||
|
||||
static UIElementPtr create() { return UIElementPtr(new UIElement); }
|
||||
void destroyLater();
|
||||
virtual void destroy();
|
||||
virtual void destroyCheck();
|
||||
|
||||
/// Draw element
|
||||
virtual void render();
|
||||
|
||||
// events
|
||||
virtual void onLoad();
|
||||
virtual void onInputEvent(const InputEvent& event) { }
|
||||
virtual void onFocusChange() { }
|
||||
virtual void onRectUpdate() { }
|
||||
|
||||
UIElementPtr backwardsGetElementById(const std::string& id);
|
||||
|
||||
void moveTo(Point pos);
|
||||
|
||||
void setLocked(bool locked);
|
||||
|
||||
void setLayout(const UILayoutPtr& layout) { m_layout = layout; }
|
||||
UILayoutPtr getLayout() const;
|
||||
|
||||
void applyDefaultSkin();
|
||||
void setSkin(const UIElementSkinPtr& skin);
|
||||
UIElementSkinPtr getSkin() const { return m_skin; }
|
||||
|
||||
void setParent(UIContainerPtr parent);
|
||||
UIContainerPtr getParent() const { return m_parent.lock(); }
|
||||
|
||||
void setId(const std::string& id) { m_id = id; }
|
||||
const std::string& getId() const { return m_id; }
|
||||
|
||||
void setEnabled(bool enabled) { m_enabled = enabled; }
|
||||
bool isEnabled() const { return m_enabled; }
|
||||
|
||||
void setFocused(bool focused);
|
||||
bool isFocused() const;
|
||||
|
||||
void setMouseOver(bool mouseOver) { m_mouseOver = mouseOver; }
|
||||
bool isMouseOver() const { return m_mouseOver; }
|
||||
|
||||
void setVisible(bool visible) { m_visible = visible; }
|
||||
bool isVisible() const { return m_visible; }
|
||||
|
||||
virtual bool isFocusable() const { return false; }
|
||||
UI::ElementType getElementType() const { return m_type; }
|
||||
|
||||
UIElementPtr asUIElement() { return std::static_pointer_cast<UIElement>(shared_from_this()); }
|
||||
virtual UIContainerPtr asUIContainer() { return UIContainerPtr(); }
|
||||
virtual const char *getLuaTypeName() const { return "UIElement"; }
|
||||
|
||||
void setSize(const Size& size);
|
||||
void setSize(int width, int height) { setSize(Size(width, height)); }
|
||||
Size getSize() const { return m_rect.size(); }
|
||||
|
||||
void setWidth(int width) { setSize(width, getSize().height()); }
|
||||
int getWidth() const { return getSize().width(); }
|
||||
|
||||
void setHeight(int height) { setSize(getSize().width(), height); }
|
||||
int getHeight() const { return getSize().height(); }
|
||||
|
||||
/// Set the layout rect, always absolute position
|
||||
void setRect(const Rect& rect);
|
||||
/// Get layout size, it always return the absolute position
|
||||
Rect getRect() const { return m_rect; }
|
||||
|
||||
void setMargin(int top, int right, int bottom, int left) { m_marginLeft = left; m_marginRight = right; m_marginTop = top; m_marginBottom = bottom; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
void setMargin(int vertical, int horizontal) { m_marginLeft = m_marginRight = horizontal; m_marginTop = m_marginBottom = vertical; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
void setMargin(int margin) { m_marginLeft = m_marginRight = m_marginTop = m_marginBottom = margin; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
void setMarginLeft(int margin) { m_marginLeft = margin; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
void setMarginRight(int margin) { m_marginRight = margin; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
void setMarginTop(int margin) { m_marginTop = margin; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
void setMarginBottom(int margin) { m_marginBottom = margin; getLayout()->recalculateElementLayout(asUIElement()); }
|
||||
|
||||
int getMarginLeft() const { return m_marginLeft; }
|
||||
int getMarginRight() const { return m_marginRight; }
|
||||
int getMarginTop() const { return m_marginTop; }
|
||||
int getMarginBottom() const { return m_marginBottom; }
|
||||
|
||||
void centerIn(const std::string& targetId);
|
||||
void addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge);
|
||||
|
||||
private:
|
||||
UI::ElementType m_type;
|
||||
UIContainerWeakPtr m_parent;
|
||||
UIElementSkinPtr m_skin;
|
||||
UILayoutPtr m_layout;
|
||||
std::string m_id;
|
||||
bool m_visible;
|
||||
bool m_enabled;
|
||||
bool m_mouseOver;
|
||||
bool m_destroyed;
|
||||
|
||||
Rect m_rect;
|
||||
int m_marginLeft;
|
||||
int m_marginRight;
|
||||
int m_marginTop;
|
||||
int m_marginBottom;
|
||||
};
|
||||
|
||||
#endif // UIELEMENT_H
|
@@ -1,68 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <ui/uiskins.h>
|
||||
#include <ui/uielement.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include <graphics/borderedimage.h>
|
||||
#include <graphics/textures.h>
|
||||
#include <graphics/fonts.h>
|
||||
|
||||
void UIElementSkin::load(OTMLNode* node)
|
||||
{
|
||||
m_defaultSize = node->readAt("default size", Size());
|
||||
m_defaultImage = loadImage(node);
|
||||
m_font = loadFont(node);
|
||||
m_fontColor = node->readAt("font color", g_uiSkins.getDefaultFontColor());
|
||||
}
|
||||
|
||||
void UIElementSkin::apply(UIElement* element)
|
||||
{
|
||||
if(!element->getRect().isValid() && m_defaultSize.isValid())
|
||||
element->setSize(m_defaultSize);
|
||||
}
|
||||
|
||||
void UIElementSkin::draw(UIElement *element)
|
||||
{
|
||||
if(m_defaultImage)
|
||||
m_defaultImage->draw(element->getRect());
|
||||
}
|
||||
|
||||
ImagePtr UIElementSkin::loadImage(OTMLNode* node)
|
||||
{
|
||||
ImagePtr image;
|
||||
TexturePtr texture;
|
||||
|
||||
if(OTMLNode* cnode = node->at("bordered image")) {
|
||||
image = BorderedImage::loadFromOTMLNode(cnode, g_uiSkins.getDefaultTexture());
|
||||
if(!image)
|
||||
logError(node->generateErrorMessage("failed to load bordered image"));
|
||||
} else if(OTMLNode* cnode = node->at("image")) {
|
||||
texture = g_textures.get(cnode->value());
|
||||
if(texture)
|
||||
image = ImagePtr(new Image(texture));
|
||||
if(!m_defaultSize.isValid())
|
||||
m_defaultSize = texture->getSize();
|
||||
|
||||
if(!image)
|
||||
logError(cnode->generateErrorMessage("failed to load image"));
|
||||
}
|
||||
|
||||
if(texture) {
|
||||
bool antialised = node->readAt("antialised", false);
|
||||
if(antialised)
|
||||
texture->enableBilinearFilter();
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
FontPtr UIElementSkin::loadFont(OTMLNode* node)
|
||||
{
|
||||
FontPtr font;
|
||||
if(OTMLNode* cnode = node->at("font"))
|
||||
font = g_fonts.get(cnode->value());
|
||||
if(!font)
|
||||
font = g_uiSkins.getDefaultFont();
|
||||
return font;
|
||||
}
|
||||
|
||||
|
@@ -1,48 +0,0 @@
|
||||
#ifndef UIELEMENTSKIN_H
|
||||
#define UIELEMENTSKIN_H
|
||||
|
||||
#include <global.h>
|
||||
#include <graphics/image.h>
|
||||
#include <ui/uielement.h>
|
||||
#include <graphics/font.h>
|
||||
#include <otml/otmlnode.h>
|
||||
|
||||
class UIElementSkin
|
||||
{
|
||||
public:
|
||||
UIElementSkin(const std::string& name, UI::ElementType elementType) :
|
||||
m_name(name),
|
||||
m_elementType(elementType) { }
|
||||
UIElementSkin() : m_elementType(UI::Element) { }
|
||||
virtual ~UIElementSkin() { }
|
||||
|
||||
/// Load the skin from a FML node
|
||||
virtual void load(OTMLNode* node);
|
||||
/// Apply the skin to an element
|
||||
virtual void apply(UIElement *element);
|
||||
/// Draw the skinned element
|
||||
virtual void draw(UIElement *element);
|
||||
|
||||
const std::string& getName() const { return m_name; }
|
||||
const Size& getDefaultSize() const { return m_defaultSize; }
|
||||
UI::ElementType getElementType() const { return m_elementType; }
|
||||
ImagePtr getDefaultImage() const { return m_defaultImage; }
|
||||
FontPtr getFont() const { return m_font; }
|
||||
Color getFontColor() const { return m_fontColor; }
|
||||
|
||||
protected:
|
||||
ImagePtr loadImage(OTMLNode* node);
|
||||
FontPtr loadFont(OTMLNode* node);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
UI::ElementType m_elementType;
|
||||
Size m_defaultSize;
|
||||
ImagePtr m_defaultImage;
|
||||
FontPtr m_font;
|
||||
Color m_fontColor;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<UIElementSkin> UIElementSkinPtr;
|
||||
|
||||
#endif // UIELEMENTSKIN_H
|
18
src/framework/ui/uievent.h
Normal file
18
src/framework/ui/uievent.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef UIEVENT_H
|
||||
#define UIEVENT_H
|
||||
|
||||
#include <global.h>
|
||||
|
||||
struct UIMouseEvent {
|
||||
Point mousePos;
|
||||
Point mouseMoved;
|
||||
MouseButton button;
|
||||
MouseWheelDirection wheelDirection;
|
||||
};
|
||||
|
||||
struct UIKeyEvent {
|
||||
uchar keycode;
|
||||
int keyboardModifiers;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,11 +1,39 @@
|
||||
#include <global.h>
|
||||
#include <ui/uilabel.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include "uilabel.h"
|
||||
#include <graphics/font.h>
|
||||
#include <otml/otmlnode.h>
|
||||
|
||||
void UILabel::setText(const std::string& text)
|
||||
UILabel::UILabel() : UIWidget(UITypeLabel)
|
||||
{
|
||||
m_text = text;
|
||||
// text size changed, reaplly skin
|
||||
if(getSkin())
|
||||
getSkin()->apply(this);
|
||||
m_align = AlignLeft;
|
||||
}
|
||||
|
||||
UILabelPtr UILabel::create()
|
||||
{
|
||||
UILabelPtr label(new UILabel);
|
||||
label->setStyle("Label");
|
||||
return label;
|
||||
}
|
||||
|
||||
void UILabel::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||
{
|
||||
UIWidget::loadStyleFromOTML(styleNode);
|
||||
|
||||
m_text = styleNode->readAt("text", m_text);
|
||||
|
||||
if(styleNode->hasChild("align"))
|
||||
m_align = parseAlignment(styleNode->readAt<std::string>("align"));
|
||||
|
||||
// auto resize if no size supplied
|
||||
if(!m_text.empty() && !getGeometry().isValid())
|
||||
resizeToText();
|
||||
}
|
||||
|
||||
void UILabel::render()
|
||||
{
|
||||
getFont()->renderText(m_text, getGeometry(), m_align, getColor());
|
||||
}
|
||||
|
||||
void UILabel::resizeToText()
|
||||
{
|
||||
resize(getFont()->calculateTextRectSize(m_text));
|
||||
}
|
||||
|
@@ -1,35 +1,29 @@
|
||||
#ifndef UILABEL_H
|
||||
#define UILABEL_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uielement.h>
|
||||
#include <graphics/font.h>
|
||||
#include "uiwidget.h"
|
||||
|
||||
class UILabel;
|
||||
typedef std::shared_ptr<UILabel> UILabelPtr;
|
||||
|
||||
class UILabel : public UIElement
|
||||
class UILabel : public UIWidget
|
||||
{
|
||||
public:
|
||||
UILabel() :
|
||||
UIElement(UI::Label),
|
||||
m_align(AlignLeftCenter) { }
|
||||
UILabel();
|
||||
|
||||
static UILabelPtr create() { return UILabelPtr(new UILabel); }
|
||||
static UILabelPtr create();
|
||||
|
||||
void setText(const std::string& text);
|
||||
std::string getText() const { return m_text; }
|
||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||
virtual void render();
|
||||
|
||||
void resizeToText();
|
||||
|
||||
void setText(const std::string& text) { m_text = text; }
|
||||
void setAlign(AlignmentFlag align) { m_align = align; }
|
||||
AlignmentFlag getAlign() const { return m_align; }
|
||||
|
||||
virtual const char *getLuaTypeName() const { return "UILabel"; }
|
||||
std::string getText() const { return m_text; }
|
||||
AlignmentFlag getAlign() const { return m_align; }
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
AlignmentFlag m_align;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<UILabel> UILabelPtr;
|
||||
|
||||
#endif // UILABEL_H
|
||||
#endif
|
||||
|
@@ -1,23 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <graphics/fonts.h>
|
||||
#include <ui/uilabelskin.h>
|
||||
#include <ui/uilabel.h>
|
||||
|
||||
void UILabelSkin::load(OTMLNode* node)
|
||||
{
|
||||
UIElementSkin::load(node);
|
||||
}
|
||||
|
||||
void UILabelSkin::apply(UIElement* element)
|
||||
{
|
||||
UIElementSkin::apply(element);
|
||||
UILabel *label = static_cast<UILabel*>(element);
|
||||
label->setSize(getFont()->calculateTextRectSize(label->getText()));
|
||||
}
|
||||
|
||||
void UILabelSkin::draw(UIElement *element)
|
||||
{
|
||||
UIElementSkin::draw(element);
|
||||
UILabel *label = static_cast<UILabel*>(element);
|
||||
getFont()->renderText(label->getText(), label->getRect(), label->getAlign(), getFontColor());
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
#ifndef UILABELSKIN_H
|
||||
#define UILABELSKIN_H
|
||||
|
||||
#include <global.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/uielementskin.h>
|
||||
|
||||
class UILabelSkin : public UIElementSkin
|
||||
{
|
||||
public:
|
||||
UILabelSkin(const std::string& name) :
|
||||
UIElementSkin(name, UI::Label) { }
|
||||
|
||||
void load(OTMLNode* node);
|
||||
void apply(UIElement *element);
|
||||
void draw(UIElement *element);
|
||||
};
|
||||
|
||||
#endif // UILABELSKIN_H
|
2
src/framework/ui/uilayout.cpp
Normal file
2
src/framework/ui/uilayout.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "uilayout.h"
|
||||
|
@@ -1,21 +1,13 @@
|
||||
#ifndef UILAYOUT_H
|
||||
#define UILAYOUT_H
|
||||
|
||||
#include <global.h>
|
||||
#include "uideclarations.h"
|
||||
|
||||
class UIElement;
|
||||
typedef std::shared_ptr<UIElement> UIElementPtr;
|
||||
|
||||
class UILayout;
|
||||
typedef std::shared_ptr<UILayout> UILayoutPtr;
|
||||
|
||||
class UILayout : public std::enable_shared_from_this<UILayout>
|
||||
class UILayout
|
||||
{
|
||||
public:
|
||||
UILayout() { }
|
||||
|
||||
virtual void recalculateElementLayout(const UIElementPtr& element) = 0;
|
||||
virtual void recalculateChildrenLayout(const UIElementPtr& parent) = 0;
|
||||
virtual void updateWidget(const UIWidgetPtr& widget) = 0;
|
||||
virtual void updateWidgetChildren(const UIWidgetPtr& widget) = 0;
|
||||
};
|
||||
|
||||
#endif // UILAYOUT_H
|
||||
#endif
|
||||
|
34
src/framework/ui/uilineedit.cpp
Normal file
34
src/framework/ui/uilineedit.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "uilineedit.h"
|
||||
#include <graphics/font.h>
|
||||
#include <otml/otmlnode.h>
|
||||
|
||||
UILineEdit::UILineEdit() : UIWidget(UITypeLabel)
|
||||
{
|
||||
}
|
||||
|
||||
UILineEditPtr UILineEdit::create()
|
||||
{
|
||||
UILineEditPtr lineEdit(new UILineEdit);
|
||||
lineEdit->setStyle("LineEdit");
|
||||
return lineEdit;
|
||||
}
|
||||
|
||||
void UILineEdit::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||
{
|
||||
UIWidget::loadStyleFromOTML(styleNode);
|
||||
|
||||
m_textArea.setFont(getFont());
|
||||
m_textArea.setColor(getColor());
|
||||
setText(styleNode->readAt("text", getText()));
|
||||
}
|
||||
|
||||
void UILineEdit::render()
|
||||
{
|
||||
UIWidget::render();
|
||||
m_textArea.draw();
|
||||
}
|
||||
|
||||
void UILineEdit::onGeometryUpdate()
|
||||
{
|
||||
m_textArea.setScreenCoords(getGeometry());
|
||||
}
|
28
src/framework/ui/uilineedit.h
Normal file
28
src/framework/ui/uilineedit.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef UILINEEDIT_H
|
||||
#define UILINEEDIT_H
|
||||
|
||||
#include "uiwidget.h"
|
||||
#include <graphics/textarea.h>
|
||||
|
||||
class UILineEdit : public UIWidget
|
||||
{
|
||||
public:
|
||||
UILineEdit();
|
||||
|
||||
static UILineEditPtr create();
|
||||
|
||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||
virtual void render();
|
||||
|
||||
void setText(const std::string& text) { m_textArea.setText(text); }
|
||||
std::string getText() const { return m_textArea.getText(); }
|
||||
|
||||
protected:
|
||||
virtual void onGeometryUpdate();
|
||||
|
||||
private:
|
||||
//TODO: move textarea to here
|
||||
TextArea m_textArea;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,260 +0,0 @@
|
||||
#include <global.h>
|
||||
#include <core/resources.h>
|
||||
#include <ui/ui.h>
|
||||
#include <ui/uiloader.h>
|
||||
#include <script/luainterface.h>
|
||||
#include <otml/otml.h>
|
||||
#include <ui/uianchorlayout.h>
|
||||
#include <util/translator.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
UILoader g_uiLoader;
|
||||
|
||||
UIElementPtr UILoader::createElementFromId(const std::string& id)
|
||||
{
|
||||
UIElementPtr element;
|
||||
|
||||
std::vector<std::string> split;
|
||||
boost::split(split, id, boost::is_any_of(std::string("#")));
|
||||
if(split.size() != 2) {
|
||||
return element;
|
||||
}
|
||||
|
||||
std::string elementType = split[0].substr(1);
|
||||
std::string elementId = split[1];
|
||||
|
||||
if(elementType == "panel")
|
||||
element = UIElementPtr(new UIContainer(UI::Panel));
|
||||
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);
|
||||
else if(elementType == "lineDecoration")
|
||||
element = UIElementPtr(new UIElement(UI::LineDecoration));
|
||||
else if(elementType == "checkBox")
|
||||
element = UIElementPtr(new UICheckBox);
|
||||
|
||||
if(element)
|
||||
element->setId(elementId);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
UIElementPtr UILoader::loadFromFile(std::string fileName, const UIContainerPtr& parent)
|
||||
{
|
||||
UIElementPtr element;
|
||||
|
||||
if(!boost::ends_with(".otml", fileName))
|
||||
fileName += ".otml";
|
||||
|
||||
std::stringstream fin;
|
||||
if(!g_resources.loadFile(fileName, fin))
|
||||
return element;
|
||||
|
||||
try {
|
||||
OTMLParser parser(fin, fileName);
|
||||
OTMLNode* doc = parser.getDocument();
|
||||
|
||||
// get element id
|
||||
std::string elementId = doc->front()->tag();
|
||||
|
||||
// first we should populate all elements
|
||||
// only after that we can load anchors
|
||||
|
||||
// create element interpreting it's id
|
||||
element = createElementFromId(elementId);
|
||||
if(!element) {
|
||||
logError(doc->front()->generateErrorMessage("invalid root element type"));
|
||||
return element;
|
||||
}
|
||||
parent->addChild(element);
|
||||
|
||||
// populete it
|
||||
if(element->asUIContainer())
|
||||
populateContainer(element->asUIContainer(), doc->front());
|
||||
|
||||
// now do the real load
|
||||
loadElements(element, doc->front());
|
||||
|
||||
// report onLoad events
|
||||
element->onLoad();
|
||||
} catch(OTMLException e) {
|
||||
logError("ERROR: Failed to load ui ", g_resources.resolvePath(fileName) ,": ", e.what());
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
void UILoader::populateContainer(const UIContainerPtr& parent, OTMLNode* node)
|
||||
{
|
||||
// populate ordered elements
|
||||
foreach(OTMLNode* cnode, *node) {
|
||||
std::string id = cnode->tag();
|
||||
if(id[0] == '%') {
|
||||
UIElementPtr element = createElementFromId(id);
|
||||
if(!element) {
|
||||
logError(cnode->generateErrorMessage("invalid element type"));
|
||||
continue;
|
||||
}
|
||||
parent->addChild(element);
|
||||
|
||||
// also populate this element if it's a parent
|
||||
if(element->asUIContainer())
|
||||
populateContainer(element->asUIContainer(), cnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UILoader::loadElements(const UIElementPtr& parent, OTMLNode* node)
|
||||
{
|
||||
loadElement(parent, node);
|
||||
|
||||
if(UIContainerPtr container = parent->asUIContainer()) {
|
||||
foreach(const UIElementPtr& element, container->getChildren()) {
|
||||
foreach(OTMLNode* cnode, *node) {
|
||||
// node found, load it
|
||||
if(boost::ends_with(cnode->tag(), "#" + element->getId())) {
|
||||
loadElements(element, cnode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UILoader::loadElement(const UIElementPtr& element, OTMLNode* node)
|
||||
{
|
||||
// set element skin
|
||||
if(OTMLNode* cnode = node->at("skin")) {
|
||||
if(cnode->hasValue()) {
|
||||
element->setSkin(g_uiSkins.getElementSkin(element->getElementType(), cnode->value()));
|
||||
} else {
|
||||
UIElementSkinPtr skin = UIElementSkinPtr(new UIElementSkin());
|
||||
skin->load(cnode);
|
||||
element->setSkin(skin);
|
||||
}
|
||||
} else // apply default skin
|
||||
element->applyDefaultSkin();
|
||||
|
||||
// load size
|
||||
Size size = element->getSize();
|
||||
size = node->readAt("size", size);
|
||||
size.setWidth(node->readAt("width", size.width()));
|
||||
size.setHeight(node->readAt("height", size.height()));
|
||||
if(size.isValid())
|
||||
element->setSize(size);
|
||||
|
||||
// load margins
|
||||
element->setMarginLeft(node->readAtPath("margin/left", 0));
|
||||
element->setMarginRight(node->readAtPath("margin/right", 0));
|
||||
element->setMarginTop(node->readAtPath("margin/top", 0));
|
||||
element->setMarginBottom(node->readAtPath("margin/bottom", 0));
|
||||
|
||||
// load anchors
|
||||
loadElementAnchor(element, AnchorLeft, node->atPath("anchors/left"));
|
||||
loadElementAnchor(element, AnchorRight, node->atPath("anchors/right"));
|
||||
loadElementAnchor(element, AnchorTop, node->atPath("anchors/top"));
|
||||
loadElementAnchor(element, AnchorBottom, node->atPath("anchors/bottom"));
|
||||
loadElementAnchor(element, AnchorHorizontalCenter, node->atPath("anchors/horizontalCenter"));
|
||||
loadElementAnchor(element, AnchorVerticalCenter, node->atPath("anchors/verticalCenter"));
|
||||
|
||||
// load basic element events
|
||||
loadElementScriptFunction(element, node->at("onLoad"));
|
||||
|
||||
// load specific element type
|
||||
switch(element->getElementType()) {
|
||||
case UI::Button:
|
||||
loadButton(std::static_pointer_cast<UIButton>(element), node);
|
||||
break;
|
||||
case UI::Window:
|
||||
loadWindow(std::static_pointer_cast<UIWindow>(element), node);
|
||||
break;
|
||||
case UI::Label:
|
||||
loadLabel(std::static_pointer_cast<UILabel>(element), node);
|
||||
break;
|
||||
case UI::TextEdit:
|
||||
loadTextEdit(std::static_pointer_cast<UITextEdit>(element), node);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UILoader::loadElementAnchor(const UIElementPtr& anchoredElement, AnchorPoint anchoredEdge, OTMLNode* node)
|
||||
{
|
||||
if(!node)
|
||||
return;
|
||||
|
||||
std::string anchorDescription = node->value();
|
||||
if(anchorDescription.empty()) {
|
||||
logError(node->generateErrorMessage("anchor is empty, did you forget to fill it?"));
|
||||
return;
|
||||
}
|
||||
|
||||
UIAnchorLayoutPtr layout = std::dynamic_pointer_cast<UIAnchorLayout>(anchoredElement->getLayout());
|
||||
if(!layout) {
|
||||
logError(node->generateErrorMessage("could not add anchor, because this element does not participate of an anchor layout"));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> split;
|
||||
boost::split(split, anchorDescription, boost::is_any_of(std::string(".")));
|
||||
if(split.size() != 2) {
|
||||
logError(node->generateErrorMessage("invalid anchor"));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string anchorLineElementId = split[0];
|
||||
AnchorPoint anchorLineEdge = UIAnchorLayout::parseAnchorPoint(split[1]);
|
||||
|
||||
if(anchorLineEdge == AnchorNone) {
|
||||
logError(node->generateErrorMessage("invalid anchor type"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!layout->addAnchor(anchoredElement, anchoredEdge, AnchorLine(anchorLineElementId, anchorLineEdge)))
|
||||
logError(node->generateErrorMessage("anchoring failed"));
|
||||
}
|
||||
|
||||
void UILoader::loadElementScriptFunction(const UIElementPtr& element, OTMLNode* node)
|
||||
{
|
||||
if(!node)
|
||||
return;
|
||||
|
||||
std::string functionDesc;
|
||||
functionDesc += g_resources.resolvePath(node->what()) + ":" + element->getId();
|
||||
functionDesc += "[" + node->tag() + "]";
|
||||
|
||||
LuaValuePtr function = g_lua.loadFunction(node->value(), functionDesc);
|
||||
if(function->isFunction())
|
||||
element->setField(node->tag(), function);
|
||||
else
|
||||
logError(node->generateErrorMessage("failed to parse inline lua script"));
|
||||
}
|
||||
|
||||
void UILoader::loadButton(const UIButtonPtr& button, OTMLNode* node)
|
||||
{
|
||||
button->setText(node->valueAt("text"));
|
||||
loadElementScriptFunction(button, node->at("onClick"));
|
||||
}
|
||||
|
||||
void UILoader::loadWindow(const UIWindowPtr& window, OTMLNode* node)
|
||||
{
|
||||
window->setTitle(node->readAt("title", std::string()));
|
||||
}
|
||||
|
||||
void UILoader::loadLabel(const UILabelPtr& label, OTMLNode* node)
|
||||
{
|
||||
label->setText(node->readAt("text", std::string()));
|
||||
label->setAlign(parseAlignment(node->readAt("align", std::string("left"))));
|
||||
}
|
||||
|
||||
void UILoader::loadTextEdit(const UITextEditPtr& textEdit, OTMLNode* node)
|
||||
{
|
||||
textEdit->setText(node->readAt("text", std::string()));
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <ui/uicontainer.h>
|
||||
#include <ui/uibutton.h>
|
||||
#include <ui/uiwindow.h>
|
||||
#include <ui/uilabel.h>
|
||||
#include <ui/uitextedit.h>
|
||||
#include <ui/uianchorlayout.h>
|
||||
|
||||
class UILoader
|
||||
{
|
||||
public:
|
||||
/// Loads an UIElement and it's children from a FML file
|
||||
UIElementPtr loadFromFile(std::string fileName, const UIContainerPtr& parent = UIContainer::getRoot());
|
||||
|
||||
private:
|
||||
/// Detect element type and create it
|
||||
UIElementPtr createElementFromId(const std::string& id);
|
||||
|
||||
/// Populate container children from a OTML node
|
||||
void populateContainer(const UIContainerPtr& parent, OTMLNode* node);
|
||||
|
||||
/// Load element and its children from a OTML node
|
||||
void loadElements(const UIElementPtr& parent, OTMLNode* node);
|
||||
|
||||
/// Load element proprieties from a OTML node
|
||||
void loadElement(const UIElementPtr& element, OTMLNode* node);
|
||||
|
||||
/// Load anchor from a OTML node
|
||||
void loadElementAnchor(const UIElementPtr& anchoredElement, AnchorPoint anchoredEdge, OTMLNode* node);
|
||||
|
||||
/// Load element lua function
|
||||
void loadElementScriptFunction(const UIElementPtr& element, OTMLNode* node);
|
||||
|
||||
// specific elements loading
|
||||
void loadButton(const UIButtonPtr& button, OTMLNode* node);
|
||||
void loadWindow(const UIWindowPtr& window, OTMLNode* node);
|
||||
void loadLabel(const UILabelPtr& label, OTMLNode* node);
|
||||
void loadTextEdit(const UITextEditPtr& textEdit, OTMLNode* node);
|
||||
};
|
||||
|
||||
extern UILoader g_uiLoader;
|
||||
|
||||
#endif // UILOADER_H
|
238
src/framework/ui/uimanager.cpp
Normal file
238
src/framework/ui/uimanager.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "uimanager.h"
|
||||
#include "ui.h"
|
||||
#include "uianchorlayout.h"
|
||||
|
||||
#include <otml/otml.h>
|
||||
#include <graphics/graphics.h>
|
||||
|
||||
UIManager g_ui;
|
||||
|
||||
void UIManager::init()
|
||||
{
|
||||
// creates root widget
|
||||
m_rootWidget = UIWidgetPtr(new UIWidget);
|
||||
m_rootWidget->setId("root");
|
||||
|
||||
UIAnchorLayoutPtr anchorLayout(new UIAnchorLayout);
|
||||
m_rootWidget->setLayout(anchorLayout);
|
||||
m_rootWidget->resize(g_graphics.getScreenSize());
|
||||
}
|
||||
|
||||
void UIManager::terminate()
|
||||
{
|
||||
// destroy root widget and its children'
|
||||
m_rootWidget->destroy();
|
||||
m_rootWidget.reset();
|
||||
}
|
||||
|
||||
void UIManager::render()
|
||||
{
|
||||
if(m_rootWidget)
|
||||
m_rootWidget->render();
|
||||
}
|
||||
|
||||
void UIManager::resize(const Size& size)
|
||||
{
|
||||
if(m_rootWidget)
|
||||
m_rootWidget->resize(size);
|
||||
}
|
||||
|
||||
void UIManager::inputEvent(const InputEvent& event)
|
||||
{
|
||||
// translate input event to ui events
|
||||
if(m_rootWidget) {
|
||||
if(event.type & EventTextEnter) {
|
||||
m_rootWidget->onKeyboardText(std::string(1, event.keychar));
|
||||
} else if(event.type & EventKeyboardAction) {
|
||||
UIKeyEvent e;
|
||||
e.keycode = event.keycode;
|
||||
e.keyboardModifiers = KeyboardNoModifier;
|
||||
if(event.ctrl)
|
||||
e.keyboardModifiers |= KeyboardCtrlModifier;
|
||||
if(event.shift)
|
||||
e.keyboardModifiers |= KeyboardShiftModifier;
|
||||
if(event.alt)
|
||||
e.keyboardModifiers |= KeyboardAltModifier;
|
||||
|
||||
if(event.type & EventKeyDown)
|
||||
m_rootWidget->onKeyPress(e);
|
||||
else
|
||||
m_rootWidget->onKeyRelease(e);
|
||||
} else if(event.type & EventMouseAction) {
|
||||
UIMouseEvent e;
|
||||
e.mouseMoved = event.mouseMoved;
|
||||
e.mousePos = event.mousePos;
|
||||
e.button = MouseNoButton;
|
||||
e.wheelDirection = MouseNoWheel;
|
||||
|
||||
if(event.type == EventMouseMove) {
|
||||
m_rootWidget->onMouseMove(e);
|
||||
}
|
||||
else if(event.type & EventMouseWheel) {
|
||||
if(event.type & EventDown)
|
||||
e.wheelDirection = MouseWheelDown;
|
||||
else if(event.type & EventUp)
|
||||
e.wheelDirection = MouseWheelUp;
|
||||
m_rootWidget->onMouseWheel(e);
|
||||
} else {
|
||||
if(event.type & EventMouseLeftButton)
|
||||
e.button = MouseLeftButton;
|
||||
else if(event.type & EventMouseMidButton)
|
||||
e.button = MouseMidButton;
|
||||
else if(event.type & EventMouseRightButton)
|
||||
e.button = MouseRightButton;
|
||||
|
||||
if(event.type & EventDown)
|
||||
m_rootWidget->onMousePress(e);
|
||||
else if(event.type & EventUp)
|
||||
m_rootWidget->onMouseRelease(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIManager::lockWidget(const UIWidgetPtr& widgetToLock)
|
||||
{
|
||||
assert(m_rootWidget->hasChild(widgetToLock));
|
||||
|
||||
// disable all other widgets
|
||||
for(const UIWidgetPtr& widget : m_rootWidget->getChildren()) {
|
||||
if(widget == widgetToLock)
|
||||
widget->setEnabled(true);
|
||||
else
|
||||
widget->setEnabled(false);
|
||||
}
|
||||
|
||||
m_lockedWidgets.push_front(widgetToLock);
|
||||
}
|
||||
|
||||
void UIManager::unlockWidget(const UIWidgetPtr& widgetToUnlock)
|
||||
{
|
||||
assert(m_rootWidget->hasChild(widgetToUnlock));
|
||||
|
||||
auto it = std::find(m_lockedWidgets.begin(), m_lockedWidgets.end(), widgetToUnlock);
|
||||
if(it != m_lockedWidgets.end()) {
|
||||
m_lockedWidgets.erase(it);
|
||||
UIWidgetPtr newLockedWidget;
|
||||
if(m_lockedWidgets.size() > 0)
|
||||
newLockedWidget = m_lockedWidgets.front();
|
||||
for(const UIWidgetPtr& child : m_rootWidget->getChildren()) {
|
||||
if(newLockedWidget) {
|
||||
if(child == newLockedWidget)
|
||||
child->setEnabled(true);
|
||||
else
|
||||
child->setEnabled(false);
|
||||
} else
|
||||
child->setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UIManager::importStyles(const std::string& file)
|
||||
{
|
||||
try {
|
||||
OTMLDocumentPtr doc = OTMLDocument::parse(file);
|
||||
|
||||
for(const OTMLNodePtr& styleNode : doc->childNodes())
|
||||
importStyleFromOTML(styleNode);
|
||||
return true;
|
||||
} catch(std::exception& e) {
|
||||
logError("ERROR: failed to import ui styles from '", file, "':\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void UIManager::importStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||
{
|
||||
std::string tag = styleNode->tag();
|
||||
std::vector<std::string> split;
|
||||
boost::split(split, tag, boost::is_any_of(std::string("<")));
|
||||
if(split.size() != 2)
|
||||
throw OTMLException(styleNode, "not a valid style declaration");
|
||||
|
||||
std::string name = split[0];
|
||||
std::string base = split[1];
|
||||
|
||||
boost::trim(name);
|
||||
boost::trim(base);
|
||||
|
||||
auto it = m_styles.find(name);
|
||||
if(it != m_styles.end())
|
||||
logWarning("WARNING: style '", name, "' is being redefined");
|
||||
|
||||
OTMLNodePtr style = getStyle(base)->clone();
|
||||
style->merge(styleNode);
|
||||
style->setTag(name);
|
||||
m_styles[name] = style;
|
||||
}
|
||||
|
||||
OTMLNodePtr UIManager::getStyle(const std::string& styleName)
|
||||
{
|
||||
if(boost::starts_with(styleName, "UI")) {
|
||||
OTMLNodePtr node(new OTMLNode());
|
||||
node->writeAt("__widgetType", styleName);
|
||||
return node;
|
||||
}
|
||||
|
||||
auto it = m_styles.find(styleName);
|
||||
if(it == m_styles.end())
|
||||
throw std::logic_error(aux::make_string("style '", styleName, "' is not a defined style"));
|
||||
return m_styles[styleName];
|
||||
}
|
||||
|
||||
UIWidgetPtr UIManager::loadUI(const std::string& file)
|
||||
{
|
||||
try {
|
||||
OTMLDocumentPtr doc = OTMLDocument::parse(file);
|
||||
UIWidgetPtr widget;
|
||||
for(const OTMLNodePtr& node : doc->childNodes()) {
|
||||
std::string tag = node->tag();
|
||||
|
||||
// import styles in these files too
|
||||
if(tag.find("<") != std::string::npos)
|
||||
importStyleFromOTML(node);
|
||||
else {
|
||||
if(widget)
|
||||
throw OTMLException(node, "cannot have multiple main widgets in .otui files");
|
||||
widget = loadWidgetFromOTML(node);
|
||||
}
|
||||
}
|
||||
return widget;
|
||||
} catch(std::exception& e) {
|
||||
logError("ERROR: failed to load ui from '", file, "':\n", e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
UIWidgetPtr UIManager::loadWidgetFromOTML(const OTMLNodePtr& widgetNode)
|
||||
{
|
||||
OTMLNodePtr styleNode = getStyle(widgetNode->tag())->clone();
|
||||
styleNode->merge(widgetNode);
|
||||
|
||||
std::string widgetType = styleNode->readAt<std::string>("__widgetType");
|
||||
|
||||
UIWidgetPtr widget;
|
||||
if(widgetType == "UIWidget")
|
||||
widget = UIWidgetPtr(new UIWidget);
|
||||
else if(widgetType == "UILabel")
|
||||
widget = UIWidgetPtr(new UILabel);
|
||||
else if(widgetType == "UIButton")
|
||||
widget = UIWidgetPtr(new UIButton);
|
||||
else if(widgetType == "UILineEdit")
|
||||
widget = UIWidgetPtr(new UILineEdit);
|
||||
else if(widgetType == "UIWindow")
|
||||
widget = UIWidgetPtr(new UIWindow);
|
||||
else
|
||||
throw OTMLException(styleNode, "cannot determine widget type");
|
||||
|
||||
widget->loadStyleFromOTML(styleNode);
|
||||
widget->updateGeometry();
|
||||
|
||||
for(const OTMLNodePtr& childNode : widgetNode->childNodes()) {
|
||||
if(!childNode->isUnique())
|
||||
widget->addChild(loadWidgetFromOTML(childNode));
|
||||
}
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
38
src/framework/ui/uimanager.h
Normal file
38
src/framework/ui/uimanager.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef UIMANAGER_H
|
||||
#define UIMANAGER_H
|
||||
|
||||
#include "uideclarations.h"
|
||||
#include <core/inputevent.h>
|
||||
#include <otml/otmldeclarations.h>
|
||||
|
||||
class UIManager
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
void terminate();
|
||||
|
||||
void render();
|
||||
void resize(const Size& size);
|
||||
void inputEvent(const InputEvent& event);
|
||||
|
||||
void lockWidget(const UIWidgetPtr& widgetToLock);
|
||||
void unlockWidget(const UIWidgetPtr& widgetToUnlock);
|
||||
|
||||
bool importStyles(const std::string& file);
|
||||
void importStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||
OTMLNodePtr getStyle(const std::string& styleName);
|
||||
|
||||
UIWidgetPtr loadUI(const std::string& file);
|
||||
UIWidgetPtr loadWidgetFromOTML(const OTMLNodePtr& widgetNode);
|
||||
|
||||
UIWidgetPtr getRootWidget() { return m_rootWidget; }
|
||||
|
||||
private:
|
||||
UIWidgetPtr m_rootWidget;
|
||||
UIWidgetList m_lockedWidgets;
|
||||
std::map<std::string, OTMLNodePtr> m_styles;
|
||||
};
|
||||
|
||||
extern UIManager g_ui;
|
||||
|
||||
#endif
|
@@ -1,105 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <core/resources.h>
|
||||
#include <graphics/textures.h>
|
||||
#include <ui/uiskins.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include <ui/uibuttonskin.h>
|
||||
#include <ui/uiwindowskin.h>
|
||||
#include <ui/uitexteditskin.h>
|
||||
#include <ui/uilabelskin.h>
|
||||
#include <graphics/fonts.h>
|
||||
#include <otml/otml.h>
|
||||
|
||||
UISkins g_uiSkins;
|
||||
|
||||
void UISkins::load(const std::string& skinName)
|
||||
{
|
||||
g_resources.pushCurrentPath("skins");
|
||||
|
||||
std::stringstream fin;
|
||||
if(!g_resources.loadFile(skinName + ".otml", fin))
|
||||
logFatal("FATAL ERROR: Could not load skin '",skinName,"'");
|
||||
|
||||
try {
|
||||
OTMLParser parser(fin, skinName);
|
||||
OTMLNode* doc = parser.getDocument();
|
||||
|
||||
m_defaultFont = g_fonts.get(doc->valueAt("default font"));
|
||||
if(!m_defaultFont)
|
||||
logFatal("FATAL ERROR: Could not load skin default font");
|
||||
|
||||
m_defaultFontColor = doc->readAt("default font color", Color::white);
|
||||
|
||||
std::string defaultTextureName = doc->readAt("default texture", std::string());
|
||||
if(!defaultTextureName.empty())
|
||||
m_defaultTexture = g_textures.get(defaultTextureName);
|
||||
|
||||
foreach(OTMLNode* node, *doc) {
|
||||
UIElementSkinPtr skin;
|
||||
foreach(OTMLNode* cnode, *node) {
|
||||
if(node->tag() == "buttons")
|
||||
skin = UIElementSkinPtr(new UIButtonSkin(cnode->tag()));
|
||||
else if(node->tag() == "panels")
|
||||
skin = UIElementSkinPtr(new UIElementSkin(cnode->tag(), UI::Panel));
|
||||
else if(node->tag() == "windows")
|
||||
skin = UIElementSkinPtr(new UIWindowSkin(cnode->tag()));
|
||||
else if(node->tag() == "labels")
|
||||
skin = UIElementSkinPtr(new UILabelSkin(cnode->tag()));
|
||||
else if(node->tag() == "text edits")
|
||||
skin = UIElementSkinPtr(new UITextEditSkin(cnode->tag()));
|
||||
else if(node->tag() == "line decorations")
|
||||
skin = UIElementSkinPtr(new UIElementSkin(cnode->tag(), UI::LineDecoration));
|
||||
else {
|
||||
break;
|
||||
}
|
||||
skin->load(cnode);
|
||||
m_elementSkins.push_back(skin);
|
||||
}
|
||||
}
|
||||
} catch(OTMLException e) {
|
||||
logFatal("FATAL ERROR: Malformed skin file '",skinName,"':\n ",e.what());
|
||||
}
|
||||
|
||||
g_resources.popCurrentPath();
|
||||
}
|
||||
|
||||
void UISkins::terminate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
UIElementSkinPtr UISkins::getElementSkin(UI::ElementType elementType, const std::string& name)
|
||||
{
|
||||
for(auto it = m_elementSkins.begin(); it != m_elementSkins.end(); ++it) {
|
||||
const UIElementSkinPtr& skin = (*it);
|
||||
if(elementType == skin->getElementType() && name == skin->getName())
|
||||
return skin;
|
||||
}
|
||||
logWarning("Element skin '",name,"' not found");
|
||||
return UIElementSkinPtr();
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
/* 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 UISKIN_H
|
||||
#define UISKIN_H
|
||||
|
||||
#include <global.h>
|
||||
#include <graphics/texture.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include <graphics/font.h>
|
||||
|
||||
class UISkins
|
||||
{
|
||||
public:
|
||||
UISkins() { }
|
||||
|
||||
void load(const std::string& skinName);
|
||||
void terminate();
|
||||
|
||||
UIElementSkinPtr getElementSkin(UI::ElementType elementType, const std::string& name = "default");
|
||||
TexturePtr getDefaultTexture() { return m_defaultTexture; }
|
||||
FontPtr getDefaultFont() { return m_defaultFont; }
|
||||
Color getDefaultFontColor() { return m_defaultFontColor; }
|
||||
|
||||
private:
|
||||
TexturePtr m_defaultTexture;
|
||||
FontPtr m_defaultFont;
|
||||
Color m_defaultFontColor;
|
||||
|
||||
std::vector<UIElementSkinPtr> m_elementSkins;
|
||||
};
|
||||
|
||||
extern UISkins g_uiSkins;
|
||||
|
||||
#endif // UISKIN_H
|
@@ -1,77 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <ui/uitextedit.h>
|
||||
#include <ui/uitexteditskin.h>
|
||||
#include <ui/uicontainer.h>
|
||||
|
||||
UITextEdit::UITextEdit() :
|
||||
UIElement(UI::TextEdit)
|
||||
{
|
||||
m_textArea.enableCursor();
|
||||
}
|
||||
|
||||
void UITextEdit::onInputEvent(const InputEvent& event)
|
||||
{
|
||||
if(event.type == EV_TEXT_ENTER) {
|
||||
m_textArea.appendCharacter(event.keychar);
|
||||
} else if(event.type == EV_KEY_DOWN) {
|
||||
if(event.keycode == KC_DELETE) // erase right character
|
||||
m_textArea.removeCharacter(true);
|
||||
else if(event.keycode == KC_BACK) // erase left character
|
||||
m_textArea.removeCharacter(false);
|
||||
else if(event.keycode == KC_RIGHT) // move cursor right
|
||||
m_textArea.moveCursor(true);
|
||||
else if(event.keycode == KC_LEFT) // move cursor left
|
||||
m_textArea.moveCursor(false);
|
||||
else if(event.keycode == KC_HOME) // move cursor to first character
|
||||
m_textArea.setCursorPos(0);
|
||||
else if(event.keycode == KC_END) // move cursor to last character
|
||||
m_textArea.setCursorPos(m_textArea.getText().length());
|
||||
else if(event.keycode == KC_TAB) // focus next parent element
|
||||
getParent()->focusNextElement();
|
||||
} else if(event.type == EV_MOUSE_LDOWN) {
|
||||
int pos = m_textArea.getTextPos(event.mousePos);
|
||||
if(pos >= 0)
|
||||
m_textArea.setCursorPos(m_textArea.getTextPos(event.mousePos));
|
||||
} else if(event.type == EV_MOUSE_LUP && getRect().contains(event.mousePos)) {
|
||||
}
|
||||
}
|
||||
|
||||
void UITextEdit::onRectUpdate()
|
||||
{
|
||||
UITextEditSkin *skin = static_cast<UITextEditSkin*>(getSkin().get());
|
||||
Rect textRect = getRect();
|
||||
int margin = skin->getTextMargin();
|
||||
textRect.setLeft(textRect.left()+margin);
|
||||
textRect.setRight(textRect.right()-margin);
|
||||
m_textArea.setScreenCoords(textRect);
|
||||
}
|
||||
|
||||
void UITextEdit::onFocusChange()
|
||||
{
|
||||
m_textArea.setCursorVisible(isFocused());
|
||||
}
|
||||
|
@@ -1,36 +0,0 @@
|
||||
#ifndef UITEXTEDIT_H
|
||||
#define UITEXTEDIT_H
|
||||
|
||||
#include <global.h>
|
||||
#include <graphics/textarea.h>
|
||||
#include <ui/uielement.h>
|
||||
|
||||
class Font;
|
||||
|
||||
class UITextEdit;
|
||||
typedef std::shared_ptr<UITextEdit> UITextEditPtr;
|
||||
|
||||
class UITextEdit : public UIElement
|
||||
{
|
||||
public:
|
||||
UITextEdit();
|
||||
|
||||
static UITextEditPtr create() { return UITextEditPtr(new UITextEdit); }
|
||||
|
||||
void onInputEvent(const InputEvent& event);
|
||||
void onRectUpdate();
|
||||
void onFocusChange();
|
||||
|
||||
void setText(const std::string& text) { m_textArea.setText(text); }
|
||||
std::string getText() const { return m_textArea.getText(); }
|
||||
TextArea& getTextArea() { return m_textArea; }
|
||||
|
||||
bool isFocusable() const { return true; }
|
||||
|
||||
virtual const char *getLuaTypeName() const { return "UITextEdit"; }
|
||||
|
||||
private:
|
||||
TextArea m_textArea;
|
||||
};
|
||||
|
||||
#endif // UITEXTEDIT_H
|
@@ -1,50 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <graphics/fonts.h>
|
||||
#include <ui/uitexteditskin.h>
|
||||
#include <ui/uitextedit.h>
|
||||
|
||||
void UITextEditSkin::load(OTMLNode* node)
|
||||
{
|
||||
UIElementSkin::load(node);
|
||||
m_textMargin = node->readAt("text margin", 2);
|
||||
}
|
||||
|
||||
void UITextEditSkin::apply(UIElement* element)
|
||||
{
|
||||
UIElementSkin::apply(element);
|
||||
UITextEdit *textEdit = static_cast<UITextEdit*>(element);
|
||||
textEdit->getTextArea().setFont(getFont());
|
||||
textEdit->getTextArea().setColor(getFontColor());
|
||||
}
|
||||
|
||||
void UITextEditSkin::draw(UIElement* element)
|
||||
{
|
||||
UIElementSkin::draw(element);
|
||||
|
||||
UITextEdit *textEdit = static_cast<UITextEdit*>(element);
|
||||
textEdit->getTextArea().draw();
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <ui/uielementskin.h>
|
||||
|
||||
class Font;
|
||||
|
||||
class UITextEditSkin : public UIElementSkin
|
||||
{
|
||||
public:
|
||||
UITextEditSkin(const std::string& name) :
|
||||
UIElementSkin(name, UI::TextEdit) { }
|
||||
|
||||
void load(OTMLNode* node);
|
||||
void apply(UIElement *element);
|
||||
void draw(UIElement *element);
|
||||
|
||||
int getTextMargin() const { return m_textMargin; }
|
||||
|
||||
private:
|
||||
int m_textMargin;
|
||||
};
|
||||
|
||||
#endif // UITEXTEDITSKIN_H
|
670
src/framework/ui/uiwidget.cpp
Normal file
670
src/framework/ui/uiwidget.cpp
Normal file
@@ -0,0 +1,670 @@
|
||||
#include "uiwidget.h"
|
||||
#include "uimanager.h"
|
||||
#include "uilayout.h"
|
||||
#include "uianchorlayout.h"
|
||||
|
||||
#include <core/eventdispatcher.h>
|
||||
#include <graphics/image.h>
|
||||
#include <graphics/borderimage.h>
|
||||
#include <graphics/fontmanager.h>
|
||||
#include <otml/otmlnode.h>
|
||||
|
||||
UIWidget::UIWidget(UIWidgetType type)
|
||||
{
|
||||
m_type = type;
|
||||
m_visible = true;
|
||||
m_enabled = true;
|
||||
m_hovered = false;
|
||||
m_focusable = false;
|
||||
m_destroyed = false;
|
||||
m_updateScheduled = false;
|
||||
m_marginLeft = m_marginRight = m_marginTop = m_marginBottom = 0;
|
||||
m_color = Color::white;
|
||||
|
||||
// generate an unique id, this is need because anchored layouts find widgets by id
|
||||
static unsigned long id = 1;
|
||||
m_id = aux::make_string("widget", id++);
|
||||
}
|
||||
|
||||
UIWidget::~UIWidget()
|
||||
{
|
||||
//logTraceDebug(m_id);
|
||||
if(!m_destroyed)
|
||||
logWarning("WARNING: widget '", m_id, "' was destructed without being explicit destroyed");
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::create()
|
||||
{
|
||||
UIWidgetPtr widget(new UIWidget);
|
||||
//widget->setStyle("Widget");
|
||||
return widget;
|
||||
}
|
||||
|
||||
void UIWidget::destroy()
|
||||
{
|
||||
//TODO: onDestroy event
|
||||
// destroy only once
|
||||
if(!m_destroyed) {
|
||||
// clear additional reference
|
||||
m_focusedChild.reset();
|
||||
|
||||
// destroy children
|
||||
while(m_children.size() > 0) {
|
||||
UIWidgetPtr child = m_children.front(); //hold reference
|
||||
child->destroy();
|
||||
}
|
||||
|
||||
// remove itself from parent
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
parent->removeChild(asUIWidget());
|
||||
|
||||
// add destroy check event
|
||||
g_dispatcher.addEvent(std::bind(&UIWidget::destroyCheck, asUIWidget()));
|
||||
m_destroyed = true;
|
||||
m_enabled = false;
|
||||
m_visible = false;
|
||||
} else
|
||||
logWarning("WARNING: attempt to destroy widget '", m_id, "' again");
|
||||
}
|
||||
|
||||
void UIWidget::destroyCheck()
|
||||
{
|
||||
// collect lua garbage before checking
|
||||
g_lua.collectGarbage();
|
||||
|
||||
// get real use count
|
||||
int realUseCount = shared_from_this().use_count() - 2;
|
||||
|
||||
// check for leaks upon widget destruction
|
||||
if(realUseCount > 0)
|
||||
logWarning("WARNING: destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left");
|
||||
}
|
||||
|
||||
void UIWidget::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// load styles used by all widgets
|
||||
for(const OTMLNodePtr& node : styleNode->childNodes()) {
|
||||
// id
|
||||
if(node->tag() == "id") {
|
||||
setId(node->value());
|
||||
}
|
||||
// image
|
||||
else if(node->tag() == "image") {
|
||||
setImage(Image::loadFromOTML(node));
|
||||
}
|
||||
else if(node->tag() == "border-image") {
|
||||
setImage(BorderImage::loadFromOTML(node));
|
||||
}
|
||||
// font
|
||||
else if(node->tag() == "font") {
|
||||
setFont(g_fonts.getFont(node->value()));
|
||||
}
|
||||
// color
|
||||
else if(node->tag() == "color") {
|
||||
setColor(node->read<Color>());
|
||||
}
|
||||
// size
|
||||
else if(node->tag() == "size") {
|
||||
resize(node->read<Size>());
|
||||
}
|
||||
else if(node->tag() == "width") {
|
||||
setWidth(node->read<int>());
|
||||
}
|
||||
else if(node->tag() == "height") {
|
||||
setHeight(node->read<int>());
|
||||
}
|
||||
// position
|
||||
else if(node->tag() == "position") {
|
||||
move(node->read<Point>());
|
||||
}
|
||||
else if(node->tag() == "x") {
|
||||
setX(node->read<int>());
|
||||
}
|
||||
else if(node->tag() == "y") {
|
||||
setY(node->read<int>());
|
||||
}
|
||||
// margins
|
||||
else if(node->tag() == "margin.left") {
|
||||
setMarginLeft(node->read<int>());
|
||||
}
|
||||
else if(node->tag() == "margin.right") {
|
||||
setMarginRight(node->read<int>());
|
||||
}
|
||||
else if(node->tag() == "margin.top") {
|
||||
setMarginTop(node->read<int>());
|
||||
}
|
||||
else if(node->tag() == "margin.bottom") {
|
||||
setMarginBottom(node->read<int>());
|
||||
}
|
||||
// anchors
|
||||
else if(boost::starts_with(node->tag(), "anchors.")) {
|
||||
std::string what = node->tag().substr(8);
|
||||
if(what == "fill") {
|
||||
fill(node->value());
|
||||
} else if(what == "centerIn") {
|
||||
centerIn(node->value());
|
||||
} else {
|
||||
AnchorPoint myEdge = parseAnchorPoint(what);
|
||||
|
||||
std::string anchorDescription = node->value();
|
||||
std::vector<std::string> split;
|
||||
boost::split(split, anchorDescription, boost::is_any_of(std::string(".")));
|
||||
if(split.size() != 2)
|
||||
throw OTMLException(node, "invalid anchor description");
|
||||
|
||||
std::string target = split[0];
|
||||
AnchorPoint targetEdge = parseAnchorPoint(split[1]);
|
||||
|
||||
if(myEdge == AnchorNone)
|
||||
throw OTMLException(node, "invalid anchor edge");
|
||||
|
||||
if(targetEdge == AnchorNone)
|
||||
throw OTMLException(node, "invalid anchor target edge");
|
||||
|
||||
addAnchor(myEdge, target, targetEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_font)
|
||||
m_font = g_fonts.getDefaultFont();
|
||||
}
|
||||
|
||||
void UIWidget::render()
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(m_image)
|
||||
m_image->draw(getGeometry());
|
||||
|
||||
for(const UIWidgetPtr& child : m_children) {
|
||||
if(child->isVisible())
|
||||
child->render();
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::updateGeometry()
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(UILayoutPtr layout = getLayout())
|
||||
layout->updateWidget(asUIWidget());
|
||||
}
|
||||
|
||||
void UIWidget::setParent(const UIWidgetPtr& parent)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
UIWidgetPtr me = asUIWidget();
|
||||
|
||||
// remove from old parent
|
||||
UIWidgetPtr oldParent = m_parent.lock();
|
||||
if(oldParent && oldParent->hasChild(me)) {
|
||||
oldParent->removeChild(me);
|
||||
}
|
||||
m_parent.reset();
|
||||
|
||||
if(parent) {
|
||||
m_parent = UIWidgetWeakPtr(parent);
|
||||
if(!parent->hasChild(me))
|
||||
parent->addChild(me);
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::setStyle(const std::string& styleName)
|
||||
{
|
||||
try {
|
||||
OTMLNodePtr styleNode = g_ui.getStyle(styleName);
|
||||
loadStyleFromOTML(styleNode);
|
||||
|
||||
// forces layout recalculation
|
||||
updateGeometry();
|
||||
} catch(std::exception& e) {
|
||||
logError("ERROR: couldn't change widget '", m_id, "' style: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::setGeometry(const Rect& rect)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
Rect oldRect = m_rect;
|
||||
m_rect = rect;
|
||||
if(UILayoutPtr layout = getLayout())
|
||||
layout->updateWidgetChildren(asUIWidget());
|
||||
|
||||
// avoid massive updates
|
||||
if(!m_updateScheduled) {
|
||||
UIWidgetPtr self = asUIWidget();
|
||||
g_dispatcher.addEvent([self]() {
|
||||
self->m_updateScheduled = false;
|
||||
// this widget could be destroyed before the event happens
|
||||
if(!self->isDestroyed())
|
||||
self->onGeometryUpdate();
|
||||
});
|
||||
m_updateScheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::focusChild(const UIWidgetPtr& focusedChild)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(focusedChild != m_focusedChild) {
|
||||
UIWidgetPtr oldFocused = m_focusedChild;
|
||||
m_focusedChild = focusedChild;
|
||||
|
||||
if(oldFocused) {
|
||||
g_dispatcher.addEvent([oldFocused]() {
|
||||
// the widget can be destroyed before the event happens
|
||||
if(!oldFocused->isDestroyed())
|
||||
oldFocused->onFocusChange(false);
|
||||
});
|
||||
}
|
||||
if(focusedChild) {
|
||||
g_dispatcher.addEvent([focusedChild]() {
|
||||
// the widget can be destroyed before the event happens
|
||||
if(!focusedChild->isDestroyed())
|
||||
focusedChild->onFocusChange(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// when containers are focused they go to the top
|
||||
if(focusedChild && focusedChild->hasChildren())
|
||||
moveChildToTop(focusedChild);
|
||||
}
|
||||
|
||||
bool UIWidget::hasFocus()
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(UIWidgetPtr parent = m_parent.lock())
|
||||
return (parent->getFocusedChild() == shared_from_this());
|
||||
// root widget always has focus
|
||||
else if(asUIWidget() == g_ui.getRootWidget())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UIWidget::hasChild(const UIWidgetPtr& child)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
auto it = std::find(m_children.begin(), m_children.end(), child);
|
||||
if(it != m_children.end())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
UILayoutPtr UIWidget::getLayout() const
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(m_layout)
|
||||
return m_layout;
|
||||
else if(getParent() && getParent()->getLayout())
|
||||
return getParent()->getLayout();
|
||||
// fallback to root layout
|
||||
return g_ui.getRootWidget()->getLayout();
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::getChildAfter(const UIWidgetPtr& relativeChild)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
for(auto it = m_children.begin(); it != m_children.end(); ++it) {
|
||||
const UIWidgetPtr& child = (*it);
|
||||
if(child == relativeChild) {
|
||||
if(++it != m_children.end())
|
||||
return (*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::getChildBefore(const UIWidgetPtr& relativeChild)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
|
||||
const UIWidgetPtr& child = (*it);
|
||||
if(child == relativeChild) {
|
||||
if(++it != m_children.rend())
|
||||
return (*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::getChildById(const std::string& childId)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(getId() == childId || childId == "self")
|
||||
return asUIWidget();
|
||||
else if(childId == "parent")
|
||||
return getParent();
|
||||
else if(childId == "root")
|
||||
return g_ui.getRootWidget();
|
||||
else if(childId == "prev") {
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
return parent->getChildBefore(asUIWidget());
|
||||
} else if(childId == "next") {
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
return parent->getChildAfter(asUIWidget());
|
||||
} else {
|
||||
for(const UIWidgetPtr& child : m_children) {
|
||||
if(child->getId() == childId)
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::getChildByPos(const Point& childPos)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
|
||||
const UIWidgetPtr& widget = (*it);
|
||||
if(widget->getGeometry().contains(childPos))
|
||||
return widget;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::getChildByIndex(int childIndex)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(childIndex >= 0 && childIndex < getChildCount())
|
||||
return m_children.at(childIndex);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::recursiveGetChildById(const std::string& childId)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
if(getId() == childId || childId == "self")
|
||||
return asUIWidget();
|
||||
else if(childId == "parent")
|
||||
return getParent();
|
||||
else if(childId == "root")
|
||||
return g_ui.getRootWidget();
|
||||
else if(childId == "prev") {
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
return parent->getChildBefore(asUIWidget());
|
||||
} else if(childId == "next") {
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
return parent->getChildAfter(asUIWidget());
|
||||
} else {
|
||||
for(const UIWidgetPtr& child : m_children) {
|
||||
if(child->getId() == childId)
|
||||
return child;
|
||||
else
|
||||
return child->recursiveGetChildById(childId);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
for(const UIWidgetPtr& child : m_children) {
|
||||
if(child->getGeometry().contains(childPos)) {
|
||||
if(UIWidgetPtr subChild = child->recursiveGetChildByPos(childPos))
|
||||
return subChild;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIWidgetPtr UIWidget::backwardsGetWidgetById(const std::string& id)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
UIWidgetPtr widget;
|
||||
if(getId() == id || id == "self")
|
||||
widget = asUIWidget();
|
||||
else if(id == "parent")
|
||||
widget = getParent();
|
||||
else if(id == "root")
|
||||
widget = g_ui.getRootWidget();
|
||||
else if(id == "prev") {
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
widget = parent->getChildBefore(asUIWidget());
|
||||
} else if(id == "next") {
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
widget = parent->getChildAfter(asUIWidget());
|
||||
} else {
|
||||
widget = recursiveGetChildById(id);
|
||||
if(widget)
|
||||
return widget;
|
||||
|
||||
if(UIWidgetPtr parent = getParent())
|
||||
widget = parent->backwardsGetWidgetById(id);
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
||||
void UIWidget::addChild(const UIWidgetPtr& childToAdd)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
assert(!hasChild(childToAdd));
|
||||
m_children.push_back(childToAdd);
|
||||
childToAdd->setParent(asUIWidget());
|
||||
|
||||
// updates child position
|
||||
childToAdd->updateGeometry();
|
||||
|
||||
// focus it if there is no focused child yet
|
||||
if(!m_focusedChild && childToAdd->isFocusable())
|
||||
focusChild(childToAdd);
|
||||
}
|
||||
|
||||
void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// defocus if needed
|
||||
if(m_focusedChild == childToRemove)
|
||||
focusChild(nullptr);
|
||||
|
||||
// remove from children list
|
||||
auto it = std::find(m_children.begin(), m_children.end(), childToRemove);
|
||||
assert(it != m_children.end());
|
||||
m_children.erase(it);
|
||||
|
||||
// reset child parent
|
||||
assert(childToRemove->getParent() == asUIWidget());
|
||||
childToRemove->setParent(nullptr);
|
||||
}
|
||||
|
||||
void UIWidget::focusNextChild()
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
UIWidgetPtr toFocus;
|
||||
UIWidgetList rotatedChildren(m_children);
|
||||
auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedChild);
|
||||
if(focusedIt != rotatedChildren.end()) {
|
||||
std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end());
|
||||
rotatedChildren.pop_front();
|
||||
for(const UIWidgetPtr& child : rotatedChildren) {
|
||||
if(child->isFocusable()) {
|
||||
toFocus = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if(m_children.size() > 0)
|
||||
toFocus = m_children.back();
|
||||
|
||||
if(toFocus)
|
||||
focusChild(toFocus);
|
||||
}
|
||||
|
||||
void UIWidget::moveChildToTop(const UIWidgetPtr& childToMove)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// remove and push child again
|
||||
auto it = std::find(m_children.begin(), m_children.end(), childToMove);
|
||||
assert(it != m_children.end());
|
||||
m_children.erase(it);
|
||||
m_children.push_back(childToMove);
|
||||
}
|
||||
|
||||
void UIWidget::addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
UIAnchorLayoutPtr layout = std::dynamic_pointer_cast<UIAnchorLayout>(getLayout());
|
||||
assert(layout);
|
||||
if(layout)
|
||||
layout->addAnchor(asUIWidget(), edge, AnchorLine(targetId, targetEdge));
|
||||
}
|
||||
|
||||
void UIWidget::centerIn(const std::string& targetId)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
addAnchor(AnchorHorizontalCenter, targetId, AnchorHorizontalCenter);
|
||||
addAnchor(AnchorVerticalCenter, targetId, AnchorVerticalCenter);
|
||||
}
|
||||
|
||||
void UIWidget::fill(const std::string& targetId)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
addAnchor(AnchorLeft, targetId, AnchorLeft);
|
||||
addAnchor(AnchorRight, targetId, AnchorRight);
|
||||
addAnchor(AnchorTop, targetId, AnchorTop);
|
||||
addAnchor(AnchorBottom, targetId, AnchorBottom);
|
||||
}
|
||||
|
||||
void UIWidget::onKeyboardText(const std::string& text)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
// key events go only to containers or focused child
|
||||
if(child->hasChildren() || child->hasFocus())
|
||||
child->onKeyboardText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::onKeyPress(const UIKeyEvent& event)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
// key events go only to containers or focused child
|
||||
if(child->hasChildren() || child->hasFocus())
|
||||
child->onKeyPress(event);
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::onKeyRelease(const UIKeyEvent& event)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
// key events go only to containers or focused child
|
||||
if(child->hasChildren() || child->hasFocus())
|
||||
child->onKeyRelease(event);
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::onMousePress(const UIMouseEvent& event)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
// mouse press events only go to children that contains the mouse position
|
||||
if(child->getGeometry().contains(event.mousePos) && child == getChildByPos(event.mousePos)) {
|
||||
child->onMousePress(event);
|
||||
|
||||
// focus it
|
||||
if(child->isFocusable())
|
||||
focusChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::onMouseRelease(const UIMouseEvent& event)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
// mouse release events go to all children
|
||||
child->onMouseRelease(event);
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::onMouseMove(const UIMouseEvent& event)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
|
||||
// update child over status
|
||||
bool overChild = (child->getGeometry().contains(event.mousePos) && child == getChildByPos(event.mousePos));
|
||||
if(overChild != child->isHovered()) {
|
||||
child->setHovered(overChild);
|
||||
child->onHoverChange(overChild);
|
||||
}
|
||||
// mouse move events go to all children
|
||||
child->onMouseMove(event);
|
||||
}
|
||||
}
|
||||
|
||||
void UIWidget::onMouseWheel(const UIMouseEvent& event)
|
||||
{
|
||||
assert(!m_destroyed);
|
||||
|
||||
// do a backup of children list, because it may change while looping it
|
||||
UIWidgetList children = m_children;
|
||||
for(const UIWidgetPtr& child : children) {
|
||||
if(!child->isEnabled() || !child->isVisible())
|
||||
continue;
|
||||
// mouse wheel events only go to children that contains the mouse position
|
||||
if(child->getGeometry().contains(event.mousePos) && child == getChildByPos(event.mousePos))
|
||||
child->onMouseWheel(event);
|
||||
}
|
||||
}
|
166
src/framework/ui/uiwidget.h
Normal file
166
src/framework/ui/uiwidget.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#ifndef UIWIDGET_H
|
||||
#define UIWIDGET_H
|
||||
|
||||
#include "uideclarations.h"
|
||||
#include "uievent.h"
|
||||
#include <luascript/luaobject.h>
|
||||
#include <graphics/graphicsdeclarations.h>
|
||||
#include <otml/otmldeclarations.h>
|
||||
|
||||
class UIWidget : public LuaObject
|
||||
{
|
||||
public:
|
||||
UIWidget(UIWidgetType type = UITypeWidget);
|
||||
virtual ~UIWidget();
|
||||
|
||||
static UIWidgetPtr create();
|
||||
|
||||
/// Remove this widget from parent then destroy it and its children
|
||||
virtual void destroy();
|
||||
|
||||
/// Load style from otml node
|
||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||
|
||||
/// Draw widget on screen
|
||||
virtual void render();
|
||||
|
||||
/// Notifies the layout system that this widget has changed and may need to change geometry
|
||||
void updateGeometry();
|
||||
|
||||
void setEnabled(bool enable) { m_enabled = enable; }
|
||||
void setLayout(const UILayoutPtr& layout) { m_layout = layout; }
|
||||
void setId(const std::string& id) { m_id = id; }
|
||||
void setFocusable(bool focusable) { m_focusable = focusable; }
|
||||
void setHovered(bool hovered) { m_hovered = hovered; }
|
||||
void setVisible(bool visible) { m_visible = visible; }
|
||||
void setParent(const UIWidgetPtr& parent);
|
||||
void setStyle(const std::string& styleName);
|
||||
void setGeometry(const Rect& rect);
|
||||
void setX(int x) { move(Point(x, getY())); }
|
||||
void setY(int y) { move(Point(getX(), y)); }
|
||||
void setWidth(int width) { resize(Size(width, getHeight())); }
|
||||
void setHeight(int height) { resize(Size(getWidth(), height)); }
|
||||
void resize(const Size& size) { setGeometry(Rect(getPosition(), size)); }
|
||||
void move(const Point& pos) { setGeometry(Rect(pos, getSize())); }
|
||||
|
||||
void setImage(const ImagePtr& image) { m_image = image; }
|
||||
void setFont(const FontPtr& font) { m_font = font; }
|
||||
void setColor(const Color& color) { m_color = color; }
|
||||
void setMarginLeft(int margin) { m_marginLeft = margin; updateGeometry(); }
|
||||
void setMarginRight(int margin) { m_marginRight = margin; updateGeometry(); }
|
||||
void setMarginTop(int margin) { m_marginTop = margin; updateGeometry(); }
|
||||
void setMarginBottom(int margin) { m_marginBottom = margin; updateGeometry(); }
|
||||
|
||||
void hide() { setVisible(false); }
|
||||
void show() { setVisible(true); }
|
||||
void disable() { setEnabled(false); }
|
||||
void enable() { setEnabled(true); }
|
||||
|
||||
bool isEnabled() const { return m_enabled; }
|
||||
bool isVisible() const { return m_visible; }
|
||||
bool isHovered() const { return m_hovered; }
|
||||
bool isFocusable() const { return m_focusable; }
|
||||
bool isDestroyed() const { return m_destroyed; }
|
||||
bool hasChildren() const { return m_children.size() > 0; }
|
||||
bool hasFocus();
|
||||
bool hasChild(const UIWidgetPtr& child);
|
||||
|
||||
UIWidgetType getWidgetType() const { return m_type; }
|
||||
UILayoutPtr getLayout() const;
|
||||
std::string getId() const { return m_id; }
|
||||
int getChildCount() const { return m_children.size(); }
|
||||
UIWidgetPtr getParent() const { return m_parent.lock(); }
|
||||
Point getPosition() const { return m_rect.topLeft(); }
|
||||
Size getSize() const { return m_rect.size(); }
|
||||
Rect getGeometry() const { return m_rect; }
|
||||
int getX() const { return m_rect.x(); }
|
||||
int getY() const { return m_rect.y(); }
|
||||
int getWidth() const { return m_rect.width(); }
|
||||
int getHeight() const { return m_rect.height(); }
|
||||
|
||||
ImagePtr getImage() const { return m_image; }
|
||||
FontPtr getFont() const { return m_font; }
|
||||
Color getColor() const { return m_color; }
|
||||
int getMarginLeft() const { return m_marginLeft; }
|
||||
int getMarginRight() const { return m_marginRight; }
|
||||
int getMarginTop() const { return m_marginTop; }
|
||||
int getMarginBottom() const { return m_marginBottom; }
|
||||
|
||||
UIWidgetList getChildren() const { return m_children; }
|
||||
UIWidgetPtr getFocusedChild() const { return m_focusedChild; }
|
||||
UIWidgetPtr getChildAfter(const UIWidgetPtr& relativeChild);
|
||||
UIWidgetPtr getChildBefore(const UIWidgetPtr& relativeChild);
|
||||
UIWidgetPtr getChildById(const std::string& childId);
|
||||
UIWidgetPtr getChildByPos(const Point& childPos);
|
||||
UIWidgetPtr getChildByIndex(int childIndex);
|
||||
UIWidgetPtr recursiveGetChildById(const std::string& childId);
|
||||
UIWidgetPtr recursiveGetChildByPos(const Point& childPos);
|
||||
UIWidgetPtr backwardsGetWidgetById(const std::string& id);
|
||||
|
||||
void addChild(const UIWidgetPtr& childToAdd);
|
||||
void removeChild(const UIWidgetPtr& childToRemove);
|
||||
void focusChild(const UIWidgetPtr& childToFocus);
|
||||
void focusNextChild();
|
||||
void moveChildToTop(const UIWidgetPtr& childToMove);
|
||||
|
||||
// for using only with anchor layouts
|
||||
void addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge);
|
||||
void centerIn(const std::string& targetId);
|
||||
void fill(const std::string& targetId);
|
||||
|
||||
UIWidgetPtr asUIWidget() { return std::static_pointer_cast<UIWidget>(shared_from_this()); }
|
||||
|
||||
protected:
|
||||
/// Triggered when widget is moved or resized
|
||||
virtual void onGeometryUpdate() { }
|
||||
/// Triggered when widget is shown or hidden
|
||||
//virtual void onVisibilityChange(bool visible);
|
||||
/// Triggered when widget gets or loses focus
|
||||
virtual void onFocusChange(bool focused) { }
|
||||
/// Triggered when the mouse enters or leaves widget area
|
||||
virtual void onHoverChange(bool hovered) { }
|
||||
/// Triggered when user generates a text from keyboard
|
||||
virtual void onKeyboardText(const std::string& text);
|
||||
/// Triggered when user presses key while widget has focus
|
||||
virtual void onKeyPress(const UIKeyEvent& event);
|
||||
/// Triggered when user releases key while widget has focus
|
||||
virtual void onKeyRelease(const UIKeyEvent& event);
|
||||
/// Triggered when a mouse button is pressed down while mouse pointer is inside widget area
|
||||
virtual void onMousePress(const UIMouseEvent& event);
|
||||
/// Triggered when a mouse button is released
|
||||
virtual void onMouseRelease(const UIMouseEvent& event);
|
||||
/// Triggered when mouse moves (even when the mouse is outside widget area)
|
||||
virtual void onMouseMove(const UIMouseEvent& event);
|
||||
/// Triggered when mouse middle button wheels inside widget area
|
||||
virtual void onMouseWheel(const UIMouseEvent& event);
|
||||
|
||||
friend class UIManager;
|
||||
|
||||
private:
|
||||
void destroyCheck();
|
||||
|
||||
UIWidgetType m_type;
|
||||
bool m_enabled;
|
||||
bool m_visible;
|
||||
bool m_hovered;
|
||||
bool m_focusable;
|
||||
bool m_destroyed;
|
||||
bool m_updateScheduled;
|
||||
Rect m_rect;
|
||||
UILayoutPtr m_layout;
|
||||
UIWidgetWeakPtr m_parent;
|
||||
UIWidgetList m_children;
|
||||
UIWidgetPtr m_focusedChild;
|
||||
std::string m_id;
|
||||
|
||||
// basic style components used by all widgets
|
||||
ImagePtr m_image;
|
||||
FontPtr m_font;
|
||||
Color m_color;
|
||||
int m_marginLeft;
|
||||
int m_marginRight;
|
||||
int m_marginTop;
|
||||
int m_marginBottom;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,49 +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.
|
||||
*/
|
||||
#include "uiwindow.h"
|
||||
#include <graphics/borderimage.h>
|
||||
#include <graphics/font.h>
|
||||
#include <otml/otml.h>
|
||||
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uiwindow.h>
|
||||
#include <ui/uiwindowskin.h>
|
||||
|
||||
void UIWindow::onInputEvent(const InputEvent& event)
|
||||
UIWindow::UIWindow(): UIWidget(UITypeWindow)
|
||||
{
|
||||
UIContainer::onInputEvent(event);
|
||||
if(event.type == EV_MOUSE_LDOWN) {
|
||||
UIWindowSkin *skin = static_cast<UIWindowSkin*>(getSkin().get());
|
||||
Rect headRect = getRect();
|
||||
headRect.setHeight(skin->getHeadHeight());
|
||||
if(headRect.contains(event.mousePos)) {
|
||||
m_moving = true;
|
||||
m_movingReference = event.mousePos - getRect().topLeft();
|
||||
}
|
||||
} else if(event.type == EV_MOUSE_LUP) {
|
||||
if(m_moving) {
|
||||
m_moving = false;
|
||||
}
|
||||
} else if(event.type == EV_MOUSE_MOVE) {
|
||||
if(m_moving) {
|
||||
moveTo(event.mousePos - m_movingReference);
|
||||
}
|
||||
m_moving = false;
|
||||
setFocusable(true);
|
||||
}
|
||||
|
||||
UIWindowPtr UIWindow::create()
|
||||
{
|
||||
UIWindowPtr window(new UIWindow);
|
||||
window->setStyle("Window");
|
||||
return window;
|
||||
}
|
||||
|
||||
void UIWindow::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||
{
|
||||
UIWidget::loadStyleFromOTML(styleNode);
|
||||
|
||||
if(OTMLNodePtr headNode = styleNode->get("head")) {
|
||||
if(OTMLNodePtr node = headNode->get("border-image"))
|
||||
m_headImage = BorderImage::loadFromOTML(node);
|
||||
m_headHeight = headNode->readAt("height", m_headImage->getDefaultSize().height());
|
||||
m_headMargin = headNode->readAt("margin", 0);
|
||||
m_titleAlign = parseAlignment(headNode->readAt("text align", std::string("center")));
|
||||
} else {
|
||||
m_headHeight = 0;
|
||||
m_headMargin = 0;
|
||||
m_titleAlign = AlignCenter;
|
||||
}
|
||||
|
||||
if(OTMLNodePtr bodyNode = styleNode->get("body")) {
|
||||
if(OTMLNodePtr node = bodyNode->get("border-image"))
|
||||
m_bodyImage = BorderImage::loadFromOTML(node);
|
||||
}
|
||||
|
||||
m_title = styleNode->readAt("title", aux::empty_string);
|
||||
}
|
||||
|
||||
void UIWindow::render()
|
||||
{
|
||||
// draw window head
|
||||
Rect headRect = getGeometry();
|
||||
headRect.setHeight(m_headHeight);
|
||||
|
||||
if(m_headImage && m_headHeight > 0) {
|
||||
m_headImage->draw(headRect);
|
||||
|
||||
// draw window head text
|
||||
Rect headTextRect = headRect;
|
||||
if(m_titleAlign & AlignLeft)
|
||||
headTextRect.addLeft(-m_headMargin);
|
||||
else if(m_titleAlign & AlignRight)
|
||||
headTextRect.addRight(-m_headMargin);
|
||||
getFont()->renderText(m_title, headTextRect, m_titleAlign, getColor());
|
||||
}
|
||||
|
||||
// draw window body
|
||||
Rect bodyRect = getGeometry();
|
||||
bodyRect.setTop(headRect.bottom() + 1);
|
||||
if(m_bodyImage)
|
||||
m_bodyImage->draw(bodyRect);
|
||||
|
||||
// render children
|
||||
UIWidget::render();
|
||||
}
|
||||
|
||||
void UIWindow::onGeometryUpdate()
|
||||
{
|
||||
UIWidget::onGeometryUpdate();
|
||||
|
||||
// bind window rect to parent rect
|
||||
Rect boundRect = getGeometry();
|
||||
UIWidgetPtr parent = getParent();
|
||||
if(parent) {
|
||||
Rect parentRect = parent->getGeometry();
|
||||
if(boundRect.left() < parentRect.left())
|
||||
boundRect.moveLeft(parentRect.left());
|
||||
if(boundRect.top() < parentRect.top())
|
||||
boundRect.moveTop(parentRect.top());
|
||||
if(boundRect.bottom() > parentRect.bottom())
|
||||
boundRect.moveBottom(parentRect.bottom());
|
||||
if(boundRect.right() > parentRect.right())
|
||||
boundRect.moveRight(parentRect.right());
|
||||
}
|
||||
if(boundRect != getGeometry())
|
||||
setGeometry(boundRect);
|
||||
}
|
||||
|
||||
void UIWindow::onMousePress(const UIMouseEvent& event)
|
||||
{
|
||||
UIWidget::onMousePress(event);
|
||||
Rect headRect = getGeometry();
|
||||
headRect.setHeight(m_headHeight);
|
||||
if(headRect.contains(event.mousePos)) {
|
||||
m_moving = true;
|
||||
m_movingReference = event.mousePos - getGeometry().topLeft();
|
||||
}
|
||||
}
|
||||
|
||||
void UIWindow::onMouseRelease(const UIMouseEvent& event)
|
||||
{
|
||||
UIWidget::onMouseRelease(event);
|
||||
if(m_moving)
|
||||
m_moving = false;
|
||||
}
|
||||
|
||||
void UIWindow::onMouseMove(const UIMouseEvent& event)
|
||||
{
|
||||
UIWidget::onMouseMove(event);
|
||||
if(m_moving)
|
||||
move(event.mousePos - m_movingReference);
|
||||
}
|
||||
|
@@ -1,34 +1,38 @@
|
||||
#ifndef UIWINDOW_H
|
||||
#define UIWINDOW_H
|
||||
|
||||
#include <global.h>
|
||||
#include <ui/uicontainer.h>
|
||||
#include "uiwidget.h"
|
||||
|
||||
class UIWindow;
|
||||
typedef std::shared_ptr<UIWindow> UIWindowPtr;
|
||||
|
||||
class UIWindow : public UIContainer
|
||||
class UIWindow : public UIWidget
|
||||
{
|
||||
public:
|
||||
UIWindow() :
|
||||
UIContainer(UI::Window),
|
||||
m_moving(false) { }
|
||||
UIWindow();
|
||||
|
||||
static UIWindowPtr create() { return UIWindowPtr(new UIWindow); }
|
||||
static UIWindowPtr create();
|
||||
|
||||
void onInputEvent(const InputEvent& event);
|
||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||
virtual void render();
|
||||
|
||||
void setTitle(const std::string& title) { m_title = title; }
|
||||
std::string getTitle() const { return m_title; }
|
||||
|
||||
virtual const char *getLuaTypeName() const { return "UIWindow"; }
|
||||
|
||||
virtual bool isFocusable() const { return true; }
|
||||
protected:
|
||||
virtual void onGeometryUpdate();
|
||||
virtual void onMousePress(const UIMouseEvent& event);
|
||||
virtual void onMouseRelease(const UIMouseEvent& event);
|
||||
virtual void onMouseMove(const UIMouseEvent& event);
|
||||
|
||||
private:
|
||||
std::string m_title;
|
||||
bool m_moving;
|
||||
Point m_movingReference;
|
||||
|
||||
// styling
|
||||
BorderImagePtr m_headImage;
|
||||
ImagePtr m_bodyImage;
|
||||
int m_headHeight;
|
||||
int m_headMargin;
|
||||
AlignmentFlag m_titleAlign;
|
||||
};
|
||||
|
||||
#endif // UIWINDOW_H
|
||||
#endif
|
||||
|
@@ -1,66 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <graphics/fonts.h>
|
||||
#include <ui/uiwindowskin.h>
|
||||
#include <ui/uiwindow.h>
|
||||
#include <util/translator.h>
|
||||
|
||||
void UIWindowSkin::load(OTMLNode* node)
|
||||
{
|
||||
UIElementSkin::load(node);
|
||||
|
||||
OTMLNode* headNode = node->at("head");
|
||||
OTMLNode* bodyNode = node->at("body");
|
||||
|
||||
m_headImage = std::dynamic_pointer_cast<BorderedImage>(loadImage(headNode));
|
||||
m_headHeight = headNode->readAt("height", m_headImage->getDefaultSize().height());
|
||||
m_headMargin = headNode->readAt("margin", 0);
|
||||
m_titleAlign = parseAlignment(headNode->readAt("text align", std::string("center")));
|
||||
m_bodyImage = loadImage(bodyNode);
|
||||
}
|
||||
|
||||
void UIWindowSkin::draw(UIElement* element)
|
||||
{
|
||||
UIElementSkin::draw(element);
|
||||
UIWindow *window = static_cast<UIWindow*>(element);
|
||||
|
||||
// draw window head
|
||||
Rect headRect = window->getRect();
|
||||
headRect.setHeight(m_headHeight);
|
||||
m_headImage->draw(headRect);
|
||||
|
||||
// draw window head text
|
||||
Rect headTextRect = headRect;
|
||||
if(m_titleAlign & AlignLeft)
|
||||
headTextRect.addLeft(-m_headMargin);
|
||||
else if(m_titleAlign & AlignRight)
|
||||
headTextRect.addRight(-m_headMargin);
|
||||
getFont()->renderText(window->getTitle(), headTextRect, m_titleAlign, getFontColor());
|
||||
|
||||
// draw window body
|
||||
Rect bodyRect = window->getRect();
|
||||
bodyRect.setTop(headRect.bottom() + 1);
|
||||
m_bodyImage->draw(bodyRect);
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
/* 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 <global.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/uielementskin.h>
|
||||
#include <graphics/borderedimage.h>
|
||||
|
||||
class UIWindowSkin : public UIElementSkin
|
||||
{
|
||||
public:
|
||||
UIWindowSkin(const std::string& name) :
|
||||
UIElementSkin(name, UI::Window) { }
|
||||
|
||||
void load(OTMLNode* node);
|
||||
void draw(UIElement *element);
|
||||
|
||||
int getHeadHeight() const { return m_headHeight; }
|
||||
|
||||
private:
|
||||
BorderedImagePtr m_headImage;
|
||||
ImagePtr m_bodyImage;
|
||||
FontPtr m_titleFont;
|
||||
int m_headHeight;
|
||||
int m_headMargin;
|
||||
AlignmentFlag m_titleAlign;
|
||||
};
|
||||
|
||||
#endif // UIWINDOWSKIN_H
|
Reference in New Issue
Block a user