#include #include #include #include "context.hpp" #include "actions.hpp" #include "luamanagerimp.hpp" namespace MWLua { namespace { std::set allElements; class UiAction final : public Action { public: enum Type { CREATE = 0, UPDATE, DESTROY, }; UiAction(Type type, std::shared_ptr element, LuaUtil::LuaState* state) : Action(state) , mType{ type } , mElement{ std::move(element) } {} void apply(WorldView&) const override { try { switch (mType) { case CREATE: mElement->create(); break; case UPDATE: mElement->update(); break; case DESTROY: mElement->destroy(); break; } } catch (std::exception& e) { // prevent any actions on a potentially corrupted widget mElement->mRoot = nullptr; throw; } } std::string toString() const override { std::string result; switch (mType) { case CREATE: result += "Create"; break; case UPDATE: result += "Update"; break; case DESTROY: result += "Destroy"; break; } result += " UI"; return result; } private: Type mType; std::shared_ptr mElement; }; // Lua arrays index from 1 inline size_t fromLuaIndex(size_t i) { return i - 1; } inline size_t toLuaIndex(size_t i) { return i + 1; } } sol::table initUserInterfacePackage(const Context& context) { auto uiContent = context.mLua->sol().new_usertype("UiContent"); uiContent[sol::meta_function::length] = [](const LuaUi::Content& content) { return content.size(); }; uiContent[sol::meta_function::index] = sol::overload( [](const LuaUi::Content& content, size_t index) { return content.at(fromLuaIndex(index)); }, [](const LuaUi::Content& content, std::string_view name) { return content.at(name); }); uiContent[sol::meta_function::new_index] = sol::overload( [](LuaUi::Content& content, size_t index, const sol::table& table) { content.assign(fromLuaIndex(index), table); }, [](LuaUi::Content& content, size_t index, sol::nil_t nil) { content.remove(fromLuaIndex(index)); }, [](LuaUi::Content& content, std::string_view name, const sol::table& table) { content.assign(name, table); }, [](LuaUi::Content& content, std::string_view name, sol::nil_t nil) { content.remove(name); }); uiContent["insert"] = [](LuaUi::Content& content, size_t index, const sol::table& table) { content.insert(fromLuaIndex(index), table); }; uiContent["add"] = [](LuaUi::Content& content, const sol::table& table) { content.insert(content.size(), table); }; uiContent["indexOf"] = [](LuaUi::Content& content, const sol::table& table) -> sol::optional { size_t index = content.indexOf(table); if (index < content.size()) return toLuaIndex(index); else return sol::nullopt; }; auto element = context.mLua->sol().new_usertype("Element"); element["layout"] = sol::property( [](LuaUi::Element& element) { return element.mLayout; }, [](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; } ); element["update"] = [context](const std::shared_ptr& element) { if (element->mDestroy || element->mUpdate) return; element->mUpdate = true; context.mLuaManager->addAction(std::make_unique(UiAction::UPDATE, element, context.mLua)); }; element["destroy"] = [context](const std::shared_ptr& element) { if (element->mDestroy) return; allElements.erase(element.get()); element->mDestroy = true; context.mLuaManager->addAction(std::make_unique(UiAction::DESTROY, element, context.mLua)); }; sol::table api = context.mLua->newTable(); api["showMessage"] = [luaManager=context.mLuaManager](std::string_view message) { luaManager->addUIMessage(message); }; api["content"] = [](const sol::table& table) { return LuaUi::Content(table); }; api["create"] = [context](const sol::table& layout) { auto element = std::make_shared(layout); allElements.emplace(element.get()); context.mLuaManager->addAction(std::make_unique(UiAction::CREATE, element, context.mLua)); return element; }; sol::table typeTable = context.mLua->newTable(); for (const auto& it : LuaUi::widgetTypeToName()) typeTable.set(it.second, it.first); api["TYPE"] = LuaUtil::makeReadOnly(typeTable); return LuaUtil::makeReadOnly(api); } void clearUserInterface() { for (auto element : allElements) element->destroy(); allElements.clear(); } }