diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index 1356409340..bd9f84fec2 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -8,162 +8,165 @@ namespace LuaUi { - namespace LayoutKeys + namespace { - constexpr std::string_view type = "type"; - constexpr std::string_view name = "name"; - constexpr std::string_view layer = "layer"; - constexpr std::string_view templateLayout = "template"; - constexpr std::string_view props = "props"; - constexpr std::string_view events = "events"; - constexpr std::string_view content = "content"; - constexpr std::string_view external = "external"; - } + namespace LayoutKeys + { + constexpr std::string_view type = "type"; + constexpr std::string_view name = "name"; + constexpr std::string_view layer = "layer"; + constexpr std::string_view templateLayout = "template"; + constexpr std::string_view props = "props"; + constexpr std::string_view events = "events"; + constexpr std::string_view content = "content"; + constexpr std::string_view external = "external"; + } - const std::string defaultWidgetType = "LuaWidget"; + const std::string defaultWidgetType = "LuaWidget"; - const uint64_t maxDepth = 250; + constexpr uint64_t maxDepth = 250; - std::string widgetType(const sol::table& layout) - { - sol::object typeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::type); - std::string type = LuaUtil::getValueOrDefault(typeField, defaultWidgetType); - sol::object templateTypeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::templateLayout, LayoutKeys::type); - if (templateTypeField != sol::nil) + std::string widgetType(const sol::table& layout) { - std::string templateType = LuaUtil::getValueOrDefault(templateTypeField, defaultWidgetType); - if (typeField != sol::nil && templateType != type) - throw std::logic_error(std::string("Template layout type ") + type - + std::string(" doesn't match template type ") + templateType); - type = templateType; + sol::object typeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::type); + std::string type = LuaUtil::getValueOrDefault(typeField, defaultWidgetType); + sol::object templateTypeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::templateLayout, LayoutKeys::type); + if (templateTypeField != sol::nil) + { + std::string templateType = LuaUtil::getValueOrDefault(templateTypeField, defaultWidgetType); + if (typeField != sol::nil && templateType != type) + throw std::logic_error(std::string("Template layout type ") + type + + std::string(" doesn't match template type ") + templateType); + type = templateType; + } + return type; } - return type; - } - - void destroyWidget(LuaUi::WidgetExtension* ext) - { - ext->deinitialize(); - MyGUI::Gui::getInstancePtr()->destroyWidget(ext->widget()); - } - - WidgetExtension* createWidget(const sol::table& layout, uint64_t depth); - void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth); - std::vector updateContent( - const std::vector& children, const sol::object& contentObj, uint64_t depth) - { - ++depth; - std::vector result; - if (contentObj == sol::nil) + void destroyWidget(LuaUi::WidgetExtension* ext) { - for (WidgetExtension* w : children) - destroyWidget(w); - return result; + ext->deinitialize(); + MyGUI::Gui::getInstancePtr()->destroyWidget(ext->widget()); } - if (!contentObj.is()) - throw std::logic_error("Layout content field must be a openmw.ui.content"); - Content content = contentObj.as(); - result.resize(content.size()); - size_t minSize = std::min(children.size(), content.size()); - for (size_t i = 0; i < minSize; i++) + + WidgetExtension* createWidget(const sol::table& layout, uint64_t depth); + void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth); + + std::vector updateContent( + const std::vector& children, const sol::object& contentObj, uint64_t depth) { - WidgetExtension* ext = children[i]; - sol::table newLayout = content.at(i); - if (ext->widget()->getTypeName() == widgetType(newLayout)) + ++depth; + std::vector result; + if (contentObj == sol::nil) { - updateWidget(ext, newLayout, depth); + for (WidgetExtension* w : children) + destroyWidget(w); + return result; } - else + if (!contentObj.is()) + throw std::logic_error("Layout content field must be a openmw.ui.content"); + Content content = contentObj.as(); + result.resize(content.size()); + size_t minSize = std::min(children.size(), content.size()); + for (size_t i = 0; i < minSize; i++) { - destroyWidget(ext); - ext = createWidget(newLayout, depth); + WidgetExtension* ext = children[i]; + sol::table newLayout = content.at(i); + if (ext->widget()->getTypeName() == widgetType(newLayout)) + { + updateWidget(ext, newLayout, depth); + } + else + { + destroyWidget(ext); + ext = createWidget(newLayout, depth); + } + result[i] = ext; } - result[i] = ext; + for (size_t i = minSize; i < children.size(); i++) + destroyWidget(children[i]); + for (size_t i = minSize; i < content.size(); i++) + result[i] = createWidget(content.at(i), depth); + return result; } - for (size_t i = minSize; i < children.size(); i++) - destroyWidget(children[i]); - for (size_t i = minSize; i < content.size(); i++) - result[i] = createWidget(content.at(i), depth); - return result; - } - void setTemplate(WidgetExtension* ext, const sol::object& templateLayout, uint64_t depth) - { - ++depth; - sol::object props = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::props); - ext->setTemplateProperties(props); - sol::object content = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::content); - ext->setTemplateChildren(updateContent(ext->templateChildren(), content, depth)); - } + void setTemplate(WidgetExtension* ext, const sol::object& templateLayout, uint64_t depth) + { + ++depth; + sol::object props = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::props); + ext->setTemplateProperties(props); + sol::object content = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::content); + ext->setTemplateChildren(updateContent(ext->templateChildren(), content, depth)); + } - void setEventCallbacks(LuaUi::WidgetExtension* ext, const sol::object& eventsObj) - { - ext->clearCallbacks(); - if (eventsObj == sol::nil) - return; - if (!eventsObj.is()) - throw std::logic_error("The \"events\" layout field must be a table of callbacks"); - auto events = eventsObj.as(); - events.for_each([ext](const sol::object& name, const sol::object& callback) + void setEventCallbacks(LuaUi::WidgetExtension* ext, const sol::object& eventsObj) { - if (name.is() && callback.is()) - ext->setCallback(name.as(), callback.as()); - else if (!name.is()) - Log(Debug::Warning) << "UI event key must be a string"; - else if (!callback.is()) - Log(Debug::Warning) << "UI event handler for key \"" << name.as() - << "\" must be an openmw.async.callback"; - }); - } + ext->clearCallbacks(); + if (eventsObj == sol::nil) + return; + if (!eventsObj.is()) + throw std::logic_error("The \"events\" layout field must be a table of callbacks"); + auto events = eventsObj.as(); + events.for_each([ext](const sol::object& name, const sol::object& callback) + { + if (name.is() && callback.is()) + ext->setCallback(name.as(), callback.as()); + else if (!name.is()) + Log(Debug::Warning) << "UI event key must be a string"; + else if (!callback.is()) + Log(Debug::Warning) << "UI event handler for key \"" << name.as() + << "\" must be an openmw.async.callback"; + }); + } - WidgetExtension* createWidget(const sol::table& layout, uint64_t depth) - { - static auto widgetTypeMap = widgetTypeToName(); - std::string type = widgetType(layout); - if (widgetTypeMap.find(type) == widgetTypeMap.end()) - throw std::logic_error(std::string("Invalid widget type ") += type); - - std::string name = layout.get_or(LayoutKeys::name, std::string()); - MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT( - type, "", - MyGUI::IntCoord(), MyGUI::Align::Default, - std::string(), name); - - WidgetExtension* ext = dynamic_cast(widget); - if (!ext) - throw std::runtime_error("Invalid widget!"); - ext->initialize(layout.lua_state(), widget); - - updateWidget(ext, layout, depth); - return ext; - } + WidgetExtension* createWidget(const sol::table& layout, uint64_t depth) + { + static auto widgetTypeMap = widgetTypeToName(); + std::string type = widgetType(layout); + if (widgetTypeMap.find(type) == widgetTypeMap.end()) + throw std::logic_error(std::string("Invalid widget type ") += type); - void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth) - { - if (depth >= maxDepth) - throw std::runtime_error("Maximum layout depth exceeded, probably caused by a circular reference"); - ext->reset(); - ext->setLayout(layout); - ext->setExternal(layout.get(LayoutKeys::external)); - setTemplate(ext, layout.get(LayoutKeys::templateLayout), depth); - ext->setProperties(layout.get(LayoutKeys::props)); - setEventCallbacks(ext, layout.get(LayoutKeys::events)); - ext->setChildren(updateContent(ext->children(), layout.get(LayoutKeys::content), depth)); - ext->updateCoord(); - } + std::string name = layout.get_or(LayoutKeys::name, std::string()); + MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT( + type, "", + MyGUI::IntCoord(), MyGUI::Align::Default, + std::string(), name); - std::string setLayer(WidgetExtension* ext, const sol::table& layout) - { - MyGUI::ILayer* layerNode = ext->widget()->getLayer(); - std::string currentLayer = layerNode ? layerNode->getName() : std::string(); - std::string newLayer = layout.get_or(LayoutKeys::layer, std::string()); - if (!newLayer.empty() && !MyGUI::LayerManager::getInstance().isExist(newLayer)) - throw std::logic_error(std::string("Layer ") + newLayer + " doesn't exist"); - else if (newLayer != currentLayer) + WidgetExtension* ext = dynamic_cast(widget); + if (!ext) + throw std::runtime_error("Invalid widget!"); + ext->initialize(layout.lua_state(), widget); + + updateWidget(ext, layout, depth); + return ext; + } + + void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth) { - MyGUI::LayerManager::getInstance().attachToLayerNode(newLayer, ext->widget()); + if (depth >= maxDepth) + throw std::runtime_error("Maximum layout depth exceeded, probably caused by a circular reference"); + ext->reset(); + ext->setLayout(layout); + ext->setExternal(layout.get(LayoutKeys::external)); + setTemplate(ext, layout.get(LayoutKeys::templateLayout), depth); + ext->setProperties(layout.get(LayoutKeys::props)); + setEventCallbacks(ext, layout.get(LayoutKeys::events)); + ext->setChildren(updateContent(ext->children(), layout.get(LayoutKeys::content), depth)); + ext->updateCoord(); + } + + std::string setLayer(WidgetExtension* ext, const sol::table& layout) + { + MyGUI::ILayer* layerNode = ext->widget()->getLayer(); + std::string currentLayer = layerNode ? layerNode->getName() : std::string(); + std::string newLayer = layout.get_or(LayoutKeys::layer, std::string()); + if (!newLayer.empty() && !MyGUI::LayerManager::getInstance().isExist(newLayer)) + throw std::logic_error(std::string("Layer ") + newLayer + " doesn't exist"); + else if (newLayer != currentLayer) + { + MyGUI::LayerManager::getInstance().attachToLayerNode(newLayer, ext->widget()); + } + return newLayer; } - return newLayer; } std::map> Element::sAllElements;