mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-31 20:15:34 +00:00
Merge branch 'lua_ui_layers' into 'master'
Lua UI Layers See merge request OpenMW/openmw!1406
This commit is contained in:
commit
e188f042d9
7 changed files with 190 additions and 6 deletions
|
@ -1,6 +1,7 @@
|
|||
#include <components/lua_ui/content.hpp>
|
||||
#include <components/lua_ui/widgetlist.hpp>
|
||||
#include <components/lua_ui/element.hpp>
|
||||
#include <components/lua_ui/layers.hpp>
|
||||
#include <components/lua_ui/content.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "actions.hpp"
|
||||
|
@ -81,6 +82,41 @@ namespace MWLua
|
|||
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<LuaUi::Content>("UiContent");
|
||||
|
@ -175,6 +211,32 @@ namespace MWLua
|
|||
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();
|
||||
for (const auto& it : LuaUi::widgetTypeToName())
|
||||
typeTable.set(it.second, it.first);
|
||||
|
|
|
@ -162,7 +162,7 @@ add_component_dir (queries
|
|||
)
|
||||
|
||||
add_component_dir (lua_ui
|
||||
widget widgetlist element content
|
||||
widget widgetlist element layers content
|
||||
text textedit window
|
||||
)
|
||||
|
||||
|
|
|
@ -119,6 +119,17 @@ namespace LuaUtil
|
|||
// String representation of a Lua object. Should be used for debugging/logging purposes only.
|
||||
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.
|
||||
// Needed to forbid any changes in common resources that can be accessed from different sandboxes.
|
||||
sol::table makeReadOnly(sol::table);
|
||||
|
|
|
@ -55,7 +55,6 @@ namespace LuaUi
|
|||
{
|
||||
std::string type = widgetType(layout);
|
||||
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());
|
||||
|
||||
static auto widgetTypeMap = widgetTypeToName();
|
||||
|
@ -65,7 +64,7 @@ namespace LuaUi
|
|||
MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT(
|
||||
type, skin,
|
||||
MyGUI::IntCoord(), MyGUI::Align::Default,
|
||||
layer, name);
|
||||
std::string(), name);
|
||||
|
||||
LuaUi::WidgetExtension* ext = dynamic_cast<LuaUi::WidgetExtension*>(widget);
|
||||
if (!ext)
|
||||
|
@ -124,17 +123,36 @@ namespace LuaUi
|
|||
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()
|
||||
{
|
||||
assert(!mRoot);
|
||||
if (!mRoot)
|
||||
{
|
||||
mRoot = createWidget(mLayout, nullptr);
|
||||
setLayer(mLayout, mRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::update()
|
||||
{
|
||||
if (mRoot && mUpdate)
|
||||
{
|
||||
updateWidget(mLayout, mRoot);
|
||||
setLayer(mLayout, mRoot);
|
||||
}
|
||||
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.
|
||||
5. | `name`: an arbitrary string, the only limitatiion is it being unique within a `Content`.
|
||||
| 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
|
||||
--------
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
---
|
||||
-- @field [parent=#ui] #WIDGET_TYPE WIDGET_TYPE
|
||||
|
||||
---
|
||||
-- Tools for working with layers
|
||||
-- @field [parent=#ui] #Layers layers
|
||||
|
||||
---
|
||||
-- @type WIDGET_TYPE
|
||||
-- @field [parent=#WIDGET_TYPE] Widget Base widget type
|
||||
|
@ -40,6 +44,27 @@
|
|||
-- @field #table events Optional table of event callbacks
|
||||
-- @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
|
||||
-- @type Content
|
||||
|
|
Loading…
Reference in a new issue