pull/3227/head
uramer 3 years ago committed by Cody Glassman
parent b4fca045c6
commit 03659bef86

@ -82,38 +82,6 @@ namespace MWLua
std::shared_ptr<LuaUi::Element> mElement;
};
class InsertLayerAction final : public LuaManager::Action
{
public:
InsertLayerAction(std::string_view name, size_t index,
LuaUi::Layer::Options options, LuaUtil::LuaState* state)
: Action(state)
, mName(name)
, mIndex(index)
, mOptions(options)
{}
void apply(WorldView&) const override
{
LuaUi::Layer::insert(mIndex, mName, mOptions);
}
std::string toString() const override
{
std::string result("Insert UI layer \"");
result += mName;
result += "\" at \"";
result += mIndex;
result += "\"";
return result;
}
private:
std::string mName;
size_t mIndex;
LuaUi::Layer::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; }
@ -252,6 +220,18 @@ namespace MWLua
context.mLuaManager->addAction(std::make_unique<UiAction>(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<LuaUi::Layer>("UiLayer");
uiLayer["name"] = sol::property([](LuaUi::Layer& self) { return self.name(); });
@ -287,7 +267,7 @@ namespace MWLua
if (index == LuaUi::Layer::count())
throw std::logic_error(std::string("Layer not found"));
index++;
context.mLuaManager->addAction(std::make_unique<InsertLayerAction>(name, index, options, context.mLua));
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)
{
@ -296,7 +276,7 @@ namespace MWLua
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<InsertLayerAction>(name, index, options, context.mLua));
context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
};
{
auto pairs = [layers](const sol::object&)

@ -20,9 +20,11 @@ namespace LuaUi
constexpr std::string_view external = "external";
}
const std::string defaultWidgetType = "LuaWidget";
std::string widgetType(const sol::table& layout)
{
return layout.get_or(LayoutKeys::type, std::string("LuaWidget"));
return layout.get_or(LayoutKeys::type, defaultWidgetType);
}
void destroyWidget(LuaUi::WidgetExtension* ext)
@ -74,11 +76,6 @@ namespace LuaUi
void setTemplate(WidgetExtension* ext, const sol::object& templateLayout)
{
// \todo remove when none of the widgets require this workaround
sol::object skin = LuaUtil::getFieldOrNil(templateLayout, "skin");
if (skin.is<std::string>())
ext->widget()->changeWidgetSkin(skin.as<std::string>());
sol::object props = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::props);
ext->setTemplateProperties(props);
sol::object content = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::content);
@ -107,13 +104,22 @@ namespace LuaUi
WidgetExtension* createWidget(const sol::table& layout)
{
std::string type = widgetType(layout);
std::string name = layout.get_or(LayoutKeys::name, std::string());
sol::object typeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::type);
std::string type = LuaUtil::getValueOrDefault(typeField, defaultWidgetType);
sol::object templateTypeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::templateLayout, LayoutKeys::type);
if (templateTypeField != sol::nil)
{
std::string templateType = LuaUtil::getValueOrDefault(templateTypeField, defaultWidgetType);
if (typeField != sol::nil && templateType != type)
throw std::logic_error(std::string("Template layout type ") + type
+ std::string(" doesn't match template type ") + templateType);
type = templateType;
}
static auto widgetTypeMap = widgetTypeToName();
if (widgetTypeMap.find(type) == widgetTypeMap.end())
throw std::logic_error(std::string("Invalid widget type ") += type);
std::string name = layout.get_or(LayoutKeys::name, std::string());
MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT(
type, "",
MyGUI::IntCoord(), MyGUI::Align::Default,

@ -9,6 +9,13 @@ namespace LuaUi
{
static std::shared_ptr<Element> make(sol::table layout);
template<class Callback>
static void forEach(Callback callback)
{
for(auto& [e, _] : sAllElements)
callback(e);
}
WidgetExtension* mRoot;
WidgetExtension* mAttachedTo;
sol::table mLayout;

@ -56,24 +56,25 @@ namespace LuaUi
MyGUI::IntSize flexSize = calculateSize();
int growSize = 0;
float growFactor = 0;
if (totalGrow > 0)
if (totalGrow > 0 && !mAutoSized)
{
growSize = primary(flexSize) - primary(childrenSize);
growFactor = growSize / totalGrow;
}
}
MyGUI::IntPoint childPosition;
primary(childPosition) = alignSize(primary(flexSize) - growSize, primary(childrenSize), mAlign);
secondary(childPosition) = alignSize(secondary(flexSize), secondary(childrenSize), mArrange);
for (auto* w : children())
{
MyGUI::IntSize size = w->calculateSize();
secondary(childPosition) = alignSize(secondary(flexSize), secondary(size), mArrange);
w->forcePosition(childPosition);
MyGUI::IntSize size = w->widget()->getSize();
primary(size) += static_cast<int>(growFactor * getGrow(w));
w->forceSize(size);
primary(childPosition) += primary(size);
w->updateCoord();
}
WidgetExtension::updateProperties();
WidgetExtension::updateChildren();
}
MyGUI::IntSize LuaFlex::calculateSize()
@ -85,4 +86,10 @@ namespace LuaUi
}
return size;
}
void LuaFlex::updateCoord()
{
updateChildren();
WidgetExtension::updateCoord();
}
}

