From 9ae61f19322c711acec37e51af83b45e72b5990c Mon Sep 17 00:00:00 2001 From: uramer Date: Wed, 6 Mar 2024 22:01:26 +0100 Subject: [PATCH] Fix child UI Elements created in the same frame as parent --- apps/openmw/mwlua/uibindings.cpp | 13 +++++---- components/lua_ui/element.cpp | 46 ++++++++++++++++++-------------- components/lua_ui/element.hpp | 14 +++++++--- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index f21fdb337a..a8df03ba25 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -134,7 +134,10 @@ namespace MWLua }; api["updateAll"] = [luaManager = context.mLuaManager, menu]() { - LuaUi::Element::forEach(menu, [](LuaUi::Element* e) { e->mUpdate = true; }); + LuaUi::Element::forEach(menu, [](LuaUi::Element* e) { + if (e->mState == LuaUi::Element::Created) + e->mState = LuaUi::Element::Update; + }); luaManager->addAction([menu]() { LuaUi::Element::forEach(menu, [](LuaUi::Element* e) { e->update(); }); }, "Update all menu UI elements"); }; @@ -305,15 +308,15 @@ namespace MWLua element["layout"] = sol::property([](const LuaUi::Element& element) { return element.mLayout; }, [](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; }); element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr& element) { - if (element->mDestroy || element->mUpdate) + if (element->mState != LuaUi::Element::Created) return; - element->mUpdate = true; + element->mState = LuaUi::Element::Update; luaManager->addAction([element] { wrapAction(element, [&] { element->update(); }); }, "Update UI"); }; element["destroy"] = [luaManager = context.mLuaManager](const std::shared_ptr& element) { - if (element->mDestroy) + if (element->mState == LuaUi::Element::Destroyed) return; - element->mDestroy = true; + element->mState = LuaUi::Element::Destroy; luaManager->addAction( [element] { wrapAction(element, [&] { LuaUi::Element::erase(element.get()); }); }, "Destroy UI"); }; diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index 5a54cd91b5..6e7fe9ee16 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -89,12 +89,16 @@ namespace LuaUi root->updateCoord(); } - WidgetExtension* pluckElementRoot(const sol::object& child) + WidgetExtension* pluckElementRoot(const sol::object& child, uint64_t depth) { std::shared_ptr element = child.as>(); - WidgetExtension* root = element->mRoot; - if (!root) + if (element->mState == Element::Destroyed || element->mState == Element::Destroy) throw std::logic_error("Using a destroyed element as a layout child"); + // child Element was created in the same frame and its action hasn't been processed yet + if (element->mState == Element::New) + element->create(depth + 1); + WidgetExtension* root = element->mRoot; + assert(root); WidgetExtension* parent = root->getParent(); if (parent) { @@ -107,7 +111,7 @@ namespace LuaUi return root; } - WidgetExtension* createWidget(const sol::table& layout, uint64_t depth); + WidgetExtension* createWidget(const sol::table& layout, bool isRoot, uint64_t depth); void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth); std::vector updateContent( @@ -130,7 +134,7 @@ namespace LuaUi sol::object child = content.at(i); if (child.is()) { - WidgetExtension* root = pluckElementRoot(child); + WidgetExtension* root = pluckElementRoot(child, depth); if (ext != root) destroyChild(ext); result[i] = root; @@ -145,7 +149,7 @@ namespace LuaUi else { destroyChild(ext); - ext = createWidget(newLayout, depth); + ext = createWidget(newLayout, false, depth); } result[i] = ext; } @@ -156,9 +160,9 @@ namespace LuaUi { sol::object child = content.at(i); if (child.is()) - result[i] = pluckElementRoot(child); + result[i] = pluckElementRoot(child, depth); else - result[i] = createWidget(child.as(), depth); + result[i] = createWidget(child.as(), false, depth); } return result; } @@ -191,7 +195,7 @@ namespace LuaUi }); } - WidgetExtension* createWidget(const sol::table& layout, uint64_t depth) + WidgetExtension* createWidget(const sol::table& layout, bool isRoot, uint64_t depth) { static auto widgetTypeMap = widgetTypeToName(); std::string type = widgetType(layout); @@ -205,7 +209,7 @@ namespace LuaUi WidgetExtension* ext = dynamic_cast(widget); if (!ext) throw std::runtime_error("Invalid widget!"); - ext->initialize(layout.lua_state(), widget, depth == 0); + ext->initialize(layout.lua_state(), widget, isRoot); updateWidget(ext, layout, depth); return ext; @@ -247,8 +251,7 @@ namespace LuaUi : mRoot(nullptr) , mLayout(std::move(layout)) , mLayer() - , mUpdate(false) - , mDestroy(false) + , mState(Element::New) { } @@ -267,12 +270,12 @@ namespace LuaUi sGameElements.erase(element); } - void Element::create() + void Element::create(uint64_t depth) { assert(!mRoot); - if (!mRoot) + if (mState == New) { - mRoot = createWidget(layout(), 0); + mRoot = createWidget(layout(), true, depth); mLayer = setLayer(mRoot, layout()); updateRootCoord(mRoot); } @@ -280,15 +283,16 @@ namespace LuaUi void Element::update() { - if (mRoot && mUpdate) + if (mState == Update) { + assert(mRoot); if (mRoot->widget()->getTypeName() != widgetType(layout())) { destroyRoot(mRoot); WidgetExtension* parent = mRoot->getParent(); auto children = parent->children(); auto it = std::find(children.begin(), children.end(), mRoot); - mRoot = createWidget(layout(), 0); + mRoot = createWidget(layout(), true, 0); assert(it != children.end()); *it = mRoot; parent->setChildren(children); @@ -301,16 +305,18 @@ namespace LuaUi mLayer = setLayer(mRoot, layout()); updateRootCoord(mRoot); } - mUpdate = false; + mState = Created; } void Element::destroy() { - if (mRoot) + if (mState != Destroyed) { destroyRoot(mRoot); mRoot = nullptr; - mLayout = sol::make_object(mLayout.lua_state(), sol::nil); + if (mState != New) + mLayout = sol::make_object(mLayout.lua_state(), sol::nil); + mState = Destroyed; } } } diff --git a/components/lua_ui/element.hpp b/components/lua_ui/element.hpp index 4398a769df..39a1fdd769 100644 --- a/components/lua_ui/element.hpp +++ b/components/lua_ui/element.hpp @@ -21,10 +21,18 @@ namespace LuaUi WidgetExtension* mRoot; sol::object mLayout; std::string mLayer; - bool mUpdate; - bool mDestroy; - void create(); + enum State + { + New, + Created, + Update, + Destroy, + Destroyed, + }; + State mState; + + void create(uint64_t dept = 0); void update();