|
|
|
@ -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<WidgetExtension*> updateContent(
|
|
|
|
|
const std::vector<WidgetExtension*>& children, const sol::object& contentObj, uint64_t depth)
|
|
|
|
|
{
|
|
|
|
|
++depth;
|
|
|
|
|
std::vector<WidgetExtension*> 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<Content>())
|
|
|
|
|
throw std::logic_error("Layout content field must be a openmw.ui.content");
|
|
|
|
|
Content content = contentObj.as<Content>();
|
|
|
|
|
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<WidgetExtension*> updateContent(
|
|
|
|
|
const std::vector<WidgetExtension*>& 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<WidgetExtension*> result;
|
|
|
|
|
if (contentObj == sol::nil)
|
|
|
|
|
{
|
|
|
|
|
updateWidget(ext, newLayout, depth);
|
|
|
|
|
for (WidgetExtension* w : children)
|
|
|
|
|
destroyWidget(w);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (!contentObj.is<Content>())
|
|
|
|
|
throw std::logic_error("Layout content field must be a openmw.ui.content");
|
|
|
|
|
Content content = contentObj.as<Content>();
|
|
|
|
|
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<sol::table>())
|
|
|
|
|
throw std::logic_error("The \"events\" layout field must be a table of callbacks");
|
|
|
|
|
auto events = eventsObj.as<sol::table>();
|
|
|
|
|
events.for_each([ext](const sol::object& name, const sol::object& callback)
|
|
|
|
|
void setEventCallbacks(LuaUi::WidgetExtension* ext, const sol::object& eventsObj)
|
|
|
|
|
{
|
|
|
|
|
if (name.is<std::string>() && callback.is<LuaUtil::Callback>())
|
|
|
|
|
ext->setCallback(name.as<std::string>(), callback.as<LuaUtil::Callback>());
|
|
|
|
|
else if (!name.is<std::string>())
|
|
|
|
|
Log(Debug::Warning) << "UI event key must be a string";
|
|
|
|
|
else if (!callback.is<LuaUtil::Callback>())
|
|
|
|
|
Log(Debug::Warning) << "UI event handler for key \"" << name.as<std::string>()
|
|
|
|
|
<< "\" must be an openmw.async.callback";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
ext->clearCallbacks();
|
|
|
|
|
if (eventsObj == sol::nil)
|
|
|
|
|
return;
|
|
|
|
|
if (!eventsObj.is<sol::table>())
|
|
|
|
|
throw std::logic_error("The \"events\" layout field must be a table of callbacks");
|
|
|
|
|
auto events = eventsObj.as<sol::table>();
|
|
|
|
|
events.for_each([ext](const sol::object& name, const sol::object& callback)
|
|
|
|
|
{
|
|
|
|
|
if (name.is<std::string>() && callback.is<LuaUtil::Callback>())
|
|
|
|
|
ext->setCallback(name.as<std::string>(), callback.as<LuaUtil::Callback>());
|
|
|
|
|
else if (!name.is<std::string>())
|
|
|
|
|
Log(Debug::Warning) << "UI event key must be a string";
|
|
|
|
|
else if (!callback.is<LuaUtil::Callback>())
|
|
|
|
|
Log(Debug::Warning) << "UI event handler for key \"" << name.as<std::string>()
|
|
|
|
|
<< "\" 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<WidgetExtension*>(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<sol::object>(LayoutKeys::external));
|
|
|
|
|
setTemplate(ext, layout.get<sol::object>(LayoutKeys::templateLayout), depth);
|
|
|
|
|
ext->setProperties(layout.get<sol::object>(LayoutKeys::props));
|
|
|
|
|
setEventCallbacks(ext, layout.get<sol::object>(LayoutKeys::events));
|
|
|
|
|
ext->setChildren(updateContent(ext->children(), layout.get<sol::object>(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<WidgetExtension*>(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<sol::object>(LayoutKeys::external));
|
|
|
|
|
setTemplate(ext, layout.get<sol::object>(LayoutKeys::templateLayout), depth);
|
|
|
|
|
ext->setProperties(layout.get<sol::object>(LayoutKeys::props));
|
|
|
|
|
setEventCallbacks(ext, layout.get<sol::object>(LayoutKeys::events));
|
|
|
|
|
ext->setChildren(updateContent(ext->children(), layout.get<sol::object>(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*, std::shared_ptr<Element>> Element::sAllElements;
|
|
|
|
|