make hover, dragging, press and mouse release work in a proper way

This commit is contained in:
Eduardo Bart 2012-02-07 17:21:53 -02:00
parent d6ade5a8e0
commit e51789378a
9 changed files with 332 additions and 106 deletions

View File

@ -66,6 +66,7 @@ function ToolTip.display(text)
toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4) toolTipLabel:resize(toolTipLabel:getWidth() + 4, toolTipLabel:getHeight() + 4)
toolTipLabel:show() toolTipLabel:show()
toolTipLabel:raise() toolTipLabel:raise()
toolTipLabel:enable()
moveToolTip(toolTipLabel) moveToolTip(toolTipLabel)
end end

View File

@ -272,7 +272,8 @@ namespace Fw
MiddleState = 256, MiddleState = 256,
LastState = 512, LastState = 512,
AlternateState = 1024, AlternateState = 1024,
LastWidgetState = 2048, DraggingState = 2048,
LastWidgetState = 4096
}; };
enum AppicationFlags { enum AppicationFlags {

View File

@ -87,7 +87,7 @@ void ModuleManager::discoverModulesPath()
"" }; "" };
for(const std::string& dir : possibleAddonsDirs) { for(const std::string& dir : possibleAddonsDirs) {
// try to add module directory // try to add module directory
if(g_resources.addToSearchPath(dir, false)) { if(g_resources.addToSearchPath(dir, true)) {
logInfo("Using addons directory '", dir.c_str(), "'"); logInfo("Using addons directory '", dir.c_str(), "'");
found = true; found = true;
break; break;

View File

@ -98,12 +98,10 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setStyleFromNode", &UIWidget::setStyleFromNode); g_lua.bindClassMemberFunction<UIWidget>("setStyleFromNode", &UIWidget::setStyleFromNode);
g_lua.bindClassMemberFunction<UIWidget>("setEnabled", &UIWidget::setEnabled); g_lua.bindClassMemberFunction<UIWidget>("setEnabled", &UIWidget::setEnabled);
g_lua.bindClassMemberFunction<UIWidget>("setVisible", &UIWidget::setVisible); g_lua.bindClassMemberFunction<UIWidget>("setVisible", &UIWidget::setVisible);
g_lua.bindClassMemberFunction<UIWidget>("setPressed", &UIWidget::setPressed);
g_lua.bindClassMemberFunction<UIWidget>("setOn", &UIWidget::setOn); g_lua.bindClassMemberFunction<UIWidget>("setOn", &UIWidget::setOn);
g_lua.bindClassMemberFunction<UIWidget>("setChecked", &UIWidget::setChecked); g_lua.bindClassMemberFunction<UIWidget>("setChecked", &UIWidget::setChecked);
g_lua.bindClassMemberFunction<UIWidget>("setFocusable", &UIWidget::setFocusable); g_lua.bindClassMemberFunction<UIWidget>("setFocusable", &UIWidget::setFocusable);
g_lua.bindClassMemberFunction<UIWidget>("setPhantom", &UIWidget::setPhantom); g_lua.bindClassMemberFunction<UIWidget>("setPhantom", &UIWidget::setPhantom);
g_lua.bindClassMemberFunction<UIWidget>("setDragging", &UIWidget::setDragging);
g_lua.bindClassMemberFunction<UIWidget>("setDragable", &UIWidget::setDragable); g_lua.bindClassMemberFunction<UIWidget>("setDragable", &UIWidget::setDragable);
g_lua.bindClassMemberFunction<UIWidget>("setFixedSize", &UIWidget::setFixedSize); g_lua.bindClassMemberFunction<UIWidget>("setFixedSize", &UIWidget::setFixedSize);
g_lua.bindClassMemberFunction<UIWidget>("setLastFocusReason", &UIWidget::setLastFocusReason); g_lua.bindClassMemberFunction<UIWidget>("setLastFocusReason", &UIWidget::setLastFocusReason);
@ -143,13 +141,13 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("isAlternate", &UIWidget::isAlternate); g_lua.bindClassMemberFunction<UIWidget>("isAlternate", &UIWidget::isAlternate);
g_lua.bindClassMemberFunction<UIWidget>("isChecked", &UIWidget::isChecked); g_lua.bindClassMemberFunction<UIWidget>("isChecked", &UIWidget::isChecked);
g_lua.bindClassMemberFunction<UIWidget>("isOn", &UIWidget::isOn); g_lua.bindClassMemberFunction<UIWidget>("isOn", &UIWidget::isOn);
g_lua.bindClassMemberFunction<UIWidget>("isDragging", &UIWidget::isDragging);
g_lua.bindClassMemberFunction<UIWidget>("isHidden", &UIWidget::isHidden); g_lua.bindClassMemberFunction<UIWidget>("isHidden", &UIWidget::isHidden);
g_lua.bindClassMemberFunction<UIWidget>("isExplicitlyEnabled", &UIWidget::isExplicitlyEnabled); g_lua.bindClassMemberFunction<UIWidget>("isExplicitlyEnabled", &UIWidget::isExplicitlyEnabled);
g_lua.bindClassMemberFunction<UIWidget>("isExplicitlyVisible", &UIWidget::isExplicitlyVisible); g_lua.bindClassMemberFunction<UIWidget>("isExplicitlyVisible", &UIWidget::isExplicitlyVisible);
g_lua.bindClassMemberFunction<UIWidget>("isFocusable", &UIWidget::isFocusable); g_lua.bindClassMemberFunction<UIWidget>("isFocusable", &UIWidget::isFocusable);
g_lua.bindClassMemberFunction<UIWidget>("isPhantom", &UIWidget::isPhantom); g_lua.bindClassMemberFunction<UIWidget>("isPhantom", &UIWidget::isPhantom);
g_lua.bindClassMemberFunction<UIWidget>("isDragable", &UIWidget::isDragable); g_lua.bindClassMemberFunction<UIWidget>("isDragable", &UIWidget::isDragable);
g_lua.bindClassMemberFunction<UIWidget>("isDragging", &UIWidget::isDragging);
g_lua.bindClassMemberFunction<UIWidget>("isFixedSize", &UIWidget::isFixedSize); g_lua.bindClassMemberFunction<UIWidget>("isFixedSize", &UIWidget::isFixedSize);
g_lua.bindClassMemberFunction<UIWidget>("isDestroyed", &UIWidget::isDestroyed); g_lua.bindClassMemberFunction<UIWidget>("isDestroyed", &UIWidget::isDestroyed);
g_lua.bindClassMemberFunction<UIWidget>("hasChildren", &UIWidget::hasChildren); g_lua.bindClassMemberFunction<UIWidget>("hasChildren", &UIWidget::hasChildren);

View File

@ -46,6 +46,8 @@ void UIManager::terminate()
m_keyboardReceiver = nullptr; m_keyboardReceiver = nullptr;
m_rootWidget = nullptr; m_rootWidget = nullptr;
m_draggingWidget = nullptr; m_draggingWidget = nullptr;
m_hoveredWidget = nullptr;
m_pressedWidget = nullptr;
} }
void UIManager::render() void UIManager::render()
@ -76,28 +78,33 @@ void UIManager::inputEvent(const InputEvent& event)
break; break;
case Fw::MousePressInputEvent: case Fw::MousePressInputEvent:
m_mouseReceiver->propagateOnMousePress(event.mousePos, event.mouseButton); m_mouseReceiver->propagateOnMousePress(event.mousePos, event.mouseButton);
if(event.mouseButton == Fw::MouseLeftButton)
updatePressedWidget(m_rootWidget->recursiveGetChildByPos(event.mousePos), event.mousePos);
break; break;
case Fw::MouseReleaseInputEvent: case Fw::MouseReleaseInputEvent:
m_mouseReceiver->propagateOnMouseRelease(event.mousePos, event.mouseButton); m_mouseReceiver->propagateOnMouseRelease(event.mousePos, event.mouseButton);
if(m_draggingWidget && event.mouseButton == Fw::MouseLeftButton) { if(event.mouseButton == Fw::MouseLeftButton) {
auto clickedChildren = m_rootWidget->recursiveGetChildrenByPos(event.mousePos); // release pressed widget
UIWidgetPtr droppedWidget; if(m_pressedWidget)
for(const UIWidgetPtr& child : clickedChildren) { updatePressedWidget(nullptr, event.mousePos);
if(child->onDrop(m_draggingWidget, event.mousePos)) {
droppedWidget = child;
break;
}
}
m_draggingWidget->onDragLeave(droppedWidget, event.mousePos); // release dragging widget
m_draggingWidget->setDragging(false); if(m_draggingWidget)
m_draggingWidget = nullptr; updateDraggingWidget(nullptr, event.mousePos);
} }
break; break;
case Fw::MouseMoveInputEvent: case Fw::MouseMoveInputEvent: {
m_mouseReceiver->updateState(Fw::HoverState); // start dragging when moves a pressed widget
if(m_pressedWidget && m_pressedWidget->isDragable() && m_draggingWidget != m_pressedWidget)
updateDraggingWidget(m_pressedWidget, event.mousePos - event.mouseMoved);
if(m_draggingWidget)
m_draggingWidget->onDragMove(event.mousePos, event.mouseMoved);
updateHoveredWidget();
m_mouseReceiver->propagateOnMouseMove(event.mousePos, event.mouseMoved); m_mouseReceiver->propagateOnMouseMove(event.mousePos, event.mouseMoved);
break; break;
}
case Fw::MouseWheelInputEvent: case Fw::MouseWheelInputEvent:
m_mouseReceiver->propagateOnMouseWheel(event.mousePos, event.wheelDirection); m_mouseReceiver->propagateOnMouseWheel(event.mousePos, event.wheelDirection);
break; break;
@ -105,6 +112,103 @@ void UIManager::inputEvent(const InputEvent& event)
m_isOnInputEvent = false; m_isOnInputEvent = false;
} }
void UIManager::updatePressedWidget(const UIWidgetPtr& newPressedWidget, const Point& clickedPos)
{
UIWidgetPtr oldPressedWidget = m_pressedWidget;
m_pressedWidget = newPressedWidget;
if(newPressedWidget)
newPressedWidget->updateState(Fw::PressedState);
if(oldPressedWidget) {
oldPressedWidget->updateState(Fw::PressedState);
// when releasing mouse inside pressed widget area send onClick event
if(!clickedPos.isNull() && oldPressedWidget->containsPoint(clickedPos))
oldPressedWidget->onClick(clickedPos);
// must send mouse events even if the mouse is outside widget area when releasing a pressed widget
else
oldPressedWidget->onMouseRelease(clickedPos, Fw::MouseLeftButton);
}
}
void UIManager::updateHoveredWidget()
{
UIWidgetPtr hoveredWidget = m_rootWidget->recursiveGetChildByPos(g_window.getMousePosition());
if(hoveredWidget != m_hoveredWidget) {
UIWidgetPtr oldHovered = m_hoveredWidget;
m_hoveredWidget = hoveredWidget;
if(oldHovered)
oldHovered->updateState(Fw::HoverState);
if(hoveredWidget)
hoveredWidget->updateState(Fw::HoverState);
}
}
void UIManager::updateDraggingWidget(const UIWidgetPtr& draggingWidget, const Point& clickedPos)
{
UIWidgetPtr oldDraggingWidget = m_draggingWidget;
if(oldDraggingWidget) {
auto clickedChildren = m_rootWidget->recursiveGetChildrenByPos(clickedPos);
UIWidgetPtr droppedWidget;
for(const UIWidgetPtr& child : clickedChildren) {
if(child->onDrop(oldDraggingWidget, clickedPos)) {
droppedWidget = child;
break;
}
}
oldDraggingWidget->onDragLeave(droppedWidget, clickedPos);
}
m_draggingWidget = draggingWidget;
if(oldDraggingWidget)
oldDraggingWidget->updateState(Fw::DraggingState);
if(draggingWidget) {
draggingWidget->updateState(Fw::DraggingState);
draggingWidget->onDragEnter(clickedPos);
}
}
void UIManager::onWidgetAppear(const UIWidgetPtr& widget)
{
if(widget->getRootParent() != m_rootWidget || !widget->isVisible())
return;
if(widget->getRect().contains(g_window.getMousePosition()))
updateHoveredWidget();
}
void UIManager::onWidgetDisappear(const UIWidgetPtr& widget)
{
if(widget->getRootParent() != m_rootWidget || widget->isVisible())
return;
if(m_hoveredWidget == widget)
updateHoveredWidget();
if(m_pressedWidget == widget)
updatePressedWidget(nullptr);
}
void UIManager::onWidgetDestroy(const UIWidgetPtr& widget)
{
// release input grabs
if(m_keyboardReceiver == widget)
resetKeyboardReceiver();
if(m_mouseReceiver == widget)
resetMouseReceiver();
if(m_hoveredWidget == widget)
updateHoveredWidget();
if(m_pressedWidget == widget)
updatePressedWidget(nullptr);
}
bool UIManager::importStyle(const std::string& file) bool UIManager::importStyle(const std::string& file)
{ {
try { try {

View File

@ -37,6 +37,10 @@ public:
void resize(const Size& size); void resize(const Size& size);
void inputEvent(const InputEvent& event); void inputEvent(const InputEvent& event);
void updatePressedWidget(const UIWidgetPtr& newPressedWidget, const Point& clickedPos = Point());
void updateHoveredWidget();
void updateDraggingWidget(const UIWidgetPtr& draggingWidget, const Point& clickedPos);
bool importStyle(const std::string& file); bool importStyle(const std::string& file);
void importStyleFromOTML(const OTMLNodePtr& styleNode); void importStyleFromOTML(const OTMLNodePtr& styleNode);
OTMLNodePtr getStyle(const std::string& styleName); OTMLNodePtr getStyle(const std::string& styleName);
@ -48,22 +52,32 @@ public:
void setMouseReceiver(const UIWidgetPtr& widget) { m_mouseReceiver = widget; } void setMouseReceiver(const UIWidgetPtr& widget) { m_mouseReceiver = widget; }
void setKeyboardReceiver(const UIWidgetPtr& widget) { m_keyboardReceiver = widget; } void setKeyboardReceiver(const UIWidgetPtr& widget) { m_keyboardReceiver = widget; }
void setDebugBoxesDrawing(bool enabled) { m_drawDebugBoxes = enabled; } void setDebugBoxesDrawing(bool enabled) { m_drawDebugBoxes = enabled; }
void setDraggingWidget(const UIWidgetPtr& widget) { m_draggingWidget = widget; }
void resetMouseReceiver() { m_mouseReceiver = m_rootWidget; } void resetMouseReceiver() { m_mouseReceiver = m_rootWidget; }
void resetKeyboardReceiver() { m_keyboardReceiver = m_rootWidget; } void resetKeyboardReceiver() { m_keyboardReceiver = m_rootWidget; }
UIWidgetPtr getMouseReceiver() { return m_mouseReceiver; } UIWidgetPtr getMouseReceiver() { return m_mouseReceiver; }
UIWidgetPtr getKeyboardReceiver() { return m_keyboardReceiver; } UIWidgetPtr getKeyboardReceiver() { return m_keyboardReceiver; }
UIWidgetPtr getDraggingWidget() { return m_draggingWidget; } UIWidgetPtr getDraggingWidget() { return m_draggingWidget; }
UIWidgetPtr getHoveredWidget() { return m_hoveredWidget; }
UIWidgetPtr getPressedWidget() { return m_pressedWidget; }
UIWidgetPtr getRootWidget() { return m_rootWidget; } UIWidgetPtr getRootWidget() { return m_rootWidget; }
bool isOnInputEvent() { return m_isOnInputEvent; } bool isOnInputEvent() { return m_isOnInputEvent; }
bool isDrawingDebugBoxes() { return m_drawDebugBoxes; } bool isDrawingDebugBoxes() { return m_drawDebugBoxes; }
protected:
void onWidgetAppear(const UIWidgetPtr& widget);
void onWidgetDisappear(const UIWidgetPtr& widget);
void onWidgetDestroy(const UIWidgetPtr& widget);
friend class UIWidget;
private: private:
UIWidgetPtr m_rootWidget; UIWidgetPtr m_rootWidget;
UIWidgetPtr m_mouseReceiver; UIWidgetPtr m_mouseReceiver;
UIWidgetPtr m_keyboardReceiver; UIWidgetPtr m_keyboardReceiver;
UIWidgetPtr m_draggingWidget; UIWidgetPtr m_draggingWidget;
UIWidgetPtr m_hoveredWidget;
UIWidgetPtr m_pressedWidget;
bool m_isOnInputEvent; bool m_isOnInputEvent;
Boolean<false> m_drawDebugBoxes; Boolean<false> m_drawDebugBoxes;
std::map<std::string, OTMLNodePtr> m_styles; std::map<std::string, OTMLNodePtr> m_styles;

View File

@ -93,6 +93,8 @@ Fw::WidgetState Fw::translateState(std::string state)
return Fw::LastState; return Fw::LastState;
else if(state == "alternate") else if(state == "alternate")
return Fw::AlternateState; return Fw::AlternateState;
else if(state == "dragging")
return Fw::DraggingState;
else else
return Fw::InvalidState; return Fw::InvalidState;
} }

View File

@ -127,6 +127,8 @@ void UIWidget::addChild(const UIWidgetPtr& child)
// update new child states // update new child states
child->updateStates(); child->updateStates();
updateChildrenIndexStates(); updateChildrenIndexStates();
g_ui.onWidgetAppear(child);
} }
void UIWidget::insertChild(int index, const UIWidgetPtr& child) void UIWidget::insertChild(int index, const UIWidgetPtr& child)
@ -160,6 +162,8 @@ void UIWidget::insertChild(int index, const UIWidgetPtr& child)
// update new child states // update new child states
child->updateStates(); child->updateStates();
updateChildrenIndexStates(); updateChildrenIndexStates();
g_ui.onWidgetAppear(child);
} }
void UIWidget::removeChild(UIWidgetPtr child) void UIWidget::removeChild(UIWidgetPtr child)
@ -191,6 +195,8 @@ void UIWidget::removeChild(UIWidgetPtr child)
if(focusAnother && !m_focusedChild) if(focusAnother && !m_focusedChild)
focusPreviousChild(Fw::ActiveFocusReason); focusPreviousChild(Fw::ActiveFocusReason);
g_ui.onWidgetDisappear(child);
} else } else
logError("Attempt to remove an unknown child from a UIWidget"); logError("Attempt to remove an unknown child from a UIWidget");
} }
@ -198,6 +204,9 @@ void UIWidget::removeChild(UIWidgetPtr child)
void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason) void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason)
{ {
if(m_destroyed)
return;
if(child == m_focusedChild) if(child == m_focusedChild)
return; return;
@ -226,6 +235,9 @@ void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason)
void UIWidget::focusNextChild(Fw::FocusReason reason) void UIWidget::focusNextChild(Fw::FocusReason reason)
{ {
if(m_destroyed)
return;
UIWidgetPtr toFocus; UIWidgetPtr toFocus;
UIWidgetList rotatedChildren(m_children); UIWidgetList rotatedChildren(m_children);
@ -251,6 +263,9 @@ void UIWidget::focusNextChild(Fw::FocusReason reason)
void UIWidget::focusPreviousChild(Fw::FocusReason reason) void UIWidget::focusPreviousChild(Fw::FocusReason reason)
{ {
if(m_destroyed)
return;
UIWidgetPtr toFocus; UIWidgetPtr toFocus;
UIWidgetList rotatedChildren(m_children); UIWidgetList rotatedChildren(m_children);
std::reverse(rotatedChildren.begin(), rotatedChildren.end()); std::reverse(rotatedChildren.begin(), rotatedChildren.end());
@ -277,6 +292,9 @@ void UIWidget::focusPreviousChild(Fw::FocusReason reason)
void UIWidget::lowerChild(UIWidgetPtr child) void UIWidget::lowerChild(UIWidgetPtr child)
{ {
if(m_destroyed)
return;
if(!child) if(!child)
return; return;
@ -290,6 +308,9 @@ void UIWidget::lowerChild(UIWidgetPtr child)
void UIWidget::raiseChild(UIWidgetPtr child) void UIWidget::raiseChild(UIWidgetPtr child)
{ {
if(m_destroyed)
return;
if(!child) if(!child)
return; return;
@ -303,6 +324,9 @@ void UIWidget::raiseChild(UIWidgetPtr child)
void UIWidget::moveChildToIndex(const UIWidgetPtr& child, int index) void UIWidget::moveChildToIndex(const UIWidgetPtr& child, int index)
{ {
if(m_destroyed)
return;
if(!child) if(!child)
return; return;
@ -316,6 +340,9 @@ void UIWidget::moveChildToIndex(const UIWidgetPtr& child, int index)
void UIWidget::lockChild(const UIWidgetPtr& child) void UIWidget::lockChild(const UIWidgetPtr& child)
{ {
if(m_destroyed)
return;
if(!child) if(!child)
return; return;
@ -344,6 +371,9 @@ void UIWidget::lockChild(const UIWidgetPtr& child)
void UIWidget::unlockChild(const UIWidgetPtr& child) void UIWidget::unlockChild(const UIWidgetPtr& child)
{ {
if(m_destroyed)
return;
if(!child) if(!child)
return; return;
@ -385,6 +415,9 @@ void UIWidget::unlockChild(const UIWidgetPtr& child)
void UIWidget::applyStyle(const OTMLNodePtr& styleNode) void UIWidget::applyStyle(const OTMLNodePtr& styleNode)
{ {
if(m_destroyed)
return;
if(styleNode->size() == 0) if(styleNode->size() == 0)
return; return;
@ -406,8 +439,12 @@ void UIWidget::applyStyle(const OTMLNodePtr& styleNode)
} }
m_loadingStyle = false; m_loadingStyle = false;
} }
void UIWidget::addAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge) void UIWidget::addAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge)
{ {
if(m_destroyed)
return;
if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout())
anchorLayout->addAnchor(asUIWidget(), anchoredEdge, hookedWidgetId, hookedEdge); anchorLayout->addAnchor(asUIWidget(), anchoredEdge, hookedWidgetId, hookedEdge);
else else
@ -416,6 +453,9 @@ void UIWidget::addAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedW
void UIWidget::centerIn(const std::string& hookedWidgetId) void UIWidget::centerIn(const std::string& hookedWidgetId)
{ {
if(m_destroyed)
return;
if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) { if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) {
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorHorizontalCenter, hookedWidgetId, Fw::AnchorHorizontalCenter); anchorLayout->addAnchor(asUIWidget(), Fw::AnchorHorizontalCenter, hookedWidgetId, Fw::AnchorHorizontalCenter);
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorVerticalCenter, hookedWidgetId, Fw::AnchorVerticalCenter); anchorLayout->addAnchor(asUIWidget(), Fw::AnchorVerticalCenter, hookedWidgetId, Fw::AnchorVerticalCenter);
@ -425,6 +465,9 @@ void UIWidget::centerIn(const std::string& hookedWidgetId)
void UIWidget::fill(const std::string& hookedWidgetId) void UIWidget::fill(const std::string& hookedWidgetId)
{ {
if(m_destroyed)
return;
if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) { if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) {
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorLeft, hookedWidgetId, Fw::AnchorLeft); anchorLayout->addAnchor(asUIWidget(), Fw::AnchorLeft, hookedWidgetId, Fw::AnchorLeft);
anchorLayout->addAnchor(asUIWidget(), Fw::AnchorRight, hookedWidgetId, Fw::AnchorRight); anchorLayout->addAnchor(asUIWidget(), Fw::AnchorRight, hookedWidgetId, Fw::AnchorRight);
@ -436,12 +479,18 @@ void UIWidget::fill(const std::string& hookedWidgetId)
void UIWidget::breakAnchors() void UIWidget::breakAnchors()
{ {
if(m_destroyed)
return;
if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout()) if(UIAnchorLayoutPtr anchorLayout = getAnchoredLayout())
anchorLayout->removeAnchors(asUIWidget()); anchorLayout->removeAnchors(asUIWidget());
} }
void UIWidget::updateParentLayout() void UIWidget::updateParentLayout()
{ {
if(m_destroyed)
return;
if(UIWidgetPtr parent = getParent()) if(UIWidgetPtr parent = getParent())
parent->updateLayout(); parent->updateLayout();
else else
@ -450,6 +499,9 @@ void UIWidget::updateParentLayout()
void UIWidget::updateLayout() void UIWidget::updateLayout()
{ {
if(m_destroyed)
return;
if(m_layout) if(m_layout)
m_layout->update(); m_layout->update();
@ -461,26 +513,39 @@ void UIWidget::updateLayout()
void UIWidget::lock() void UIWidget::lock()
{ {
if(m_destroyed)
return;
if(UIWidgetPtr parent = getParent()) if(UIWidgetPtr parent = getParent())
parent->lockChild(asUIWidget()); parent->lockChild(asUIWidget());
} }
void UIWidget::unlock() void UIWidget::unlock()
{ {
if(m_destroyed)
return;
if(UIWidgetPtr parent = getParent()) if(UIWidgetPtr parent = getParent())
parent->unlockChild(asUIWidget()); parent->unlockChild(asUIWidget());
} }
void UIWidget::focus() void UIWidget::focus()
{ {
if(m_destroyed)
return;
if(!m_focusable) if(!m_focusable)
return; return;
if(UIWidgetPtr parent = getParent()) if(UIWidgetPtr parent = getParent())
parent->focusChild(asUIWidget(), Fw::ActiveFocusReason); parent->focusChild(asUIWidget(), Fw::ActiveFocusReason);
} }
void UIWidget::lower() void UIWidget::lower()
{ {
if(m_destroyed)
return;
UIWidgetPtr parent = getParent(); UIWidgetPtr parent = getParent();
if(parent) if(parent)
parent->lowerChild(asUIWidget()); parent->lowerChild(asUIWidget());
@ -488,6 +553,9 @@ void UIWidget::lower()
void UIWidget::raise() void UIWidget::raise()
{ {
if(m_destroyed)
return;
UIWidgetPtr parent = getParent(); UIWidgetPtr parent = getParent();
if(parent) if(parent)
parent->raiseChild(asUIWidget()); parent->raiseChild(asUIWidget());
@ -495,28 +563,43 @@ void UIWidget::raise()
void UIWidget::grabMouse() void UIWidget::grabMouse()
{ {
if(m_destroyed)
return;
g_ui.setMouseReceiver(asUIWidget()); g_ui.setMouseReceiver(asUIWidget());
} }
void UIWidget::ungrabMouse() void UIWidget::ungrabMouse()
{ {
if(m_destroyed)
return;
if(g_ui.getMouseReceiver() == asUIWidget()) if(g_ui.getMouseReceiver() == asUIWidget())
g_ui.resetMouseReceiver(); g_ui.resetMouseReceiver();
} }
void UIWidget::grabKeyboard() void UIWidget::grabKeyboard()
{ {
if(m_destroyed)
return;
g_ui.setKeyboardReceiver(asUIWidget()); g_ui.setKeyboardReceiver(asUIWidget());
} }
void UIWidget::ungrabKeyboard() void UIWidget::ungrabKeyboard()
{ {
if(m_destroyed)
return;
if(g_ui.getKeyboardReceiver() == asUIWidget()) if(g_ui.getKeyboardReceiver() == asUIWidget())
g_ui.resetKeyboardReceiver(); g_ui.resetKeyboardReceiver();
} }
void UIWidget::bindRectToParent() void UIWidget::bindRectToParent()
{ {
if(m_destroyed)
return;
Rect boundRect = m_rect; Rect boundRect = m_rect;
UIWidgetPtr parent = getParent(); UIWidgetPtr parent = getParent();
if(parent) { if(parent) {
@ -532,15 +615,11 @@ void UIWidget::destroy()
if(m_destroyed) if(m_destroyed)
logWarning("attempt to destroy widget '", m_id, "' two times"); logWarning("attempt to destroy widget '", m_id, "' two times");
m_destroyed = true;
setVisible(false); setVisible(false);
setEnabled(false); setEnabled(false);
// release input grabs g_ui.onWidgetDestroy(asUIWidget());
if(g_ui.getKeyboardReceiver() == asUIWidget())
g_ui.resetKeyboardReceiver();
if(g_ui.getMouseReceiver() == asUIWidget())
g_ui.resetMouseReceiver();
// remove itself from parent // remove itself from parent
if(UIWidgetPtr parent = getParent()) { if(UIWidgetPtr parent = getParent()) {
@ -557,19 +636,13 @@ void UIWidget::destroy()
#ifdef DEBUG #ifdef DEBUG
auto self = asUIWidget(); auto self = asUIWidget();
if(self != g_ui.getRootWidget()) { if(self != g_ui.getRootWidget()) {
g_lua.collectGarbage(); g_dispatcher.scheduleEvent([self] {
g_dispatcher.addEvent([self] {
g_lua.collectGarbage();
g_dispatcher.addEvent([self] {
g_lua.collectGarbage(); g_lua.collectGarbage();
if(self->getUseCount() != 1) if(self->getUseCount() != 1)
logWarning("widget '", self->getId(), "' destroyed but still have ", self->getUseCount()-1, " reference(s) left"); logWarning("widget '", self->getId(), "' destroyed but still have ", self->getUseCount()-1, " reference(s) left");
}); }, 100);
});
} }
#endif #endif
m_destroyed = true;
} }
void UIWidget::destroyChildren() void UIWidget::destroyChildren()
@ -683,7 +756,6 @@ void UIWidget::setEnabled(bool enabled)
updateState(Fw::DisabledState); updateState(Fw::DisabledState);
updateState(Fw::ActiveState); updateState(Fw::ActiveState);
updateState(Fw::HoverState);
} }
} }
@ -692,25 +764,22 @@ void UIWidget::setVisible(bool visible)
if(m_visible != visible) { if(m_visible != visible) {
m_visible = visible; m_visible = visible;
// visibility can change the current hovered widget
if(visible)
g_ui.onWidgetAppear(asUIWidget());
else
g_ui.onWidgetDisappear(asUIWidget());
// hiding a widget make it lose focus // hiding a widget make it lose focus
if(!visible && isFocused()) { if(!visible && isFocused()) {
if(UIWidgetPtr parent = getParent()) if(UIWidgetPtr parent = getParent())
parent->focusPreviousChild(Fw::ActiveFocusReason); parent->focusPreviousChild(Fw::ActiveFocusReason);
} }
// visibility can change can change parent layout // visibility can change change parent layout
updateParentLayout(); updateParentLayout();
updateState(Fw::ActiveState); updateState(Fw::ActiveState);
updateState(Fw::HoverState);
}
}
void UIWidget::setPressed(bool pressed)
{
if(pressed != m_pressed) {
m_pressed = pressed;
updateState(Fw::PressedState);
} }
} }
@ -743,11 +812,6 @@ void UIWidget::setPhantom(bool phantom)
m_phantom = phantom; m_phantom = phantom;
} }
void UIWidget::setDragging(bool dragging)
{
m_dragging = dragging;
}
void UIWidget::setDragable(bool dragable) void UIWidget::setDragable(bool dragable)
{ {
m_dragable = dragable; m_dragable = dragable;
@ -885,7 +949,8 @@ UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos)
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) { for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
const UIWidgetPtr& child = (*it); const UIWidgetPtr& child = (*it);
if(child->isExplicitlyVisible() && child->containsPoint(childPos)) { if(child->isExplicitlyVisible() && child->containsPoint(childPos)) {
if(UIWidgetPtr subChild = child->recursiveGetChildByPos(childPos)) UIWidgetPtr subChild = child->recursiveGetChildByPos(childPos);
if(subChild)
return subChild; return subChild;
else if(!child->isPhantom()) else if(!child->isPhantom())
return child; return child;
@ -948,6 +1013,9 @@ bool UIWidget::hasState(Fw::WidgetState state)
void UIWidget::updateState(Fw::WidgetState state) void UIWidget::updateState(Fw::WidgetState state)
{ {
if(m_destroyed)
return;
bool newStatus = true; bool newStatus = true;
bool oldStatus = hasState(state); bool oldStatus = hasState(state);
bool updateChildren = false; bool updateChildren = false;
@ -973,16 +1041,15 @@ void UIWidget::updateState(Fw::WidgetState state)
break; break;
} }
case Fw::HoverState: { case Fw::HoverState: {
updateChildren = true; newStatus = (g_ui.getHoveredWidget() == asUIWidget());
Point mousePos = g_window.getMousePosition();
UIWidgetPtr self = asUIWidget();
UIWidgetPtr rootParent = self->getRootParent();
if(!rootParent || rootParent->recursiveGetChildByPos(mousePos) != self)
newStatus = false;
break; break;
} }
case Fw::PressedState: { case Fw::PressedState: {
newStatus = m_pressed; newStatus = (g_ui.getPressedWidget() == asUIWidget());
break;
}
case Fw::DraggingState: {
newStatus = (g_ui.getDraggingWidget() == asUIWidget());
break; break;
} }
case Fw::DisabledState: { case Fw::DisabledState: {
@ -1018,6 +1085,7 @@ void UIWidget::updateState(Fw::WidgetState state)
return; return;
} }
if(setState(state, newStatus)) {
if(updateChildren) { if(updateChildren) {
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
@ -1025,7 +1093,6 @@ void UIWidget::updateState(Fw::WidgetState state)
child->updateState(state); child->updateState(state);
} }
if(setState(state, newStatus)) {
if(state == Fw::FocusState) { if(state == Fw::FocusState) {
g_dispatcher.addEvent(std::bind(&UIWidget::onFocusChange, asUIWidget(), newStatus, m_lastFocusReason)); g_dispatcher.addEvent(std::bind(&UIWidget::onFocusChange, asUIWidget(), newStatus, m_lastFocusReason));
} else if(state == Fw::HoverState) } else if(state == Fw::HoverState)
@ -1035,12 +1102,18 @@ void UIWidget::updateState(Fw::WidgetState state)
void UIWidget::updateStates() void UIWidget::updateStates()
{ {
if(m_destroyed)
return;
for(int state = 1; state != Fw::LastWidgetState; state <<= 1) for(int state = 1; state != Fw::LastWidgetState; state <<= 1)
updateState((Fw::WidgetState)state); updateState((Fw::WidgetState)state);
} }
void UIWidget::updateChildrenIndexStates() void UIWidget::updateChildrenIndexStates()
{ {
if(m_destroyed)
return;
for(const UIWidgetPtr& child : m_children) { for(const UIWidgetPtr& child : m_children) {
child->updateState(Fw::FirstState); child->updateState(Fw::FirstState);
child->updateState(Fw::MiddleState); child->updateState(Fw::MiddleState);
@ -1051,6 +1124,9 @@ void UIWidget::updateChildrenIndexStates()
void UIWidget::updateStyle() void UIWidget::updateStyle()
{ {
if(m_destroyed)
return;
if(m_loadingStyle && !m_updateStyleScheduled) { if(m_loadingStyle && !m_updateStyleScheduled) {
UIWidgetPtr self = asUIWidget(); UIWidgetPtr self = asUIWidget();
g_dispatcher.addEvent([self] { g_dispatcher.addEvent([self] {
@ -1110,6 +1186,9 @@ void UIWidget::updateStyle()
void UIWidget::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode) void UIWidget::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
{ {
if(m_destroyed)
return;
// first set id // first set id
if(const OTMLNodePtr& node = styleNode->get("id")) if(const OTMLNodePtr& node = styleNode->get("id"))
setId(node->value()); setId(node->value());
@ -1121,70 +1200,105 @@ void UIWidget::onStyleApply(const std::string& styleName, const OTMLNodePtr& sty
void UIWidget::onGeometryChange(const Rect& oldRect, const Rect& newRect) void UIWidget::onGeometryChange(const Rect& oldRect, const Rect& newRect)
{ {
if(m_destroyed)
return;
callLuaField("onGeometryChange", oldRect, newRect); callLuaField("onGeometryChange", oldRect, newRect);
} }
void UIWidget::onFocusChange(bool focused, Fw::FocusReason reason) void UIWidget::onFocusChange(bool focused, Fw::FocusReason reason)
{ {
if(m_destroyed)
return;
callLuaField("onFocusChange", focused, reason); callLuaField("onFocusChange", focused, reason);
} }
void UIWidget::onChildFocusChange(const UIWidgetPtr& focusedChild, const UIWidgetPtr& unfocusedChild, Fw::FocusReason reason) void UIWidget::onChildFocusChange(const UIWidgetPtr& focusedChild, const UIWidgetPtr& unfocusedChild, Fw::FocusReason reason)
{ {
if(m_destroyed)
return;
callLuaField("onChildFocusChange", focusedChild, unfocusedChild, reason); callLuaField("onChildFocusChange", focusedChild, unfocusedChild, reason);
} }
void UIWidget::onHoverChange(bool hovered) void UIWidget::onHoverChange(bool hovered)
{ {
callLuaField("onHoverChange", hovered); if(m_destroyed)
return;
// check for new hovered elements when the current widget is removed callLuaField("onHoverChange", hovered);
if(!hovered && !getParent() && g_ui.getRootWidget())
g_ui.getRootWidget()->updateState(Fw::HoverState);
} }
void UIWidget::onDragEnter(const Point& mousePos) void UIWidget::onDragEnter(const Point& mousePos)
{ {
if(m_destroyed)
return;
callLuaField("onDragEnter", mousePos); callLuaField("onDragEnter", mousePos);
} }
void UIWidget::onDragLeave(UIWidgetPtr droppedWidget, const Point& mousePos) void UIWidget::onDragLeave(UIWidgetPtr droppedWidget, const Point& mousePos)
{ {
if(m_destroyed)
return;
callLuaField("onDragLeave", droppedWidget, mousePos); callLuaField("onDragLeave", droppedWidget, mousePos);
} }
bool UIWidget::onDragMove(const Point& mousePos, const Point& mouseMoved) bool UIWidget::onDragMove(const Point& mousePos, const Point& mouseMoved)
{ {
if(m_destroyed)
return false;
return callLuaField("onDragMove", mousePos, mouseMoved); return callLuaField("onDragMove", mousePos, mouseMoved);
} }
bool UIWidget::onDrop(UIWidgetPtr draggedWidget, const Point& mousePos) bool UIWidget::onDrop(UIWidgetPtr draggedWidget, const Point& mousePos)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onDrop", draggedWidget, mousePos); return callLuaField<bool>("onDrop", draggedWidget, mousePos);
} }
bool UIWidget::onKeyText(const std::string& keyText) bool UIWidget::onKeyText(const std::string& keyText)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onKeyText", keyText); return callLuaField<bool>("onKeyText", keyText);
} }
bool UIWidget::onKeyDown(uchar keyCode, int keyboardModifiers) bool UIWidget::onKeyDown(uchar keyCode, int keyboardModifiers)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onKeyDown", keyCode, keyboardModifiers); return callLuaField<bool>("onKeyDown", keyCode, keyboardModifiers);
} }
bool UIWidget::onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks) bool UIWidget::onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onKeyPress", keyCode, keyboardModifiers, autoRepeatTicks); return callLuaField<bool>("onKeyPress", keyCode, keyboardModifiers, autoRepeatTicks);
} }
bool UIWidget::onKeyUp(uchar keyCode, int keyboardModifiers) bool UIWidget::onKeyUp(uchar keyCode, int keyboardModifiers)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onKeyUp", keyCode, keyboardModifiers); return callLuaField<bool>("onKeyUp", keyCode, keyboardModifiers);
} }
bool UIWidget::onMousePress(const Point& mousePos, Fw::MouseButton button) bool UIWidget::onMousePress(const Point& mousePos, Fw::MouseButton button)
{ {
if(m_destroyed)
return false;
if(button == Fw::MouseLeftButton) { if(button == Fw::MouseLeftButton) {
if(m_clickTimer.running() && m_clickTimer.ticksElapsed() <= 500) { if(m_clickTimer.running() && m_clickTimer.ticksElapsed() <= 500) {
if(onDoubleClick(mousePos)) if(onDoubleClick(mousePos))
@ -1201,40 +1315,41 @@ bool UIWidget::onMousePress(const Point& mousePos, Fw::MouseButton button)
bool UIWidget::onMouseRelease(const Point& mousePos, Fw::MouseButton button) bool UIWidget::onMouseRelease(const Point& mousePos, Fw::MouseButton button)
{ {
if(isPressed() && getRect().contains(mousePos)) if(m_destroyed)
onClick(mousePos); return false;
return callLuaField<bool>("onMouseRelease", mousePos, button); return callLuaField<bool>("onMouseRelease", mousePos, button);
} }
bool UIWidget::onMouseMove(const Point& mousePos, const Point& mouseMoved) bool UIWidget::onMouseMove(const Point& mousePos, const Point& mouseMoved)
{ {
if(isDragable() && isPressed() && !m_dragging && !g_ui.getDraggingWidget()) { if(m_destroyed)
setDragging(true); return false;
g_ui.setDraggingWidget(asUIWidget());
onDragEnter(mousePos - mouseMoved);
}
if(m_dragging) {
if(onDragMove(mousePos, mouseMoved))
return true;
}
return callLuaField<bool>("onMouseMove", mousePos, mouseMoved); return callLuaField<bool>("onMouseMove", mousePos, mouseMoved);
} }
bool UIWidget::onMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction) bool UIWidget::onMouseWheel(const Point& mousePos, Fw::MouseWheelDirection direction)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onMouseWheel", mousePos, direction); return callLuaField<bool>("onMouseWheel", mousePos, direction);
} }
bool UIWidget::onClick(const Point& mousePos) bool UIWidget::onClick(const Point& mousePos)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onClick", mousePos); return callLuaField<bool>("onClick", mousePos);
} }
bool UIWidget::onDoubleClick(const Point& mousePos) bool UIWidget::onDoubleClick(const Point& mousePos)
{ {
if(m_destroyed)
return false;
return callLuaField<bool>("onDoubleClick", mousePos); return callLuaField<bool>("onDoubleClick", mousePos);
} }
@ -1356,12 +1471,8 @@ bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton butt
} }
// only non phatom widgets receives mouse events // only non phatom widgets receives mouse events
if(!isPhantom()) { if(!isPhantom())
bool ret = onMousePress(mousePos, button); return onMousePress(mousePos, button);
if(button == Fw::MouseLeftButton && !isPressed())
setPressed(true);
return ret;
}
return false; return false;
} }
@ -1369,28 +1480,27 @@ bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton butt
bool UIWidget::propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button) bool UIWidget::propagateOnMouseRelease(const Point& mousePos, Fw::MouseButton button)
{ {
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children; UIWidgetPtr clickedChild;
for(const UIWidgetPtr& child : m_children) { for(const UIWidgetPtr& child : m_children) {
// events on hidden or disabled widgets are discarded // events on hidden or disabled widgets are discarded
if((!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) && (!child->isPressed() && button == Fw::MouseLeftButton)) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// mouse release events go to all children // mouse press events only go to children that contains the mouse position
children.push_back(child); if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) {
clickedChild = child;
break;
}
} }
for(const UIWidgetPtr& child : children) { if(clickedChild) {
if(child->propagateOnMouseRelease(mousePos, button)) if(clickedChild->propagateOnMouseRelease(mousePos, button))
return true; return true;
} }
// only non phatom widgets receives mouse events // only non phatom widgets receives mouse events
if(!isPhantom()) { if(!isPhantom())
bool ret = onMouseRelease(mousePos, button); onMouseRelease(mousePos, button);
if(isPressed() && button == Fw::MouseLeftButton)
setPressed(false);
return ret;
}
return false; return false;
} }

View File

@ -61,10 +61,8 @@ protected:
Boolean<true> m_visible; Boolean<true> m_visible;
Boolean<true> m_focusable; Boolean<true> m_focusable;
Boolean<false> m_fixedSize; Boolean<false> m_fixedSize;
Boolean<false> m_pressed;
Boolean<false> m_phantom; Boolean<false> m_phantom;
Boolean<false> m_dragable; Boolean<false> m_dragable;
Boolean<false> m_dragging;
Boolean<false> m_destroyed; Boolean<false> m_destroyed;
UILayoutPtr m_layout; UILayoutPtr m_layout;
UIWidgetWeakPtr m_parent; UIWidgetWeakPtr m_parent;
@ -115,12 +113,10 @@ public:
void setStyleFromNode(const OTMLNodePtr& styleNode); void setStyleFromNode(const OTMLNodePtr& styleNode);
void setEnabled(bool enabled); void setEnabled(bool enabled);
void setVisible(bool visible); void setVisible(bool visible);
void setPressed(bool pressed);
void setOn(bool on); void setOn(bool on);
void setChecked(bool checked); void setChecked(bool checked);
void setFocusable(bool focusable); void setFocusable(bool focusable);
void setPhantom(bool phantom); void setPhantom(bool phantom);
void setDragging(bool dragging);
void setDragable(bool dragable); void setDragable(bool dragable);
void setFixedSize(bool fixed); void setFixedSize(bool fixed);
void setLastFocusReason(Fw::FocusReason reason); void setLastFocusReason(Fw::FocusReason reason);
@ -220,13 +216,13 @@ public:
bool isAlternate() { return hasState(Fw::AlternateState); } bool isAlternate() { return hasState(Fw::AlternateState); }
bool isChecked() { return hasState(Fw::CheckedState); } bool isChecked() { return hasState(Fw::CheckedState); }
bool isOn() { return hasState(Fw::OnState); } bool isOn() { return hasState(Fw::OnState); }
bool isDragging() { return hasState(Fw::DraggingState); }
bool isHidden() { return !isVisible(); } bool isHidden() { return !isVisible(); }
bool isExplicitlyEnabled() { return m_enabled; } bool isExplicitlyEnabled() { return m_enabled; }
bool isExplicitlyVisible() { return m_visible; } bool isExplicitlyVisible() { return m_visible; }
bool isFocusable() { return m_focusable; } bool isFocusable() { return m_focusable; }
bool isPhantom() { return m_phantom; } bool isPhantom() { return m_phantom; }
bool isDragable() { return m_dragable; } bool isDragable() { return m_dragable; }
bool isDragging() { return m_dragging; }
bool isFixedSize() { return m_fixedSize; } bool isFixedSize() { return m_fixedSize; }
bool isDestroyed() { return m_destroyed; } bool isDestroyed() { return m_destroyed; }