mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 09:35:28 +00:00
Merge branch 'settings_styling' into 'master'
Styling for Settings See merge request OpenMW/openmw!1840
This commit is contained in:
commit
072337c9b8
@ -7,11 +7,6 @@ namespace LuaUi
|
||||
void LuaContainer::updateChildren()
|
||||
{
|
||||
WidgetExtension::updateChildren();
|
||||
for (auto w : children())
|
||||
{
|
||||
w->onCoordChange([this](WidgetExtension* child, MyGUI::IntCoord coord)
|
||||
{ updateSizeToFit(); });
|
||||
}
|
||||
updateSizeToFit();
|
||||
}
|
||||
|
||||
@ -20,16 +15,39 @@ namespace LuaUi
|
||||
return MyGUI::IntSize();
|
||||
}
|
||||
|
||||
MyGUI::IntSize LuaContainer::templateScalingSize()
|
||||
{
|
||||
return mInnerSize;
|
||||
}
|
||||
|
||||
void LuaContainer::updateSizeToFit()
|
||||
{
|
||||
MyGUI::IntSize size;
|
||||
MyGUI::IntSize innerSize = MyGUI::IntSize();
|
||||
for (auto w : children())
|
||||
{
|
||||
MyGUI::IntCoord coord = w->widget()->getCoord();
|
||||
size.width = std::max(size.width, coord.left + coord.width);
|
||||
size.height = std::max(size.height, coord.top + coord.height);
|
||||
MyGUI::IntCoord coord = w->calculateCoord();
|
||||
innerSize.width = std::max(innerSize.width, coord.left + coord.width);
|
||||
innerSize.height = std::max(innerSize.height, coord.top + coord.height);
|
||||
}
|
||||
forceSize(size);
|
||||
updateCoord();
|
||||
MyGUI::IntSize outerSize = innerSize;
|
||||
for (auto w : templateChildren())
|
||||
{
|
||||
MyGUI::IntCoord coord = w->calculateCoord();
|
||||
outerSize.width = std::max(outerSize.width, coord.left + coord.width);
|
||||
outerSize.height = std::max(outerSize.height, coord.top + coord.height);
|
||||
}
|
||||
mInnerSize = innerSize;
|
||||
mOuterSize = outerSize;
|
||||
}
|
||||
|
||||
MyGUI::IntSize LuaContainer::calculateSize()
|
||||
{
|
||||
return mOuterSize;
|
||||
}
|
||||
|
||||
void LuaContainer::updateCoord()
|
||||
{
|
||||
updateSizeToFit();
|
||||
WidgetExtension::updateCoord();
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,18 @@ namespace LuaUi
|
||||
{
|
||||
MYGUI_RTTI_DERIVED(LuaContainer)
|
||||
|
||||
MyGUI::IntSize calculateSize() override;
|
||||
void updateCoord() override;
|
||||
|
||||
protected:
|
||||
void updateChildren() override;
|
||||
MyGUI::IntSize childScalingSize() override;
|
||||
MyGUI::IntSize templateScalingSize() override;
|
||||
|
||||
private:
|
||||
void updateSizeToFit();
|
||||
MyGUI::IntSize mInnerSize;
|
||||
MyGUI::IntSize mOuterSize;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,18 @@ namespace LuaUi
|
||||
|
||||
std::string widgetType(const sol::table& layout)
|
||||
{
|
||||
return layout.get_or(LayoutKeys::type, defaultWidgetType);
|
||||
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;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
void destroyWidget(LuaUi::WidgetExtension* ext)
|
||||
@ -103,18 +114,8 @@ namespace LuaUi
|
||||
|
||||
WidgetExtension* createWidget(const sol::table& layout)
|
||||
{
|
||||
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();
|
||||
std::string type = widgetType(layout);
|
||||
if (widgetTypeMap.find(type) == widgetTypeMap.end())
|
||||
throw std::logic_error(std::string("Invalid widget type ") += type);
|
||||
|
||||
@ -242,7 +243,7 @@ namespace LuaUi
|
||||
if (!mLayer.empty())
|
||||
Log(Debug::Warning) << "Ignoring element's layer " << mLayer << " because it's attached to a widget";
|
||||
mAttachedTo->setChildren({ mRoot });
|
||||
mRoot->updateCoord();
|
||||
mAttachedTo->updateCoord();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ namespace LuaUi
|
||||
MyGUI::IntSize flexSize = calculateSize();
|
||||
int growSize = 0;
|
||||
float growFactor = 0;
|
||||
if (totalGrow > 0 && !mAutoSized)
|
||||
if (totalGrow > 0)
|
||||
{
|
||||
growSize = primary(flexSize) - primary(childrenSize);
|
||||
growFactor = growSize / totalGrow;
|
||||
@ -67,22 +67,32 @@ namespace LuaUi
|
||||
for (auto* w : children())
|
||||
{
|
||||
MyGUI::IntSize size = w->calculateSize();
|
||||
primary(size) += static_cast<int>(growFactor * getGrow(w));
|
||||
float stretch = std::clamp(w->externalValue("stretch", 0.0f), 0.0f, 1.0f);
|
||||
secondary(size) = std::max(secondary(size), static_cast<int>(stretch * secondary(flexSize)));
|
||||
secondary(childPosition) = alignSize(secondary(flexSize), secondary(size), mArrange);
|
||||
w->forcePosition(childPosition);
|
||||
primary(size) += static_cast<int>(growFactor * getGrow(w));
|
||||
w->forceSize(size);
|
||||
w->updateCoord();
|
||||
primary(childPosition) += primary(size);
|
||||
w->updateCoord();
|
||||
}
|
||||
WidgetExtension::updateChildren();
|
||||
}
|
||||
|
||||
MyGUI::IntSize LuaFlex::childScalingSize()
|
||||
{
|
||||
// Call the base method to prevent relativeSize feedback loop
|
||||
MyGUI::IntSize size = WidgetExtension::calculateSize();
|
||||
if (mAutoSized)
|
||||
primary(size) = 0;
|
||||
return size;
|
||||
}
|
||||
|
||||
MyGUI::IntSize LuaFlex::calculateSize()
|
||||
{
|
||||
MyGUI::IntSize size = WidgetExtension::calculateSize();
|
||||
if (mAutoSized) {
|
||||
primary(size) = primary(mChildrenSize);
|
||||
primary(size) = std::max(primary(size), primary(mChildrenSize));
|
||||
secondary(size) = std::max(secondary(size), secondary(mChildrenSize));
|
||||
}
|
||||
return size;
|
||||
|
@ -14,10 +14,8 @@ namespace LuaUi
|
||||
MyGUI::IntSize calculateSize() override;
|
||||
void updateProperties() override;
|
||||
void updateChildren() override;
|
||||
MyGUI::IntSize childScalingSize() override
|
||||
{
|
||||
return MyGUI::IntSize();
|
||||
}
|
||||
MyGUI::IntSize childScalingSize() override;
|
||||
|
||||
void updateCoord() override;
|
||||
|
||||
private:
|
||||
|
@ -31,6 +31,7 @@ namespace LuaUi
|
||||
{
|
||||
changeWidgetSkin("LuaImage");
|
||||
mTileRect = dynamic_cast<LuaTileRect*>(getSubWidgetMain());
|
||||
WidgetExtension::initialize();
|
||||
}
|
||||
|
||||
void LuaImage::updateProperties()
|
||||
|
@ -6,38 +6,57 @@ namespace LuaUi
|
||||
{
|
||||
void LuaTextEdit::initialize()
|
||||
{
|
||||
changeWidgetSkin("LuaTextEdit");
|
||||
|
||||
eventEditTextChange += MyGUI::newDelegate(this, &LuaTextEdit::textChange);
|
||||
|
||||
mEditBox = createWidget<MyGUI::EditBox>("LuaTextEdit", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
|
||||
mEditBox->eventEditTextChange += MyGUI::newDelegate(this, &LuaTextEdit::textChange);
|
||||
registerEvents(mEditBox);
|
||||
WidgetExtension::initialize();
|
||||
}
|
||||
|
||||
void LuaTextEdit::deinitialize()
|
||||
{
|
||||
eventEditTextChange -= MyGUI::newDelegate(this, &LuaTextEdit::textChange);
|
||||
mEditBox->eventEditTextChange -= MyGUI::newDelegate(this, &LuaTextEdit::textChange);
|
||||
clearEvents(mEditBox);
|
||||
WidgetExtension::deinitialize();
|
||||
}
|
||||
|
||||
void LuaTextEdit::updateProperties()
|
||||
{
|
||||
setCaption(propertyValue("text", std::string()));
|
||||
setFontHeight(propertyValue("textSize", 10));
|
||||
setTextColour(propertyValue("textColor", MyGUI::Colour(0, 0, 0, 1)));
|
||||
setEditMultiLine(propertyValue("multiline", false));
|
||||
setEditWordWrap(propertyValue("wordWrap", false));
|
||||
mEditBox->setCaption(propertyValue("text", std::string()));
|
||||
mEditBox->setFontHeight(propertyValue("textSize", 10));
|
||||
mEditBox->setTextColour(propertyValue("textColor", MyGUI::Colour(0, 0, 0, 1)));
|
||||
mEditBox->setEditMultiLine(propertyValue("multiline", false));
|
||||
mEditBox->setEditWordWrap(propertyValue("wordWrap", false));
|
||||
|
||||
Alignment horizontal(propertyValue("textAlignH", Alignment::Start));
|
||||
Alignment vertical(propertyValue("textAlignV", Alignment::Start));
|
||||
setTextAlign(alignmentToMyGui(horizontal, vertical));
|
||||
mEditBox->setTextAlign(alignmentToMyGui(horizontal, vertical));
|
||||
|
||||
setEditStatic(propertyValue("readOnly", false));
|
||||
mEditBox->setEditStatic(propertyValue("readOnly", false));
|
||||
|
||||
WidgetExtension::updateProperties();
|
||||
}
|
||||
|
||||
void LuaTextEdit::textChange(MyGUI::EditBox*)
|
||||
{
|
||||
triggerEvent("textChanged", sol::make_object(lua(), getCaption().asUTF8()));
|
||||
triggerEvent("textChanged", sol::make_object(lua(), mEditBox->getCaption().asUTF8()));
|
||||
}
|
||||
|
||||
void LuaTextEdit::updateCoord()
|
||||
{
|
||||
WidgetExtension::updateCoord();
|
||||
{
|
||||
MyGUI::IntSize slotSize = slot()->calculateSize();
|
||||
MyGUI::IntPoint slotPosition = slot()->widget()->getAbsolutePosition() - widget()->getAbsolutePosition();
|
||||
MyGUI::IntCoord slotCoord(slotPosition, slotSize);
|
||||
mEditBox->setCoord(slotCoord);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaTextEdit::updateChildren()
|
||||
{
|
||||
WidgetExtension::updateChildren();
|
||||
// otherwise it won't be focusable
|
||||
mEditBox->detachFromWidget();
|
||||
mEditBox->attachToWidget(this);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
namespace LuaUi
|
||||
{
|
||||
class LuaTextEdit : public MyGUI::EditBox, public WidgetExtension
|
||||
class LuaTextEdit : public MyGUI::Widget, public WidgetExtension
|
||||
{
|
||||
MYGUI_RTTI_DERIVED(LuaTextEdit)
|
||||
|
||||
@ -15,9 +15,13 @@ namespace LuaUi
|
||||
void initialize() override;
|
||||
void deinitialize() override;
|
||||
void updateProperties() override;
|
||||
void updateCoord() override;
|
||||
void updateChildren() override;
|
||||
|
||||
private:
|
||||
void textChange(MyGUI::EditBox*);
|
||||
|
||||
MyGUI::EditBox* mEditBox;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ namespace LuaUi
|
||||
{ "LuaWindow", "Window" },
|
||||
{ "LuaImage", "Image" },
|
||||
{ "LuaFlex", "Flex" },
|
||||
{ "LuaContainer", "Container" },
|
||||
};
|
||||
return types;
|
||||
}
|
||||
|
@ -35,37 +35,13 @@ namespace LuaUi
|
||||
void WidgetExtension::initialize()
|
||||
{
|
||||
// \todo might be more efficient to only register these if there are Lua callbacks
|
||||
mWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WidgetExtension::keyPress);
|
||||
mWidget->eventKeyButtonReleased += MyGUI::newDelegate(this, &WidgetExtension::keyRelease);
|
||||
mWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &WidgetExtension::mouseClick);
|
||||
mWidget->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WidgetExtension::mouseDoubleClick);
|
||||
mWidget->eventMouseButtonPressed += MyGUI::newDelegate(this, &WidgetExtension::mousePress);
|
||||
mWidget->eventMouseButtonReleased += MyGUI::newDelegate(this, &WidgetExtension::mouseRelease);
|
||||
mWidget->eventMouseMove += MyGUI::newDelegate(this, &WidgetExtension::mouseMove);
|
||||
mWidget->eventMouseDrag += MyGUI::newDelegate(this, &WidgetExtension::mouseDrag);
|
||||
|
||||
mWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &WidgetExtension::focusGain);
|
||||
mWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &WidgetExtension::focusLoss);
|
||||
mWidget->eventKeySetFocus += MyGUI::newDelegate(this, &WidgetExtension::focusGain);
|
||||
mWidget->eventKeyLostFocus += MyGUI::newDelegate(this, &WidgetExtension::focusLoss);
|
||||
registerEvents(mWidget);
|
||||
}
|
||||
|
||||
void WidgetExtension::deinitialize()
|
||||
{
|
||||
clearCallbacks();
|
||||
mWidget->eventKeyButtonPressed.clear();
|
||||
mWidget->eventKeyButtonReleased.clear();
|
||||
mWidget->eventMouseButtonClick.clear();
|
||||
mWidget->eventMouseButtonDoubleClick.clear();
|
||||
mWidget->eventMouseButtonPressed.clear();
|
||||
mWidget->eventMouseButtonReleased.clear();
|
||||
mWidget->eventMouseMove.clear();
|
||||
mWidget->eventMouseDrag.m_event.clear();
|
||||
|
||||
mWidget->eventMouseSetFocus.clear();
|
||||
mWidget->eventMouseLostFocus.clear();
|
||||
mWidget->eventKeySetFocus.clear();
|
||||
mWidget->eventKeyLostFocus.clear();
|
||||
clearEvents(mWidget);
|
||||
|
||||
mOnCoordChange.reset();
|
||||
|
||||
@ -75,6 +51,39 @@ namespace LuaUi
|
||||
w->deinitialize();
|
||||
}
|
||||
|
||||
void WidgetExtension::registerEvents(MyGUI::Widget* w)
|
||||
{
|
||||
w->eventKeyButtonPressed += MyGUI::newDelegate(this, &WidgetExtension::keyPress);
|
||||
w->eventKeyButtonReleased += MyGUI::newDelegate(this, &WidgetExtension::keyRelease);
|
||||
w->eventMouseButtonClick += MyGUI::newDelegate(this, &WidgetExtension::mouseClick);
|
||||
w->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &WidgetExtension::mouseDoubleClick);
|
||||
w->eventMouseButtonPressed += MyGUI::newDelegate(this, &WidgetExtension::mousePress);
|
||||
w->eventMouseButtonReleased += MyGUI::newDelegate(this, &WidgetExtension::mouseRelease);
|
||||
w->eventMouseMove += MyGUI::newDelegate(this, &WidgetExtension::mouseMove);
|
||||
w->eventMouseDrag += MyGUI::newDelegate(this, &WidgetExtension::mouseDrag);
|
||||
|
||||
w->eventMouseSetFocus += MyGUI::newDelegate(this, &WidgetExtension::focusGain);
|
||||
w->eventMouseLostFocus += MyGUI::newDelegate(this, &WidgetExtension::focusLoss);
|
||||
w->eventKeySetFocus += MyGUI::newDelegate(this, &WidgetExtension::focusGain);
|
||||
w->eventKeyLostFocus += MyGUI::newDelegate(this, &WidgetExtension::focusLoss);
|
||||
}
|
||||
void WidgetExtension::clearEvents(MyGUI::Widget* w)
|
||||
{
|
||||
w->eventKeyButtonPressed.clear();
|
||||
w->eventKeyButtonReleased.clear();
|
||||
w->eventMouseButtonClick.clear();
|
||||
w->eventMouseButtonDoubleClick.clear();
|
||||
w->eventMouseButtonPressed.clear();
|
||||
w->eventMouseButtonReleased.clear();
|
||||
w->eventMouseMove.clear();
|
||||
w->eventMouseDrag.m_event.clear();
|
||||
|
||||
w->eventMouseSetFocus.clear();
|
||||
w->eventMouseLostFocus.clear();
|
||||
w->eventKeySetFocus.clear();
|
||||
w->eventKeyLostFocus.clear();
|
||||
}
|
||||
|
||||
void WidgetExtension::reset()
|
||||
{
|
||||
// detach all children from the slot widget, in case it gets destroyed
|
||||
@ -188,25 +197,11 @@ namespace LuaUi
|
||||
|
||||
void WidgetExtension::updateTemplate()
|
||||
{
|
||||
WidgetExtension* oldSlot = mSlot;
|
||||
WidgetExtension* slot = findDeepInTemplates("slot");
|
||||
if (slot == nullptr)
|
||||
mSlot = this;
|
||||
else
|
||||
mSlot = slot->mSlot;
|
||||
if (mSlot != oldSlot)
|
||||
{
|
||||
MyGUI::IntSize slotSize = mSlot->widget()->getSize();
|
||||
MyGUI::IntPoint slotPosition = mSlot->widget()->getAbsolutePosition() - widget()->getAbsolutePosition();
|
||||
MyGUI::IntCoord slotCoord(slotPosition, slotSize);
|
||||
MyGUI::Widget* clientWidget = mWidget->getClientWidget();
|
||||
if (!clientWidget)
|
||||
clientWidget = mWidget;
|
||||
if (clientWidget->getSubWidgetMain())
|
||||
clientWidget->getSubWidgetMain()->setCoord(slotCoord);
|
||||
if (clientWidget->getSubWidgetText())
|
||||
clientWidget->getSubWidgetText()->setCoord(slotCoord);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetExtension::setCallback(const std::string& name, const LuaUtil::Callback& callback)
|
||||
@ -290,10 +285,12 @@ namespace LuaUi
|
||||
|
||||
MyGUI::IntSize WidgetExtension::parentSize()
|
||||
{
|
||||
if (mParent && !mTemplateChild)
|
||||
return mParent->childScalingSize();
|
||||
if (!mParent)
|
||||
return widget()->getParentSize(); // size of the layer
|
||||
if (mTemplateChild)
|
||||
return mParent->templateScalingSize();
|
||||
else
|
||||
return widget()->getParentSize();
|
||||
return mParent->childScalingSize();
|
||||
}
|
||||
|
||||
MyGUI::IntSize WidgetExtension::calculateSize()
|
||||
@ -334,6 +331,11 @@ namespace LuaUi
|
||||
return mSlot->widget()->getSize();
|
||||
}
|
||||
|
||||
MyGUI::IntSize WidgetExtension::templateScalingSize()
|
||||
{
|
||||
return widget()->getSize();
|
||||
}
|
||||
|
||||
void WidgetExtension::triggerEvent(std::string_view name, sol::object argument) const
|
||||
{
|
||||
auto it = mCallbacks.find(name);
|
||||
|
@ -70,16 +70,20 @@ namespace LuaUi
|
||||
|
||||
virtual MyGUI::IntSize calculateSize();
|
||||
virtual MyGUI::IntPoint calculatePosition(const MyGUI::IntSize& size);
|
||||
MyGUI::IntCoord calculateCoord();
|
||||
|
||||
protected:
|
||||
virtual void initialize();
|
||||
void registerEvents(MyGUI::Widget* w);
|
||||
void clearEvents(MyGUI::Widget* w);
|
||||
|
||||
sol::table makeTable() const;
|
||||
sol::object keyEvent(MyGUI::KeyCode) const;
|
||||
sol::object mouseEvent(int left, int top, MyGUI::MouseButton button) const;
|
||||
|
||||
MyGUI::IntSize parentSize();
|
||||
MyGUI::IntCoord calculateCoord();
|
||||
virtual MyGUI::IntSize childScalingSize();
|
||||
virtual MyGUI::IntSize templateScalingSize();
|
||||
|
||||
template<typename T>
|
||||
T propertyValue(std::string_view name, const T& defaultValue)
|
||||
|
@ -111,3 +111,6 @@ Sources can be found in ``resources/vfs/openmw_aux``. In theory mods can overrid
|
||||
* - :ref:`Settings <Interface Settings>`
|
||||
- by player and global scripts
|
||||
- Save, display and track changes of setting values.
|
||||
* - :ref:`MWUI <Interface MWUI>`
|
||||
- by player scripts
|
||||
- Morrowind-style UI templates.
|
||||
|
@ -476,6 +476,9 @@ The order in which the scripts are started is important. So if one mod should ov
|
||||
* - :ref:`Settings <Interface Settings>`
|
||||
- by player and global scripts
|
||||
- Save, display and track changes of setting values.
|
||||
* - :ref:`MWUI <Interface MWUI>`
|
||||
- by player scripts
|
||||
- Morrowind-style UI templates.
|
||||
|
||||
Event system
|
||||
============
|
||||
|
@ -82,6 +82,7 @@ Widget types
|
||||
TextEdit: Accepts text input from the user. <widgets/textedit>
|
||||
Image: Renders a texture. <widgets/image>
|
||||
Flex: Aligns children in a column/row <widgets/flex>
|
||||
Container: Wraps around its children <widgets/container>
|
||||
|
||||
Example
|
||||
-------
|
||||
|
@ -0,0 +1,8 @@
|
||||
Container Widget
|
||||
================
|
||||
|
||||
Wraps around its children. Convenient for creating border-type templates.
|
||||
|
||||
Relative size and position don't work for children.
|
||||
|
||||
For template children, relative size and position depend on the children's combined size.
|
@ -15,7 +15,8 @@ Properties
|
||||
- description
|
||||
* - horizontal
|
||||
- bool (false)
|
||||
- Flex aligns its children in a row if true, otherwise in a column.
|
||||
- | Flex aligns its children in a row (main axis is horizontal) if true,
|
||||
| otherwise in a column (main axis is vertical).
|
||||
* - autoSize
|
||||
- bool (true)
|
||||
- | If true, Flex will automatically resize to fit its contents.
|
||||
@ -41,3 +42,6 @@ External
|
||||
- | Grow factor for the child. If there is unused space in the Flex,
|
||||
| it will be split between widgets according to this value.
|
||||
| Has no effect if `autoSize` is `true`.
|
||||
* - stretch
|
||||
- float (0)
|
||||
- | Stretches the child to a percentage of the Flex's cross axis size.
|
||||
|
@ -21,6 +21,7 @@ set(LUA_BUILTIN_FILES
|
||||
scripts/omw/settings/global.lua
|
||||
scripts/omw/settings/common.lua
|
||||
scripts/omw/settings/render.lua
|
||||
scripts/omw/settings/renderers.lua
|
||||
|
||||
l10n/Calendar/en.yaml
|
||||
|
||||
@ -29,6 +30,7 @@ set(LUA_BUILTIN_FILES
|
||||
scripts/omw/mwui/box.lua
|
||||
scripts/omw/mwui/text.lua
|
||||
scripts/omw/mwui/textEdit.lua
|
||||
scripts/omw/mwui/space.lua
|
||||
scripts/omw/mwui/init.lua
|
||||
)
|
||||
|
||||
|
@ -1,120 +1,203 @@
|
||||
local ui = require('openmw.ui')
|
||||
local util = require('openmw.util')
|
||||
|
||||
local auxUi = require('openmw_aux.ui')
|
||||
|
||||
local constants = require('scripts.omw.mwui.constants')
|
||||
|
||||
local v2 = util.vector2
|
||||
local whiteTexture = ui.texture{ path = 'white' }
|
||||
local menuTransparency = ui._getMenuTransparency()
|
||||
|
||||
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),
|
||||
left = v2(0, 0),
|
||||
right = v2(1, 0),
|
||||
top = v2(0, 0),
|
||||
bottom = v2(0, 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),
|
||||
top_left = v2(0, 0),
|
||||
top_right = v2(1, 0),
|
||||
bottom_left = v2(0, 1),
|
||||
bottom_right = v2(1, 1),
|
||||
}
|
||||
|
||||
local resources = {}
|
||||
local borderSidePattern = 'textures/menu_thin_border_%s.dds'
|
||||
local borderCornerPattern = 'textures/menu_thin_border_%s_corner.dds'
|
||||
|
||||
local borderResources = {}
|
||||
do
|
||||
local boxBorderPattern = 'textures/menu_thin_border_%s.dds'
|
||||
for k, _ in pairs(sideParts) do
|
||||
resources[k] = ui.texture{ path = boxBorderPattern:format(k) }
|
||||
for k in pairs(sideParts) do
|
||||
borderResources[k] = ui.texture{ path = borderSidePattern:format(k) }
|
||||
end
|
||||
for k, _ in pairs(cornerParts) do
|
||||
resources[k] = ui.texture{ path = boxBorderPattern:format(k) }
|
||||
for k in pairs(cornerParts) do
|
||||
borderResources[k] = ui.texture{ path = borderCornerPattern: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] = {
|
||||
for k in pairs(sideParts) do
|
||||
local horizontal = k == 'top' or k == 'bottom'
|
||||
borderPieces[k] = {
|
||||
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,
|
||||
resource = borderResources[k],
|
||||
tileH = horizontal,
|
||||
tileV = not horizontal,
|
||||
},
|
||||
}
|
||||
end
|
||||
for k in pairs(cornerParts) do
|
||||
borderPieces[k] = {
|
||||
type = ui.TYPE.Image,
|
||||
props = {
|
||||
resource = borderResources[k],
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
for k, align in pairs(cornerParts) do
|
||||
local resource = resources[k]
|
||||
borderPieces[#borderPieces + 1] = {
|
||||
|
||||
|
||||
local function borderTemplates(borderSize)
|
||||
local borderV = v2(1, 1) * borderSize
|
||||
local result = {}
|
||||
result.horizontalLine = {
|
||||
type = ui.TYPE.Image,
|
||||
props = {
|
||||
resource = resource,
|
||||
relativePosition = align,
|
||||
anchor = align,
|
||||
size = v2(1, 1) * constants.borderSize,
|
||||
resource = borderResources.top,
|
||||
tileH = true,
|
||||
tileV = false,
|
||||
size = v2(0, borderSize),
|
||||
relativeSize = v2(1, 0),
|
||||
},
|
||||
}
|
||||
|
||||
result.verticalLine = {
|
||||
type = ui.TYPE.Image,
|
||||
props = {
|
||||
resource = borderResources.left,
|
||||
tileH = false,
|
||||
tileV = true,
|
||||
size = v2(borderSize, 0),
|
||||
relativeSize = v2(0, 1),
|
||||
},
|
||||
}
|
||||
|
||||
result.borders = {
|
||||
content = ui.content {},
|
||||
}
|
||||
for k, v in pairs(sideParts) do
|
||||
local horizontal = k == 'top' or k == 'bottom'
|
||||
local direction = horizontal and v2(1, 0) or v2(0, 1)
|
||||
result.borders.content:add {
|
||||
template = borderPieces[k],
|
||||
props = {
|
||||
position = (direction - v) * borderSize,
|
||||
relativePosition = v,
|
||||
size = (v2(1, 1) - direction * 3) * borderSize,
|
||||
relativeSize = direction,
|
||||
}
|
||||
}
|
||||
end
|
||||
for k, v in pairs(cornerParts) do
|
||||
result.borders.content:add {
|
||||
template = borderPieces[k],
|
||||
props = {
|
||||
position = -v * borderSize,
|
||||
relativePosition = v,
|
||||
size = borderV,
|
||||
},
|
||||
}
|
||||
end
|
||||
result.borders.content:add {
|
||||
external = { slot = true },
|
||||
props = {
|
||||
position = borderV,
|
||||
size = borderV * -2,
|
||||
relativeSize = v2(1, 1),
|
||||
}
|
||||
}
|
||||
|
||||
result.box = {
|
||||
type = ui.TYPE.Container,
|
||||
content = ui.content{},
|
||||
}
|
||||
for k, v in pairs(sideParts) do
|
||||
local horizontal = k == 'top' or k == 'bottom'
|
||||
local direction = horizontal and v2(1, 0) or v2(0, 1)
|
||||
result.box.content:add {
|
||||
template = borderPieces[k],
|
||||
props = {
|
||||
position = (direction + v) * borderSize,
|
||||
relativePosition = v,
|
||||
size = (v2(1, 1) - direction) * borderSize,
|
||||
relativeSize = direction,
|
||||
}
|
||||
}
|
||||
end
|
||||
for k, v in pairs(cornerParts) do
|
||||
result.box.content:add {
|
||||
template = borderPieces[k],
|
||||
props = {
|
||||
position = v * borderSize,
|
||||
relativePosition = v,
|
||||
size = borderV,
|
||||
},
|
||||
}
|
||||
end
|
||||
result.box.content:add {
|
||||
external = { slot = true },
|
||||
props = {
|
||||
position = borderV,
|
||||
relativeSize = v2(1, 1),
|
||||
}
|
||||
}
|
||||
|
||||
local backgroundTransparent = {
|
||||
type = ui.TYPE.Image,
|
||||
props = {
|
||||
resource = whiteTexture,
|
||||
color = util.color.rgb(0, 0, 0),
|
||||
alpha = menuTransparency,
|
||||
},
|
||||
}
|
||||
local backgroundSolid = {
|
||||
type = ui.TYPE.Image,
|
||||
props = {
|
||||
resource = whiteTexture,
|
||||
color = util.color.rgb(0, 0, 0),
|
||||
},
|
||||
}
|
||||
|
||||
result.boxTransparent = auxUi.deepLayoutCopy(result.box)
|
||||
result.boxTransparent.content:insert(1, {
|
||||
template = backgroundTransparent,
|
||||
props = {
|
||||
relativeSize = v2(1, 1),
|
||||
size = borderV * 2,
|
||||
},
|
||||
})
|
||||
|
||||
result.boxSolid = auxUi.deepLayoutCopy(result.box)
|
||||
result.boxSolid.content:insert(1, {
|
||||
template = backgroundSolid,
|
||||
props = {
|
||||
relativeSize = v2(1, 1),
|
||||
size = borderV * 2,
|
||||
},
|
||||
})
|
||||
|
||||
return result
|
||||
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),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
local thinBorders = borderTemplates(constants.border)
|
||||
local thickBorders = borderTemplates(constants.thickBorder)
|
||||
|
||||
return function(templates)
|
||||
templates.borders = borders
|
||||
templates.horizontalLine = horizontalLine
|
||||
templates.verticalLine = verticalLine
|
||||
for k, t in pairs(thinBorders) do
|
||||
templates[k] = t
|
||||
end
|
||||
for k, t in pairs(thickBorders) do
|
||||
templates[k .. 'Thick'] = t
|
||||
end
|
||||
end
|
@ -2,7 +2,10 @@ local util = require('openmw.util')
|
||||
|
||||
return {
|
||||
textNormalSize = 16,
|
||||
sandColor = util.color.rgb(202 / 255, 165 / 255, 96 / 255),
|
||||
borderSize = 4,
|
||||
textHeaderSize = 16,
|
||||
headerColor = util.color.rgb(223 / 255, 201 / 255, 159 / 255),
|
||||
normalColor = util.color.rgb(202 / 255, 165 / 255, 96 / 255),
|
||||
border = 2,
|
||||
thickBorder = 4,
|
||||
padding = 2,
|
||||
}
|
@ -58,20 +58,26 @@ end
|
||||
local templates = {}
|
||||
|
||||
---
|
||||
-- Standard rectangular border
|
||||
-- @field [parent=#Templates] openmw.ui#Layout border
|
||||
require('scripts.omw.mwui.borders')(templates)
|
||||
-- Container that adds padding around its content.
|
||||
-- @field [parent=#MWUI] #table padding
|
||||
---
|
||||
-- Standard spacing interval
|
||||
-- @field [parent=#MWUI] #number interval
|
||||
require('scripts.omw.mwui.space')(templates)
|
||||
|
||||
---
|
||||
-- Border combined with a transparent background
|
||||
-- Standard rectangular border
|
||||
-- @field [parent=#Templates] openmw.ui#Layout border
|
||||
---
|
||||
-- Container wrapping the content with borders
|
||||
-- @field [parent=#Templates] openmw.ui#Layout box
|
||||
---
|
||||
-- A transparent background
|
||||
-- @field [parent=#Templates] openmw.ui#Layout backgroundTransparent
|
||||
-- Same as box, but with a semi-transparent background
|
||||
-- @field [parent=#Templates] openmw.ui#Layout boxTransparent
|
||||
---
|
||||
-- A solid, non-transparent background
|
||||
-- @field [parent=#Templates] openmw.ui#Layout backgroundSolid
|
||||
require('scripts.omw.mwui.box')(templates)
|
||||
-- Same as box, but with a solid background
|
||||
-- @field [parent=#Templates] openmw.ui#Layout boxSolid
|
||||
require('scripts.omw.mwui.borders')(templates)
|
||||
|
||||
---
|
||||
-- Standard "sand" colored text
|
||||
|
39
files/builtin_scripts/scripts/omw/mwui/space.lua
Normal file
39
files/builtin_scripts/scripts/omw/mwui/space.lua
Normal file
@ -0,0 +1,39 @@
|
||||
local ui = require('openmw.ui')
|
||||
local util = require('openmw.util')
|
||||
|
||||
local constants = require('scripts.omw.mwui.constants')
|
||||
|
||||
local borderV = util.vector2(1, 1) * constants.border
|
||||
|
||||
return function(templates)
|
||||
templates.padding = {
|
||||
type = ui.TYPE.Container,
|
||||
content = ui.content {
|
||||
{
|
||||
props = {
|
||||
size = borderV,
|
||||
},
|
||||
},
|
||||
{
|
||||
external = { slot = true },
|
||||
props = {
|
||||
position = borderV,
|
||||
relativeSize = util.vector2(1, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
props = {
|
||||
position = borderV,
|
||||
relativePosition = util.vector2(1, 1),
|
||||
size = borderV,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
templates.interval = {
|
||||
type = ui.TYPE.Widget,
|
||||
props = {
|
||||
size = borderV,
|
||||
},
|
||||
}
|
||||
end
|
@ -6,10 +6,19 @@ local textNormal = {
|
||||
type = ui.TYPE.Text,
|
||||
props = {
|
||||
textSize = constants.textNormalSize,
|
||||
textColor = constants.sandColor,
|
||||
textColor = constants.normalColor,
|
||||
},
|
||||
}
|
||||
|
||||
local textHeader = {
|
||||
type = ui.TYPE.Text,
|
||||
props = {
|
||||
textSize = constants.textHeaderSize,
|
||||
textColor = constants.headerColor,
|
||||
},
|
||||
}
|
||||
|
||||
return function(templates)
|
||||
templates.textNormal = textNormal
|
||||
templates.textHeader = textHeader
|
||||
end
|
@ -3,6 +3,8 @@ local ui = require('openmw.ui')
|
||||
|
||||
local constants = require('scripts.omw.mwui.constants')
|
||||
|
||||
local borderOffset = util.vector2(1, 1) * constants.border
|
||||
|
||||
return function(templates)
|
||||
local borderContent = ui.content {
|
||||
{
|
||||
@ -12,10 +14,14 @@ return function(templates)
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
props = {
|
||||
position = borderOffset,
|
||||
relativeSize = util.vector2(1, 1),
|
||||
},
|
||||
external = {
|
||||
slot = true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -23,10 +29,9 @@ return function(templates)
|
||||
templates.textEditLine = {
|
||||
type = ui.TYPE.TextEdit,
|
||||
props = {
|
||||
size = util.vector2(150, constants.textNormalSize) + borderOffset * 4,
|
||||
textSize = constants.textNormalSize,
|
||||
textColor = constants.sandColor,
|
||||
textAlignH = ui.ALIGNMENT.Start,
|
||||
textAlignV = ui.ALIGNMENT.Center,
|
||||
textColor = constants.normalColor,
|
||||
multiline = false,
|
||||
},
|
||||
content = borderContent,
|
||||
@ -35,10 +40,9 @@ return function(templates)
|
||||
templates.textEditBox = {
|
||||
type = ui.TYPE.TextEdit,
|
||||
props = {
|
||||
size = util.vector2(150, 5 * constants.textNormalSize) + borderOffset * 4,
|
||||
textSize = constants.textNormalSize,
|
||||
textColor = constants.sandColor,
|
||||
textAlignH = ui.ALIGNMENT.Start,
|
||||
textAlignV = ui.ALIGNMENT.Start,
|
||||
textColor = constants.normalColor,
|
||||
multiline = true,
|
||||
wordWrap = true,
|
||||
},
|
||||
|
@ -21,8 +21,8 @@ local function validateSettingOptions(options)
|
||||
if type(options.name) ~= 'string' then
|
||||
error('Setting must have a name localization key')
|
||||
end
|
||||
if type(options.description) ~= 'string' then
|
||||
error('Setting must have a descripiton localization key')
|
||||
if options.description ~= nil and type(options.description) ~= 'string' then
|
||||
error('Setting description key must be a string')
|
||||
end
|
||||
end
|
||||
|
||||
@ -49,8 +49,8 @@ local function validateGroupOptions(options)
|
||||
if type(options.name) ~= 'string' then
|
||||
error('Group must have a name localization key')
|
||||
end
|
||||
if type(options.description) ~= 'string' then
|
||||
error('Group must have a description localization key')
|
||||
if options.description ~= nil and type(options.description) ~= 'string' then
|
||||
error('Group description key must be a string')
|
||||
end
|
||||
if type(options.settings) ~= 'table' then
|
||||
error('Group must have a table of settings')
|
||||
@ -89,8 +89,9 @@ local function registerGroup(options)
|
||||
settings = {},
|
||||
}
|
||||
local valueSection = contextSection(options.key)
|
||||
for _, opt in ipairs(options.settings) do
|
||||
for i, opt in ipairs(options.settings) do
|
||||
local setting = registerSetting(opt)
|
||||
setting.order = i
|
||||
if group.settings[setting.key] then
|
||||
error(('Duplicate setting key %s'):format(options.key))
|
||||
end
|
||||
@ -123,7 +124,7 @@ return {
|
||||
local section = contextSection(groupKey)
|
||||
saved[groupKey] = {}
|
||||
for key, value in pairs(section:asTable()) do
|
||||
if not group.settings[key].permanentStorage then
|
||||
if group.settings[key] and not group.settings[key].permanentStorage then
|
||||
saved[groupKey][key] = value
|
||||
end
|
||||
end
|
||||
|
@ -1,39 +1,21 @@
|
||||
local ui = require('openmw.ui')
|
||||
local async = require('openmw.async')
|
||||
local util = require('openmw.util')
|
||||
|
||||
local common = require('scripts.omw.settings.common')
|
||||
local render = require('scripts.omw.settings.render')
|
||||
|
||||
render.registerRenderer('text', function(value, set, arg)
|
||||
return {
|
||||
type = ui.TYPE.TextEdit,
|
||||
props = {
|
||||
size = util.vector2(arg and arg.size or 150, 30),
|
||||
text = value,
|
||||
textColor = util.color.rgb(1, 1, 1),
|
||||
textSize = 15,
|
||||
textAlignV = ui.ALIGNMENT.End,
|
||||
},
|
||||
events = {
|
||||
textChanged = async:callback(function(s) set(s) end),
|
||||
},
|
||||
}
|
||||
end)
|
||||
require('scripts.omw.settings.renderers')(render.registerRenderer)
|
||||
|
||||
---
|
||||
-- @type PageOptions
|
||||
-- @field #string key A unique key
|
||||
-- @field #string l10n A localization context (an argument of core.l10n)
|
||||
-- @field #string name A key from the localization context
|
||||
-- @field #string description A key from the localization context
|
||||
-- @field #string description A key from the localization context (optional, can be `nil`)
|
||||
|
||||
---
|
||||
-- @type GroupOptions
|
||||
-- @field #string key A unique key, starts with "Settings" by convention
|
||||
-- @field #string l10n A localization context (an argument of core.l10n)
|
||||
-- @field #string name A key from the localization context
|
||||
-- @field #string description A key from the localization context
|
||||
-- @field #string description A key from the localization context (optional, can be `nil`)
|
||||
-- @field #string page Key of a page which will contain this group
|
||||
-- @field #number order Groups within the same page are sorted by this number, or their key for equal values.
|
||||
-- Defaults to 0.
|
||||
@ -43,7 +25,7 @@ end)
|
||||
-- @type SettingOptions
|
||||
-- @field #string key A unique key
|
||||
-- @field #string name A key from the localization context
|
||||
-- @field #string description A key from the localization context
|
||||
-- @field #string description A key from the localization context (optional, can be `nil`)
|
||||
-- @field default A default value
|
||||
-- @field #string renderer A renderer key
|
||||
-- @field argument An argument for the renderer
|
||||
@ -57,26 +39,33 @@ return {
|
||||
-- -- In a player script
|
||||
-- local storage = require('openmw.storage')
|
||||
-- local I = require('openmw.interfaces')
|
||||
-- I.Settings.registerGroup({
|
||||
-- I.Settings.registerPage {
|
||||
-- key = 'MyModPage',
|
||||
-- l10n = 'MyMod',
|
||||
-- name = 'My Mod Name',
|
||||
-- description = 'My Mod Description',
|
||||
-- }
|
||||
-- I.Settings.registerGroup {
|
||||
-- key = 'SettingsPlayerMyMod',
|
||||
-- page = 'MyPage',
|
||||
-- l10n = 'mymod',
|
||||
-- name = 'modName',
|
||||
-- description = 'modDescription',
|
||||
-- page = 'MyModPage',
|
||||
-- l10n = 'MyMod',
|
||||
-- name = 'My Group Name',
|
||||
-- description = 'My Group Description',
|
||||
-- settings = {
|
||||
-- {
|
||||
-- key = 'Greeting',
|
||||
-- renderer = 'text',
|
||||
-- name = 'greetingName',
|
||||
-- description = 'greetingDescription',
|
||||
-- renderer = 'textLine',
|
||||
-- name = 'Greeting',
|
||||
-- description = 'Text to display when the game starts',
|
||||
-- default = 'Hello, world!',
|
||||
-- argument = {
|
||||
-- size = 200,
|
||||
-- },
|
||||
-- permanentStorage = false,
|
||||
-- },
|
||||
-- },
|
||||
-- })
|
||||
-- }
|
||||
-- local playerSettings = storage.playerSection('SettingsPlayerMyMod')
|
||||
-- ...
|
||||
-- ui.showMessage(playerSettings:get('Greeting'))
|
||||
-- -- ...
|
||||
-- -- access a setting page registered by a global script
|
||||
-- local globalSettings = storage.globalSection('SettingsGlobalMyMod')
|
||||
interface = {
|
||||
@ -132,22 +121,19 @@ return {
|
||||
-- settings = {
|
||||
-- {
|
||||
-- key = 'Greeting',
|
||||
-- saveOnly = true,
|
||||
-- permanentStorage = true,
|
||||
-- default = 'Hi',
|
||||
-- renderer = 'text',
|
||||
-- argument = {
|
||||
-- size = 200,
|
||||
-- },
|
||||
-- renderer = 'textLine',
|
||||
-- name = 'Text Input',
|
||||
-- description = 'Short text input',
|
||||
-- },
|
||||
-- {
|
||||
-- key = 'Key',
|
||||
-- saveOnly = false,
|
||||
-- default = input.KEY.LeftAlt,
|
||||
-- renderer = 'keybind',
|
||||
-- name = 'Key',
|
||||
-- description = 'Bind Key',
|
||||
-- key = 'Flag',
|
||||
-- permanentStorage = false,
|
||||
-- default = false,
|
||||
-- renderer = 'yeNo',
|
||||
-- name = 'Flag',
|
||||
-- description = 'Flag toggle',
|
||||
-- },
|
||||
-- }
|
||||
-- }
|
||||
|
@ -3,6 +3,7 @@ local util = require('openmw.util')
|
||||
local async = require('openmw.async')
|
||||
local core = require('openmw.core')
|
||||
local storage = require('openmw.storage')
|
||||
local I = require('openmw.interfaces')
|
||||
|
||||
local common = require('scripts.omw.settings.common')
|
||||
|
||||
@ -15,34 +16,68 @@ local pages = {}
|
||||
local groups = {}
|
||||
local pageOptions = {}
|
||||
|
||||
local padding = function(size)
|
||||
local interval = { template = I.MWUI.templates.interval }
|
||||
local growingIntreval = {
|
||||
template = I.MWUI.templates.interval,
|
||||
external = {
|
||||
grow = 1,
|
||||
},
|
||||
}
|
||||
local spacer = {
|
||||
props = {
|
||||
size = util.vector2(0, 10),
|
||||
},
|
||||
}
|
||||
local bigSpacer = {
|
||||
props = {
|
||||
size = util.vector2(0, 50),
|
||||
},
|
||||
}
|
||||
local stretchingLine = {
|
||||
template = I.MWUI.templates.horizontalLine,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
}
|
||||
local spacedLines = function(count)
|
||||
local content = {}
|
||||
table.insert(content, spacer)
|
||||
table.insert(content, stretchingLine)
|
||||
for i = 2, count do
|
||||
table.insert(content, interval)
|
||||
table.insert(content, stretchingLine)
|
||||
end
|
||||
table.insert(content, spacer)
|
||||
return {
|
||||
props = {
|
||||
size = util.vector2(size, size),
|
||||
}
|
||||
type = ui.TYPE.Flex,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content(content),
|
||||
}
|
||||
end
|
||||
local smallPadding = padding(10)
|
||||
local bigPadding = padding(25)
|
||||
|
||||
local pageHeader = {
|
||||
props = {
|
||||
textColor = util.color.rgb(1, 1, 1),
|
||||
textSize = 30,
|
||||
},
|
||||
}
|
||||
local groupHeader = {
|
||||
props = {
|
||||
textColor = util.color.rgb(1, 1, 1),
|
||||
textSize = 25,
|
||||
},
|
||||
}
|
||||
local normal = {
|
||||
props = {
|
||||
textColor = util.color.rgb(1, 1, 1),
|
||||
textSize = 20,
|
||||
},
|
||||
}
|
||||
local function interlaceSeparator(layouts, separator)
|
||||
local result = {}
|
||||
result[1] = layouts[1]
|
||||
for i = 2, #layouts do
|
||||
table.insert(result, separator)
|
||||
table.insert(result, layouts[i])
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function setSettingValue(global, groupKey, settingKey, value)
|
||||
if global then
|
||||
core.sendGlobalEvent(common.setGlobalEvent, {
|
||||
groupKey = groupKey,
|
||||
settingKey = settingKey,
|
||||
value = value,
|
||||
})
|
||||
else
|
||||
storage.playerSection(groupKey):set(settingKey, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function renderSetting(group, setting, value, global)
|
||||
local renderFunction = renderers[setting.renderer]
|
||||
@ -50,55 +85,47 @@ local function renderSetting(group, setting, value, global)
|
||||
error(('Setting %s of %s has unknown renderer %s'):format(setting.key, group.key, setting.renderer))
|
||||
end
|
||||
local set = function(value)
|
||||
if global then
|
||||
core.sendGlobalEvent(common.setGlobalEvent, {
|
||||
groupKey = group.key,
|
||||
settingKey = setting.key,
|
||||
value = value,
|
||||
})
|
||||
else
|
||||
storage.playerSection(group.key):set(setting.key, value)
|
||||
end
|
||||
setSettingValue(global, group.key, setting.key, value)
|
||||
end
|
||||
local l10n = core.l10n(group.l10n)
|
||||
return {
|
||||
name = setting.key,
|
||||
local titleLayout = {
|
||||
type = ui.TYPE.Flex,
|
||||
content = ui.content {
|
||||
{
|
||||
type = ui.TYPE.Flex,
|
||||
template = I.MWUI.templates.textNormal,
|
||||
props = {
|
||||
horizontal = true,
|
||||
align = ui.ALIGNMENT.Start,
|
||||
arrange = ui.ALIGNMENT.End,
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
type = ui.TYPE.Text,
|
||||
template = normal,
|
||||
props = {
|
||||
text = l10n(setting.name),
|
||||
},
|
||||
},
|
||||
smallPadding,
|
||||
renderFunction(value, set, setting.argument),
|
||||
smallPadding,
|
||||
{
|
||||
type = ui.TYPE.Text,
|
||||
template = normal,
|
||||
props = {
|
||||
text = 'Reset',
|
||||
},
|
||||
events = {
|
||||
mouseClick = async:callback(function()
|
||||
set(setting.default)
|
||||
end),
|
||||
},
|
||||
},
|
||||
text = l10n(setting.name),
|
||||
textSize = 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if setting.description then
|
||||
titleLayout.content:add(interval)
|
||||
titleLayout.content:add {
|
||||
template = I.MWUI.templates.textNormal,
|
||||
props = {
|
||||
text = l10n(setting.description),
|
||||
textSize = 16,
|
||||
},
|
||||
}
|
||||
end
|
||||
return {
|
||||
name = setting.key,
|
||||
type = ui.TYPE.Flex,
|
||||
props = {
|
||||
horizontal = true,
|
||||
arrange = ui.ALIGNMENT.Center,
|
||||
},
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content {
|
||||
titleLayout,
|
||||
growingIntreval,
|
||||
renderFunction(value, set, setting.argument),
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local groupLayoutName = function(key, global)
|
||||
@ -107,52 +134,101 @@ end
|
||||
|
||||
local function renderGroup(group, global)
|
||||
local l10n = core.l10n(group.l10n)
|
||||
local layout = {
|
||||
|
||||
local valueSection = common.getSection(global, group.key)
|
||||
local settingLayouts = {}
|
||||
local sortedSettings = {}
|
||||
for _, setting in pairs(group.settings) do
|
||||
sortedSettings[setting.order] = setting
|
||||
end
|
||||
for _, setting in ipairs(sortedSettings) do
|
||||
table.insert(settingLayouts, renderSetting(group, setting, valueSection:get(setting.key), global))
|
||||
end
|
||||
local settingsContent = ui.content(interlaceSeparator(settingLayouts, spacedLines(1)))
|
||||
|
||||
local resetButtonLayout = {
|
||||
template = I.MWUI.templates.box,
|
||||
content = ui.content {
|
||||
{
|
||||
template = I.MWUI.templates.padding,
|
||||
content = ui.content {
|
||||
{
|
||||
template = I.MWUI.templates.textNormal,
|
||||
props = {
|
||||
text = 'Reset',
|
||||
},
|
||||
events = {
|
||||
mouseClick = async:callback(function()
|
||||
for _, setting in pairs(group.settings) do
|
||||
setSettingValue(global, group.key, setting.key, setting.default)
|
||||
end
|
||||
end),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local titleLayout = {
|
||||
type = ui.TYPE.Flex,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
template = I.MWUI.templates.textHeader,
|
||||
props = {
|
||||
text = l10n(group.name),
|
||||
textSize = 20,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
if group.description then
|
||||
titleLayout.content:add(interval)
|
||||
titleLayout.content:add {
|
||||
template = I.MWUI.templates.textHeader,
|
||||
props = {
|
||||
text = l10n(group.description),
|
||||
textSize = 18,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
name = groupLayoutName(group.key, global),
|
||||
type = ui.TYPE.Flex,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
type = ui.TYPE.Flex,
|
||||
props = {
|
||||
horizontal = true,
|
||||
align = ui.ALIGNMENT.Start,
|
||||
arrange = ui.ALIGNMENT.End,
|
||||
arrange = ui.ALIGNMENT.Center,
|
||||
},
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
name = 'name',
|
||||
type = ui.TYPE.Text,
|
||||
template = groupHeader,
|
||||
props = {
|
||||
text = l10n(group.name),
|
||||
},
|
||||
},
|
||||
smallPadding,
|
||||
{
|
||||
name = 'description',
|
||||
type = ui.TYPE.Text,
|
||||
template = normal,
|
||||
props = {
|
||||
text = l10n(group.description),
|
||||
},
|
||||
},
|
||||
titleLayout,
|
||||
growingIntreval,
|
||||
resetButtonLayout,
|
||||
},
|
||||
},
|
||||
smallPadding,
|
||||
spacedLines(2),
|
||||
{
|
||||
name = 'settings',
|
||||
type = ui.TYPE.Flex,
|
||||
content = ui.content{},
|
||||
content = settingsContent,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
},
|
||||
bigPadding,
|
||||
},
|
||||
}
|
||||
local settingsContent = layout.content.settings.content
|
||||
local valueSection = common.getSection(global, group.key)
|
||||
for _, setting in pairs(group.settings) do
|
||||
settingsContent:add(renderSetting(group, setting, valueSection:get(setting.key), global))
|
||||
end
|
||||
return layout
|
||||
end
|
||||
|
||||
local function pageGroupComparator(a, b)
|
||||
@ -165,16 +241,22 @@ local function generateSearchHints(page)
|
||||
local hints = {}
|
||||
local l10n = core.l10n(page.l10n)
|
||||
table.insert(hints, l10n(page.name))
|
||||
table.insert(hints, l10n(page.description))
|
||||
if page.description then
|
||||
table.insert(hints, l10n(page.description))
|
||||
end
|
||||
local pageGroups = groups[page.key]
|
||||
for _, pageGroup in pairs(pageGroups) do
|
||||
local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key)
|
||||
local l10n = core.l10n(group.l10n)
|
||||
table.insert(hints, l10n(group.name))
|
||||
table.insert(hints, l10n(group.description))
|
||||
if group.description then
|
||||
table.insert(hints, l10n(group.description))
|
||||
end
|
||||
for _, setting in pairs(group.settings) do
|
||||
table.insert(hints, l10n(setting.name))
|
||||
table.insert(hints, l10n(setting.description))
|
||||
if setting.description then
|
||||
table.insert(hints, l10n(setting.description))
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.concat(hints, ' ')
|
||||
@ -182,55 +264,60 @@ end
|
||||
|
||||
local function renderPage(page)
|
||||
local l10n = core.l10n(page.l10n)
|
||||
local sortedGroups = {}
|
||||
for i, v in ipairs(groups[page.key]) do sortedGroups[i] = v end
|
||||
table.sort(sortedGroups, pageGroupComparator)
|
||||
local groupLayouts = {}
|
||||
for _, pageGroup in ipairs(sortedGroups) do
|
||||
local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key)
|
||||
table.insert(groupLayouts, renderGroup(group, pageGroup.global))
|
||||
end
|
||||
local groupsLayout = {
|
||||
name = 'groups',
|
||||
type = ui.TYPE.Flex,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content(interlaceSeparator(groupLayouts, bigSpacer)),
|
||||
}
|
||||
local titleLayout = {
|
||||
type = ui.TYPE.Flex,
|
||||
external = {
|
||||
stretch = 1,
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
template = I.MWUI.templates.textHeader,
|
||||
props = {
|
||||
text = l10n(page.name),
|
||||
textSize = 22,
|
||||
},
|
||||
},
|
||||
spacedLines(3),
|
||||
},
|
||||
}
|
||||
if page.description then
|
||||
titleLayout.content:add {
|
||||
template = I.MWUI.templates.textNormal,
|
||||
props = {
|
||||
text = l10n(page.description),
|
||||
textSize = 20,
|
||||
},
|
||||
}
|
||||
end
|
||||
local layout = {
|
||||
name = page.key,
|
||||
type = ui.TYPE.Flex,
|
||||
props = {
|
||||
position = util.vector2(10, 10),
|
||||
},
|
||||
content = ui.content {
|
||||
smallPadding,
|
||||
{
|
||||
type = ui.TYPE.Flex,
|
||||
props = {
|
||||
horizontal = true,
|
||||
align = ui.ALIGNMENT.Start,
|
||||
arrange = ui.ALIGNMENT.End,
|
||||
},
|
||||
content = ui.content {
|
||||
{
|
||||
name = 'name',
|
||||
type = ui.TYPE.Text,
|
||||
template = pageHeader,
|
||||
props = {
|
||||
text = l10n(page.name),
|
||||
},
|
||||
},
|
||||
smallPadding,
|
||||
{
|
||||
name = 'description',
|
||||
type = ui.TYPE.Text,
|
||||
template = normal,
|
||||
props = {
|
||||
text = l10n(page.description),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bigPadding,
|
||||
{
|
||||
name = 'groups',
|
||||
type = ui.TYPE.Flex,
|
||||
content = ui.content {},
|
||||
},
|
||||
titleLayout,
|
||||
bigSpacer,
|
||||
groupsLayout,
|
||||
bigSpacer,
|
||||
},
|
||||
}
|
||||
local groupsContent = layout.content.groups.content
|
||||
local pageGroups = groups[page.key]
|
||||
local sortedGroups = {}
|
||||
for i, v in ipairs(pageGroups) do sortedGroups[i] = v end
|
||||
table.sort(sortedGroups, pageGroupComparator)
|
||||
for _, pageGroup in ipairs(sortedGroups) do
|
||||
local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key)
|
||||
groupsContent:add(renderGroup(group, pageGroup.global))
|
||||
end
|
||||
return {
|
||||
name = l10n(page.name),
|
||||
element = ui.create(layout),
|
||||
@ -241,13 +328,15 @@ end
|
||||
local function onSettingChanged(global)
|
||||
return async:callback(function(groupKey, settingKey)
|
||||
local group = common.getSection(global, common.groupSectionKey):get(groupKey)
|
||||
if not pageOptions[group.page] then return end
|
||||
if not group or not pageOptions[group.page] then return end
|
||||
|
||||
local value = common.getSection(global, group.key):get(settingKey)
|
||||
|
||||
local element = pageOptions[group.page].element
|
||||
local groupLayout = element.layout.content.groups.content[groupLayoutName(group.key, global)]
|
||||
local settingsLayout = groupLayout.content.settings
|
||||
local value = common.getSection(global, group.key):get(settingKey)
|
||||
settingsLayout.content[settingKey] = renderSetting(group, group.settings[settingKey], value, global)
|
||||
local groupsLayout = element.layout.content.groups
|
||||
local groupLayout = groupsLayout.content[groupLayoutName(group.key, global)]
|
||||
local settingsContent = groupLayout.content.settings.content
|
||||
settingsContent[settingKey] = renderSetting(group, group.settings[settingKey], value, global)
|
||||
element:update()
|
||||
end)
|
||||
end
|
||||
@ -293,8 +382,8 @@ local function registerPage(options)
|
||||
if type(options.name) ~= 'string' then
|
||||
error('Page must have a name')
|
||||
end
|
||||
if type(options.description) ~= 'string' then
|
||||
error('Page must have a description')
|
||||
if options.description ~= nil and type(options.description) ~= 'string' then
|
||||
error('Page description key must be a string')
|
||||
end
|
||||
local page = {
|
||||
key = options.key,
|
||||
|
39
files/builtin_scripts/scripts/omw/settings/renderers.lua
Normal file
39
files/builtin_scripts/scripts/omw/settings/renderers.lua
Normal file
@ -0,0 +1,39 @@
|
||||
local ui = require('openmw.ui')
|
||||
local async = require('openmw.async')
|
||||
local I = require('openmw.interfaces')
|
||||
|
||||
return function(registerRenderer)
|
||||
registerRenderer('textLine', function(value, set)
|
||||
return {
|
||||
template = I.MWUI.templates.textEditLine,
|
||||
props = {
|
||||
text = tostring(value),
|
||||
},
|
||||
events = {
|
||||
textChanged = async:callback(function(s) set(s) end),
|
||||
},
|
||||
}
|
||||
end)
|
||||
|
||||
registerRenderer('yesNo', function(value, set)
|
||||
return {
|
||||
template = I.MWUI.templates.box,
|
||||
content = ui.content {
|
||||
{
|
||||
template = I.MWUI.templates.padding,
|
||||
content = ui.content {
|
||||
{
|
||||
template = I.MWUI.templates.textNormal,
|
||||
props = {
|
||||
text = value and 'Yes' or 'No',
|
||||
},
|
||||
events = {
|
||||
mouseClick = async:callback(function() set(not value) end),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
end)
|
||||
end
|
@ -12,7 +12,7 @@
|
||||
</Resource>
|
||||
|
||||
<Resource type="ResourceSkin" name="LuaTextEditClient" size="16 16">
|
||||
<BasisSkin type="EditText" offset="0 0 16 16" align="Stretch" name="Client" />
|
||||
<BasisSkin type="EditText" offset="0 0 16 16" align="Stretch" />
|
||||
</Resource>
|
||||
|
||||
<Resource type="ResourceSkin" name="LuaTextEdit" size="16 16">
|
||||
|
Loading…
x
Reference in New Issue
Block a user