rework ui layouts, now it is more flexible and modular

master
Eduardo Bart 13 years ago
parent 6d871b305f
commit 51fe97644d

@ -90,7 +90,7 @@ SET(SOURCES
src/framework/graphics/textarea.cpp
# framework ui
src/framework/ui/uilayout.cpp
src/framework/ui/uianchorlayout.cpp
src/framework/ui/uielement.cpp
src/framework/ui/uielementskin.cpp
src/framework/ui/uibuttonskin.cpp

@ -4,8 +4,7 @@ function onEnterMenuState()
end
function onLeaveMenuState()
mainMenu:destroy()
mainMenu = nil
end
function onApplicationClose()

@ -34,7 +34,6 @@ panels:
# the default panel is empty
roundedGridPanel:
default size: [117, 171]
bordered image:
left border: [0,214,5,32]
right border: [6,214,5,32]

@ -39,6 +39,7 @@ void Engine::init()
// initialize stuff
g_graphics.init();
g_fonts.init("tibia-12px-rounded");
g_lua.init();
}
void Engine::terminate()
@ -46,6 +47,14 @@ void Engine::terminate()
// terminate stuff
g_fonts.terminate();
g_graphics.terminate();
// destroy root ui
UIContainer::getRoot()->destroy();
g_lua.terminate();
// poll remaning events
g_engine.poll();
}
void Engine::poll()
@ -116,14 +125,6 @@ void Engine::run()
m_stopping = false;
m_running = false;
g_lua.collectGarbage();
// destroy root ui
rootContainer->destroy();
// poll remaning events
g_engine.poll();
}
void Engine::stop()

@ -41,11 +41,8 @@ void registerLuaFunctions()
g_lua.registerMemberFunction("load", &lua_UI_load);
g_lua.registerMemberFunction("getRootContainer", &lua_UI_getRootContainer);
// UILayout
g_lua.registerClass("UILayout");
// UIElement
g_lua.registerClass("UIElement", "UILayout");
g_lua.registerClass("UIElement");
g_lua.registerMemberField("id", &lua_UIElement_getId, &lua_UIElement_setId);
g_lua.registerMemberField("enabled", &lua_UIElement_isEnabled, &lua_UIElement_setEnabled);
g_lua.registerMemberField("visible", &lua_UIElement_isVisible, &lua_UIElement_setVisible);
@ -224,7 +221,7 @@ int lua_UIElement_setLocked()
int lua_UIElement_destroy()
{
if(UIElementPtr element = boost::dynamic_pointer_cast<UIElement>(g_lua.popClassInstance()))
element->destroy();
element->destroyLater();
g_dispatcher.addTask(boost::bind(&LuaScript::collectGarbage, &g_lua));
return 1;
}

@ -30,7 +30,7 @@
LuaScript g_lua;
LuaScript::LuaScript()
void LuaScript::init()
{
L = luaL_newstate();
if(!L)
@ -45,9 +45,11 @@ LuaScript::LuaScript()
registerLuaFunctions();
}
LuaScript::~LuaScript()
void LuaScript::terminate()
{
collectGarbage();
lua_close(L);
L = NULL;
}
void LuaScript::loadAllModules()

