From e092ee2624376c5235594cdabb1b0f3c2df7af86 Mon Sep 17 00:00:00 2001 From: uramer Date: Mon, 7 Feb 2022 23:37:08 +0100 Subject: [PATCH] Document the Lua Image widget, add UI texture resources --- apps/openmw/mwlua/luamanagerimp.cpp | 2 + apps/openmw/mwlua/luamanagerimp.hpp | 6 ++ apps/openmw/mwlua/uibindings.cpp | 88 +++++++++++-------- components/CMakeLists.txt | 2 +- components/lua_ui/image.cpp | 23 ++++- components/lua_ui/resources.cpp | 23 +++++ components/lua_ui/resources.hpp | 55 ++++++++++++ .../lua-scripting/user_interface.rst | 1 + .../reference/lua-scripting/widgets/image.rst | 22 +++++ files/lua_api/openmw/ui.lua | 30 +++++-- 10 files changed, 209 insertions(+), 43 deletions(-) create mode 100644 components/lua_ui/resources.cpp create mode 100644 components/lua_ui/resources.hpp create mode 100644 docs/source/reference/lua-scripting/widgets/image.rst diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 0dcd72d359..3319e975cc 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -37,6 +37,8 @@ namespace MWLua mLocalLoader = createUserdataSerializer(true, mWorldView.getObjectRegistry(), &mContentFileMapping); mGlobalScripts.setSerializer(mGlobalSerializer.get()); + + mUiResourceManager = std::make_unique(vfs); } void LuaManager::initConfiguration() diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index b292eb3c1b..fecb9478e0 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -8,6 +8,8 @@ #include #include +#include + #include "../mwbase/luamanager.hpp" #include "actions.hpp" @@ -89,10 +91,14 @@ namespace MWLua return [this, c](Arg arg) { this->queueCallback(c, sol::make_object(c.mFunc.lua_state(), arg)); }; } + LuaUi::ResourceManager* uiResourceManager() { return mUiResourceManager.get(); } + private: void initConfiguration(); LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr, ESM::LuaScriptCfg::Flags); + std::unique_ptr mUiResourceManager; + bool mInitialized = false; bool mGlobalScriptsStarted = false; LuaUtil::ScriptsConfiguration mConfiguration; diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 248f82f28a..0572365b57 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "context.hpp" #include "actions.hpp" @@ -77,46 +78,46 @@ namespace MWLua std::shared_ptr mElement; }; + class LayerAction final : public Action + { + public: + LayerAction(std::string_view name, std::string_view afterName, + LuaUi::Layers::Options options, LuaUtil::LuaState* state) + : Action(state) + , mName(name) + , mAfterName(afterName) + , mOptions(options) + {} + + void apply(WorldView&) const override + { + size_t index = LuaUi::Layers::indexOf(mAfterName); + if (index == LuaUi::Layers::size()) + throw std::logic_error(std::string("Layer not found")); + LuaUi::Layers::insert(index, mName, mOptions); + } + + std::string toString() const override + { + std::string result("Insert UI layer \""); + result += mName; + result += "\" after \""; + result += mAfterName; + result += "\""; + return result; + } + + private: + std::string mName; + std::string mAfterName; + LuaUi::Layers::Options mOptions; + }; + // 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; } } - class LayerAction final : public Action - { - public: - LayerAction(std::string_view name, std::string_view afterName, - LuaUi::Layers::Options options, LuaUtil::LuaState* state) - : Action(state) - , mName(name) - , mAfterName(afterName) - , mOptions(options) - {} - - void apply(WorldView&) const override - { - size_t index = LuaUi::Layers::indexOf(mAfterName); - if (index == LuaUi::Layers::size()) - throw std::logic_error(std::string("Layer not found")); - LuaUi::Layers::insert(index, mName, mOptions); - } - - std::string toString() const override - { - std::string result("Insert UI layer \""); - result += mName; - result += "\" after \""; - result += mAfterName; - result += "\""; - return result; - } - - private: - std::string mName; - std::string mAfterName; - LuaUi::Layers::Options mOptions; - }; - sol::table initUserInterfacePackage(const Context& context) { auto uiContent = context.mLua->sol().new_usertype("UiContent"); @@ -278,6 +279,23 @@ namespace MWLua 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() and !path.as().empty()) + data.mPath = path.as(); + else + throw sol::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); + }; + return LuaUtil::makeReadOnly(api); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 929df9b20d..e183d54a95 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -259,7 +259,7 @@ add_component_dir (queries add_component_dir (lua_ui registerscriptsettings scriptsettings - properties widget element util layers content alignment + properties widget element util layers content alignment resources adapter text textedit window image container ) diff --git a/components/lua_ui/image.cpp b/components/lua_ui/image.cpp index c72bac21e9..9dab34868e 100644 --- a/components/lua_ui/image.cpp +++ b/components/lua_ui/image.cpp @@ -2,6 +2,8 @@ #include +#include "resources.hpp" + namespace LuaUi { void LuaTileRect::_setAlign(const MyGUI::IntSize& _oldsize) @@ -33,19 +35,38 @@ namespace LuaUi void LuaImage::updateProperties() { - setImageTexture(propertyValue("path", std::string())); + TextureResource* resource = propertyValue("resource", nullptr); + MyGUI::IntCoord atlasCoord; + if (resource) + { + auto& data = resource->data(); + atlasCoord = MyGUI::IntCoord( + static_cast(data.mOffset.x()), + static_cast(data.mOffset.y()), + static_cast(data.mSize.x()), + static_cast(data.mSize.y())); + setImageTexture(data.mPath); + } bool tileH = propertyValue("tileH", false); bool tileV = propertyValue("tileV", false); + MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(_getTextureName()); MyGUI::IntSize textureSize; if (texture != nullptr) textureSize = MyGUI::IntSize(texture->getWidth(), texture->getHeight()); + mTileRect->updateSize(MyGUI::IntSize( tileH ? textureSize.width : 0, tileV ? textureSize.height : 0 )); + if (atlasCoord.width == 0) + atlasCoord.width = textureSize.width; + if (atlasCoord.height == 0) + atlasCoord.height = textureSize.height; + setImageCoord(atlasCoord); + WidgetExtension::updateProperties(); } } diff --git a/components/lua_ui/resources.cpp b/components/lua_ui/resources.cpp new file mode 100644 index 0000000000..421375f9cb --- /dev/null +++ b/components/lua_ui/resources.cpp @@ -0,0 +1,23 @@ +#include "resources.hpp" + +#include + +namespace LuaUi +{ + std::shared_ptr ResourceManager::registerTexture(TextureData data) + { + std::string normalizedPath = vfs()->normalizeFilename(data.mPath); + if (!vfs()->exists(normalizedPath)) + { + std::string error("Texture with path \""); + error += data.mPath; + error += "\" doesn't exist"; + throw std::logic_error(error); + } + data.mPath = normalizedPath; + + TextureResources& list = mTextures[normalizedPath]; + list.push_back(std::make_shared(data)); + return list.back(); + } +} diff --git a/components/lua_ui/resources.hpp b/components/lua_ui/resources.hpp new file mode 100644 index 0000000000..6bad0b0337 --- /dev/null +++ b/components/lua_ui/resources.hpp @@ -0,0 +1,55 @@ +#ifndef OPENMW_LUAUI_RESOURCES +#define OPENMW_LUAUI_RESOURCES + +#include +#include +#include +#include + +#include + +namespace VFS +{ + class Manager; +} + +namespace LuaUi +{ + struct TextureData + { + std::string mPath; + osg::Vec2f mOffset; + osg::Vec2f mSize; + }; + + class TextureResource + { + public: + TextureResource(TextureData data) + : mData(data) + {} + + const TextureData& data() { return mData; } + private: + TextureData mData; + }; + + class ResourceManager { + public: + ResourceManager(const VFS::Manager* vfs) + : mVfs(vfs) + {} + + std::shared_ptr registerTexture(TextureData data); + + protected: + const VFS::Manager* vfs() const { return mVfs; } + + private: + const VFS::Manager* mVfs; + using TextureResources = std::vector>; + std::unordered_map mTextures; + }; +} + +#endif // OPENMW_LUAUI_LAYERS diff --git a/docs/source/reference/lua-scripting/user_interface.rst b/docs/source/reference/lua-scripting/user_interface.rst index e00aa8bf63..71ad61b410 100644 --- a/docs/source/reference/lua-scripting/user_interface.rst +++ b/docs/source/reference/lua-scripting/user_interface.rst @@ -80,6 +80,7 @@ Widget types Widget: Base widget type, all the other widgets inherit its properties and events. Text: Displays text. TextEdit: Accepts text input from the user. + Image: Renders a texture. Example ------- diff --git a/docs/source/reference/lua-scripting/widgets/image.rst b/docs/source/reference/lua-scripting/widgets/image.rst new file mode 100644 index 0000000000..cae8753d52 --- /dev/null +++ b/docs/source/reference/lua-scripting/widgets/image.rst @@ -0,0 +1,22 @@ +Image Widget +============ + +Properties +---------- + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type (default value) + - description + * - resource + - ui.texture + - The texture resource to display + * - tileH + - boolean (false) + - Whether to tile the texture horizontally + * - tileV + - boolean (false) + - Whether to tile the texture vertically diff --git a/files/lua_api/openmw/ui.lua b/files/lua_api/openmw/ui.lua index 478b9235a5..c9df230900 100644 --- a/files/lua_api/openmw/ui.lua +++ b/files/lua_api/openmw/ui.lua @@ -53,7 +53,15 @@ --- -- Adds a settings page to main menu setting's Scripts tab. -- @function [parent=#ui] registerSettingsPage --- @param #SettingsPage page +-- @param #SettingsPageOptions page + +--- +-- Table with settings page options, passed as an argument to ui.registerSettingsPage +-- @type SettingsPageOptions +-- @field #string name Name of the page, displayed in the list, used for search +-- @field #string searchHints Additional keywords used in search, not displayed anywhere +-- @field #Element element The page's UI, which will be attached to the settings tab. The root widget has to have a fixed size (set `size` field in `props`, see Widget documentation, `relativeSize` is ignored) + --- -- Layout @@ -162,10 +170,20 @@ -- @field #number button Mouse button which triggered the event (could be nil) --- --- Settings page parameters, passed as an argument to ui.registerSettingsPage --- @type SettingsPage --- @field #string name Name of the page, displayed in the list, used for search --- @field #string searchHints Additional keywords used in search, not displayed anywhere --- @field #Element element The page's UI, which will be attached to the settings tab. The root widget has to have a fixed size (set `size` field in `props`, see Widget documentation, `relativeSize` is ignored) +-- Register a new texture resource +-- @function [parent=#ui] texture #TextureResource +-- @param #TextureResourceOptions options + +--- +-- A texture ready to be used by UI widgets +-- @type TextureResource + +--- +-- Table with argument passed to ui.texture +-- @type TextureResourceOptions +-- @field #string path Path to the texture file. Required +-- @field openmw.util#Vector2 offset Offset of this resource in the texture. (0, 0) by default +-- @field openmw.util#Vector2 size Size of the resource in the texture. (0, 0) by default. +-- 0 means the whole texture size is used. return nil