tibia-client/src/framework/ui/uiwidget.cpp

984 lines
27 KiB
C++
Raw Normal View History

2011-08-14 04:09:11 +02:00
#include "uiwidget.h"
#include "uimanager.h"
2011-08-15 16:06:15 +02:00
#include <framework/core/eventdispatcher.h>
#include <framework/graphics/image.h>
#include <framework/graphics/borderimage.h>
#include <framework/graphics/fontmanager.h>
#include <framework/otml/otmlnode.h>
#include <framework/graphics/graphics.h>
2011-08-21 21:43:05 +02:00
#include "uianchor.h"
2011-08-14 04:09:11 +02:00
UIWidget::UIWidget(UIWidgetType type)
{
m_type = type;
m_visible = true;
m_enabled = true;
m_hovered = false;
2011-08-21 03:01:46 +02:00
m_focusable = true;
2011-08-14 04:09:11 +02:00
m_destroyed = false;
2011-08-21 21:43:05 +02:00
m_layoutUpdated = true;
m_updateEventScheduled = false;
m_layoutUpdateScheduled = false;
m_childrenLayoutUpdateScheduled = false;
m_opacity = 255;
2011-08-14 04:09:11 +02:00
m_marginLeft = m_marginRight = m_marginTop = m_marginBottom = 0;
2011-08-20 22:30:41 +02:00
m_backgroundColor = Color::white;
m_foregroundColor = Color::white;
2011-08-14 04:09:11 +02:00
// generate an unique id, this is need because anchored layouts find widgets by id
static unsigned long id = 1;
2011-08-15 16:06:15 +02:00
m_id = fw::mkstr("widget", id++);
2011-08-14 04:09:11 +02:00
}
UIWidget::~UIWidget()
{
if(!m_destroyed)
2011-08-20 22:30:41 +02:00
logWarning("widget '", m_id, "' was destructed without being explicit destroyed");
2011-08-14 04:09:11 +02:00
}
UIWidgetPtr UIWidget::create()
{
UIWidgetPtr widget(new UIWidget);
return widget;
}
void UIWidget::destroy()
{
//TODO: onDestroy event
// destroy only once
if(!m_destroyed) {
2011-08-20 22:30:41 +02:00
releaseLuaFieldsTable();
2011-08-14 04:09:11 +02:00
// clear additional reference
m_lockedWidgets.clear();
2011-08-14 04:09:11 +02:00
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
2011-08-20 22:30:41 +02:00
logWarning("attempt to destroy widget '", m_id, "' again");
2011-08-14 04:09:11 +02:00
}
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)
2011-08-20 22:30:41 +02:00
logWarning("destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left");
}
2011-08-14 04:09:11 +02:00
void UIWidget::loadStyleFromOTML(const OTMLNodePtr& styleNode)
{
assert(!m_destroyed);
// load styles used by all widgets
for(const OTMLNodePtr& node : styleNode->children()) {
2011-08-14 04:09:11 +02:00
// 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()));
}
// font color
2011-08-20 22:30:41 +02:00
else if(node->tag() == "color") {
setForegroundColor(node->value<Color>());
}
2011-08-14 04:09:11 +02:00
// color
2011-08-20 22:30:41 +02:00
else if(node->tag() == "background-color") {
setBackgroundColor(node->value<Color>());
2011-08-14 04:09:11 +02:00
}
// opacity
else if(node->tag() == "opacity") {
setOpacity(node->value<int>());
}
2011-08-14 04:09:11 +02:00
// size
else if(node->tag() == "size") {
resize(node->value<Size>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "width") {
setWidth(node->value<int>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "height") {
setHeight(node->value<int>());
2011-08-14 04:09:11 +02:00
}
// position
else if(node->tag() == "position") {
move(node->value<Point>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "x") {
setX(node->value<int>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "y") {
setY(node->value<int>());
2011-08-14 04:09:11 +02:00
}
// margins
else if(node->tag() == "margin.left") {
setMarginLeft(node->value<int>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "margin.right") {
setMarginRight(node->value<int>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "margin.top") {
setMarginTop(node->value<int>());
2011-08-14 04:09:11 +02:00
}
else if(node->tag() == "margin.bottom") {
setMarginBottom(node->value<int>());
2011-08-14 04:09:11 +02:00
}
// 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 {
2011-08-21 21:43:05 +02:00
AnchorEdge myEdge = fw::translateAnchorEdge(what);
2011-08-14 04:09:11 +02:00
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];
2011-08-21 21:43:05 +02:00
AnchorEdge hookedEdge = fw::translateAnchorEdge(split[1]);
2011-08-14 04:09:11 +02:00
if(myEdge == AnchorNone)
throw OTMLException(node, "invalid anchor edge");
2011-08-21 21:43:05 +02:00
if(hookedEdge == AnchorNone)
2011-08-14 04:09:11 +02:00
throw OTMLException(node, "invalid anchor target edge");
2011-08-21 21:43:05 +02:00
addAnchor(myEdge, target, hookedEdge);
2011-08-14 04:09:11 +02:00
}
}
else if(node->tag() == "onLoad") {
g_lua.loadFunction(node->value<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
luaSetField("onLoad");
}
2011-08-14 04:09:11 +02:00
}
}
void UIWidget::render()
{
assert(!m_destroyed);
if(m_image)
2011-08-21 21:43:05 +02:00
m_image->draw(getRect());
2011-08-14 04:09:11 +02:00
for(const UIWidgetPtr& child : m_children) {
2011-08-21 03:01:46 +02:00
if(child->isExplicitlyVisible()) {
int oldOpacity = g_graphics.getOpacity();
if(child->getOpacity() < oldOpacity)
g_graphics.setOpacity(child->getOpacity());
2011-08-20 22:30:41 +02:00
g_graphics.bindColor(child->getBackgroundColor());
2011-08-14 04:09:11 +02:00
child->render();
2011-08-20 22:30:41 +02:00
// debug draw box
//g_graphics.bindColor(Color::green);
2011-08-21 21:43:05 +02:00
//g_graphics.drawBoundingRect(child->getRect());
2011-08-20 22:30:41 +02:00
g_graphics.setOpacity(oldOpacity);
}
2011-08-14 04:09:11 +02:00
}
}
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
2011-08-21 21:43:05 +02:00
updateLayout();
2011-08-14 04:09:11 +02:00
} catch(std::exception& e) {
2011-08-20 22:30:41 +02:00
logError("couldn't change widget '", m_id, "' style: ", e.what());
2011-08-14 04:09:11 +02:00
}
}
2011-08-21 21:43:05 +02:00
void UIWidget::setRect(const Rect& rect)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
Rect oldRect = m_rect;
m_rect = rect;
2011-08-21 21:43:05 +02:00
// updates children geometry
updateChildrenLayout();
// avoid massive update events
if(!m_updateEventScheduled) {
2011-08-14 04:09:11 +02:00
UIWidgetPtr self = asUIWidget();
g_dispatcher.addEvent([self, oldRect]() {
2011-08-21 21:43:05 +02:00
self->m_updateEventScheduled = false;
UIRectUpdateEvent e(oldRect, self->getRect());
2011-08-14 04:09:11 +02:00
// this widget could be destroyed before the event happens
if(!self->isDestroyed())
2011-08-21 21:43:05 +02:00
self->onRectUpdate(e);
2011-08-14 04:09:11 +02:00
});
2011-08-21 21:43:05 +02:00
m_updateEventScheduled = true;
2011-08-14 04:09:11 +02:00
}
}
bool UIWidget::isEnabled()
{
if(!m_enabled)
return false;
else if(UIWidgetPtr parent = getParent())
return parent->isEnabled();
else
return false;
2011-08-14 04:09:11 +02:00
}
bool UIWidget::hasFocus()
{
assert(!m_destroyed);
2011-08-21 03:01:46 +02:00
if(UIWidgetPtr parent = getParent()) {
if(parent->hasFocus() && parent->getFocusedChild() == shared_from_this())
return true;
}
2011-08-21 23:49:31 +02:00
// root parent always has focus
else if(asUIWidget() == getRootParent())
2011-08-14 04:09:11 +02:00
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;
}
2011-08-21 21:43:05 +02:00
UIWidgetPtr UIWidget::getRootParent()
2011-08-14 04:09:11 +02:00
{
2011-08-21 21:43:05 +02:00
if(UIWidgetPtr parent = getParent())
return parent->getRootParent();
else
return asUIWidget();
2011-08-14 04:09:11 +02:00
}
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")
2011-08-21 23:49:31 +02:00
return getRootParent();
2011-08-14 04:09:11 +02:00
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);
2011-08-21 21:43:05 +02:00
if(widget->isExplicitlyVisible() && widget->getRect().contains(childPos))
2011-08-14 04:09:11 +02:00
return widget;
}
return nullptr;
}
UIWidgetPtr UIWidget::getChildByIndex(int childIndex)
{
assert(!m_destroyed);
if(childIndex >= 0 && childIndex < getChildCount())
return m_children.at(childIndex);
return nullptr;
}
2011-08-21 21:43:05 +02:00
UIWidgetPtr UIWidget::recursiveGetChildById(const std::string& id)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
2011-08-21 21:43:05 +02:00
UIWidgetPtr widget = getChildById(id);
if(!widget) {
2011-08-14 04:09:11 +02:00
for(const UIWidgetPtr& child : m_children) {
2011-08-21 21:43:05 +02:00
widget = child->recursiveGetChildById(id);
if(widget)
break;
2011-08-14 04:09:11 +02:00
}
}
2011-08-21 21:43:05 +02:00
return widget;
2011-08-14 04:09:11 +02:00
}
UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos)
{
assert(!m_destroyed);
for(const UIWidgetPtr& child : m_children) {
2011-08-21 21:43:05 +02:00
if(child->getRect().contains(childPos)) {
2011-08-14 04:09:11 +02:00
if(UIWidgetPtr subChild = child->recursiveGetChildByPos(childPos))
return subChild;
return child;
}
}
return nullptr;
}
UIWidgetPtr UIWidget::backwardsGetWidgetById(const std::string& id)
{
assert(!m_destroyed);
2011-08-21 21:43:05 +02:00
UIWidgetPtr widget = getChildById(id);
if(!widget) {
2011-08-14 04:09:11 +02:00
if(UIWidgetPtr parent = getParent())
widget = parent->backwardsGetWidgetById(id);
}
2011-08-21 21:43:05 +02:00
2011-08-14 04:09:11 +02:00
return widget;
}
void UIWidget::focusChild(const UIWidgetPtr& focusedChild, FocusReason reason)
{
assert(!m_destroyed);
if(focusedChild != m_focusedChild) {
UIWidgetPtr oldFocused = m_focusedChild;
m_focusedChild = focusedChild;
if(oldFocused) {
2011-08-21 03:01:46 +02:00
UIFocusEvent e(reason, false);
oldFocused->onFocusChange(e);
}
if(focusedChild) {
2011-08-21 03:01:46 +02:00
UIFocusEvent e(reason, focusedChild->hasFocus());
focusedChild->onFocusChange(e);
}
}
}
2011-08-14 04:09:11 +02:00
void UIWidget::addChild(const UIWidgetPtr& childToAdd)
{
assert(!m_destroyed);
if(!childToAdd)
return;
2011-08-14 04:09:11 +02:00
assert(!hasChild(childToAdd));
m_children.push_back(childToAdd);
childToAdd->setParent(asUIWidget());
2011-08-21 21:43:05 +02:00
// recalculate anchors
getRootParent()->recalculateAnchoredWidgets();
// may need to update children layout
updateChildrenLayout();
2011-08-14 04:09:11 +02:00
2011-08-21 03:01:46 +02:00
// always focus new children
2011-08-21 23:49:31 +02:00
if(childToAdd->isFocusable() && childToAdd->isExplicitlyVisible() && childToAdd->isExplicitlyEnabled())
focusChild(childToAdd, ActiveFocusReason);
2011-08-14 04:09:11 +02:00
}
2011-08-20 22:30:41 +02:00
void UIWidget::insertChild(const UIWidgetPtr& childToInsert, int index)
{
assert(!m_destroyed);
if(!childToInsert)
return;
assert(!hasChild(childToInsert));
if(index < 0)
index = m_children.size() + index -1;
assert((uint)index <= m_children.size());
auto it = m_children.begin() + index;
m_children.insert(it, childToInsert);
childToInsert->setParent(asUIWidget());
2011-08-21 21:43:05 +02:00
// recalculate anchors
getRootParent()->recalculateAnchoredWidgets();
// may need to update children layout
updateChildrenLayout();
2011-08-20 22:30:41 +02:00
}
2011-08-14 04:09:11 +02:00
void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
{
assert(!m_destroyed);
if(!childToRemove)
return;
2011-08-14 04:09:11 +02:00
// defocus if needed
if(m_focusedChild == childToRemove)
focusChild(nullptr, ActiveFocusReason);
// try to unlock
unlockChild(childToRemove);
2011-08-14 04:09:11 +02:00
// 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);
2011-08-21 21:43:05 +02:00
// recalculate anchors
UIWidgetPtr parent = getRootParent();
parent->recalculateAnchoredWidgets();
// may need to update children layout
updateChildrenLayout();
2011-08-14 04:09:11 +02:00
}
void UIWidget::focusNextChild(FocusReason reason)
2011-08-14 04:09:11 +02:00
{
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, reason);
2011-08-14 04:09:11 +02:00
}
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::lockChild(const UIWidgetPtr& childToLock)
{
assert(hasChild(childToLock));
// disable all other widgets
for(const UIWidgetPtr& widget : m_children) {
if(widget == childToLock)
widget->setEnabled(true);
else
widget->setEnabled(false);
}
m_lockedWidgets.push_front(childToLock);
2011-08-20 22:30:41 +02:00
2011-08-21 03:01:46 +02:00
// lock child focus
if(childToLock->isFocusable())
focusChild(childToLock, ActiveFocusReason);
}
void UIWidget::unlockChild(const UIWidgetPtr& childToUnlock)
{
assert(hasChild(childToUnlock));
auto it = std::find(m_lockedWidgets.begin(), m_lockedWidgets.end(), childToUnlock);
2011-08-20 22:30:41 +02:00
if(it != m_lockedWidgets.end())
m_lockedWidgets.erase(it);
2011-08-20 22:30:41 +02:00
UIWidgetPtr newLockedWidget;
if(m_lockedWidgets.size() > 0)
newLockedWidget = m_lockedWidgets.front();
for(const UIWidgetPtr& child : m_children) {
if(newLockedWidget) {
if(child == newLockedWidget)
child->setEnabled(true);
2011-08-20 22:30:41 +02:00
else
child->setEnabled(false);
} else
child->setEnabled(true);
}
}
2011-08-21 21:43:05 +02:00
void UIWidget::updateLayout()
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
2011-08-21 21:43:05 +02:00
if(!m_layoutUpdateScheduled) {
m_layoutUpdateScheduled = true;
UIWidgetPtr self = asUIWidget();
g_dispatcher.addEvent([self] {
self->m_layoutUpdateScheduled = false;
if(!self->isDestroyed())
self->internalUpdateLayout();
});
}
2011-08-14 04:09:11 +02:00
}
2011-08-21 21:43:05 +02:00
void UIWidget::updateChildrenLayout()
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
2011-08-21 21:43:05 +02:00
if(!m_childrenLayoutUpdateScheduled) {
m_childrenLayoutUpdateScheduled = true;
UIWidgetPtr self = asUIWidget();
g_dispatcher.addEvent([self] {
self->m_childrenLayoutUpdateScheduled = false;
if(!self->isDestroyed())
self->internalUpdateChildrenLayout();
});
}
2011-08-14 04:09:11 +02:00
}
2011-08-21 21:43:05 +02:00
bool UIWidget::addAnchor(AnchorEdge edge, const std::string& hookedWidgetId, AnchorEdge hookedEdge)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
2011-08-21 21:43:05 +02:00
UIAnchor anchor(edge, hookedWidgetId, hookedEdge);
UIWidgetPtr hookedWidget = backwardsGetWidgetById(hookedWidgetId);
anchor.setHookedWidget(hookedWidget);
// we can never anchor with itself
if(hookedWidget == asUIWidget()) {
logError("anchoring with itself is not possible");
return false;
}
// we must never anchor to an anchor child
//TODO: this check
// duplicated anchors must be replaced
for(auto it = m_anchors.begin(); it != m_anchors.end(); ++it) {
const UIAnchor& otherAnchor = *it;
if(otherAnchor.getAnchoredEdge() == edge) {
m_anchors.erase(it);
break;
}
}
m_anchors.push_back(anchor);
updateLayout();
return true;
}
void UIWidget::centerIn(const std::string& hookedWidgetId)
{
assert(!m_destroyed);
addAnchor(AnchorHorizontalCenter, hookedWidgetId, AnchorHorizontalCenter);
addAnchor(AnchorVerticalCenter, hookedWidgetId, AnchorVerticalCenter);
}
void UIWidget::fill(const std::string& hookedWidgetId)
{
assert(!m_destroyed);
addAnchor(AnchorLeft, hookedWidgetId, AnchorLeft);
addAnchor(AnchorRight, hookedWidgetId, AnchorRight);
addAnchor(AnchorTop, hookedWidgetId, AnchorTop);
addAnchor(AnchorBottom, hookedWidgetId, AnchorBottom);
}
void UIWidget::internalUpdateLayout()
{
assert(!m_destroyed);
for(const UIAnchor& anchor : m_anchors) {
// ignore invalid anchors
if(!anchor.getHookedWidget())
continue;
// the update should only happens if the hooked widget is already updated
if(!anchor.getHookedWidget()->m_layoutUpdated)
return;
}
Rect newRect = m_rect;
bool verticalMoved = false;
bool horizontalMoved = false;
// calculate new rect based on anchors
for(const UIAnchor& anchor : m_anchors) {
int point = anchor.getHookedPoint();
// ignore invalid anchors
if(point == UIAnchor::INVALID_POINT)
continue;
switch(anchor.getAnchoredEdge()) {
case AnchorHorizontalCenter:
newRect.moveHorizontalCenter(point + getMarginLeft() - getMarginRight());
horizontalMoved = true;
break;
case AnchorLeft:
if(!horizontalMoved) {
newRect.moveLeft(point + getMarginLeft());
horizontalMoved = true;
} else
newRect.setLeft(point + getMarginLeft());
break;
case AnchorRight:
if(!horizontalMoved) {
newRect.moveRight(point - getMarginRight());
horizontalMoved = true;
} else
newRect.setRight(point - getMarginRight());
break;
case AnchorVerticalCenter:
newRect.moveVerticalCenter(point + getMarginTop() - getMarginBottom());
verticalMoved = true;
break;
case AnchorTop:
if(!verticalMoved) {
newRect.moveTop(point + getMarginTop());
verticalMoved = true;
} else
newRect.setTop(point + getMarginTop());
break;
case AnchorBottom:
if(!verticalMoved) {
newRect.moveBottom(point - getMarginBottom());
verticalMoved = true;
} else
newRect.setBottom(point - getMarginBottom());
break;
default:
break;
}
}
m_layoutUpdated = true;
// changes the rect only if really needed
if(newRect != m_rect) {
// setRect will update children layout too
setRect(newRect);
} else {
// update children
internalUpdateChildrenLayout();
}
}
void UIWidget::internalUpdateChildrenLayout()
{
assert(!m_destroyed);
// reset all children anchors update state
resetLayoutUpdateState(false);
// update children layouts
for(const UIWidgetWeakPtr& anchoredWidgetWeak : m_anchoredWidgets) {
if(UIWidgetPtr anchoredWidget = anchoredWidgetWeak.lock())
anchoredWidget->updateLayout();
}
}
void UIWidget::resetLayoutUpdateState(bool resetOwn)
{
if(resetOwn)
m_layoutUpdated = false;
// resets children layout update state too
for(const UIWidgetWeakPtr& anchoredWidgetWeak : m_anchoredWidgets) {
if(UIWidgetPtr anchoredWidget = anchoredWidgetWeak.lock())
anchoredWidget->resetLayoutUpdateState(true);
}
}
void UIWidget::addAnchoredWidget(const UIWidgetPtr& widget)
{
// prevent duplicated anchored widgets
for(const UIWidgetWeakPtr& anchoredWidget : m_anchoredWidgets)
if(anchoredWidget.lock() == widget)
return;
m_anchoredWidgets.push_back(widget);
}
void UIWidget::recalculateAnchoredWidgets()
{
clearAnchoredWidgets();
computeAnchoredWidgets();
}
void UIWidget::clearAnchoredWidgets()
{
m_anchoredWidgets.clear();
for(const UIWidgetPtr& child : m_children)
child->clearAnchoredWidgets();
}
void UIWidget::computeAnchoredWidgets()
{
// update anchors's hooked widget
for(UIAnchor& anchor : m_anchors) {
UIWidgetPtr hookedWidget = backwardsGetWidgetById(anchor.getHookedWidgetId());
anchor.setHookedWidget(hookedWidget);
if(hookedWidget)
hookedWidget->addAnchoredWidget(asUIWidget());
}
for(const UIWidgetPtr& child : m_children)
child->computeAnchoredWidgets();
2011-08-14 04:09:11 +02:00
}
2011-08-21 03:01:46 +02:00
void UIWidget::onFocusChange(UIFocusEvent& event)
{
if(m_focusedChild)
m_focusedChild->onFocusChange(event);
}
void UIWidget::onKeyPress(UIKeyEvent& event)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
event.ignore();
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
2011-08-21 03:01:46 +02:00
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
2011-08-14 04:09:11 +02:00
continue;
2011-08-14 04:09:11 +02:00
// key events go only to containers or focused child
2011-08-21 03:01:46 +02:00
if(child->hasChildren() || (child->isFocusable() && child->hasFocus())) {
event.accept();
2011-08-14 04:09:11 +02:00
child->onKeyPress(event);
}
if(event.isAccepted())
break;
2011-08-14 04:09:11 +02:00
}
}
void UIWidget::onKeyRelease(UIKeyEvent& event)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
event.ignore();
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
2011-08-21 03:01:46 +02:00
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
2011-08-14 04:09:11 +02:00
continue;
2011-08-14 04:09:11 +02:00
// key events go only to containers or focused child
2011-08-21 03:01:46 +02:00
if(child->hasChildren() || (child->isFocusable() && child->hasFocus())) {
event.accept();
2011-08-14 04:09:11 +02:00
child->onKeyRelease(event);
}
if(event.isAccepted())
break;
2011-08-14 04:09:11 +02:00
}
}
void UIWidget::onMousePress(UIMouseEvent& event)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
event.ignore();
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
2011-08-21 03:01:46 +02:00
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
2011-08-14 04:09:11 +02:00
continue;
// mouse press events only go to children that contains the mouse position
2011-08-21 21:43:05 +02:00
if(child->getRect().contains(event.pos()) && child == getChildByPos(event.pos())) {
2011-08-14 04:09:11 +02:00
// focus it
if(child->isFocusable())
focusChild(child, MouseFocusReason);
event.accept();
child->onMousePress(event);
2011-08-14 04:09:11 +02:00
}
if(event.isAccepted())
break;
2011-08-14 04:09:11 +02:00
}
}
void UIWidget::onMouseRelease(UIMouseEvent& event)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
event.ignore();
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
2011-08-21 03:01:46 +02:00
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
2011-08-14 04:09:11 +02:00
continue;
2011-08-14 04:09:11 +02:00
// mouse release events go to all children
event.accept();
2011-08-14 04:09:11 +02:00
child->onMouseRelease(event);
if(event.isAccepted())
break;
2011-08-14 04:09:11 +02:00
}
}
void UIWidget::onMouseMove(UIMouseEvent& event)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
event.ignore();
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
2011-08-21 03:01:46 +02:00
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
2011-08-14 04:09:11 +02:00
continue;
// check if the mouse is relally over this child
bool overChild = (isHovered() &&
2011-08-21 21:43:05 +02:00
child->getRect().contains(event.pos()) &&
child == getChildByPos(event.pos()));
2011-08-14 04:09:11 +02:00
if(overChild != child->isHovered()) {
child->setHovered(overChild);
UIHoverEvent e(overChild);
child->onHoverChange(e);
2011-08-14 04:09:11 +02:00
}
2011-08-14 04:09:11 +02:00
// mouse move events go to all children
event.accept();
2011-08-14 04:09:11 +02:00
child->onMouseMove(event);
if(event.isAccepted())
break;
2011-08-14 04:09:11 +02:00
}
}
void UIWidget::onMouseWheel(UIMouseEvent& event)
2011-08-14 04:09:11 +02:00
{
assert(!m_destroyed);
event.ignore();
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
2011-08-21 03:01:46 +02:00
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
2011-08-14 04:09:11 +02:00
continue;
2011-08-14 04:09:11 +02:00
// mouse wheel events only go to children that contains the mouse position
2011-08-21 21:43:05 +02:00
if(child->getRect().contains(event.pos()) && child == getChildByPos(event.pos())) {
event.accept();
2011-08-14 04:09:11 +02:00
child->onMouseWheel(event);
}
if(event.isAccepted())
break;
2011-08-14 04:09:11 +02:00
}
}