mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-29 13:20:35 +00:00
Lua UI Layers
This commit is contained in:
parent
029eb1ade6
commit
c865114b9b
@ -1,6 +1,7 @@
|
|||||||
#include <components/lua_ui/content.hpp>
|
|
||||||
#include <components/lua_ui/widgetlist.hpp>
|
#include <components/lua_ui/widgetlist.hpp>
|
||||||
#include <components/lua_ui/element.hpp>
|
#include <components/lua_ui/element.hpp>
|
||||||
|
#include <components/lua_ui/layers.hpp>
|
||||||
|
#include <components/lua_ui/content.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include "actions.hpp"
|
#include "actions.hpp"
|
||||||
@ -81,6 +82,41 @@ namespace MWLua
|
|||||||
inline size_t toLuaIndex(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)
|
sol::table initUserInterfacePackage(const Context& context)
|
||||||
{
|
{
|
||||||
auto uiContent = context.mLua->sol().new_usertype<LuaUi::Content>("UiContent");
|
auto uiContent = context.mLua->sol().new_usertype<LuaUi::Content>("UiContent");
|
||||||
@ -175,6 +211,32 @@ namespace MWLua
|
|||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sol::table layers = context.mLua->newTable();
|
||||||
|
layers[sol::meta_function::length] = []()
|
||||||
|
{
|
||||||
|
return LuaUi::Layers::size();
|
||||||
|
};
|
||||||
|
layers[sol::meta_function::index] = [](size_t index)
|
||||||
|
{
|
||||||
|
index = fromLuaIndex(index);
|
||||||
|
return LuaUi::Layers::at(index);
|
||||||
|
};
|
||||||
|
layers["indexOf"] = [](std::string_view name) -> sol::optional<size_t>
|
||||||
|
{
|
||||||
|
size_t index = LuaUi::Layers::indexOf(name);
|
||||||
|
if (index == LuaUi::Layers::size())
|
||||||
|
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;
|
||||||
|
options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true);
|
||||||
|
context.mLuaManager->addAction(std::make_unique<LayerAction>(name, afterName, options, context.mLua));
|
||||||
|
};
|
||||||
|
api["layers"] = LuaUtil::makeReadOnly(layers);
|
||||||
|
|
||||||
sol::table typeTable = context.mLua->newTable();
|
sol::table typeTable = context.mLua->newTable();
|
||||||
for (const auto& it : LuaUi::widgetTypeToName())
|
for (const auto& it : LuaUi::widgetTypeToName())
|
||||||
typeTable.set(it.second, it.first);
|
typeTable.set(it.second, it.first);
|
||||||
|
@ -162,7 +162,7 @@ add_component_dir (queries
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (lua_ui
|
add_component_dir (lua_ui
|
||||||
widget widgetlist element content
|
widget widgetlist element layers content
|
||||||
text textedit window
|
text textedit window
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -119,6 +119,17 @@ namespace LuaUtil
|
|||||||
// String representation of a Lua object. Should be used for debugging/logging purposes only.
|
// String representation of a Lua object. Should be used for debugging/logging purposes only.
|
||||||
std::string toString(const sol::object&);
|
std::string toString(const sol::object&);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T getValueOrDefault(const sol::object& obj, const T& defaultValue)
|
||||||
|
{
|
||||||
|
if (obj == sol::nil)
|
||||||
|
return defaultValue;
|
||||||
|
if (obj.is<T>())
|
||||||
|
return obj.as<T>();
|
||||||
|
else
|
||||||
|
throw std::logic_error(std::string("Value \"") + toString(obj) + std::string("\" has unexpected type"));
|
||||||
|
}
|
||||||
|
|
||||||
// Makes a table read only (when accessed from Lua) by wrapping it with an empty userdata.
|
// Makes a table read only (when accessed from Lua) by wrapping it with an empty userdata.
|
||||||
// Needed to forbid any changes in common resources that can be accessed from different sandboxes.
|
// Needed to forbid any changes in common resources that can be accessed from different sandboxes.
|
||||||
sol::table makeReadOnly(sol::table);
|
sol::table makeReadOnly(sol::table);
|
||||||
|
@ -55,7 +55,6 @@ namespace LuaUi
|
|||||||
{
|
{
|
||||||
std::string type = widgetType(layout);
|
std::string type = widgetType(layout);
|
||||||
std::string skin = layout.get_or("skin", std::string());
|
std::string skin = layout.get_or("skin", std::string());
|
||||||
std::string layer = layout.get_or("layer", std::string("Windows"));
|
|
||||||
std::string name = layout.get_or("name", std::string());
|
std::string name = layout.get_or("name", std::string());
|
||||||
|
|
||||||
static auto widgetTypeMap = widgetTypeToName();
|
static auto widgetTypeMap = widgetTypeToName();
|
||||||
@ -65,7 +64,7 @@ namespace LuaUi
|
|||||||
MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT(
|
MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT(
|
||||||
type, skin,
|
type, skin,
|
||||||
MyGUI::IntCoord(), MyGUI::Align::Default,
|
MyGUI::IntCoord(), MyGUI::Align::Default,
|
||||||
layer, name);
|
std::string(), name);
|
||||||
|
|
||||||
LuaUi::WidgetExtension* ext = dynamic_cast<LuaUi::WidgetExtension*>(widget);
|
LuaUi::WidgetExtension* ext = dynamic_cast<LuaUi::WidgetExtension*>(widget);
|
||||||
if (!ext)
|
if (!ext)
|
||||||
@ -124,17 +123,36 @@ namespace LuaUi
|
|||||||
ext->addChild(createWidget(newContent.at(i), ext));
|
ext->addChild(createWidget(newContent.at(i), ext));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setLayer(const sol::table& layout, LuaUi::WidgetExtension* ext)
|
||||||
|
{
|
||||||
|
MyGUI::ILayer* layerNode = ext->widget()->getLayer();
|
||||||
|
std::string currentLayer = layerNode ? layerNode->getName() : std::string();
|
||||||
|
std::string newLayer = layout.get_or("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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Element::create()
|
void Element::create()
|
||||||
{
|
{
|
||||||
assert(!mRoot);
|
assert(!mRoot);
|
||||||
if (!mRoot)
|
if (!mRoot)
|
||||||
|
{
|
||||||
mRoot = createWidget(mLayout, nullptr);
|
mRoot = createWidget(mLayout, nullptr);
|
||||||
|
setLayer(mLayout, mRoot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::update()
|
void Element::update()
|
||||||
{
|
{
|
||||||
if (mRoot && mUpdate)
|
if (mRoot && mUpdate)
|
||||||
|
{
|
||||||
updateWidget(mLayout, mRoot);
|
updateWidget(mLayout, mRoot);
|
||||||
|
setLayer(mLayout, mRoot);
|
||||||
|
}
|
||||||
mUpdate = false;
|
mUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
components/lua_ui/layers.hpp
Normal file
55
components/lua_ui/layers.hpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef OPENMW_LUAUI_LAYERS
|
||||||
|
#define OPENMW_LUAUI_LAYERS
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <MyGUI_LayerManager.h>
|
||||||
|
#include <MyGUI_OverlappedLayer.h>
|
||||||
|
|
||||||
|
namespace LuaUi
|
||||||
|
{
|
||||||
|
namespace Layers
|
||||||
|
{
|
||||||
|
struct Options {
|
||||||
|
bool mInteractive;
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
auto layer = MyGUI::LayerManager::getInstance()
|
||||||
|
.createLayerAt(std::string(name), "OverlappedLayer", index);
|
||||||
|
auto overlappedLayer = dynamic_cast<MyGUI::OverlappedLayer*>(layer);
|
||||||
|
overlappedLayer->setPick(options.mInteractive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OPENMW_LUAUI_LAYERS
|
@ -22,9 +22,22 @@ Every widget is defined by a layout, which is a Lua table with the following fie
|
|||||||
4. `content`: a Content (`openmw.ui.content`), which contains layouts for the children of this widget.
|
4. `content`: a Content (`openmw.ui.content`), which contains layouts for the children of this widget.
|
||||||
5. | `name`: an arbitrary string, the only limitatiion is it being unique within a `Content`.
|
5. | `name`: an arbitrary string, the only limitatiion is it being unique within a `Content`.
|
||||||
| Helpful for navigatilng through the layouts.
|
| Helpful for navigatilng through the layouts.
|
||||||
6. `layer`: only applies for the root widget. (Windows, HUD, etc)
|
6. `layer`: only applies for the root widget.
|
||||||
|
|
||||||
.. TODO: Write a more detailed documentation for layers when they are finished
|
Layers
|
||||||
|
------
|
||||||
|
Layers control how widgets overlap - layers with higher indexes cover render over layers with lower indexes.
|
||||||
|
Widgets within the same layer which were added later overlap the ones created earlier.
|
||||||
|
A layer can also be set as non-interactive, which prevents all mouse interactions with the widgets in that layer.
|
||||||
|
|
||||||
|
.. TODO: Move this list when layers are de-hardcoded
|
||||||
|
|
||||||
|
Pre-defined OpenMW layers:
|
||||||
|
|
||||||
|
1. `HUD` interactive
|
||||||
|
2. `Windows` interactive
|
||||||
|
3. `Notification` non-interactive
|
||||||
|
4. `MessageBox` interactive
|
||||||
|
|
||||||
Elements
|
Elements
|
||||||
--------
|
--------
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
---
|
---
|
||||||
-- @field [parent=#ui] #WIDGET_TYPE WIDGET_TYPE
|
-- @field [parent=#ui] #WIDGET_TYPE WIDGET_TYPE
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Tools for working with layers
|
||||||
|
-- @field [parent=#ui] #Layers layers
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @type WIDGET_TYPE
|
-- @type WIDGET_TYPE
|
||||||
-- @field [parent=#WIDGET_TYPE] Widget Base widget type
|
-- @field [parent=#WIDGET_TYPE] Widget Base widget type
|
||||||
@ -40,6 +44,27 @@
|
|||||||
-- @field #table events Optional table of event callbacks
|
-- @field #table events Optional table of event callbacks
|
||||||
-- @field #Content content Optional @{openmw.ui#Content} of children layouts
|
-- @field #Content content Optional @{openmw.ui#Content} of children layouts
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Layers
|
||||||
|
-- @type Layers
|
||||||
|
-- @usage
|
||||||
|
-- ui.layers.insertAfter('HUD', 'NewLayer', { interactive = true })
|
||||||
|
-- local fourthLayerName = ui.layers[4]
|
||||||
|
-- local windowsIndex = ui.layers.indexOf('Windows')
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Index of the layer with the givent name. Returns nil if the layer doesn't exist
|
||||||
|
-- @function [parent=#Layers] indexOf
|
||||||
|
-- @param #string name Name of the layer
|
||||||
|
-- @return #number, #nil index
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Creates a layer and inserts it after another layer (shifts indexes of some other layers).
|
||||||
|
-- @function [parent=#Layers] insertAfter
|
||||||
|
-- @param #string afterName Name of the layer after 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
|
-- Content. An array-like container, which allows to reference elements by their name
|
||||||
-- @type Content
|
-- @type Content
|
||||||
|
Loading…
x
Reference in New Issue
Block a user