diff --git a/modules/console/console.lua b/modules/console/console.lua index 0aadd155..8f5a5a69 100644 --- a/modules/console/console.lua +++ b/modules/console/console.lua @@ -15,14 +15,16 @@ function Console.onLog(level, message, time) if level == LogDebug then color = '#5555ff' elseif level == LogInfo then - color = '#55ff55' + color = '#5555ff' elseif level == LogWarning then color = '#ffff00' else color = '#ff0000' end - Console.addLine(message, color) + if level ~= LogDebug then + Console.addLine(message, color) + end logLocked = false end @@ -35,12 +37,17 @@ function Console.addLine(text, color) label:setStyle('ConsoleLabel') label:setText(text) label:setForegroundColor(color) - console:insertChild(label, -1) + console:insertChild(3, label) + + local lastLabel = console:getChildByIndex(4) + if lastLabel then + lastLabel:addAnchor(AnchorBottom, "prev", AnchorTop) + end numLines = numLines + 1 if numLines > maxLines then - local firstLine = console:getChildByIndex(0) - firstLine:destroy() + local firstLabel = console:getChildByIndex(-1) + firstLabel:destroy() end end diff --git a/modules/console/console.otui b/modules/console/console.otui index 050e7441..84831876 100644 --- a/modules/console/console.otui +++ b/modules/console/console.otui @@ -1,7 +1,7 @@ ConsoleLabel < UILabel font: terminus-14px-bold height: 16 - anchors.bottom: next.top + anchors.bottom: commandBox.top anchors.left: parent.left anchors.right: parent.right margin.left: 2 @@ -13,6 +13,7 @@ RectPanel anchors.fill: parent UILabel + id: commandSymbolLabel size: 18 20 anchors.bottom: parent.bottom anchors.left: parent.left @@ -32,4 +33,4 @@ RectPanel function(self) Console.executeCommand(self:getText()) self:clearText() - end \ No newline at end of file + end diff --git a/src/framework/core/logger.cpp b/src/framework/core/logger.cpp index adf611ad..4833e507 100644 --- a/src/framework/core/logger.cpp +++ b/src/framework/core/logger.cpp @@ -20,7 +20,7 @@ void Logger::log(LogLevel level, std::string message) m_logMessages.push_back(LogMessage(level, message, now)); if(m_onLog) - g_dispatcher.addEvent(std::bind(m_onLog, level, message, now)); + m_onLog(level, message, now); } if(level == LogFatal) { @@ -46,7 +46,8 @@ void Logger::logFunc(LogLevel level, const std::string& message, std::string pre void Logger::fireOldMessages() { if(m_onLog) { - for(const LogMessage& logMessage : m_logMessages) - g_dispatcher.addEvent(std::bind(m_onLog, logMessage.level, logMessage.message, logMessage.when)); + auto backup = m_logMessages; + for(const LogMessage& logMessage : backup) + m_onLog(logMessage.level, logMessage.message, logMessage.when); } } diff --git a/src/framework/luascript/luafunctions.cpp b/src/framework/luascript/luafunctions.cpp index 8cc72dbc..9b1cf12c 100644 --- a/src/framework/luascript/luafunctions.cpp +++ b/src/framework/luascript/luafunctions.cpp @@ -44,6 +44,7 @@ void LuaInterface::registerFunctions() g_lua.bindClassMemberFunction("addAnchor", &UIWidget::addAnchor); g_lua.bindClassMemberFunction("getChildById", &UIWidget::getChildById); g_lua.bindClassMemberFunction("getChildByIndex", &UIWidget::getChildByIndex); + g_lua.bindClassMemberFunction("getChildCount", &UIWidget::getChildCount); g_lua.bindClassMemberFunction("insertChild", &UIWidget::insertChild); g_lua.bindClassMemberFunction("removeChild", &UIWidget::removeChild); g_lua.bindClassMemberFunction("addChild", &UIWidget::addChild); diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index acf7f52d..4e70c968 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -32,6 +32,7 @@ UIWidget::UIWidget() UIWidget::~UIWidget() { + //logTraceDebug(m_id); if(!m_destroyed) { logWarning("widget '", m_id, "' was destructed without being explicit destroyed"); internalDestroy(); @@ -40,6 +41,7 @@ UIWidget::~UIWidget() void UIWidget::destroy() { + //logTraceDebug(m_id); // destroy only once if(!m_destroyed) { internalDestroy(); @@ -52,10 +54,12 @@ void UIWidget::destroy() void UIWidget::internalDestroy() { + //logTraceDebug(m_id); // first release lua table, because it may contains references to children releaseLuaFieldsTable(); // clear other references + clearHookedWidgets(); m_lockedChildren.clear(); m_anchors.clear(); m_anchoredWidgets.clear(); @@ -79,12 +83,14 @@ void UIWidget::internalDestroyCheck() // collect lua garbage before checking g_lua.collectGarbage(); + //logTraceDebug(m_id); // get real use count int realUseCount = shared_from_this().use_count() - 2; // check for leaks upon widget destruction if(realUseCount > 0) - logWarning("destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left"); + {} + //logWarning("destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left"); } void UIWidget::render() @@ -122,6 +128,7 @@ void UIWidget::setParent(const UIWidgetPtr& parent) { assert(!m_destroyed); + //logTraceDebug(m_id); UIWidgetPtr self = asUIWidget(); // remove from old parent @@ -144,6 +151,7 @@ void UIWidget::setParent(const UIWidgetPtr& parent) void UIWidget::applyStyle(const std::string& styleName) { + //logTraceDebug(m_id); try { OTMLNodePtr styleNode = g_ui.getStyle(styleName); onStyleApply(styleNode); @@ -160,7 +168,7 @@ void UIWidget::setRect(const Rect& rect) m_rect = rect; // updates children geometry - updateChildrenLayout(); + internalUpdateChildrenLayout(); // avoid massive update events if(!m_updateEventScheduled) { @@ -285,12 +293,13 @@ UIWidgetPtr UIWidget::getChildByPos(const Point& childPos) return nullptr; } -UIWidgetPtr UIWidget::getChildByIndex(int childIndex) +UIWidgetPtr UIWidget::getChildByIndex(int index) { assert(!m_destroyed); - if(childIndex >= 0 && childIndex < getChildCount()) - return m_children.at(childIndex); + index = index <= 0 ? (m_children.size() + index) : index-1; + if(index >= 0 && (uint)index < m_children.size()) + return m_children.at(index); return nullptr; } @@ -358,6 +367,7 @@ void UIWidget::addChild(const UIWidgetPtr& child) { assert(!m_destroyed); + //logTraceDebug(m_id); if(!child) return; @@ -367,30 +377,29 @@ void UIWidget::addChild(const UIWidgetPtr& child) child->setParent(asUIWidget()); // recalculate anchors - getRootParent()->recalculateAnchoredWidgets(); - - // may need to update children layout - updateChildrenLayout(); + child->clearHookedWidgets(); + child->computeHookedWidgets(); + child->updateLayout(); // always focus new children if(child->isFocusable() && child->isExplicitlyVisible() && child->isExplicitlyEnabled()) focusChild(child, UI::ActiveFocusReason); } -void UIWidget::insertChild(const UIWidgetPtr& child, int index) +void UIWidget::insertChild(int index, const UIWidgetPtr& child) { assert(!m_destroyed); + //logTraceDebug(m_id); // skip null children if(!child) return; assert(!hasChild(child)); - if(index < 0) - index = m_children.size() + index -1; + index = index <= 0 ? (m_children.size() + index) : index-1; - assert((uint)index <= m_children.size()); + assert(index >= 0 && (uint)index <= m_children.size()); // retrieve child by index auto it = m_children.begin() + index; @@ -398,16 +407,16 @@ void UIWidget::insertChild(const UIWidgetPtr& child, int index) child->setParent(asUIWidget()); // recalculate anchors - getRootParent()->recalculateAnchoredWidgets(); - - // may need to update children layout - updateChildrenLayout(); + child->clearHookedWidgets(); + child->computeHookedWidgets(); + child->updateLayout(); } void UIWidget::removeChild(const UIWidgetPtr& child) { assert(!m_destroyed); + //logTraceDebug(m_id); // skip null children if(!child) return; @@ -429,10 +438,7 @@ void UIWidget::removeChild(const UIWidgetPtr& child) child->setParent(nullptr); // recalculate anchors - getRootParent()->recalculateAnchoredWidgets(); - - // may need to update children layout - updateChildrenLayout(); + child->clearHookedWidgets(); } void UIWidget::focusNextChild(UI::FocusReason reason) @@ -534,10 +540,12 @@ void UIWidget::unlockChild(const UIWidgetPtr& child) } } + void UIWidget::updateLayout() { assert(!m_destroyed); + //logTraceDebug(m_id); if(!m_layoutUpdateScheduled) { m_layoutUpdateScheduled = true; UIWidgetPtr self = asUIWidget(); @@ -553,8 +561,11 @@ void UIWidget::updateChildrenLayout() { assert(!m_destroyed); + //logTraceDebug(m_id); if(!m_childrenLayoutUpdateScheduled) { m_childrenLayoutUpdateScheduled = true; + // reset all children anchors update state + resetLayoutUpdateState(false); UIWidgetPtr self = asUIWidget(); g_dispatcher.addEvent([self] { self->m_childrenLayoutUpdateScheduled = false; @@ -568,27 +579,40 @@ bool UIWidget::addAnchor(AnchorEdge edge, const std::string& hookedWidgetId, Anc { assert(!m_destroyed); + //logTraceDebug(m_id); UIAnchor anchor(edge, hookedWidgetId, hookedEdge); UIWidgetPtr hookedWidget = backwardsGetWidgetById(hookedWidgetId); - anchor.setHookedWidget(hookedWidget); - // we can never anchor with itself - if(hookedWidget == asUIWidget()) { - logError("anchoring with itself is not possible"); - return false; - } + 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 - // we must never anchor to an anchor child - //TODO: this check + if(hookedWidget) + hookedWidget->addAnchoredWidget(asUIWidget()); + } // duplicated anchors must be replaced - for(auto it = m_anchors.begin(); it != m_anchors.end(); ++it) { - const UIAnchor& otherAnchor = *it; - if(otherAnchor.getAnchoredEdge() == edge) { - m_anchors.erase(it); - break; + 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); } + m_anchors.erase(it); } m_anchors.push_back(anchor); @@ -619,6 +643,7 @@ void UIWidget::internalUpdateLayout() { assert(!m_destroyed); + //logTraceDebug(m_id); for(const UIAnchor& anchor : m_anchors) { // ignore invalid anchors if(!anchor.getHookedWidget()) @@ -699,52 +724,71 @@ void UIWidget::internalUpdateChildrenLayout() { assert(!m_destroyed); - // reset all children anchors update state - resetLayoutUpdateState(false); + //logTraceDebug(m_id); // update children layouts - for(const UIWidgetWeakPtr& anchoredWidgetWeak : m_anchoredWidgets) { - if(UIWidgetPtr anchoredWidget = anchoredWidgetWeak.lock()) - anchoredWidget->internalUpdateLayout(); - } + for(const UIWidgetPtr& anchoredWidget : m_anchoredWidgets) + anchoredWidget->internalUpdateLayout(); } void UIWidget::resetLayoutUpdateState(bool resetOwn) { + assert(!m_destroyed); + + //logTraceDebug(m_id); if(resetOwn) m_layoutUpdated = false; // resets children layout update state too - for(const UIWidgetWeakPtr& anchoredWidgetWeak : m_anchoredWidgets) { - if(UIWidgetPtr anchoredWidget = anchoredWidgetWeak.lock()) - anchoredWidget->resetLayoutUpdateState(true); - } + for(const UIWidgetPtr& anchoredWidget : m_anchoredWidgets) + anchoredWidget->resetLayoutUpdateState(true); } void UIWidget::addAnchoredWidget(const UIWidgetPtr& widget) { + assert(!m_destroyed); + + //logTraceDebug(m_id); // prevent duplicated anchored widgets - for(const UIWidgetWeakPtr& anchoredWidget : m_anchoredWidgets) - if(anchoredWidget.lock() == widget) + for(const UIWidgetPtr& anchoredWidget : m_anchoredWidgets) + if(anchoredWidget == widget) return; m_anchoredWidgets.push_back(widget); } -void UIWidget::recalculateAnchoredWidgets() +void UIWidget::removeAnchoredWidget(const UIWidgetPtr& widget) { - clearAnchoredWidgets(); - computeAnchoredWidgets(); + 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); } -void UIWidget::clearAnchoredWidgets() +void UIWidget::clearHookedWidgets() { - m_anchoredWidgets.clear(); + assert(!m_destroyed); + + //logTraceDebug(m_id); + for(UIAnchor& anchor : m_anchors) { + UIWidgetPtr hookedWidget = anchor.getHookedWidget(); + if(hookedWidget) { + hookedWidget->removeAnchoredWidget(asUIWidget()); + anchor.setHookedWidget(nullptr); + } + } + for(const UIWidgetPtr& child : m_children) - child->clearAnchoredWidgets(); + child->clearHookedWidgets(); } -void UIWidget::computeAnchoredWidgets() +void UIWidget::computeHookedWidgets() { + assert(!m_destroyed); + + //logTraceDebug(m_id); + // update anchors's hooked widget for(UIAnchor& anchor : m_anchors) { UIWidgetPtr hookedWidget = backwardsGetWidgetById(anchor.getHookedWidgetId()); @@ -754,7 +798,7 @@ void UIWidget::computeAnchoredWidgets() } for(const UIWidgetPtr& child : m_children) - child->computeAnchoredWidgets(); + child->computeHookedWidgets(); } void UIWidget::onStyleApply(const OTMLNodePtr& styleNode) @@ -766,6 +810,7 @@ void UIWidget::onStyleApply(const OTMLNodePtr& styleNode) // id if(node->tag() == "id") { setId(node->value()); + //logTraceDebug(m_id); } // background image else if(node->tag() == "image") { diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 7939d4bc..a921151d 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -92,13 +92,13 @@ public: UIWidgetPtr getChildBefore(const UIWidgetPtr& relativeChild); UIWidgetPtr getChildById(const std::string& childId); UIWidgetPtr getChildByPos(const Point& childPos); - UIWidgetPtr getChildByIndex(int childIndex); + UIWidgetPtr getChildByIndex(int index); UIWidgetPtr recursiveGetChildById(const std::string& id); UIWidgetPtr recursiveGetChildByPos(const Point& childPos); UIWidgetPtr backwardsGetWidgetById(const std::string& id); void addChild(const UIWidgetPtr& child); - void insertChild(const UIWidgetPtr& child, int index); + void insertChild(int index, const UIWidgetPtr& child); void removeChild(const UIWidgetPtr& child); void focusChild(const UIWidgetPtr& child, UI::FocusReason reason); void focusNextChild(UI::FocusReason reason); @@ -123,9 +123,9 @@ private: void internalUpdateChildrenLayout(); void addAnchoredWidget(const UIWidgetPtr& widget); - void recalculateAnchoredWidgets(); - void clearAnchoredWidgets(); - void computeAnchoredWidgets(); + void removeAnchoredWidget(const UIWidgetPtr& widget); + void computeHookedWidgets(); + void clearHookedWidgets(); void resetLayoutUpdateState(bool resetOwn); bool m_layoutUpdated; @@ -165,7 +165,7 @@ protected: bool m_destroyed; Rect m_rect; UIAnchorList m_anchors; - UIWeakWidgetList m_anchoredWidgets; + UIWidgetList m_anchoredWidgets; UIWidgetWeakPtr m_parent; UIWidgetList m_children; UIWidgetList m_lockedChildren;