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

1000 lines
28 KiB
C++
Raw Normal View History

2011-08-28 15:17:58 +02:00
/*
* Copyright (c) 2010-2011 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.
*/
2011-08-14 04:09:11 +02:00
#include "uiwidget.h"
#include "uimanager.h"
#include "uianchorlayout.h"
#include "uiverticallayout.h"
2011-08-14 04:09:11 +02:00
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>
#include <framework/platform/platform.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
{
2011-08-21 21:43:05 +02:00
m_updateEventScheduled = false;
m_states = Fw::DefaultState;
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;
m_id = Fw::mkstr("widget", id++);
2011-08-14 04:09:11 +02:00
}
UIWidget::~UIWidget()
{
// clear all references
releaseLuaFieldsTable();
m_focusedChild.reset();
m_layout.reset();
m_parent.reset();
m_lockedChildren.clear();
m_children.clear();
2011-08-14 04:09:11 +02:00
}
void UIWidget::setup()
2011-08-14 04:09:11 +02:00
{
setVisible(true);
setEnabled(true);
setFocusable(true);
setPressed(false);
setSizeFixed(false);
setFont(g_fonts.getDefaultFont());
setBackgroundColor(Fw::white);
setForegroundColor(Fw::white);
setOpacity(255);
setMarginTop(0);
setMarginRight(0);
setMarginBottom(0);
setMarginLeft(0);
2011-08-14 04:09:11 +02:00
}
void UIWidget::destroy()
2011-08-22 21:39:46 +02:00
{
// remove itself from parent
if(UIWidgetPtr parent = getParent())
parent->removeChild(asUIWidget());
2011-08-30 17:12:57 +02:00
setVisible(false);
setEnabled(false);
}
2011-08-14 04:09:11 +02:00
void UIWidget::render()
{
// draw background
if(m_image) {
g_graphics.bindColor(m_backgroundColor);
m_image->draw(m_rect);
}
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-23 17:09:50 +02:00
// render only visible children with a valid rect
if(child->isExplicitlyVisible() && child->getRect().isValid()) {
2011-08-22 21:13:52 +02:00
// store current graphics opacity
int oldOpacity = g_graphics.getOpacity();
2011-08-22 21:13:52 +02:00
// decrease to self opacity
if(child->getOpacity() < oldOpacity)
g_graphics.setOpacity(child->getOpacity());
2011-08-14 04:09:11 +02:00
child->render();
2011-08-20 22:30:41 +02:00
// debug draw box
2011-10-29 01:56:45 +02:00
//g_graphics.bindColor(Fw::green);
2011-08-21 21:43:05 +02:00
//g_graphics.drawBoundingRect(child->getRect());
//g_fonts.getDefaultFont()->renderText(child->getId(), child->getPosition() + Point(2, 0), Color::red);
2011-08-20 22:30:41 +02:00
g_graphics.setOpacity(oldOpacity);
}
2011-08-14 04:09:11 +02:00
}
}
2011-08-30 01:40:56 +02:00
void UIWidget::setVisible(bool visible)
{
m_visible = visible;
if(!visible && isFocused()) {
if(UIWidgetPtr parent = getParent())
parent->focusNextChild(Fw::ActiveFocusReason);
}
}
void UIWidget::setStyle(const std::string& styleName)
{
OTMLNodePtr styleNode = g_ui.getStyle(styleName);
applyStyle(styleNode);
m_style = styleNode;
}
void UIWidget::setStyleFromNode(const OTMLNodePtr& styleNode)
2011-08-14 04:09:11 +02:00
{
applyStyle(styleNode);
m_style = styleNode;
}
2011-08-14 04:09:11 +02:00
void UIWidget::setParent(const UIWidgetPtr& parent)
{
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 = getParent();
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-21 21:43:05 +02:00
void UIWidget::setRect(const Rect& rect)
2011-08-14 04:09:11 +02:00
{
// only update if the rect really changed
2011-08-14 04:09:11 +02:00
Rect oldRect = m_rect;
if(rect == oldRect)
return;
2011-08-14 04:09:11 +02:00
m_rect = rect;
// updates own layout
updateLayout();
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();
g_dispatcher.addEvent([self, oldRect]() {
2011-08-21 21:43:05 +02:00
self->m_updateEventScheduled = false;
self->onGeometryUpdate(oldRect, self->getRect());
2011-08-14 04:09:11 +02:00
});
}
m_updateEventScheduled = true;
2011-08-14 04:09:11 +02:00
}
void UIWidget::lock()
{
if(UIWidgetPtr parent = getParent())
parent->lockChild(asUIWidget());
}
void UIWidget::unlock()
{
if(UIWidgetPtr parent = getParent())
parent->unlockChild(asUIWidget());
}
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 asUIWidget() == g_ui.getRootWidget();
2011-08-14 04:09:11 +02:00
}
bool UIWidget::hasChild(const UIWidgetPtr& child)
{
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)
{
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)
{
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)
{
for(const UIWidgetPtr& child : m_children) {
if(child->getId() == childId)
return child;
2011-08-14 04:09:11 +02:00
}
return nullptr;
}
UIWidgetPtr UIWidget::getChildByPos(const Point& childPos)
{
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
{
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
{
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)
{
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)
{
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;
}
void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason)
{
if(child && !hasChild(child)) {
logError("attempt to focus an unknown child in a UIWidget");
return;
}
2011-08-22 21:13:52 +02:00
if(child != m_focusedChild) {
UIWidgetPtr oldFocused = m_focusedChild;
2011-08-22 21:13:52 +02:00
m_focusedChild = child;
if(child) {
child->setLastFocusReason(reason);
child->updateState(Fw::FocusState);
child->updateState(Fw::ActiveState);
}
2011-08-22 21:13:52 +02:00
if(oldFocused) {
oldFocused->setLastFocusReason(reason);
oldFocused->updateState(Fw::FocusState);
oldFocused->updateState(Fw::ActiveState);
}
}
}
2011-08-22 21:13:52 +02:00
void UIWidget::addChild(const UIWidgetPtr& child)
2011-08-14 04:09:11 +02:00
{
if(!child) {
logWarning("attempt to add a null child into a UIWidget");
return;
}
if(hasChild(child)) {
logWarning("attempt to add a child again into a UIWidget");
return;
}
2011-08-22 21:13:52 +02:00
m_children.push_back(child);
child->setParent(asUIWidget());
2011-08-14 04:09:11 +02:00
// always focus new child
2011-08-22 21:13:52 +02:00
if(child->isFocusable() && child->isExplicitlyVisible() && child->isExplicitlyEnabled())
focusChild(child, Fw::ActiveFocusReason);
// create default layout
if(!m_layout)
m_layout = UILayoutPtr(new UIAnchorLayout(asUIWidget()));
// add to layout and updates it
m_layout->addWidget(child);
// update new child states
child->updateStates();
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
{
if(!child) {
logWarning("attempt to insert a null child into a UIWidget");
2011-08-20 22:30:41 +02:00
return;
}
2011-08-20 22:30:41 +02:00
if(hasChild(child)) {
logWarning("attempt to insert a child again into a UIWidget");
return;
}
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
// create default layout if needed
if(!m_layout)
m_layout = UILayoutPtr(new UIAnchorLayout(asUIWidget()));
// add to layout and updates it
m_layout->addWidget(child);
// update new child states
child->updateStates();
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
{
// remove from children list
2011-08-29 20:38:01 +02:00
if(hasChild(child)) {
// defocus if needed
bool focusAnother = false;
if(m_focusedChild == child) {
focusChild(nullptr, Fw::ActiveFocusReason);
focusAnother = true;
}
2011-08-29 20:38:01 +02:00
if(isChildLocked(child))
unlockChild(child);
2011-08-29 20:38:01 +02:00
auto it = std::find(m_children.begin(), m_children.end(), child);
m_children.erase(it);
2011-08-14 04:09:11 +02:00
// reset child parent
assert(child->getParent() == asUIWidget());
child->setParent(nullptr);
2011-08-14 04:09:11 +02:00
m_layout->removeWidget(child);
2011-08-21 21:43:05 +02:00
// update child states
child->updateStates();
2011-08-29 20:38:01 +02:00
if(focusAnother && !m_focusedChild)
focusPreviousChild(Fw::ActiveFocusReason);
} else
logError("attempt to remove an unknown child from a UIWidget");
2011-08-14 04:09:11 +02:00
}
void UIWidget::focusNextChild(Fw::FocusReason reason)
2011-08-14 04:09:11 +02:00
{
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) {
2011-08-30 01:40:56 +02:00
if(child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) {
2011-08-22 21:13:52 +02:00
toFocus = child;
break;
}
}
2011-08-14 04:09:11 +02:00
if(toFocus)
focusChild(toFocus, reason);
2011-08-14 04:09:11 +02:00
}
2011-08-29 04:18:13 +02:00
void UIWidget::focusPreviousChild(Fw::FocusReason reason)
{
UIWidgetPtr toFocus;
UIWidgetList rotatedChildren(m_children);
std::reverse(m_children.begin(), m_children.end());
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();
}
}
// finds next child to focus
for(const UIWidgetPtr& child : rotatedChildren) {
2011-08-30 01:40:56 +02:00
if(child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) {
2011-08-29 04:18:13 +02:00
toFocus = child;
break;
}
}
if(toFocus)
focusChild(toFocus, reason);
}
2011-08-22 21:13:52 +02:00
void UIWidget::moveChildToTop(const UIWidgetPtr& child)
2011-08-14 04:09:11 +02:00
{
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-22 21:13:52 +02:00
if(!child)
return;
2011-08-22 21:13:52 +02:00
assert(hasChild(child));
// prevent double locks
2011-08-29 20:38:01 +02:00
if(isChildLocked(child))
unlockChild(child);
2011-08-22 21:13:52 +02:00
// disable all other children
for(const UIWidgetPtr& otherChild : m_children) {
if(otherChild == child)
child->setEnabled(true);
else
2011-08-22 21:13:52 +02:00
otherChild->setEnabled(false);
}
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())
2011-08-29 20:38:01 +02:00
focusChild(child, Fw::ActiveFocusReason);
moveChildToTop(child);
}
2011-08-22 21:13:52 +02:00
void UIWidget::unlockChild(const UIWidgetPtr& child)
{
2011-08-22 21:13:52 +02:00
if(!child)
return;
assert(hasChild(child));
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-22 21:13:52 +02:00
m_lockedChildren.erase(it);
2011-08-20 22:30:41 +02:00
2011-08-29 20:38:01 +02:00
// find new child to lock
2011-08-22 21:13:52 +02:00
UIWidgetPtr lockedChild;
2011-08-29 20:38:01 +02:00
if(m_lockedChildren.size() > 0) {
2011-08-22 21:13:52 +02:00
lockedChild = m_lockedChildren.front();
2011-08-29 20:38:01 +02:00
assert(hasChild(lockedChild));
}
2011-08-22 21:13:52 +02:00
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-29 20:38:01 +02:00
if(lockedChild) {
if(lockedChild->isFocusable())
focusChild(lockedChild, Fw::ActiveFocusReason);
moveChildToTop(lockedChild);
}
}
bool UIWidget::isChildLocked(const UIWidgetPtr& child)
{
auto it = std::find(m_lockedChildren.begin(), m_lockedChildren.end(), child);
return it != m_lockedChildren.end();
}
void UIWidget::updateParentLayout()
2011-08-14 04:09:11 +02:00
{
if(UIWidgetPtr parent = getParent())
parent->updateLayout();
else
updateLayout();
2011-08-14 04:09:11 +02:00
}
void UIWidget::updateLayout()
2011-08-14 04:09:11 +02:00
{
if(m_layout)
m_layout->update();
2011-08-14 04:09:11 +02:00
}
void UIWidget::updateState(Fw::WidgetState state)
2011-08-14 04:09:11 +02:00
{
bool newStatus = true;
bool oldStatus = hasState(state);
bool updateChildren = false;
2011-08-14 04:09:11 +02:00
if(state == Fw::ActiveState) {
UIWidgetPtr widget = asUIWidget();
UIWidgetPtr parent;
do {
parent = widget->getParent();
if(!widget->isExplicitlyEnabled() ||
((parent && parent->getFocusedChild() != widget))) {
newStatus = false;
break;
}
} while(widget = parent);
updateChildren = true;
}
else if(state == Fw::FocusState) {
newStatus = (getParent() && getParent()->getFocusedChild() == asUIWidget());
}
else if(state == Fw::HoverState) {
updateChildren = true;
Point mousePos = g_platform.getMouseCursorPos();
UIWidgetPtr widget = asUIWidget();
UIWidgetPtr parent;
do {
parent = widget->getParent();
2011-08-26 20:56:56 +02:00
if(!widget->isExplicitlyEnabled() || !widget->getRect().contains(mousePos) ||
(parent && widget != parent->getChildByPos(mousePos))) {
newStatus = false;
break;
}
} while(widget = parent);
2011-08-23 03:08:36 +02:00
}
else if(state == Fw::PressedState) {
newStatus = m_pressed;
}
else if(state == Fw::DisabledState) {
2011-08-26 20:56:56 +02:00
bool enabled = true;
updateChildren = true;
UIWidgetPtr widget = asUIWidget();
do {
if(!widget->isExplicitlyEnabled()) {
2011-08-26 20:56:56 +02:00
enabled = false;
break;
}
} while(widget = widget->getParent());
2011-08-26 20:56:56 +02:00
newStatus = !enabled;
}
else {
return;
2011-08-21 21:43:05 +02:00
}
if(updateChildren) {
for(const UIWidgetPtr& child : m_children)
child->updateState(state);
}
2011-08-21 21:43:05 +02:00
if(newStatus != oldStatus) {
if(newStatus)
m_states |= state;
else
m_states &= ~state;
2011-08-21 21:43:05 +02:00
updateStyle();
2011-08-21 21:43:05 +02:00
if(state == Fw::FocusState)
onFocusChange(newStatus, m_lastFocusReason);
else if(state == Fw::HoverState)
onHoverChange(newStatus);
}
2011-08-21 21:43:05 +02:00
}
void UIWidget::updateStates()
2011-08-21 21:43:05 +02:00
{
updateState(Fw::ActiveState);
updateState(Fw::FocusState);
updateState(Fw::DisabledState);
updateState(Fw::HoverState);
2011-08-21 21:43:05 +02:00
}
void UIWidget::updateStyle()
2011-08-21 21:43:05 +02:00
{
if(!m_style)
return;
2011-08-21 21:43:05 +02:00
OTMLNodePtr newStateStyle = OTMLNode::create();
2011-08-21 21:43:05 +02:00
// copy only the changed styles from default style
if(m_stateStyle) {
for(OTMLNodePtr node : m_stateStyle->children()) {
if(OTMLNodePtr otherNode = m_style->get(node->tag()))
newStateStyle->addChild(otherNode->clone());
2011-08-21 21:43:05 +02:00
}
}
// merge states styles, NOTE: order does matter
OTMLNodePtr style = m_style->get("state.active");
if(style && hasState(Fw::ActiveState))
newStateStyle->merge(style);
2011-08-21 21:43:05 +02:00
style = m_style->get("state.focus");
if(style && hasState(Fw::FocusState))
newStateStyle->merge(style);
2011-08-21 21:43:05 +02:00
style = m_style->get("state.hover");
if(style && hasState(Fw::HoverState))
newStateStyle->merge(style);
2011-08-21 21:43:05 +02:00
style = m_style->get("state.pressed");
if(style && hasState(Fw::PressedState))
newStateStyle->merge(style);
2011-08-23 03:08:36 +02:00
style = m_style->get("state.disabled");
if(style && hasState(Fw::DisabledState))
newStateStyle->merge(style);
2011-08-21 21:43:05 +02:00
applyStyle(newStateStyle);
m_stateStyle = newStateStyle;
2011-08-21 21:43:05 +02:00
}
void UIWidget::applyStyle(const OTMLNodePtr& styleNode)
2011-08-21 21:43:05 +02:00
{
try {
onStyleApply(styleNode);
} catch(std::exception& e) {
logError("failed to apply widget '", m_id, "' style: ", e.what());
2011-08-21 21:43:05 +02:00
}
2011-08-14 04:09:11 +02:00
}
2011-08-22 21:13:52 +02:00
void UIWidget::onStyleApply(const OTMLNodePtr& styleNode)
{
// first set id
if(const OTMLNodePtr& node = styleNode->get("id"))
setId(node->value());
2011-08-22 21:13:52 +02:00
// load styles used by all widgets
for(const OTMLNodePtr& node : styleNode->children()) {
// background image
if(node->tag() == "image") {
2011-10-29 01:56:45 +02:00
ImagePtr image = ImagePtr(new Image);
image->loadFromOTML(node);
setImage(image);
2011-08-22 21:13:52 +02:00
}
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>());
}
// focusable
else if(node->tag() == "focusable") {
setFocusable(node->value<bool>());
}
2011-08-22 21:13:52 +02:00
// 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>());
}
else if(node->tag() == "size fixed") {
setSizeFixed(node->value<bool>());
}
2011-08-22 21:13:52 +02:00
// 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>());
}
// layouts
else if(node->tag() == "layout") {
// layout is set only once
assert(!m_layout);
if(node->value() == "verticalBox") {
setLayout(UILayoutPtr(new UIVerticalLayout(asUIWidget())));
} else if(node->value() == "anchor") {
setLayout(UILayoutPtr(new UIAnchorLayout(asUIWidget())));
}
}
2011-08-22 21:13:52 +02:00
// anchors
else if(boost::starts_with(node->tag(), "anchors.")) {
UIWidgetPtr parent = getParent();
if(!parent)
throw OTMLException(node, "cannot create anchor, there is no parent widget!");
UIAnchorLayoutPtr anchorLayout = parent->getLayout()->asUIAnchorLayout();
if(!anchorLayout)
throw OTMLException(node, "cannot create anchor, the parent widget doesn't use anchor layout!");
2011-08-22 21:13:52 +02:00
std::string what = node->tag().substr(8);
if(what == "fill") {
anchorLayout->fill(asUIWidget(), node->value());
2011-08-22 21:13:52 +02:00
} else if(what == "centerIn") {
anchorLayout->centerIn(asUIWidget(), node->value());
2011-08-22 21:13:52 +02:00
} else {
Fw::AnchorEdge anchoredEdge = Fw::translateAnchorEdge(what);
2011-08-22 21:13:52 +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 hookedWidgetId = split[0];
Fw::AnchorEdge hookedEdge = Fw::translateAnchorEdge(split[1]);
2011-08-22 21:13:52 +02:00
if(anchoredEdge == Fw::AnchorNone)
2011-08-22 21:13:52 +02:00
throw OTMLException(node, "invalid anchor edge");
if(hookedEdge == Fw::AnchorNone)
2011-08-22 21:13:52 +02:00
throw OTMLException(node, "invalid anchor target edge");
anchorLayout->addAnchor(asUIWidget(), anchoredEdge, hookedWidgetId, hookedEdge);
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)
{
callLuaField("onGeometryUpdate", oldRect, newRect);
2011-08-22 14:44:26 +02:00
}
void UIWidget::onFocusChange(bool focused, Fw::FocusReason reason)
2011-08-21 03:01:46 +02:00
{
callLuaField("onHoverChange", 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
{
callLuaField("onHoverChange", hovered);
2011-08-22 14:44:26 +02:00
}
bool UIWidget::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
{
if(callLuaField<bool>("onKeyPress", keyCode, keyChar, keyboardModifiers))
return true;
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_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 04:09:11 +02:00
// key events go only to containers or focused child
if(child->isFocused())
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
if(child->onKeyPress(keyCode, keyChar, keyboardModifiers))
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::onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers)
2011-08-14 04:09:11 +02:00
{
if(callLuaField<bool>("onKeyRelease", keyCode, keyChar, keyboardModifiers))
return true;
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_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;
// key events go only to focused child
if(child->isFocused())
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
if(child->onKeyRelease(keyCode, keyChar, keyboardModifiers))
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
}
bool UIWidget::onMousePress(const Point& mousePos, Fw::MouseButton button)
2011-08-14 04:09:11 +02:00
{
if(callLuaField<bool>("onMousePress", mousePos, button))
return true;
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_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;
// mouse press events only go to children that contains the mouse position
if(child->getRect().contains(mousePos) && child == getChildByPos(mousePos))
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
// when a focusable item is focused it must gain focus
if(child->isFocusable())
focusChild(child, Fw::MouseFocusReason);
bool mustEnd = child->onMousePress(mousePos, button);
if(!child->getChildByPos(mousePos) && !child->isPressed())
child->setPressed(true);
if(mustEnd)
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
}
bool UIWidget::onMouseRelease(const Point& mousePos, Fw::MouseButton button)
2011-08-14 04:09:11 +02:00
{
if(callLuaField<bool>("onMouseRelease", mousePos, button))
return true;
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_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 04:09:11 +02:00
// mouse release events go to all children
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
bool mustEnd = child->onMouseRelease(mousePos, button);
if(child->isPressed())
child->setPressed(false);
if(mustEnd)
2011-08-22 14:44:26 +02:00
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
{
if(callLuaField<bool>("onMouseMove", mousePos, mouseMoved))
return true;
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_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;
// mouse move events go to all children
children.push_back(child);
}
for(const UIWidgetPtr& child : 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
}
bool UIWidget::onMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction)
2011-08-14 04:09:11 +02:00
{
if(callLuaField<bool>("onMouseWheel", mousePos, direction))
return true;
2011-08-14 04:09:11 +02:00
// do a backup of children list, because it may change while looping it
UIWidgetList children;
for(const UIWidgetPtr& child : m_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 04:09:11 +02:00
// mouse wheel events only go to children that contains the mouse position
if(child->getRect().contains(mousePos) && child == getChildByPos(mousePos))
children.push_back(child);
}
for(const UIWidgetPtr& child : children) {
if(child->onMouseWheel(mousePos, direction))
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
}