From 5d1fe6c2bc1ba2a58e270cb29bdc643660c100b0 Mon Sep 17 00:00:00 2001 From: uramer Date: Tue, 22 Mar 2022 17:59:53 +0100 Subject: [PATCH] Add layer size, make layers API more flexible --- apps/openmw/mwlua/uibindings.cpp | 59 ++++++++++++++-------- components/lua_ui/layers.cpp | 29 +++++++++++ components/lua_ui/layers.hpp | 87 ++++++++++++++++++-------------- files/lua_api/openmw/ui.lua | 22 ++++++-- 4 files changed, 135 insertions(+), 62 deletions(-) create mode 100644 components/lua_ui/layers.cpp diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 4c26c904f3..fdad57b13d 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "context.hpp" #include "actions.hpp" @@ -83,36 +84,33 @@ namespace MWLua class InsertLayerAction final : public Action { public: - InsertLayerAction(std::string_view name, std::string_view afterName, - LuaUi::Layers::Options options, LuaUtil::LuaState* state) + InsertLayerAction(std::string_view name, size_t index, + LuaUi::Layer::Options options, LuaUtil::LuaState* state) : Action(state) , mName(name) - , mAfterName(afterName) + , mIndex(index) , 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); + LuaUi::Layer::insert(mIndex, mName, mOptions); } std::string toString() const override { std::string result("Insert UI layer \""); result += mName; - result += "\" after \""; - result += mAfterName; + result += "\" at \""; + result += mIndex; result += "\""; return result; } private: std::string mName; - std::string mAfterName; - LuaUi::Layers::Options mOptions; + size_t mIndex; + LuaUi::Layer::Options mOptions; }; // Lua arrays index from 1 @@ -227,37 +225,58 @@ namespace MWLua return element; }; + 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::Layers::size(); + return LuaUi::Layer::count(); }; layers[sol::meta_function::index] = [](size_t index) { index = fromLuaIndex(index); - return LuaUi::Layers::at(index); + return LuaUi::Layer(index); }; layers["indexOf"] = [](std::string_view name) -> sol::optional { - size_t index = LuaUi::Layers::indexOf(name); - if (index == LuaUi::Layers::size()) + 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::Layers::Options options; + LuaUi::Layer::Options options; options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true); - context.mLuaManager->addAction(std::make_unique(name, afterName, options, context.mLua)); + 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(std::make_unique(name, index, options, context.mLua)); + }; + 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(std::make_unique(name, index, options, context.mLua)); }; { auto pairs = [layers](const sol::object&) { - auto next = [](const sol::table& l, size_t i) -> sol::optional> + auto next = [](const sol::table& l, size_t i) -> sol::optional> { - if (i < LuaUi::Layers::size()) - return std::make_tuple(i + 1, LuaUi::Layers::at(i)); + if (i < LuaUi::Layer::count()) + return std::make_tuple(i + 1, LuaUi::Layer(i)); else return sol::nullopt; }; diff --git a/components/lua_ui/layers.cpp b/components/lua_ui/layers.cpp new file mode 100644 index 0000000000..645c44f69f --- /dev/null +++ b/components/lua_ui/layers.cpp @@ -0,0 +1,29 @@ +#include "layers.hpp" + +#include + +namespace LuaUi +{ + size_t Layer::indexOf(std::string_view name) + { + for (size_t i = 0; i < count(); i++) + if (at(i)->getName() == name) + return i; + return count(); + } + + void Layer::insert(size_t index, std::string_view name, Options options) + { + if (index > count()) + throw std::logic_error("Invalid layer index"); + if (indexOf(name) < count()) + Log(Debug::Error) << "Layer \"" << name << "\" already exists"; + else + { + auto layer = MyGUI::LayerManager::getInstance() + .createLayerAt(std::string(name), "OverlappedLayer", index); + auto overlappedLayer = dynamic_cast(layer); + overlappedLayer->setPick(options.mInteractive); + } + } +} diff --git a/components/lua_ui/layers.hpp b/components/lua_ui/layers.hpp index 6fe7fc9c16..bb07e13c69 100644 --- a/components/lua_ui/layers.hpp +++ b/components/lua_ui/layers.hpp @@ -6,50 +6,63 @@ #include #include +#include namespace LuaUi { - namespace Layers + // this wrapper is necessary, because the MyGUI LayerManager + // stores layers in a vector and their indices could change + class Layer { - struct Options { - bool mInteractive; - }; + public: + Layer(size_t index) + : mName(at(index)->getName()) + , mCachedIndex(index) + {} - size_t size() - { - return MyGUI::LayerManager::getInstance().getLayerCount(); - } - - std::string at(size_t index) - { - if (index >= size()) - throw std::logic_error("Invalid layer index"); - return MyGUI::LayerManager::getInstance().getLayer(index)->getName(); - } - - size_t indexOf(std::string_view name) - { - for (size_t i = 0; i < size(); i++) - if (at(i) == name) - return i; - return size(); - } - - void insert(size_t index, std::string_view name, Options options) - { - if (index > size()) - throw std::logic_error("Invalid layer index"); - if (indexOf(name) < size()) - Log(Debug::Error) << "Layer \"" << name << "\" already exists"; - else + const std::string& name() const noexcept { return mName; }; + const osg::Vec2f size() { - auto layer = MyGUI::LayerManager::getInstance() - .createLayerAt(std::string(name), "OverlappedLayer", index); - auto overlappedLayer = dynamic_cast(layer); - overlappedLayer->setPick(options.mInteractive); + MyGUI::ILayer* p = refresh(); + MyGUI::IntSize size = p->getSize(); + return osg::Vec2f(size.width, size.height); } - } - } + + struct Options + { + bool mInteractive; + }; + + static size_t count() + { + return MyGUI::LayerManager::getInstance().getLayerCount(); + } + + static size_t indexOf(std::string_view name); + + static void insert(size_t index, std::string_view name, Options options); + + private: + static MyGUI::ILayer* at(size_t index) + { + if (index >= count()) + throw std::logic_error("Invalid layer index"); + return MyGUI::LayerManager::getInstance().getLayer(index); + } + + MyGUI::ILayer* refresh() + { + MyGUI::ILayer* p = at(mCachedIndex); + if (p->getName() != mName) + { + mCachedIndex = indexOf(mName); + p = at(mCachedIndex); + } + return p; + } + std::string mName; + size_t mCachedIndex; + }; } #endif // OPENMW_LUAUI_LAYERS diff --git a/files/lua_api/openmw/ui.lua b/files/lua_api/openmw/ui.lua index 57be0da501..0ef35a61a5 100644 --- a/files/lua_api/openmw/ui.lua +++ b/files/lua_api/openmw/ui.lua @@ -77,15 +77,20 @@ -- @field #Content content Optional @{openmw.ui#Content} of children layouts --- --- Layers. Implements [iterables#List](iterables.html#List) of #string. +-- @type Layer +-- @field #string name Name of the layer +-- @field openmw.util#vector2 size Size of the layer in pixels + +--- +-- Layers. Implements [iterables#List](iterables.html#List) of #Layer. -- @type Layers --- @list <#string> +-- @list <#Layer> -- @usage -- ui.layers.insertAfter('HUD', 'NewLayer', { interactive = true }) --- local fourthLayerName = ui.layers[4] +-- local fourthLayer = ui.layers[4] -- local windowsIndex = ui.layers.indexOf('Windows') --- for i, name in ipairs(ui.layers) do --- print('layer', i, name) +-- for i, layer in ipairs(ui.layers) do +-- print('layer', i, layer.name, layer.size) -- end --- @@ -101,6 +106,13 @@ -- @param #string name Name of the new layer -- @param #table options Table with a boolean `interactive` field (default is true). Layers with interactive = false will ignore all mouse interactions. +--- +-- Creates a layer and inserts it before another layer (shifts indexes of some other layers). +-- @function [parent=#Layers] insertBefore +-- @param #string beforeName Name of the layer before which the new layer will be inserted +-- @param #string name Name of the new layer +-- @param #table options Table with a boolean `interactive` field (default is true). Layers with interactive = false will ignore all mouse interactions. + --- -- Content. An array-like container, which allows to reference elements by their name. -- Implements [iterables#List](iterables.html#List) of #Layout and [iterables#Map](iterables.html#Map) of #string to #Layout.