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
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
UIWidget::UIWidget()
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
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;
|
2011-08-14 19:45:25 +02:00
|
|
|
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()
|
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:39:46 +02:00
|
|
|
if(!m_destroyed) {
|
2011-08-20 22:30:41 +02:00
|
|
|
logWarning("widget '", m_id, "' was destructed without being explicit destroyed");
|
2011-08-22 21:39:46 +02:00
|
|
|
internalDestroy();
|
|
|
|
}
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void UIWidget::destroy()
|
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-14 04:09:11 +02:00
|
|
|
// destroy only once
|
|
|
|
if(!m_destroyed) {
|
2011-08-22 21:39:46 +02:00
|
|
|
internalDestroy();
|
2011-08-22 21:13:52 +02:00
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
// add destroy check event
|
2011-08-22 21:39:46 +02:00
|
|
|
g_dispatcher.addEvent(std::bind(&UIWidget::internalDestroyCheck, asUIWidget()));
|
2011-08-14 04:09:11 +02:00
|
|
|
} 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
|
|
|
}
|
|
|
|
|
2011-08-22 21:39:46 +02:00
|
|
|
void UIWidget::internalDestroy()
|
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:39:46 +02:00
|
|
|
// first release lua table, because it may contains references to children
|
|
|
|
releaseLuaFieldsTable();
|
|
|
|
|
|
|
|
// clear other references
|
2011-08-23 03:08:36 +02:00
|
|
|
clearHookedWidgets();
|
2011-08-22 21:39:46 +02:00
|
|
|
m_lockedChildren.clear();
|
|
|
|
m_anchors.clear();
|
|
|
|
m_anchoredWidgets.clear();
|
|
|
|
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());
|
|
|
|
|
|
|
|
m_destroyed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UIWidget::internalDestroyCheck()
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
// collect lua garbage before checking
|
|
|
|
g_lua.collectGarbage();
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-14 04:09:11 +02:00
|
|
|
// get real use count
|
|
|
|
int realUseCount = shared_from_this().use_count() - 2;
|
|
|
|
|
|
|
|
// check for leaks upon widget destruction
|
|
|
|
if(realUseCount > 0)
|
2011-08-23 03:08:36 +02:00
|
|
|
{}
|
|
|
|
//logWarning("destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left");
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
void UIWidget::render()
|
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// draw background image
|
|
|
|
g_graphics.bindColor(m_backgroundColor);
|
2011-08-14 04:09:11 +02:00
|
|
|
if(m_image)
|
2011-08-21 21:43:05 +02:00
|
|
|
m_image->draw(getRect());
|
2011-08-14 04:09:11 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// draw children
|
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()) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// store current graphics opacity
|
2011-08-14 19:45:25 +02:00
|
|
|
int oldOpacity = g_graphics.getOpacity();
|
2011-08-22 21:13:52 +02:00
|
|
|
|
|
|
|
// decrease to self opacity
|
2011-08-14 19:45:25 +02:00
|
|
|
if(child->getOpacity() < oldOpacity)
|
|
|
|
g_graphics.setOpacity(child->getOpacity());
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// bind child color
|
2011-08-14 04:09:11 +02:00
|
|
|
child->render();
|
2011-08-14 19:45:25 +02:00
|
|
|
|
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
|
|
|
|
2011-08-14 19:45:25 +02:00
|
|
|
g_graphics.setOpacity(oldOpacity);
|
|
|
|
}
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void UIWidget::setParent(const UIWidgetPtr& parent)
|
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:13:52 +02:00
|
|
|
UIWidgetPtr self = asUIWidget();
|
2011-08-14 04:09:11 +02:00
|
|
|
|
|
|
|
// remove from old parent
|
|
|
|
UIWidgetPtr oldParent = m_parent.lock();
|
2011-08-22 21:13:52 +02:00
|
|
|
if(oldParent && oldParent->hasChild(self))
|
|
|
|
oldParent->removeChild(self);
|
|
|
|
|
|
|
|
// reset parent
|
2011-08-14 04:09:11 +02:00
|
|
|
m_parent.reset();
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// set new parent
|
2011-08-14 04:09:11 +02:00
|
|
|
if(parent) {
|
2011-08-22 21:39:46 +02:00
|
|
|
m_parent = parent;
|
2011-08-22 21:13:52 +02:00
|
|
|
|
|
|
|
// add to parent if needed
|
|
|
|
if(!parent->hasChild(self))
|
|
|
|
parent->addChild(self);
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::applyStyle(const std::string& styleName)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-14 04:09:11 +02:00
|
|
|
try {
|
|
|
|
OTMLNodePtr styleNode = g_ui.getStyle(styleName);
|
2011-08-22 14:44:26 +02:00
|
|
|
onStyleApply(styleNode);
|
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
|
2011-08-23 03:08:36 +02:00
|
|
|
internalUpdateChildrenLayout();
|
2011-08-21 21:43:05 +02:00
|
|
|
|
|
|
|
// avoid massive update events
|
|
|
|
if(!m_updateEventScheduled) {
|
2011-08-14 04:09:11 +02:00
|
|
|
UIWidgetPtr self = asUIWidget();
|
2011-08-14 16:09:26 +02:00
|
|
|
g_dispatcher.addEvent([self, oldRect]() {
|
2011-08-21 21:43:05 +02:00
|
|
|
self->m_updateEventScheduled = false;
|
2011-08-14 04:09:11 +02:00
|
|
|
// this widget could be destroyed before the event happens
|
|
|
|
if(!self->isDestroyed())
|
2011-08-22 14:44:26 +02:00
|
|
|
self->onGeometryUpdate(oldRect, self->getRect());
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-14 16:09:26 +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
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
bool UIWidget::isVisible()
|
|
|
|
{
|
|
|
|
if(!m_visible)
|
|
|
|
return false;
|
|
|
|
else if(UIWidgetPtr parent = getParent())
|
|
|
|
return parent->isVisible();
|
|
|
|
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);
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
auto it = std::find(m_children.begin(), m_children.end(), relativeChild);
|
|
|
|
if(it != m_children.end() && ++it != m_children.end())
|
|
|
|
return *it;
|
2011-08-14 04:09:11 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
UIWidgetPtr UIWidget::getChildBefore(const UIWidgetPtr& relativeChild)
|
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
auto it = std::find(m_children.rbegin(), m_children.rend(), relativeChild);
|
|
|
|
if(it != m_children.rend() && ++it != m_children.rend())
|
|
|
|
return *it;
|
2011-08-14 04:09:11 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
UIWidgetPtr UIWidget::getChildByIndex(int index)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
index = index <= 0 ? (m_children.size() + index) : index-1;
|
|
|
|
if(index >= 0 && (uint)index < m_children.size())
|
|
|
|
return m_children.at(index);
|
2011-08-14 04:09:11 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
return widget;
|
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::focusChild(const UIWidgetPtr& child, UI::FocusReason reason)
|
2011-08-14 16:09:26 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-22 21:39:46 +02:00
|
|
|
if(child)
|
|
|
|
assert(hasChild(child));
|
2011-08-22 21:13:52 +02:00
|
|
|
|
|
|
|
if(child != m_focusedChild) {
|
2011-08-14 16:09:26 +02:00
|
|
|
UIWidgetPtr oldFocused = m_focusedChild;
|
2011-08-22 21:13:52 +02:00
|
|
|
m_focusedChild = child;
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
if(oldFocused)
|
|
|
|
oldFocused->onFocusChange(false, reason);
|
2011-08-22 21:13:52 +02:00
|
|
|
|
|
|
|
if(child)
|
|
|
|
child->onFocusChange(child->hasFocus(), reason);
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::addChild(const UIWidgetPtr& child)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:13:52 +02:00
|
|
|
if(!child)
|
2011-08-19 20:53:23 +02:00
|
|
|
return;
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
assert(!hasChild(child));
|
|
|
|
|
|
|
|
m_children.push_back(child);
|
|
|
|
child->setParent(asUIWidget());
|
2011-08-14 04:09:11 +02:00
|
|
|
|
2011-08-21 21:43:05 +02:00
|
|
|
// recalculate anchors
|
2011-08-23 03:08:36 +02:00
|
|
|
child->clearHookedWidgets();
|
|
|
|
child->computeHookedWidgets();
|
|
|
|
child->updateLayout();
|
2011-08-14 04:09:11 +02:00
|
|
|
|
2011-08-21 03:01:46 +02:00
|
|
|
// always focus new children
|
2011-08-22 21:13:52 +02:00
|
|
|
if(child->isFocusable() && child->isExplicitlyVisible() && child->isExplicitlyEnabled())
|
|
|
|
focusChild(child, UI::ActiveFocusReason);
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
void UIWidget::insertChild(int index, const UIWidgetPtr& child)
|
2011-08-20 22:30:41 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:13:52 +02:00
|
|
|
// skip null children
|
|
|
|
if(!child)
|
2011-08-20 22:30:41 +02:00
|
|
|
return;
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
assert(!hasChild(child));
|
2011-08-20 22:30:41 +02:00
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
index = index <= 0 ? (m_children.size() + index) : index-1;
|
2011-08-20 22:30:41 +02:00
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
assert(index >= 0 && (uint)index <= m_children.size());
|
2011-08-20 22:30:41 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// retrieve child by index
|
2011-08-20 22:30:41 +02:00
|
|
|
auto it = m_children.begin() + index;
|
2011-08-22 21:13:52 +02:00
|
|
|
m_children.insert(it, child);
|
|
|
|
child->setParent(asUIWidget());
|
2011-08-20 22:30:41 +02:00
|
|
|
|
2011-08-21 21:43:05 +02:00
|
|
|
// recalculate anchors
|
2011-08-23 03:08:36 +02:00
|
|
|
child->clearHookedWidgets();
|
|
|
|
child->computeHookedWidgets();
|
|
|
|
child->updateLayout();
|
2011-08-20 22:30:41 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::removeChild(const UIWidgetPtr& child)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:13:52 +02:00
|
|
|
// skip null children
|
|
|
|
if(!child)
|
2011-08-19 20:53:23 +02:00
|
|
|
return;
|
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
// defocus if needed
|
2011-08-22 21:13:52 +02:00
|
|
|
if(m_focusedChild == child)
|
2011-08-22 14:44:26 +02:00
|
|
|
focusChild(nullptr, UI::ActiveFocusReason);
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// unlock child if it was locked
|
|
|
|
unlockChild(child);
|
2011-08-14 04:09:11 +02:00
|
|
|
|
|
|
|
// remove from children list
|
2011-08-22 21:13:52 +02:00
|
|
|
auto it = std::find(m_children.begin(), m_children.end(), child);
|
2011-08-14 04:09:11 +02:00
|
|
|
assert(it != m_children.end());
|
|
|
|
m_children.erase(it);
|
|
|
|
|
|
|
|
// reset child parent
|
2011-08-22 21:13:52 +02:00
|
|
|
assert(child->getParent() == asUIWidget());
|
|
|
|
child->setParent(nullptr);
|
2011-08-21 21:43:05 +02:00
|
|
|
|
|
|
|
// recalculate anchors
|
2011-08-23 03:08:36 +02:00
|
|
|
child->clearHookedWidgets();
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
void UIWidget::focusNextChild(UI::FocusReason reason)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
UIWidgetPtr toFocus;
|
|
|
|
UIWidgetList rotatedChildren(m_children);
|
2011-08-22 21:13:52 +02:00
|
|
|
|
|
|
|
if(m_focusedChild) {
|
|
|
|
auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedChild);
|
|
|
|
if(focusedIt != rotatedChildren.end()) {
|
|
|
|
std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end());
|
|
|
|
rotatedChildren.pop_front();
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-22 21:13:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// finds next child to focus
|
|
|
|
for(const UIWidgetPtr& child : rotatedChildren) {
|
|
|
|
if(child->isFocusable()) {
|
|
|
|
toFocus = child;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-08-14 04:09:11 +02:00
|
|
|
|
|
|
|
if(toFocus)
|
2011-08-14 16:09:26 +02:00
|
|
|
focusChild(toFocus, reason);
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::moveChildToTop(const UIWidgetPtr& child)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
if(!child)
|
|
|
|
return;
|
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
// remove and push child again
|
2011-08-22 21:13:52 +02:00
|
|
|
auto it = std::find(m_children.begin(), m_children.end(), child);
|
2011-08-14 04:09:11 +02:00
|
|
|
assert(it != m_children.end());
|
|
|
|
m_children.erase(it);
|
2011-08-22 21:13:52 +02:00
|
|
|
m_children.push_back(child);
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::lockChild(const UIWidgetPtr& child)
|
2011-08-14 16:09:26 +02:00
|
|
|
{
|
2011-08-22 21:13:52 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
if(!child)
|
|
|
|
return;
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
assert(hasChild(child));
|
|
|
|
|
|
|
|
// disable all other children
|
|
|
|
for(const UIWidgetPtr& otherChild : m_children) {
|
|
|
|
if(otherChild == child)
|
|
|
|
child->setEnabled(true);
|
2011-08-14 16:09:26 +02:00
|
|
|
else
|
2011-08-22 21:13:52 +02:00
|
|
|
otherChild->setEnabled(false);
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
m_lockedChildren.push_front(child);
|
2011-08-20 22:30:41 +02:00
|
|
|
|
2011-08-21 03:01:46 +02:00
|
|
|
// lock child focus
|
2011-08-22 21:13:52 +02:00
|
|
|
if(child->isFocusable())
|
|
|
|
focusChild(child, UI::ActiveFocusReason);
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::unlockChild(const UIWidgetPtr& child)
|
2011-08-14 16:09:26 +02:00
|
|
|
{
|
2011-08-22 21:13:52 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
if(!child)
|
|
|
|
return;
|
|
|
|
|
|
|
|
assert(hasChild(child));
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
auto it = std::find(m_lockedChildren.begin(), m_lockedChildren.end(), child);
|
|
|
|
if(it == m_lockedChildren.end())
|
|
|
|
return;
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
m_lockedChildren.erase(it);
|
2011-08-20 22:30:41 +02:00
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
// find new chick to lock
|
|
|
|
UIWidgetPtr lockedChild;
|
|
|
|
if(m_lockedChildren.size() > 0)
|
|
|
|
lockedChild = m_lockedChildren.front();
|
|
|
|
|
|
|
|
for(const UIWidgetPtr& otherChild : m_children) {
|
|
|
|
// lock new child
|
|
|
|
if(lockedChild) {
|
|
|
|
if(otherChild == lockedChild)
|
|
|
|
lockedChild->setEnabled(true);
|
2011-08-20 22:30:41 +02:00
|
|
|
else
|
2011-08-22 21:13:52 +02:00
|
|
|
otherChild->setEnabled(false);
|
|
|
|
}
|
|
|
|
// else unlock all
|
|
|
|
else
|
|
|
|
otherChild->setEnabled(true);
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
|
2011-08-21 21:43:05 +02:00
|
|
|
void UIWidget::updateLayout()
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
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-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-21 21:43:05 +02:00
|
|
|
if(!m_childrenLayoutUpdateScheduled) {
|
|
|
|
m_childrenLayoutUpdateScheduled = true;
|
2011-08-23 03:08:36 +02:00
|
|
|
// reset all children anchors update state
|
|
|
|
resetLayoutUpdateState(false);
|
2011-08-21 21:43:05 +02:00
|
|
|
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-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-21 21:43:05 +02:00
|
|
|
UIAnchor anchor(edge, hookedWidgetId, hookedEdge);
|
|
|
|
|
|
|
|
UIWidgetPtr hookedWidget = backwardsGetWidgetById(hookedWidgetId);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
if(hookedWidget) {
|
|
|
|
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
|
2011-08-21 21:43:05 +02:00
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
if(hookedWidget)
|
|
|
|
hookedWidget->addAnchoredWidget(asUIWidget());
|
|
|
|
}
|
2011-08-21 21:43:05 +02:00
|
|
|
|
|
|
|
// duplicated anchors must be replaced
|
2011-08-23 03:08:36 +02:00
|
|
|
auto it = std::find_if(m_anchors.begin(), m_anchors.end(),
|
|
|
|
[&](const UIAnchor& other) {
|
|
|
|
return (other.getAnchoredEdge() == edge);
|
|
|
|
});
|
|
|
|
|
|
|
|
if(it != m_anchors.end()) {
|
|
|
|
UIAnchor& other = *it;
|
|
|
|
if(other.getHookedWidget()) {
|
|
|
|
other.getHookedWidget()->removeAnchoredWidget(asUIWidget());
|
|
|
|
other.setHookedWidget(nullptr);
|
2011-08-21 21:43:05 +02:00
|
|
|
}
|
2011-08-23 03:08:36 +02:00
|
|
|
m_anchors.erase(it);
|
2011-08-21 21:43:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-21 21:43:05 +02:00
|
|
|
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);
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-21 21:43:05 +02:00
|
|
|
|
|
|
|
// update children layouts
|
2011-08-23 03:08:36 +02:00
|
|
|
for(const UIWidgetPtr& anchoredWidget : m_anchoredWidgets)
|
|
|
|
anchoredWidget->internalUpdateLayout();
|
2011-08-21 21:43:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void UIWidget::resetLayoutUpdateState(bool resetOwn)
|
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
//logTraceDebug(m_id);
|
2011-08-21 21:43:05 +02:00
|
|
|
if(resetOwn)
|
|
|
|
m_layoutUpdated = false;
|
|
|
|
|
|
|
|
// resets children layout update state too
|
2011-08-23 03:08:36 +02:00
|
|
|
for(const UIWidgetPtr& anchoredWidget : m_anchoredWidgets)
|
|
|
|
anchoredWidget->resetLayoutUpdateState(true);
|
2011-08-21 21:43:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void UIWidget::addAnchoredWidget(const UIWidgetPtr& widget)
|
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
//logTraceDebug(m_id);
|
2011-08-21 21:43:05 +02:00
|
|
|
// prevent duplicated anchored widgets
|
2011-08-23 03:08:36 +02:00
|
|
|
for(const UIWidgetPtr& anchoredWidget : m_anchoredWidgets)
|
|
|
|
if(anchoredWidget == widget)
|
2011-08-21 21:43:05 +02:00
|
|
|
return;
|
|
|
|
m_anchoredWidgets.push_back(widget);
|
|
|
|
}
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
void UIWidget::removeAnchoredWidget(const UIWidgetPtr& widget)
|
2011-08-21 21:43:05 +02:00
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
//logTraceDebug(m_id);
|
|
|
|
auto it = std::find(m_anchoredWidgets.begin(), m_anchoredWidgets.end(), widget);
|
|
|
|
if(it != m_anchoredWidgets.end())
|
|
|
|
m_anchoredWidgets.erase(it);
|
2011-08-21 21:43:05 +02:00
|
|
|
}
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
void UIWidget::clearHookedWidgets()
|
2011-08-21 21:43:05 +02:00
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
//logTraceDebug(m_id);
|
|
|
|
for(UIAnchor& anchor : m_anchors) {
|
|
|
|
UIWidgetPtr hookedWidget = anchor.getHookedWidget();
|
|
|
|
if(hookedWidget) {
|
|
|
|
hookedWidget->removeAnchoredWidget(asUIWidget());
|
|
|
|
anchor.setHookedWidget(nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-21 21:43:05 +02:00
|
|
|
for(const UIWidgetPtr& child : m_children)
|
2011-08-23 03:08:36 +02:00
|
|
|
child->clearHookedWidgets();
|
2011-08-21 21:43:05 +02:00
|
|
|
}
|
|
|
|
|
2011-08-23 03:08:36 +02:00
|
|
|
void UIWidget::computeHookedWidgets()
|
2011-08-21 21:43:05 +02:00
|
|
|
{
|
2011-08-23 03:08:36 +02:00
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
//logTraceDebug(m_id);
|
|
|
|
|
2011-08-21 21:43:05 +02:00
|
|
|
// 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)
|
2011-08-23 03:08:36 +02:00
|
|
|
child->computeHookedWidgets();
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 21:13:52 +02:00
|
|
|
void UIWidget::onStyleApply(const OTMLNodePtr& styleNode)
|
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
|
|
|
|
|
|
|
// load styles used by all widgets
|
|
|
|
for(const OTMLNodePtr& node : styleNode->children()) {
|
|
|
|
// id
|
|
|
|
if(node->tag() == "id") {
|
|
|
|
setId(node->value());
|
2011-08-23 03:08:36 +02:00
|
|
|
//logTraceDebug(m_id);
|
2011-08-22 21:13:52 +02:00
|
|
|
}
|
|
|
|
// background 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()));
|
|
|
|
}
|
|
|
|
// foreground color
|
|
|
|
else if(node->tag() == "color") {
|
|
|
|
setForegroundColor(node->value<Color>());
|
|
|
|
}
|
|
|
|
// background color
|
|
|
|
else if(node->tag() == "background-color") {
|
|
|
|
setBackgroundColor(node->value<Color>());
|
|
|
|
}
|
|
|
|
// opacity
|
|
|
|
else if(node->tag() == "opacity") {
|
|
|
|
setOpacity(node->value<int>());
|
|
|
|
}
|
|
|
|
// size
|
|
|
|
else if(node->tag() == "size") {
|
|
|
|
resize(node->value<Size>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "width") {
|
|
|
|
setWidth(node->value<int>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "height") {
|
|
|
|
setHeight(node->value<int>());
|
|
|
|
}
|
|
|
|
// absolute position
|
|
|
|
else if(node->tag() == "position") {
|
|
|
|
moveTo(node->value<Point>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "x") {
|
|
|
|
setX(node->value<int>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "y") {
|
|
|
|
setY(node->value<int>());
|
|
|
|
}
|
|
|
|
// margins
|
|
|
|
else if(node->tag() == "margin.left") {
|
|
|
|
setMarginLeft(node->value<int>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "margin.right") {
|
|
|
|
setMarginRight(node->value<int>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "margin.top") {
|
|
|
|
setMarginTop(node->value<int>());
|
|
|
|
}
|
|
|
|
else if(node->tag() == "margin.bottom") {
|
|
|
|
setMarginBottom(node->value<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 {
|
|
|
|
AnchorEdge edge = fw::translateAnchorEdge(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 hookedWidgetId = split[0];
|
|
|
|
AnchorEdge hookedEdge = fw::translateAnchorEdge(split[1]);
|
|
|
|
|
|
|
|
if(edge == AnchorNone)
|
|
|
|
throw OTMLException(node, "invalid anchor edge");
|
|
|
|
|
|
|
|
if(hookedEdge == AnchorNone)
|
|
|
|
throw OTMLException(node, "invalid anchor target edge");
|
|
|
|
|
|
|
|
addAnchor(edge, hookedWidgetId, hookedEdge);
|
|
|
|
}
|
|
|
|
}
|
2011-08-22 21:39:46 +02:00
|
|
|
/*else if(node->tag() == "onLoad") {
|
2011-08-22 21:13:52 +02:00
|
|
|
g_lua.loadFunction(node->value<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
|
|
|
|
luaSetField("onLoad");
|
2011-08-22 21:39:46 +02:00
|
|
|
}*/
|
2011-08-22 21:13:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
void UIWidget::onGeometryUpdate(const Rect& oldRect, const Rect& newRect)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void UIWidget::onFocusChange(bool focused, UI::FocusReason reason)
|
2011-08-21 03:01:46 +02:00
|
|
|
{
|
2011-08-22 21:13:52 +02:00
|
|
|
// when containers lose or get focus it's focused child do the same
|
2011-08-21 03:01:46 +02:00
|
|
|
if(m_focusedChild)
|
2011-08-22 14:44:26 +02:00
|
|
|
m_focusedChild->onFocusChange(focused, reason);
|
2011-08-21 03:01:46 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
void UIWidget::onHoverChange(bool hovered)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool UIWidget::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
|
|
|
|
{
|
|
|
|
assert(!m_destroyed);
|
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-22 21:13:52 +02:00
|
|
|
// events on hidden or disabled widgets are discarded
|
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 16:09:26 +02:00
|
|
|
|
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())) {
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->onKeyPress(keyCode, keyChar, keyboardModifiers))
|
|
|
|
return true;
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-22 14:44:26 +02:00
|
|
|
|
|
|
|
return false;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
bool UIWidget::onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
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) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// events on hidden or disabled widgets are discarded
|
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 16:09:26 +02:00
|
|
|
|
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())) {
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->onKeyRelease(keyCode, keyChar, keyboardModifiers))
|
|
|
|
return true;
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-22 14:44:26 +02:00
|
|
|
|
|
|
|
return false;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
bool UIWidget::onMousePress(const Point& mousePos, UI::MouseButton button)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
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) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// events on hidden or disabled widgets are discarded
|
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 16:09:26 +02:00
|
|
|
// mouse press events only go to children that contains the mouse position
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->getRect().contains(mousePos) && child == getChildByPos(mousePos)) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// when a focusable item is focused it must gain focus
|
2011-08-14 04:09:11 +02:00
|
|
|
if(child->isFocusable())
|
2011-08-22 14:44:26 +02:00
|
|
|
focusChild(child, UI::MouseFocusReason);
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->onMousePress(mousePos, button))
|
|
|
|
return true;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
}
|
2011-08-22 14:44:26 +02:00
|
|
|
|
|
|
|
return false;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
bool UIWidget::onMouseRelease(const Point& mousePos, UI::MouseButton button)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
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) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// events on hidden or disabled widgets are discarded
|
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 16:09:26 +02:00
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
// mouse release events go to all children
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->onMouseRelease(mousePos, button))
|
|
|
|
return true;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-22 14:44:26 +02:00
|
|
|
|
|
|
|
return false;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
bool UIWidget::onMouseMove(const Point& mousePos, const Point& mouseMoved)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
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) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// events on hidden or disabled widgets are discarded
|
2011-08-21 03:01:46 +02:00
|
|
|
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
|
2011-08-14 04:09:11 +02:00
|
|
|
continue;
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
// check if the mouse is really over this child
|
2011-08-14 16:09:26 +02:00
|
|
|
bool overChild = (isHovered() &&
|
2011-08-22 14:44:26 +02:00
|
|
|
child->getRect().contains(mousePos) &&
|
|
|
|
child == getChildByPos(mousePos));
|
|
|
|
|
|
|
|
// trigger hover events
|
2011-08-14 04:09:11 +02:00
|
|
|
if(overChild != child->isHovered()) {
|
|
|
|
child->setHovered(overChild);
|
2011-08-22 14:44:26 +02:00
|
|
|
child->onHoverChange(overChild);
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-14 16:09:26 +02:00
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
// mouse move events go to all children
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->onMouseMove(mousePos, mouseMoved))
|
|
|
|
return true;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-22 14:44:26 +02:00
|
|
|
|
|
|
|
return false;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-22 14:44:26 +02:00
|
|
|
bool UIWidget::onMouseWheel(const Point& mousePos, UI::MouseWheelDirection direction)
|
2011-08-14 04:09:11 +02:00
|
|
|
{
|
|
|
|
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) {
|
2011-08-22 21:13:52 +02:00
|
|
|
// events on hidden or disabled widgets are discarded
|
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 16:09:26 +02:00
|
|
|
|
2011-08-14 04:09:11 +02:00
|
|
|
// mouse wheel events only go to children that contains the mouse position
|
2011-08-22 14:44:26 +02:00
|
|
|
if(child->getRect().contains(mousePos) && child == getChildByPos(mousePos)) {
|
|
|
|
if(child->onMouseWheel(mousePos, direction))
|
|
|
|
return true;
|
2011-08-14 16:09:26 +02:00
|
|
|
}
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|
2011-08-22 14:44:26 +02:00
|
|
|
|
|
|
|
return false;
|
2011-08-14 04:09:11 +02:00
|
|
|
}
|