@ -18,6 +18,7 @@ namespace LuaUi
{
return MyGUI::IntSize();
}
void updateCoord() override;
private:
bool mHorizontal;

@ -197,10 +197,13 @@ namespace LuaUi
MyGUI::IntSize slotSize = mSlot->widget()->getSize();
MyGUI::IntPoint slotPosition = mSlot->widget()->getAbsolutePosition() - widget()->getAbsolutePosition();
MyGUI::IntCoord slotCoord(slotPosition, slotSize);
if (mWidget->getSubWidgetMain())
mWidget->getSubWidgetMain()->setCoord(slotCoord);
if (mWidget->getSubWidgetText())
mWidget->getSubWidgetText()->setCoord(slotCoord);
MyGUI::Widget* clientWidget = mWidget->getClientWidget();
if (!clientWidget)
clientWidget = mWidget;
if (clientWidget->getSubWidgetMain())
clientWidget->getSubWidgetMain()->setCoord(slotCoord);
if (clientWidget->getSubWidgetText())
clientWidget->getSubWidgetText()->setCoord(slotCoord);
}
}
@ -250,8 +253,7 @@ namespace LuaUi
if (oldCoord != newCoord)
mWidget->setCoord(newCoord);
if (oldCoord.size() != newCoord.size())
updateChildrenCoord();
updateChildrenCoord();
if (oldCoord != newCoord && mOnCoordChange.has_value())
mOnCoordChange.value()(this, newCoord);
}

@ -52,7 +52,7 @@ namespace LuaUi
void forcePosition(const MyGUI::IntPoint& pos);
void clearForced();
void updateCoord();
virtual void updateCoord();
const sol::table& getLayout() { return mLayout; }
void setLayout(const sol::table& layout) { mLayout = layout; }

