void UIWidget::removeChild(const UIWidgetPtr& child) { // remove from children list if(hasChild(child)) { // defocus if needed bool focusAnother = false; if(m_focusedChild == child) { focusChild(nullptr, Fw::ActiveFocusReason); focusAnother = true; } if(isChildLocked(child)) unlockChild(child); auto it = std::find(m_children.begin(), m_children.end(), child); m_children.erase(it); // reset child parent assert(child->getParent() == asUIWidget()); child->setParent(nullptr); m_layout->removeWidget(child); // update child states child->updateStates(); updateChildrenIndexStates(); if(focusAnother && !m_focusedChild) focusPreviousChild(Fw::ActiveFocusReason); } else logError("Attempt to remove an unknown child from a UIWidget"); }
void UIWidget::insertChild(int index, const UIWidgetPtr& child) { if(!child) { logWarning("attempt to insert a null child into a UIWidget"); return; } if(hasChild(child)) { logWarning("attempt to insert a child again into a UIWidget"); return; } index = index <= 0 ? (m_children.size() + index) : index-1; assert(index >= 0 && (uint)index <= m_children.size()); // retrieve child by index auto it = m_children.begin() + index; m_children.insert(it, child); child->setParent(asUIWidget()); // create default layout if needed if(!m_layout) m_layout = UIAnchorLayoutPtr(new UIAnchorLayout(asUIWidget())); // add to layout and updates it m_layout->addWidget(child); // update new child states child->updateStates(); updateChildrenIndexStates(); }
bool UIWidget::setRect(const Rect& rect) { if(rect.width() > 8192 || rect.height() > 8192) { logError("attempt to set huge rect size (", rect,") for ", m_id); return false; } // only update if the rect really changed Rect oldRect = m_rect; if(rect == oldRect) return false; m_rect = rect; // updates own layout updateLayout(); // avoid massive update events if(!m_updateEventScheduled) { UIWidgetPtr self = asUIWidget(); g_eventDispatcher.addEvent([self, oldRect]() { self->m_updateEventScheduled = false; if(oldRect != self->getRect()) self->onGeometryChange(oldRect, self->getRect()); }); m_updateEventScheduled = true; } return true; }
bool UIWidget::setRect(const Rect& rect) { /* if(rect.width() > 8192 || rect.height() > 8192) { g_logger.error(stdext::format("attempt to set huge rect size (%s) for %s", stdext::to_string(rect), m_id)); return false; } */ // only update if the rect really changed Rect oldRect = m_rect; if(rect == oldRect) return false; m_rect = rect; // updates own layout updateLayout(); // avoid massive update events if(!m_updateEventScheduled) { UIWidgetPtr self = static_self_cast<UIWidget>(); g_dispatcher.addEvent([self, oldRect]() { self->m_updateEventScheduled = false; if(oldRect != self->getRect()) self->onGeometryChange(oldRect, self->getRect()); }); m_updateEventScheduled = true; } // update hovered widget when moved behind mouse area if(containsPoint(g_window.getMousePosition())) g_ui.updateHoveredWidget(); return true; }
void UIWidget::updateStyle() { if(m_destroyed) return; if(m_loadingStyle && !m_updateStyleScheduled) { UIWidgetPtr self = static_self_cast<UIWidget>(); g_dispatcher.addEvent([self] { self->m_updateStyleScheduled = false; self->updateStyle(); }); m_updateStyleScheduled = true; return; } if(!m_style) return; OTMLNodePtr newStateStyle = OTMLNode::create(); // 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()); } } // checks for states combination for(const OTMLNodePtr& style : m_style->children()) { if(stdext::starts_with(style->tag(), "$")) { std::string statesStr = style->tag().substr(1); std::vector<std::string> statesSplit = stdext::split(statesStr, " "); bool match = true; for(std::string stateStr : statesSplit) { if(stateStr.length() == 0) continue; bool notstate = (stateStr[0] == '!'); if(notstate) stateStr = stateStr.substr(1); bool stateOn = hasState(Fw::translateState(stateStr)); if((!notstate && !stateOn) || (notstate && stateOn)) match = false; } // merge states styles if(match) { newStateStyle->merge(style); } } } //TODO: prevent setting already set proprieties applyStyle(newStateStyle); m_stateStyle = newStateStyle; }
void UIWidget::lockChild(const UIWidgetPtr& child) { if(!child) return; assert(hasChild(child)); // prevent double locks if(isChildLocked(child)) unlockChild(child); // disable all other children for(const UIWidgetPtr& otherChild : m_children) { if(otherChild == child) child->setEnabled(true); else otherChild->setEnabled(false); } m_lockedChildren.push_front(child); // lock child focus if(child->isFocusable()) focusChild(child, Fw::ActiveFocusReason); moveChildToTop(child); }
void UIWidget::addChild(const UIWidgetPtr& child) { 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; } m_children.push_back(child); child->setParent(asUIWidget()); // create default layout if(!m_layout) m_layout = UIAnchorLayoutPtr(new UIAnchorLayout(asUIWidget())); // add to layout and updates it m_layout->addWidget(child); // update new child states child->updateStates(); updateChildrenIndexStates(); }
bool UIManager::updateDraggingWidget(const UIWidgetPtr& draggingWidget, const Point& clickedPos) { bool accepted = false; UIWidgetPtr oldDraggingWidget = m_draggingWidget; m_draggingWidget = nullptr; if(oldDraggingWidget) { UIWidgetPtr droppedWidget; if(!clickedPos.isNull()) { auto clickedChildren = m_rootWidget->recursiveGetChildrenByPos(clickedPos); for(const UIWidgetPtr& child : clickedChildren) { if(child->onDrop(oldDraggingWidget, clickedPos)) { droppedWidget = child; break; } } } accepted = oldDraggingWidget->onDragLeave(droppedWidget, clickedPos); oldDraggingWidget->updateState(Fw::DraggingState); } if(draggingWidget) { if(draggingWidget->onDragEnter(clickedPos)) { m_draggingWidget = draggingWidget; draggingWidget->updateState(Fw::DraggingState); accepted = true; } } return accepted; }
void UIWidget::lockChild(const UIWidgetPtr& child) { if(m_destroyed) return; if(!child) return; if(!hasChild(child)) { g_logger.traceError("cannot find child"); return; } // prevent double locks if(isChildLocked(child)) unlockChild(child); // disable all other children for(const UIWidgetPtr& otherChild : m_children) { if(otherChild == child) child->setEnabled(true); else otherChild->setEnabled(false); } m_lockedChildren.push_front(child); // lock child focus if(child->isFocusable()) focusChild(child, Fw::ActiveFocusReason); }
void UIWidget::setParent(const UIWidgetPtr& parent) { // remove from old parent UIWidgetPtr oldParent = getParent(); // the parent is already the same if(oldParent == parent) return; UIWidgetPtr self = static_self_cast<UIWidget>(); if(oldParent && oldParent->hasChild(self)) oldParent->removeChild(self); // reset parent m_parent.reset(); // set new parent if(parent) { m_parent = parent; // add to parent if needed if(!parent->hasChild(self)) parent->addChild(self); } }
void UIWidget::removeChild(UIWidgetPtr child) { // remove from children list if(hasChild(child)) { // defocus if needed bool focusAnother = false; if(m_focusedChild == child) { focusChild(nullptr, Fw::ActiveFocusReason); focusAnother = true; } if(isChildLocked(child)) unlockChild(child); auto it = std::find(m_children.begin(), m_children.end(), child); m_children.erase(it); // reset child parent assert(child->getParent() == static_self_cast<UIWidget>()); child->setParent(nullptr); m_layout->removeWidget(child); // update child states child->updateStates(); updateChildrenIndexStates(); if(m_autoFocusPolicy != Fw::AutoFocusNone && focusAnother && !m_focusedChild) focusPreviousChild(Fw::ActiveFocusReason, true); g_ui.onWidgetDisappear(child); } else g_logger.traceError("attempt to remove an unknown child from a UIWidget"); }
UIWidgetPtr UIManager::createWidgetFromOTML(const OTMLNodePtr& widgetNode, const UIWidgetPtr& parent) { OTMLNodePtr originalStyleNode = getStyle(widgetNode->tag()); if(!originalStyleNode) stdext::throw_exception(stdext::format("'%s' is not a defined style", widgetNode->tag())); OTMLNodePtr styleNode = originalStyleNode->clone(); styleNode->merge(widgetNode); std::string widgetType = styleNode->valueAt("__class"); // call widget creation from lua UIWidgetPtr widget = g_lua.callGlobalField<UIWidgetPtr>(widgetType, "create"); if(parent) parent->addChild(widget); if(widget) { widget->callLuaField("onCreate"); widget->setStyleFromNode(styleNode); for(const OTMLNodePtr& childNode : styleNode->children()) { if(!childNode->isUnique()) { createWidgetFromOTML(childNode, widget); styleNode->removeChild(childNode); } } } else stdext::throw_exception(stdext::format("unable to create widget of type '%s'", widgetType)); widget->callLuaField("onSetup"); return widget; }
UIAnchorLayoutPtr UIWidget::getAnchoredLayout() { UIWidgetPtr parent = getParent(); if(!parent) return nullptr; return parent->getLayout()->asUIAnchorLayout(); }
void UIWidget::raise() { if(m_destroyed) return; UIWidgetPtr parent = getParent(); if(parent) parent->raiseChild(asUIWidget()); }
void UIWidget::lower() { if(m_destroyed) return; UIWidgetPtr parent = getParent(); if(parent) parent->lowerChild(asUIWidget()); }
void UIWidget::raise() { if(m_destroyed) return; UIWidgetPtr parent = getParent(); if(parent) parent->raiseChild(static_self_cast<UIWidget>()); }
void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason) { if(m_destroyed) return; if(child == m_focusedChild) return; if(child && !hasChild(child)) { g_logger.error("attempt to focus an unknown child in a UIWidget"); return; } UIWidgetPtr oldFocused = m_focusedChild; m_focusedChild = child; if(child) { child->setLastFocusReason(reason); child->updateState(Fw::FocusState); child->updateState(Fw::ActiveState); child->onFocusChange(true, reason); } if(oldFocused) { oldFocused->setLastFocusReason(reason); oldFocused->updateState(Fw::FocusState); oldFocused->updateState(Fw::ActiveState); oldFocused->onFocusChange(false, reason); } onChildFocusChange(child, oldFocused, reason); }
UIAnchorLayoutPtr UIWidget::getAnchoredLayout() { UIWidgetPtr parent = getParent(); if(!parent) return nullptr; UILayoutPtr layout = parent->getLayout(); if(layout->isUIAnchorLayout()) return layout->static_self_cast<UIAnchorLayout>(); return nullptr; }
void UIWidget::bindRectToParent() { Rect boundRect = m_rect; UIWidgetPtr parent = getParent(); if(parent) { Rect parentRect = parent->getRect(); boundRect.bound(parentRect); } setRect(boundRect); }
int UIAnchor::getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget) { // determine hooked widget edge point Rect hookedWidgetRect = hookedWidget->getRect(); if(hookedWidget == parentWidget) hookedWidgetRect = parentWidget->getPaddingRect(); int point = 0; switch(m_hookedEdge) { case Fw::AnchorLeft: point = hookedWidgetRect.left(); break; case Fw::AnchorRight: point = hookedWidgetRect.right(); break; case Fw::AnchorTop: point = hookedWidgetRect.top(); break; case Fw::AnchorBottom: point = hookedWidgetRect.bottom(); break; case Fw::AnchorHorizontalCenter: point = hookedWidgetRect.horizontalCenter(); break; case Fw::AnchorVerticalCenter: point = hookedWidgetRect.verticalCenter(); break; default: // must never happens assert(false); break; } if(hookedWidget == parentWidget) { switch(m_hookedEdge) { case Fw::AnchorLeft: case Fw::AnchorRight: case Fw::AnchorHorizontalCenter: point -= parentWidget->getVirtualOffset().x; break; case Fw::AnchorBottom: case Fw::AnchorTop: case Fw::AnchorVerticalCenter: point -= parentWidget->getVirtualOffset().y; break; default: break; } } return point; }
bool UIWidget::onMouseRelease(const Point& mousePos, Fw::MouseButton button) { if(isPressed() && getRect().contains(mousePos)) callLuaField("onClick"); UIWidgetPtr draggedWidget = g_ui.getDraggingWidget(); if(draggedWidget && button == Fw::MouseLeftButton && (containsPoint(mousePos) || asUIWidget() == g_ui.getRootWidget())) { onDrop(draggedWidget, mousePos); draggedWidget->onDragLeave(asUIWidget(), mousePos); draggedWidget->setDragging(false); } return callLuaField<bool>("onMouseRelease", mousePos, button); }
void UIWidget::bindRectToParent() { if(m_destroyed) return; Rect boundRect = m_rect; UIWidgetPtr parent = getParent(); if(parent) { Rect parentRect = parent->getPaddingRect(); boundRect.bind(parentRect); } setRect(boundRect); }
UIWidgetPtr UIAnchor::getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget) { // determine hooked widget UIWidgetPtr hookedWidget; if(parentWidget) { if(m_hookedWidgetId == "parent") hookedWidget = parentWidget; else if(m_hookedWidgetId == "next") hookedWidget = parentWidget->getChildAfter(widget); else if(m_hookedWidgetId == "prev") hookedWidget = parentWidget->getChildBefore(widget); else hookedWidget = parentWidget->getChildById(m_hookedWidgetId); } return hookedWidget; }
void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason) { if(child == m_focusedChild) return; if(child && !hasChild(child)) { logError("attempt to focus an unknown child in a UIWidget"); return; } UIWidgetPtr oldFocused = m_focusedChild; m_focusedChild = child; if(child) { child->setLastFocusReason(reason); child->updateState(Fw::FocusState); child->updateState(Fw::ActiveState); } if(oldFocused) { oldFocused->setLastFocusReason(reason); oldFocused->updateState(Fw::FocusState); oldFocused->updateState(Fw::ActiveState); } }
void UIWidget::destroyChildren() { UILayoutPtr layout = getLayout(); if(layout) layout->disableUpdates(); m_focusedChild = nullptr; m_lockedChildren.clear(); while(!m_children.empty()) { UIWidgetPtr child = m_children.front(); m_children.pop_front(); child->setParent(nullptr); m_layout->removeWidget(child); child->destroy(); } layout->enableUpdates(); }
void UIWidget::unlockChild(const UIWidgetPtr& child) { if(m_destroyed) return; if(!child) return; if(!hasChild(child)) { g_logger.traceError("cannot find child"); return; } auto it = std::find(m_lockedChildren.begin(), m_lockedChildren.end(), child); if(it == m_lockedChildren.end()) return; m_lockedChildren.erase(it); // find new child to lock UIWidgetPtr lockedChild; if(m_lockedChildren.size() > 0) { lockedChild = m_lockedChildren.front(); assert(hasChild(lockedChild)); } for(const UIWidgetPtr& otherChild : m_children) { // lock new child if(lockedChild) { if(otherChild == lockedChild) lockedChild->setEnabled(true); else otherChild->setEnabled(false); } // else unlock all else otherChild->setEnabled(true); } if(lockedChild) { if(lockedChild->isFocusable()) focusChild(lockedChild, Fw::ActiveFocusReason); } }
void UIWidget::applyStyle(const OTMLNodePtr& styleNode) { if(m_destroyed) return; if(styleNode->size() == 0) return; m_loadingStyle = true; try { // translate ! style tags for(const OTMLNodePtr& node : styleNode->children()) { if(node->tag()[0] == '!') { std::string tag = node->tag().substr(1); std::string code = stdext::format("tostring(%s)", node->value()); std::string origin = "@" + node->source() + ": [" + node->tag() + "]"; g_lua.evaluateExpression(code, origin); std::string value = g_lua.popString(); node->setTag(tag); node->setValue(value); } } onStyleApply(styleNode->tag(), styleNode); callLuaField("onStyleApply", styleNode->tag(), styleNode); if(m_firstOnStyle) { UIWidgetPtr parent = getParent(); if(isFocusable() && isExplicitlyVisible() && isExplicitlyEnabled() && parent && ((!parent->getFocusedChild() && parent->getAutoFocusPolicy() == Fw::AutoFocusFirst) || parent->getAutoFocusPolicy() == Fw::AutoFocusLast)) { focus(); } } m_firstOnStyle = false; } catch(stdext::exception& e) { g_logger.traceError(stdext::format("failed to apply style to widget '%s': %s", m_id, e.what())); } m_loadingStyle = false; }
void UIWidget::setRect(const Rect& rect) { // only update if the rect really changed Rect oldRect = m_rect; if(rect == oldRect) return; m_rect = rect; // updates own layout updateLayout(); // avoid massive update events if(!m_updateEventScheduled) { UIWidgetPtr self = asUIWidget(); g_dispatcher.addEvent([self, oldRect]() { self->m_updateEventScheduled = false; if(oldRect != self->getRect()) self->onGeometryChange(oldRect, self->getRect()); }); m_updateEventScheduled = true; } }
void UIWidget::insertChild(int index, const UIWidgetPtr& child) { if(!child) { g_logger.traceWarning("attempt to insert a null child into a UIWidget"); return; } if(hasChild(child)) { g_logger.traceWarning("attempt to insert a child again into a UIWidget"); return; } index = index <= 0 ? (m_children.size() + index) : index-1; if(!(index >= 0 && (uint)index <= m_children.size())) { //g_logger.traceWarning("attempt to insert a child UIWidget into an invalid index, using nearest index..."); index = std::min(std::max(index, 0), (int)m_children.size()); } // retrieve child by index auto it = m_children.begin() + index; m_children.insert(it, child); child->setParent(static_self_cast<UIWidget>()); // create default layout if needed if(!m_layout) m_layout = UIAnchorLayoutPtr(new UIAnchorLayout(static_self_cast<UIWidget>())); // add to layout and updates it m_layout->addWidget(child); // update new child states child->updateStates(); updateChildrenIndexStates(); g_ui.onWidgetAppear(child); }
bool UIWidget::propagateOnMousePress(const Point& mousePos, Fw::MouseButton button) { // do a backup of children list, because it may change while looping it UIWidgetPtr clickedChild; for(const UIWidgetPtr& child : m_children) { // events on hidden or disabled widgets are discarded if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible()) continue; // mouse press events only go to children that contains the mouse position if(child->containsPoint(mousePos) && child == getChildByPos(mousePos)) { clickedChild = child; break; } } if(clickedChild) { // focusable child gains focus when clicked if(clickedChild->isFocusable()) focusChild(clickedChild, Fw::MouseFocusReason); // stop propagating if the child accept the event if(clickedChild->propagateOnMousePress(mousePos, button)) return true; } // only non phatom widgets receives mouse press events if(!isPhantom()) { bool ret = onMousePress(mousePos, button); if(button == Fw::MouseLeftButton && !isPressed()) setPressed(true); return ret; } return false; }