#include #include #include #include #include #include #include #include #include #include "context.hpp" #include "luamanagerimp.hpp" #include "../mwbase/windowmanager.hpp" namespace MWLua { namespace { class UiAction final : public LuaManager::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() const override { try { switch (mType) { case CREATE: mElement->create(); break; case UPDATE: mElement->update(); break; case DESTROY: mElement->destroy(); break; } } catch (std::exception&) { // 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 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; 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["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs({ { "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) }, { "Error", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Error.substr(1)) }, { "Success", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Success.substr(1)) }, { "Info", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Info.substr(1)) }, })); api["printToConsole"] = [luaManager = context.mLuaManager](const std::string& message, const Misc::Color& color) { luaManager->addInGameConsoleMessage(message + "\n", color); }; api["setConsoleMode"] = [luaManager = context.mLuaManager](std::string_view mode) { luaManager->addAction( [mode = std::string(mode)] { MWBase::Environment::get().getWindowManager()->setConsoleMode(mode); }); }; api["setConsoleSelectedObject"] = [luaManager = context.mLuaManager](const sol::object& obj) { const auto wm = MWBase::Environment::get().getWindowManager(); if (obj == sol::nil) luaManager->addAction([wm] { wm->setConsoleSelectedObject(MWWorld::Ptr()); }); else { if (!obj.is()) throw std::runtime_error("Game object expected"); luaManager->addAction([wm, obj = obj.as()] { wm->setConsoleSelectedObject(obj.ptr()); }); } }; api["content"] = LuaUi::loadContentConstructor(context.mLua); api["create"] = [context](const sol::table& layout) { auto element = LuaUi::Element::make(layout); context.mLuaManager->addAction(std::make_unique(UiAction::CREATE, element, context.mLua)); return element; }; api["updateAll"] = [context]() { LuaUi::Element::forEach([](LuaUi::Element* e) { e->mUpdate = true; }); context.mLuaManager->addAction( []() { LuaUi::Element::forEach([](LuaUi::Element* e) { e->update(); }); }, "Update all UI elements"); }; api["_getMenuTransparency"] = []() { return Settings::Manager::getFloat("menu transparency", "GUI"); }; auto uiLayer = context.mLua->sol().new_usertype("UiLayer"); uiLayer["name"] = sol::property([](LuaUi::Layer& self) { return self.name(); }); uiLayer["size"] = sol::property([](LuaUi::Layer& self) { return self.size(); }); uiLayer[sol::meta_function::to_string] = [](LuaUi::Layer& self) { return Misc::StringUtils::format("UiLayer(%s)", self.name()); }; sol::table layers = context.mLua->newTable(); layers[sol::meta_function::length] = []() { return LuaUi::Layer::count(); }; layers[sol::meta_function::index] = [](size_t index) { index = fromLuaIndex(index); return LuaUi::Layer(index); }; layers["indexOf"] = [](std::string_view name) -> sol::optional { size_t index = LuaUi::Layer::indexOf(name); if (index == LuaUi::Layer::count()) return sol::nullopt; else return toLuaIndex(index); }; layers["insertAfter"] = [context](std::string_view afterName, std::string_view name, const sol::object& opt) { LuaUi::Layer::Options options; options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true); size_t index = LuaUi::Layer::indexOf(afterName); if (index == LuaUi::Layer::count()) throw std::logic_error(std::string("Layer not found")); index++; context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer"); }; layers["insertBefore"] = [context](std::string_view beforename, std::string_view name, const sol::object& opt) { LuaUi::Layer::Options options; options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true); size_t index = LuaUi::Layer::indexOf(beforename); if (index == LuaUi::Layer::count()) throw std::logic_error(std::string("Layer not found")); context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer"); }; { auto pairs = [layers](const sol::object&) { auto next = [](const sol::table& l, size_t i) -> sol::optional> { if (i < LuaUi::Layer::count()) return std::make_tuple(i + 1, LuaUi::Layer(i)); else return sol::nullopt; }; return std::make_tuple(next, layers, 0); }; layers[sol::meta_function::pairs] = pairs; layers[sol::meta_function::ipairs] = pairs; } api["layers"] = LuaUtil::makeReadOnly(layers); sol::table typeTable = context.mLua->newTable(); for (const auto& it : LuaUi::widgetTypeToName()) typeTable.set(it.second, it.first); api["TYPE"] = LuaUtil::makeStrictReadOnly(typeTable); api["ALIGNMENT"] = LuaUtil::makeStrictReadOnly( context.mLua->tableFromPairs({ { "Start", LuaUi::Alignment::Start }, { "Center", LuaUi::Alignment::Center }, { "End", LuaUi::Alignment::End } })); api["registerSettingsPage"] = &LuaUi::registerSettingsPage; api["texture"] = [luaManager = context.mLuaManager](const sol::table& options) { LuaUi::TextureData data; sol::object path = LuaUtil::getFieldOrNil(options, "path"); if (path.is()) data.mPath = path.as(); if (data.mPath.empty()) throw std::logic_error("Invalid texture path"); sol::object offset = LuaUtil::getFieldOrNil(options, "offset"); if (offset.is()) data.mOffset = offset.as(); sol::object size = LuaUtil::getFieldOrNil(options, "size"); if (size.is()) data.mSize = size.as(); return luaManager->uiResourceManager()->registerTexture(data); }; api["screenSize"] = []() { return osg::Vec2f( Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); }; return LuaUtil::makeReadOnly(api); } }