@ -67,3 +67,4 @@ cd $FILES_DIR/builtin_scripts
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR openmw_aux/*lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/ai.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/camera.lua
$DOCUMENTOR_PATH -f doc -d $OUTPUT_DIR scripts/omw/mwui/init.lua

@ -23,8 +23,10 @@ Lua API reference
openmw_aux_calendar
openmw_aux_util
openmw_aux_time
openmw_aux_ui
interface_ai
interface_camera
interface_mwui
iterables
@ -87,6 +89,8 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw_aux.time <Package openmw_aux.time>` | everywhere | | Timers and game time utils |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw_aux.ui <Package openmw_aux.ui>` | by player scripts | | User interface utils |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
**Interfaces of built-in scripts**

@ -0,0 +1,6 @@
Interface MWUI
==============
.. raw:: html
:file: generated_html/scripts_omw_mwui_init.html

@ -0,0 +1,5 @@
Package openmw_aux.ui
=======================
.. raw:: html
:file: generated_html/openmw_aux_ui.html

@ -8,6 +8,7 @@ set(LUA_BUILTIN_FILES
openmw_aux/util.lua
openmw_aux/time.lua
openmw_aux/calendar.lua
openmw_aux/ui.lua
scripts/omw/ai.lua
scripts/omw/camera.lua
@ -18,9 +19,15 @@ set(LUA_BUILTIN_FILES
scripts/omw/console/local.lua
l10n/Calendar/en.yaml
scripts/omw/mwui/constants.lua
scripts/omw/mwui/borders.lua
scripts/omw/mwui/box.lua
scripts/omw/mwui/text.lua
scripts/omw/mwui/textEdit.lua
scripts/omw/mwui/init.lua
)
foreach (f ${LUA_BUILTIN_FILES})
copy_resource_file("${CMAKE_CURRENT_SOURCE_DIR}/${f}" "${OPENMW_RESOURCES_ROOT}" "resources/vfs/${f}")
endforeach (f)

@ -3,3 +3,4 @@ NPC,CREATURE: scripts/omw/ai.lua
PLAYER: scripts/omw/console/player.lua
GLOBAL: scripts/omw/console/global.lua
CUSTOM: scripts/omw/console/local.lua
PLAYER: scripts/omw/mwui/init.lua

@ -0,0 +1,36 @@
local ui = require('openmw.ui')
---
-- `openmw_aux.ui` defines utility functions for UI.
-- Implementation can be found in `resources/vfs/openmw_aux/ui.lua`.
-- @module ui
-- @usage local auxUi = require('openmw_aux.ui')
local aux_ui = {}
local function deepContentCopy(content)
local result = ui.content{}
for _, v in ipairs(content) do
result:add(aux_ui.deepLayoutCopy(v))
end
return result
end
---
-- @function [parent=#ui] templates
-- @param #table layout
-- @return #table copied layout
function aux_ui.deepLayoutCopy(layout)
local result = {}
for k, v in pairs(layout) do
if k == 'content' then
result[k] = deepContentCopy(v)
elseif type(v) == 'table' then
result[k] = aux_ui.deepLayoutCopy(v)
else
result[k] = v
end
end
return result
end
return aux_ui

@ -0,0 +1,120 @@
local ui = require('openmw.ui')
local util = require('openmw.util')
local constants = require('scripts.omw.mwui.constants')
local v2 = util.vector2
local sideParts = {
left = util.vector2(0, 0.5),
right = util.vector2(1, 0.5),
top = util.vector2(0.5, 0),
bottom = util.vector2(0.5, 1),
}
local cornerParts = {
top_left_corner = util.vector2(0, 0),
top_right_corner = util.vector2(1, 0),
bottom_left_corner = util.vector2(0, 1),
bottom_right_corner = util.vector2(1, 1),
}
local resources = {}
do
local boxBorderPattern = 'textures/menu_thin_border_%s.dds'
for k, _ in pairs(sideParts) do
resources[k] = ui.texture{ path = boxBorderPattern:format(k) }
end
for k, _ in pairs(cornerParts) do
resources[k] = ui.texture{ path = boxBorderPattern:format(k) }
end
end
local borderPieces = {}
for k, align in pairs(sideParts) do
local resource = resources[k]
local horizontal = align.x ~= 0.5
borderPieces[#borderPieces + 1] = {
type = ui.TYPE.Image,
props = {
resource = resource,
relativePosition = align,
anchor = align,
relativeSize = horizontal and v2(0, 1) or v2(1, 0),
size = (horizontal and v2(1, -2) or v2(-2, 1)) * constants.borderSize,
tileH = not horizontal,
tileV = horizontal,
},
}
end
for k, align in pairs(cornerParts) do
local resource = resources[k]
borderPieces[#borderPieces + 1] = {
type = ui.TYPE.Image,
props = {
resource = resource,
relativePosition = align,
anchor = align,
size = v2(1, 1) * constants.borderSize,
},
}
end
borderPieces[#borderPieces + 1] = {
external = {
slot = true,
},
props = {
position = v2(1, 1) * (constants.borderSize + constants.padding),
size = v2(-2, -2) * (constants.borderSize + constants.padding),
relativeSize = v2(1, 1),
},
}
local borders = {
content = ui.content(borderPieces)
}
borders.content:add({
external = {
slot = true,
},
props = {
size = v2(-2, -2) * constants.borderSize,
},
})
local horizontalLine = {
content = ui.content {
{
type = ui.TYPE.Image,
props = {
resource = resources.top,
tileH = true,
tileV = false,
size = v2(0, constants.borderSize),
relativeSize = v2(1, 0),
},
},
},
}
local verticalLine = {
content = ui.content {
{
type = ui.TYPE.Image,
props = {
resource = resources.left,
tileH = false,
tileV = true,
size = v2(constants.borderSize, 0),
relativeSize = v2(0, 1),
},
},
},
}
return function(templates)
templates.borders = borders
templates.horizontalLine = horizontalLine
templates.verticalLine = verticalLine
end

@ -0,0 +1,45 @@
local ui = require('openmw.ui')
local util = require('openmw.util')
local whiteTexture = ui.texture{ path = 'white' }
local menuTransparency = ui._getMenuTransparency()
return function(templates)
templates.backgroundTransparent = {
props = {
resource = whiteTexture,
color = util.color.rgb(0, 0, 0),
alpha = menuTransparency,
},
}
templates.backgroundSolid = {
props = {
resource = whiteTexture,
color = util.color.rgb(0, 0, 0),
},
}
templates.box = {
props = {
inheritAlpha = false,
},
content = ui.content {
{
type = ui.TYPE.Image,
template = templates.backgroundTransparent,
props = {
relativeSize = util.vector2(1, 1),
},
},
{
template = templates.borders,
props = {
relativeSize = util.vector2(1, 1),
},
external = {
slot = true,
},
},
},
}
end

@ -0,0 +1,8 @@
local util = require('openmw.util')
return {
textNormalSize = 16,
sandColor = util.color.rgb(202 / 255, 165 / 255, 96 / 255),
borderSize = 4,
padding = 2,
}

@ -0,0 +1,101 @@
local util = require('openmw.util')
local function shallowLayoutCopy(source, target)
for k in pairs(target) do
target[k] = nil
end
for k, v in pairs(source) do
target[k] = v
end
return target
end
---
-- @type Templates
-- @usage
-- local I = require('openmw.interfaces')
-- local ui = require('openmw.ui')
-- local auxUi = require('openmw_aux.ui')
-- ui.create {
-- template = I.MWUI.templates.textNormal,
-- layer = 'Windows',
-- type = ui.TYPE.Text,
-- props = {
-- text = 'Hello, world!',
-- },
-- }
-- -- important to copy here
-- local myText = auxUi.deepLayoutCopy(I.MWUI.templates.textNormal)
-- myText.props.textSize = 20
-- I.MWUI.templates.textNormal = myText
-- ui.updateAll()
local templatesMeta = {
__index = function(self, key)
return self.__templates[key]
end,
__newindex = function(self, key, template)
local target = self.__templates[key]
if target == template then
error("Overriding a template with itself")
else
shallowLayoutCopy(template, target)
end
end,
}
---
-- @module MWUI
-- @usage require('openmw.interfaces').MWUI
local function TemplateOverrides(templates)
return setmetatable({
__templates = util.makeReadOnly(templates),
}, templatesMeta)
end
---
-- @field [parent=#MWUI] #Templates templates
local templates = {}
---
-- Standard rectangular border
-- @field [parent=#Templates] openmw.ui#Layout border
require('scripts.omw.mwui.borders')(templates)
---
-- Border combined with a transparent background
-- @field [parent=#Templates] openmw.ui#Layout box
---
-- A transparent background
-- @field [parent=#Templates] openmw.ui#Layout backgroundTransparent
---
-- A solid, non-transparent background
-- @field [parent=#Templates] openmw.ui#Layout backgroundSolid
require('scripts.omw.mwui.box')(templates)
---
-- Standard "sand" colored text
-- @field [parent=#Templates] openmw.ui#Layout textNormal
require('scripts.omw.mwui.text')(templates)
---
-- Single line text input
-- @field [parent=#Templates] openmw.ui#Layout textEditLine
---
-- Multiline text input
-- @field [parent=#Templates] openmw.ui#Layout textEditBox
require('scripts.omw.mwui.textEdit')(templates)
---
-- Interface version
-- @field [parent=#MWUI] #number version
local interface = {
version = 0,
templates = TemplateOverrides(templates),
}
return {
interfaceName = "MWUI",
interface = interface,
}

@ -0,0 +1,15 @@
local ui = require('openmw.ui')
local constants = require('scripts.omw.mwui.constants')
local textNormal = {
type = ui.TYPE.Text,
props = {
textSize = constants.textNormalSize,
textColor = constants.sandColor,
},
}
return function(templates)
templates.textNormal = textNormal
end

@ -0,0 +1,47 @@
local util = require('openmw.util')
local ui = require('openmw.ui')
local constants = require('scripts.omw.mwui.constants')
return function(templates)
local borderContent = ui.content {
{
template = templates.borders,
props = {
relativeSize = util.vector2(1, 1),
},
content = ui.content {
{
external = {
slot = true,
},
},
}
},
}
templates.textEditLine = {
type = ui.TYPE.TextEdit,
props = {
textSize = constants.textNormalSize,
textColor = constants.sandColor,
textAlignH = ui.ALIGNMENT.Start,
textAlignV = ui.ALIGNMENT.Center,
multiline = false,
},
content = borderContent,
}
templates.textEditBox = {
type = ui.TYPE.TextEdit,
props = {
textSize = constants.textNormalSize,
textColor = constants.sandColor,
textAlignH = ui.ALIGNMENT.Start,
textAlignV = ui.ALIGNMENT.Start,
multiline = true,
wordWrap = true,
},
content = borderContent,
}
end

@ -9,6 +9,13 @@
-- Widget types
-- @field [parent=#ui] #TYPE TYPE
---
-- Alignment values (details depend on the specific property). For horizontal alignment the order is left to right, for vertical alignment the order is top to bottom.
-- @type ALIGNMENT
-- @field Start
-- @field Center
-- @field End
---
-- Alignment values (left to right, top to bottom)
-- @field [parent=#ui] #ALIGNMENT ALIGNMENT
@ -25,14 +32,6 @@
-- @field TextEdit Accepts user text input
-- @field Window Can be moved and resized by the user
---
-- Alignment values (details depend on the specific property).
-- For horizontal alignment the order is left to right, for vertical alignment the order is top to bottom.
-- @type ALIGNMENT
-- @field Start
-- @field Center
-- @field End
---
-- Shows given message at the bottom of the screen.
-- @function [parent=#ui] showMessage
@ -98,6 +97,9 @@
-- @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 the `size` field in `props`, `relativeSize` is ignored.
---
-- Update all existing UI elements. Potentially extremely slow, so only call this when necessary, e. g. after overriding a template.
-- @function [parent=#ui] updateAll
---
-- Layout

Loading…
Cancel
Save