@ -36,8 +36,10 @@ struct lua_State;
class LuaScript
{
public:
LuaScript();
virtual ~LuaScript();
LuaScript() : L(NULL) { }
void init();
void terminate();
void loadAllModules();
bool loadFile(const std::string& fileName);

@ -0,0 +1,162 @@
/* 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 <prerequisites.h>
#include <ui/uianchorlayout.h>
UIElementPtr Anchor::getAnchorLineElement() const
{
if(!m_anchoredElement.expired())
return m_anchoredElement.lock()->backwardsGetElementById(m_anchorLine.getElementId());
return UIElementPtr();
}
int Anchor::getAnchorLinePoint() const
{
UIElementPtr anchorLineElement = getAnchorLineElement();
if(anchorLineElement) {
switch(m_anchorLine.getEdge()) {
case UI::AnchorLeft:
return anchorLineElement->getRect().left();
case UI::AnchorRight:
return anchorLineElement->getRect().right();
case UI::AnchorTop:
return anchorLineElement->getRect().top();
case UI::AnchorBottom:
return anchorLineElement->getRect().bottom();
case UI::AnchorHorizontalCenter:
return anchorLineElement->getRect().horizontalCenter();
case UI::AnchorVerticalCenter:
return anchorLineElement->getRect().verticalCenter();
default:
break;
}
}
return -9999;
}
bool UIAnchorLayout::addAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
{
Anchor anchor(anchoredElement, anchoredEdge, anchorLine);
UIElementPtr anchorLineElement = anchor.getAnchorLineElement();
// we can never anchor with itself
if(anchoredElement == anchorLineElement) {
logError("ERROR: anchoring with itself is not possible");
return false;
}
// TODO: check if itself is already anchored on the anchor element
// setup the anchor
m_anchors.push_back(anchor);
// recalculate anchored element layout
recalculateElementLayout(anchoredElement);
return true;
}
void UIAnchorLayout::recalculateElementLayout(const UIElementPtr& element)
{
Rect rect = element->getRect();
bool verticalMoved = false;
bool horizontalMoved = false;
foreach(const Anchor& anchor, m_anchors) {
if(anchor.getAnchoredElement() == element && anchor.getAnchorLineElement()) {
int point = anchor.getAnchorLinePoint();
switch(anchor.getAnchoredEdge()) {
case UI::AnchorHorizontalCenter:
rect.moveHorizontalCenter(point + element->getMarginLeft() - element->getMarginRight());
horizontalMoved = true;
break;
case UI::AnchorLeft:
if(!horizontalMoved) {
rect.moveLeft(point + element->getMarginLeft());
horizontalMoved = true;
} else
rect.setLeft(point + element->getMarginLeft());
break;
case UI::AnchorRight:
if(!horizontalMoved) {
rect.moveRight(point - element->getMarginRight());
horizontalMoved = true;
} else
rect.setRight(point - element->getMarginRight());
break;
case UI::AnchorVerticalCenter:
rect.moveVerticalCenter(point + element->getMarginTop() - element->getMarginBottom());
verticalMoved = true;
break;
case UI::AnchorTop:
if(!verticalMoved) {
rect.moveTop(point + element->getMarginTop());
verticalMoved = true;
} else
rect.setTop(point + element->getMarginTop());
break;
case UI::AnchorBottom:
if(!verticalMoved) {
rect.moveBottom(point - element->getMarginBottom());
verticalMoved = true;
} else
rect.setBottom(point - element->getMarginBottom());
break;
default:
break;
}
}
}
if(rect != element->getRect())
element->setRect(rect);
}
void UIAnchorLayout::recalculateChildrenLayout(const UIElementPtr& parent)
{
foreach(const Anchor& anchor, m_anchors) {
if(anchor.getAnchorLineElement() == parent) {
UIElementPtr child = anchor.getAnchoredElement();
if(child)
recalculateElementLayout(child);
}
}
}
UI::AnchorPoint UIAnchorLayout::parseAnchorPoint(const std::string& anchorPointStr)
{
if(anchorPointStr == "left")
return UI::AnchorLeft;
else if(anchorPointStr == "right")
return UI::AnchorRight;
else if(anchorPointStr == "top")
return UI::AnchorTop;
else if(anchorPointStr == "bottom")
return UI::AnchorBottom;
else if(anchorPointStr == "horizontalCenter")
return UI::AnchorHorizontalCenter;
else if(anchorPointStr == "verticalCenter")
return UI::AnchorVerticalCenter;
return UI::AnchorNone;
}

@ -0,0 +1,77 @@
/* 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 UIANCHORLAYOUT_H
#define UIANCHORLAYOUT_H
#include <prerequisites.h>
#include <ui/uiconstants.h>
#include <ui/uilayout.h>
#include <ui/uielement.h>
class AnchorLine
{
public:
AnchorLine(const std::string& elementId, UI::AnchorPoint edge) : m_elementId(elementId), m_edge(edge) { }
AnchorLine(const AnchorLine& other) : m_elementId(other.m_elementId), m_edge(other.m_edge) { }
UI::AnchorPoint getEdge() const { return m_edge; }
const std::string& getElementId() const { return m_elementId; }
private:
std::string m_elementId;
UI::AnchorPoint m_edge;
};
class Anchor
{
public:
Anchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const AnchorLine& anchorLine)
: m_anchoredElement(anchoredElement), m_anchoredEdge(anchoredEdge), m_anchorLine(anchorLine) { }
UIElementPtr getAnchorLineElement() const ;
UIElementPtr getAnchoredElement() const { return m_anchoredElement.lock(); }
UI::AnchorPoint getAnchoredEdge() const { return m_anchoredEdge; }
int getAnchorLinePoint() const;
private:
UIElementWeakPtr m_anchoredElement;
UI::AnchorPoint m_anchoredEdge;
AnchorLine m_anchorLine;
};
class UIAnchorLayout : public UILayout
{
public:
bool addAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const AnchorLine& anchorLine);
void recalculateElementLayout(const UIElementPtr& element);
void recalculateChildrenLayout(const UIElementPtr& parent);
static UI::AnchorPoint parseAnchorPoint(const std::string& anchorPointStr);
private:
std::vector<Anchor> m_anchors;
};
typedef boost::shared_ptr<UIAnchorLayout> UIAnchorLayoutPtr;
#endif // UIANCHORLAYOUT_H

@ -29,9 +29,9 @@
void UIButton::onInputEvent(const InputEvent& event)
{
if(event.type == EV_MOUSE_LDOWN && getRect().contains(event.mousePos)) {
m_state = UI::ButtonDown;
} else if(event.type == EV_MOUSE_LUP && m_state == UI::ButtonDown) {
m_state = UI::ButtonUp;
m_state = ButtonDown;
} else if(event.type == EV_MOUSE_LUP && m_state == ButtonDown) {
m_state = ButtonUp;
if(getRect().contains(event.mousePos)) {
g_dispatcher.addTask(boost::bind(&Scriptable::callLuaTableField, shared_from_this(), "onClick"));
}

@ -35,22 +35,29 @@ typedef boost::shared_ptr<UIButton> UIButtonPtr;
class UIButton : public UIElement
{
public:
enum ButtonState
{
ButtonUp,
ButtonDown,
ButtonMouseOver
};
UIButton() :
UIElement(UI::Button),
m_state(UI::ButtonUp) { }
m_state(ButtonUp) { }
void onInputEvent(const InputEvent& event);
void setText(const std::string& text) { m_text = text; }
std::string getText() const { return m_text; }
UI::EButtonState getState() { return m_state; }
ButtonState getState() { return m_state; }
virtual const char *getScriptableName() const { return "UIButton"; }
private:
std::string m_text;
UI::EButtonState m_state;
ButtonState m_state;
};
#endif // UIBUTTON_H

@ -50,10 +50,10 @@ void UIButtonSkin::draw(UIElement *element)
Rect textRect = button->getRect();
if(button->getState() == UI::ButtonDown && m_buttonDownImage) {
if(button->getState() == UIButton::ButtonDown && m_buttonDownImage) {
m_buttonDownImage->draw(element->getRect());
textRect.translate(m_buttonDownTranslate);
} else if(button->getState() == UI::ButtonMouseOver && m_buttonHoverImage) {
} else if(button->getState() == UIButton::ButtonMouseOver && m_buttonHoverImage) {
m_buttonHoverImage->draw(element->getRect());
} else {
UIElementSkin::draw(element);

@ -25,7 +25,7 @@
#include <prerequisites.h>
#include <ui/uicheckbox.h>
UICheckBox::UICheckBox(UI::EElementType type): UIElement(type)
UICheckBox::UICheckBox(UI::ElementType type): UIElement(type)
{
}

@ -31,7 +31,7 @@
class UICheckBox : public UIElement
{
public:
UICheckBox(UI::EElementType type = UI::Element);
UICheckBox(UI::ElementType type = UI::Element);
virtual const char *getScriptableName() const { return "UICheckBox"; }
};

@ -26,33 +26,7 @@
#define UICONSTANTS_H
namespace UI {
enum EButtonState
{
ButtonUp,
ButtonDown,
ButtonMouseOver
};
enum EButtonEvent
{
ButtonPressUp,
ButtonPressDown,
ButtonEnterMouseOver,
ButtonLeaveMouseOver
};
enum EMessageBoxFlags
{
MessageBoxOk = 1 << 0,
MessageBoxCancel = 1 << 1,
MessageBoxYes = 1 << 2,
MessageBoxNo = 1 << 3,
MessageBoxOkCancel = MessageBoxOk | MessageBoxCancel,
MessageBoxYesNo = MessageBoxYes | MessageBoxNo
};
enum EElementType
{
enum ElementType {
Element = 0,
Container,
Panel,
@ -63,6 +37,16 @@ namespace UI {
CheckBox,
LineDecoration
};
enum AnchorPoint {
AnchorLeft = 0,
AnchorHorizontalCenter,
AnchorRight,
AnchorTop,
AnchorVerticalCenter,
AnchorBottom,
AnchorNone
};
}
#endif // UICONSTANTS_H

@ -25,9 +25,10 @@
#include <prerequisites.h>
#include <core/resources.h>
#include <ui/uicontainer.h>
#include <ui/uianchorlayout.h>
#include <core/dispatcher.h>
void UIContainer::internalOnDestroy()
void UIContainer::destroy()
{
//logTraceDebug(getId());
@ -37,11 +38,11 @@ void UIContainer::internalOnDestroy()
// destroy children
while(m_children.size() > 0) {
UIElementPtr element = m_children.back(); //hold reference
element->internalOnDestroy();
UIElementPtr element = m_children.front(); //hold reference
element->destroy();
}
UIElement::internalOnDestroy();
UIElement::destroy();
}
UIContainerPtr& UIContainer::getRoot()
@ -50,6 +51,7 @@ UIContainerPtr& UIContainer::getRoot()
if(!rootContainer) {
rootContainer = UIContainerPtr(new UIContainer);
rootContainer->setId("root");
rootContainer->setLayout(UILayoutPtr(new UIAnchorLayout));
}
return rootContainer;
}
@ -87,9 +89,15 @@ bool UIContainer::hasChild(const UIElementPtr& child)
UIElementPtr UIContainer::getChildById(const std::string& id)
{
if(getId() == id)
if(getId() == id || id == "self")
return asUIElement();
if(id == "parent")
return getParent();
if(id == "root")
return getRoot();
foreach(const UIElementPtr& child, m_children) {
if(child->getId() == id)
return child;
@ -111,9 +119,15 @@ UIElementPtr UIContainer::getChildByPos(const Point& pos)
UIElementPtr UIContainer::recursiveGetChildById(const std::string& id)
{
if(getId() == id)
if(getId() == id || id == "self")
return asUIElement();
if(id == "parent")
return getParent();
if(id == "root")
return getRoot();
foreach(const UIElementPtr& element, m_children) {
if(element->getId() == id)
return element;

@ -31,10 +31,11 @@
class UIContainer : public UIElement
{
public:
UIContainer(UI::EElementType type = UI::Container) :
UIContainer(UI::ElementType type = UI::Container) :
UIElement(type) { }
virtual ~UIContainer() { }
virtual void destroy();
virtual void onLoad();
virtual void render();
virtual void onInputEvent(const InputEvent& event);
@ -70,7 +71,7 @@ public:
/// Get focused element
UIElementPtr getFocusedElement() const { return m_focusedElement; }
virtual UI::EElementType getElementType() const { return UI::Container; }
virtual UI::ElementType getElementType() const { return UI::Container; }
UIContainerPtr asUIContainer() { return boost::static_pointer_cast<UIContainer>(shared_from_this()); }
virtual const char *getScriptableName() const { return "UIContainer"; }
@ -78,9 +79,6 @@ public:
/// Get root container (the container that contains everything)
static UIContainerPtr& getRoot();
protected:
virtual void internalOnDestroy();
private:
std::list<UIElementPtr> m_children;
std::list<UIElementPtr> m_lockedElements;

@ -30,11 +30,15 @@
#include <ui/uielementskin.h>
#include <ui/uicontainer.h>
UIElement::UIElement(UI::EElementType type) :
UILayout(),
UIElement::UIElement(UI::ElementType type) :
Scriptable(),
m_type(type),
m_visible(true),
m_enabled(true)
m_enabled(true),
m_marginLeft(0),
m_marginRight(0),
m_marginTop(0),
m_marginBottom(0)
{
}
@ -44,13 +48,13 @@ UIElement::~UIElement()
//logTraceDebug(getId());
}
void UIElement::destroy()
void UIElement::destroyLater()
{
//logTraceDebug(getId());
g_dispatcher.addTask(boost::bind(&UIElement::internalOnDestroy, asUIElement()));
g_dispatcher.addTask(boost::bind(&UIElement::destroy, asUIElement()));
}
void UIElement::internalOnDestroy()
void UIElement::destroy()
{
//logTraceDebug(getId());
@ -58,17 +62,16 @@ void UIElement::internalOnDestroy()
callLuaTableField("onDestroy");
// remove from parent
if(getParent()) {
if(getParent())
getParent()->removeChild(me);
}
// free script stuff
releaseLuaTableRef();
g_dispatcher.addTask(boost::bind(&UIElement::internalDestroyCheck, asUIElement()));
g_dispatcher.addTask(boost::bind(&UIElement::destroyCheck, me));
}
void UIElement::internalDestroyCheck()
void UIElement::destroyCheck()
{
//logTraceDebug(getId());
@ -79,6 +82,29 @@ void UIElement::internalDestroyCheck()
}
}
void UIElement::setSize(const Size& size)
{
Rect rect = getRect();
if(rect.isValid())
rect.setSize(size);
else
rect = Rect(0, 0, size);
setRect(rect);
getLayout()->recalculateElementLayout(asUIElement());
}
void UIElement::setRect(const Rect& rect)
{
if(rect != m_rect) {
m_rect = rect;
// rect updated, recalculate children layout
getLayout()->recalculateChildrenLayout(asUIElement());
onRectUpdate();
}
}
void UIElement::setSkin(const UIElementSkinPtr& skin)
{
m_skin = skin;
@ -100,11 +126,16 @@ void UIElement::render()
UIElementPtr UIElement::backwardsGetElementById(const std::string& id)
{
if(getId() == id)
if(getId() == id || id == "self")
return asUIElement();
UIElementPtr element;
if(id == "parent")
return getParent();
if(id == "root")
return UIContainer::getRoot();
UIElementPtr element;
if(asUIContainer()) {
element = asUIContainer()->recursiveGetChildById(id);
if(element)
@ -169,3 +200,12 @@ void UIElement::setFocused(bool focused)
}
}
}
UILayoutPtr UIElement::getLayout() const
{
if(m_layout)
return m_layout;
else if(getParent())
return getParent()->getLayout();
return UILayoutPtr();
}

@ -27,9 +27,10 @@
#include <prerequisites.h>
#include <core/input.h>
#include <script/scriptable.h>
#include <ui/uiconstants.h>
#include <ui/uilayout.h>
#include <ui/uielementskin.h>
#include <ui/uilayout.h>
class UIElementSkin;
@ -41,18 +42,15 @@ class UIElement;
typedef boost::shared_ptr<UIElement> UIElementPtr;
typedef boost::weak_ptr<UIElement> UIElementWeakPtr;
typedef boost::function<void(UIElementPtr)> UIElementCallback;
class UIElement : public UILayout
class UIElement : public Scriptable
{
public:
UIElement(UI::EElementType type = UI::Element);
UIElement(UI::ElementType type = UI::Element);
virtual ~UIElement();
/// Destroy this element by removing it from its parent
void destroy();
virtual void internalOnDestroy();
virtual void internalDestroyCheck();
void destroyLater();
virtual void destroy();
virtual void destroyCheck();
/// Draw element
virtual void render();
@ -61,11 +59,15 @@ public:
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 setLayout(const UILayoutPtr& layout) { m_layout = layout; }
UILayoutPtr getLayout() const;
void setSkin(const UIElementSkinPtr& skin);
UIElementSkinPtr getSkin() const { return m_skin; }
@ -85,19 +87,48 @@ public:
bool isVisible() const { return m_visible; }
virtual bool isFocusable() const { return false; }
UI::EElementType getElementType() const { return m_type; }
UI::ElementType getElementType() const { return m_type; }
UIElementPtr asUIElement() { return boost::static_pointer_cast<UIElement>(shared_from_this()); }
virtual UIContainerPtr asUIContainer() { return UIContainerPtr(); }
virtual const char *getScriptableName() const { return "UIElement"; }
void setSize(const Size& size);
Size getSize() { return m_rect.size(); }
/// 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; }
// margins
void setMargin(int top, int left, int bottom, int right) { m_marginLeft = left; m_marginRight = right; m_marginTop = top; m_marginBottom = bottom; getLayout()->recalculateElementLayout(asUIElement()); }
void setMargin(int horizontal, int vertical) { 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; }
private:
UI::EElementType m_type;
UI::ElementType m_type;
UIContainerWeakPtr m_parent;
UIElementSkinPtr m_skin;
UILayoutPtr m_layout;
std::string m_id;
bool m_visible;
bool m_enabled;
Rect m_rect;
int m_marginLeft;
int m_marginRight;
int m_marginTop;
int m_marginBottom;
};
#endif // UIELEMENT_H

@ -34,7 +34,7 @@ class UIElement;
class UIElementSkin
{
public:
UIElementSkin(const std::string& name, UI::EElementType elementType) :
UIElementSkin(const std::string& name, UI::ElementType elementType) :
m_name(name),
m_elementType(elementType) { }
UIElementSkin() : m_elementType(UI::Element) { }
@ -49,7 +49,7 @@ public:
const std::string& getName() const { return m_name; }
const Size& getDefaultSize() const { return m_defaultSize; }
UI::EElementType getElementType() const { return m_elementType; }
UI::ElementType getElementType() const { return m_elementType; }
ImagePtr getDefaultImage() const { return m_defaultImage; }
protected:
@ -57,7 +57,7 @@ protected:
private:
std::string m_name;
UI::EElementType m_elementType;
UI::ElementType m_elementType;
Size m_defaultSize;
ImagePtr m_defaultImage;
};

@ -1,160 +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 <prerequisites.h>
#include <ui/uielement.h>
#include <ui/uilayout.h>
int AnchorLine::getPos() const
{
UILayoutPtr element = m_relativeElement.lock();
if(element) {
switch(m_anchorType) {
case ANCHOR_LEFT:
return element->getRect().left();
case ANCHOR_RIGHT:
return element->getRect().right();
case ANCHOR_TOP:
return element->getRect().top();
case ANCHOR_BOTTOM:
return element->getRect().bottom();
case ANCHOR_HORIZONTAL_CENTER:
return element->getRect().horizontalCenter();
case ANCHOR_VERTICAL_CENTER:
return element->getRect().verticalCenter();
default:
return 0;
}
}
logError("ERROR: anchor line of an element has expired, your UI is missconfigured");
return 0;
}
void UILayout::setSize(const Size& size)
{
if(m_rect.isValid())
m_rect.setSize(size);
else
m_rect = Rect(0, 0, size);
// rect updated, recalculate itself and anchored elements positions
recalculateLayout();
}
void UILayout::setRect(const Rect& rect)
{
m_rect = rect;
// rect updated, recalculate itself and anchored elements positions
recalculateAnchoredLayout();
}
bool UILayout::addAnchor(EAnchorType type, const AnchorLine& anchorLine)
{
// we can never anchor with itself
if(anchorLine.getRelativeElement() == asUILayout()) {
logError("ERROR: anchoring with itself is not possible");
return false;
}
// check if this layout is already anchored with the relative element
// this only happens in missconfigurations
for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) {
if((*it).lock() == anchorLine.getRelativeElement()) {
logError("ERROR: anchoring elements with each other is not possible");
return false;
}
}
// setup the anchor
m_anchors[type] = anchorLine;
anchorLine.getRelativeElement()->addAnchoredElement(asUILayout());
// recalculate itself and anchored elements
recalculateLayout();
return true;
}
void UILayout::addAnchoredElement(UILayoutPtr anchoredElement)
{
// check if is already anchored
bool found = false;
for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) {
if((*it).lock() == anchoredElement) {
found = true;
break;
}
}
// if not, anchor it
if(!found)
m_anchoredElements.push_back(anchoredElement);
}
void UILayout::recalculateLayout()
{
// recalculate horizontal position
if(m_anchors[ANCHOR_HORIZONTAL_CENTER].isValid()) {
m_rect.moveHorizontalCenter(m_anchors[ANCHOR_HORIZONTAL_CENTER].getPos() + m_marginLeft - m_marginRight);
} else {
if(m_anchors[ANCHOR_LEFT].isValid() && m_anchors[ANCHOR_RIGHT].isValid()) {
m_rect.setLeft(m_anchors[ANCHOR_LEFT].getPos() + m_marginLeft);
m_rect.setRight(m_anchors[ANCHOR_RIGHT].getPos() - m_marginRight);
} else if(m_anchors[ANCHOR_LEFT].isValid()) {
m_rect.moveLeft(m_anchors[ANCHOR_LEFT].getPos() + m_marginLeft);
} else if(m_anchors[ANCHOR_RIGHT].isValid()) {
m_rect.moveRight(m_anchors[ANCHOR_RIGHT].getPos() - m_marginRight);
}
}
// recalculate vertical position
if(m_anchors[ANCHOR_VERTICAL_CENTER].isValid()) {
m_rect.moveVerticalCenter(m_anchors[ANCHOR_VERTICAL_CENTER].getPos() + m_marginTop - m_marginBottom);
} else {
if(m_anchors[ANCHOR_TOP].isValid() && m_anchors[ANCHOR_BOTTOM].isValid()) {
m_rect.setTop(m_anchors[ANCHOR_TOP].getPos() + m_marginTop);
m_rect.setBottom(m_anchors[ANCHOR_BOTTOM].getPos() - m_marginBottom);
} else if(m_anchors[ANCHOR_TOP].isValid()) {
m_rect.moveTop(m_anchors[ANCHOR_TOP].getPos() + m_marginTop);
} else if(m_anchors[ANCHOR_BOTTOM].isValid()) {
m_rect.moveBottom(m_anchors[ANCHOR_BOTTOM].getPos() - m_marginBottom);
}
}
recalculateAnchoredLayout();
// fire layout update event
onLayoutRectChange(m_rect);
}
void UILayout::recalculateAnchoredLayout()
{
// recalculate anchored elements positions
for(auto it = m_anchoredElements.begin(); it != m_anchoredElements.end(); ++it) {
UILayoutPtr element = (*it).lock();
if(element)
element->recalculateLayout();
}
}

@ -26,109 +26,20 @@
#define UILAYOUT_H
#include <prerequisites.h>
#include <ui/uiconstants.h>
#include <script/scriptable.h>
enum EAnchorType {
ANCHOR_LEFT = 0,
ANCHOR_RIGHT,
ANCHOR_TOP,
ANCHOR_BOTTOM,
ANCHOR_HORIZONTAL_CENTER,
ANCHOR_VERTICAL_CENTER,
ANCHOR_NONE
};
class UIElement;
typedef boost::shared_ptr<UIElement> UIElementPtr;
class UILayout;
typedef boost::shared_ptr<UILayout> UILayoutPtr;
typedef boost::weak_ptr<UILayout> UILayoutWeakPtr;
class AnchorLine
class UILayout : public boost::enable_shared_from_this<UILayout>
{
public:
AnchorLine() : m_anchorType(ANCHOR_NONE) { }
AnchorLine(const AnchorLine& other) :
m_relativeElement(other.m_relativeElement), m_anchorType(other.m_anchorType) { }
AnchorLine(UILayoutPtr relativeElement, EAnchorType anchorType) :
m_relativeElement(relativeElement), m_anchorType(anchorType) { }
bool isValid() const { return (m_anchorType != ANCHOR_NONE && !m_relativeElement.expired()); }
/// Get the position relative to this anchor line
int getPos() const;
EAnchorType getAnchorType() const { return m_anchorType; }
UILayoutPtr getRelativeElement() const { return m_relativeElement.lock(); }
private:
UILayoutWeakPtr m_relativeElement;
EAnchorType m_anchorType;
};
class UILayout : public Scriptable
{
public:
UILayout() : Scriptable(),
m_marginLeft(0),
m_marginRight(0),
m_marginTop(0),
m_marginBottom(0) { }
virtual ~UILayout() { }
void setSize(const Size& size);
Size getSize() { return m_rect.size(); }
/// 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; }
// anchors add methods
bool addAnchor(EAnchorType type, const AnchorLine& anchorLine);
void anchorLeft(const AnchorLine& anchorLine) { addAnchor(ANCHOR_LEFT, anchorLine); }
void anchorRight(const AnchorLine& anchorLine) { addAnchor(ANCHOR_RIGHT, anchorLine); }
void anchorTop(const AnchorLine& anchorLine) { addAnchor(ANCHOR_TOP, anchorLine); }
void anchorBottom(const AnchorLine& anchorLine) { addAnchor(ANCHOR_BOTTOM, anchorLine); }
void anchorHorizontalCenter(const AnchorLine& anchorLine) { addAnchor(ANCHOR_HORIZONTAL_CENTER, anchorLine); }
void anchorVerticalCenter(const AnchorLine& anchorLine) { addAnchor(ANCHOR_VERTICAL_CENTER, anchorLine); }
// anchor lines
AnchorLine left() { return AnchorLine(asUILayout(), ANCHOR_LEFT); }
AnchorLine right() { return AnchorLine(asUILayout(), ANCHOR_RIGHT); }
AnchorLine top() { return AnchorLine(asUILayout(), ANCHOR_TOP); }
AnchorLine bottom() { return AnchorLine(asUILayout(), ANCHOR_BOTTOM); }
AnchorLine horizontalCenter() { return AnchorLine(asUILayout(), ANCHOR_HORIZONTAL_CENTER); }
AnchorLine verticalCenter() { return AnchorLine(asUILayout(), ANCHOR_VERTICAL_CENTER); }
// margins
void setMargin(int top, int left, int bottom, int right) { m_marginLeft = left; m_marginRight = right; m_marginTop = top; m_marginBottom = bottom; recalculateLayout(); }
void setMargin(int horizontal, int vertical) { m_marginLeft = m_marginRight = horizontal; m_marginTop = m_marginBottom = vertical; recalculateLayout(); }
void setMargin(int margin) { m_marginLeft = m_marginRight = m_marginTop = m_marginBottom = margin; recalculateLayout(); }
void setMarginLeft(int margin) { m_marginLeft = margin; recalculateLayout(); }
void setMarginRight(int margin) { m_marginRight = margin; recalculateLayout(); }
void setMarginTop(int margin) { m_marginTop = margin; recalculateLayout(); }
void setMarginBottom(int margin) { m_marginBottom = margin; recalculateLayout(); }
UILayoutPtr asUILayout() { return boost::static_pointer_cast<UILayout>(shared_from_this()); }
virtual const char *getScriptableName() const { return "UILayout"; }
protected:
virtual void onLayoutRectChange(const Rect& newRect) { }
private:
/// Recalculate itself and anchored elements positions
void recalculateLayout();
/// Recalculate anchored elements positions
void recalculateAnchoredLayout();
void addAnchoredElement(UILayoutPtr anchoredElement);
AnchorLine m_anchors[6];
UILayout() { }
Rect m_rect;
int m_marginLeft;
int m_marginRight;
int m_marginTop;
int m_marginBottom;
std::list<UILayoutWeakPtr> m_anchoredElements;
virtual void recalculateElementLayout(const UIElementPtr& element) = 0;
virtual void recalculateChildrenLayout(const UIElementPtr& parent) = 0;
};
#endif // UILAYOUT_H
#endif // UILAYOUT_H

@ -28,6 +28,7 @@
#include <ui/uiloader.h>
#include <script/luascript.h>
#include <script/luafunctions.h>
#include "uianchorlayout.h"
UIElementPtr UILoader::createElementFromId(const std::string& id)
{
@ -206,22 +207,22 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
}
if(node.FindValue("anchors.left"))
loadElementAnchor(element, ANCHOR_LEFT, node["anchors.left"]);
loadElementAnchor(element, UI::AnchorLeft, node["anchors.left"]);
if(node.FindValue("anchors.right"))
loadElementAnchor(element, ANCHOR_RIGHT, node["anchors.right"]);
loadElementAnchor(element, UI::AnchorRight, node["anchors.right"]);
if(node.FindValue("anchors.top"))
loadElementAnchor(element, ANCHOR_TOP, node["anchors.top"]);
loadElementAnchor(element, UI::AnchorTop, node["anchors.top"]);
if(node.FindValue("anchors.bottom"))
loadElementAnchor(element, ANCHOR_BOTTOM, node["anchors.bottom"]);
loadElementAnchor(element, UI::AnchorBottom, node["anchors.bottom"]);
if(node.FindValue("anchors.horizontalCenter"))
loadElementAnchor(element, ANCHOR_HORIZONTAL_CENTER, node["anchors.horizontalCenter"]);
loadElementAnchor(element, UI::AnchorHorizontalCenter, node["anchors.horizontalCenter"]);
if(node.FindValue("anchors.verticalCenter"))
loadElementAnchor(element, ANCHOR_VERTICAL_CENTER, node["anchors.verticalCenter"]);
loadElementAnchor(element, UI::AnchorVerticalCenter, node["anchors.verticalCenter"]);
// load events
if(node.FindValue("onLoad")) {
@ -261,8 +262,14 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node)
}
}
void UILoader::loadElementAnchor(const UIElementPtr& element, EAnchorType type, const YAML::Node& node)
void UILoader::loadElementAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const YAML::Node& node)
{
UIAnchorLayoutPtr layout = boost::dynamic_pointer_cast<UIAnchorLayout>(anchoredElement->getLayout());
if(!layout) {
logError(YAML::Exception(node.GetMark(), "could not add anchor, because this element does not participate of an anchor layout").what());
return;
}
std::string anchorDescription;
node >> anchorDescription;
@ -273,44 +280,16 @@ void UILoader::loadElementAnchor(const UIElementPtr& element, EAnchorType type,
return;
}
std::string relativeElementId = split[0];
std::string relativeAnchorTypeId = split[1];
EAnchorType relativeAnchorType;
if(relativeAnchorTypeId == "left")
relativeAnchorType = ANCHOR_LEFT;
else if(relativeAnchorTypeId == "right")
relativeAnchorType = ANCHOR_RIGHT;
else if(relativeAnchorTypeId == "top")
relativeAnchorType = ANCHOR_TOP;
else if(relativeAnchorTypeId == "bottom")
relativeAnchorType = ANCHOR_BOTTOM;
else if(relativeAnchorTypeId == "horizontalCenter")
relativeAnchorType = ANCHOR_HORIZONTAL_CENTER;
else if(relativeAnchorTypeId == "verticalCenter")
relativeAnchorType = ANCHOR_VERTICAL_CENTER;
else {
std::string anchorLineElementId = split[0];
UI::AnchorPoint anchorLineEdge = UIAnchorLayout::parseAnchorPoint(split[1]);
if(anchorLineEdge == UI::AnchorNone) {
logError(YAML::Exception(node.GetMark(), "invalid anchor type").what());
return;
}
UILayoutPtr relativeElement;
if(relativeElementId == "parent" && element->getParent()) {
relativeElement = element->getParent()->asUILayout();
} else if(relativeElementId == "root") {
relativeElement = UIContainer::getRoot();
} else {
UIElementPtr tmp = element->backwardsGetElementById(relativeElementId);
if(tmp)
relativeElement = tmp->asUILayout();
}
if(relativeElement) {
if(!element->addAnchor(type, AnchorLine(relativeElement, relativeAnchorType)))
logError(YAML::Exception(node.GetMark(), "anchoring failed").what());
} else {
logError(YAML::Exception(node.GetMark(), "anchoring failed, does the relative element really exists?").what());
}
if(!layout->addAnchor(anchoredElement, anchoredEdge, AnchorLine(anchorLineElementId, anchorLineEdge)))
logError(YAML::Exception(node.GetMark(), "anchoring failed").what());
}
void UILoader::loadButton(const UIButtonPtr& button, const YAML::Node& node)

@ -50,7 +50,7 @@ private:
static void loadElement(const UIElementPtr& element, const YAML::Node& node);
/// Load anchor from a YAML node
static void loadElementAnchor(const UIElementPtr& element, EAnchorType type, const YAML::Node& node);
static void loadElementAnchor(const UIElementPtr& anchoredElement, UI::AnchorPoint anchoredEdge, const YAML::Node& node);
// specific elements loading
static void loadButton(const UIButtonPtr& button, const YAML::Node& node);

@ -136,7 +136,7 @@ void UISkins::terminate()
}
UIElementSkinPtr UISkins::getElementSkin(UI::EElementType elementType, const std::string& name)
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);

@ -37,7 +37,7 @@ public:
void load(const std::string& skinsFile);
void terminate();
UIElementSkinPtr getElementSkin(UI::EElementType elementType, const std::string& name = "default");
UIElementSkinPtr getElementSkin(UI::ElementType elementType, const std::string& name = "default");
TexturePtr getDefaultTexture() { return m_defaultTexture; }
private:

@ -60,10 +60,10 @@ void UITextEdit::onInputEvent(const InputEvent& event)
}
}
void UITextEdit::onLayoutRectChange(const Rect& rect)
void UITextEdit::onRectUpdate()
{
UITextEditSkin *skin = static_cast<UITextEditSkin*>(getSkin().get());
Rect textRect = rect;
Rect textRect = getRect();
int margin = skin->getTextMargin();
textRect.setLeft(textRect.left()+margin);
textRect.setRight(textRect.right()-margin);

@ -37,7 +37,7 @@ public:
UITextEdit();
void onInputEvent(const InputEvent& event);
void onLayoutRectChange(const Rect& rect);
void onRectUpdate();
void onFocusChange();
void setText(const std::string& text);

@ -38,7 +38,7 @@ template <class T>
class TSize
{
public:
inline TSize() : wd(0), ht(0) {};
inline TSize() : wd(-1), ht(-1) {};
inline TSize(T width, T height) : wd(width), ht(height) { };
inline TSize(const TSize<T>& other) : wd(other.wd), ht(other.ht) { };

Loading…
Cancel
Save