diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index efc80081e3..96e64459db 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -256,7 +256,7 @@ add_component_dir (fallback add_component_dir (lua_ui registerscriptsettings scriptsettings properties widget element util layers content alignment resources - adapter text textedit window image container + adapter text textedit window image container flex ) diff --git a/components/lua_ui/alignment.cpp b/components/lua_ui/alignment.cpp index dfd09b752d..358c5ba14b 100644 --- a/components/lua_ui/alignment.cpp +++ b/components/lua_ui/alignment.cpp @@ -9,9 +9,9 @@ namespace LuaUi align |= MyGUI::Align::Left; if (horizontal == Alignment::End) align |= MyGUI::Align::Right; - if (horizontal == Alignment::Start) + if (vertical == Alignment::Start) align |= MyGUI::Align::Top; - if (horizontal == Alignment::End) + if (vertical == Alignment::End) align |= MyGUI::Align::Bottom; return align; } diff --git a/components/lua_ui/container.cpp b/components/lua_ui/container.cpp index 867378744b..11b9c1b360 100644 --- a/components/lua_ui/container.cpp +++ b/components/lua_ui/container.cpp @@ -29,7 +29,7 @@ namespace LuaUi size.width = std::max(size.width, coord.left + coord.width); size.height = std::max(size.height, coord.top + coord.height); } - setForcedSize(size); + forceSize(size); updateCoord(); } } diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index 47dd0a3d38..a939ccdc51 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -136,8 +136,8 @@ namespace LuaUi setTemplate(ext, layout.get(LayoutKeys::templateLayout)); ext->setProperties(layout.get(LayoutKeys::props)); setEventCallbacks(ext, layout.get(LayoutKeys::events)); - ext->setChildren(updateContent(ext->children(), layout.get(LayoutKeys::content))); + ext->updateCoord(); } std::string setLayer(WidgetExtension* ext, const sol::table& layout) diff --git a/components/lua_ui/flex.cpp b/components/lua_ui/flex.cpp new file mode 100644 index 0000000000..13acfb3990 --- /dev/null +++ b/components/lua_ui/flex.cpp @@ -0,0 +1,88 @@ +#include "flex.hpp" + +namespace LuaUi +{ + void LuaFlex::updateProperties() + { + mHorizontal = propertyValue("horizontal", false); + mAutoSized = propertyValue("autoSize", true); + mAlign = propertyValue("align", Alignment::Start); + mArrange = propertyValue("arrange", Alignment::Start); + WidgetExtension::updateProperties(); + } + + namespace + { + int alignSize(int container, int content, Alignment alignment) + { + int alignedPosition = 0; + { + switch (alignment) + { + case Alignment::Start: + alignedPosition = 0; + break; + case Alignment::Center: + alignedPosition = (container - content) / 2; + break; + case Alignment::End: + alignedPosition = container - content; + break; + } + } + return alignedPosition; + } + + float getGrow(WidgetExtension* w) + { + return std::max(0.0f, w->externalValue("grow", 0.0f)); + } + } + + void LuaFlex::updateChildren() + { + float totalGrow = 0; + MyGUI::IntSize childrenSize; + for (auto* w: children()) + { + w->clearForced(); + MyGUI::IntSize size = w->calculateSize(); + primary(childrenSize) += primary(size); + secondary(childrenSize) = std::max(secondary(childrenSize), secondary(size)); + totalGrow += getGrow(w); + } + mChildrenSize = childrenSize; + + MyGUI::IntSize flexSize = calculateSize(); + int growSize = 0; + float growFactor = 0; + if (totalGrow > 0) + { + 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()) + { + w->forcePosition(childPosition); + MyGUI::IntSize size = w->widget()->getSize(); + primary(size) += static_cast(growFactor * getGrow(w)); + w->forceSize(size); + primary(childPosition) += primary(size); + } + WidgetExtension::updateProperties(); + } + + MyGUI::IntSize LuaFlex::calculateSize() + { + MyGUI::IntSize size = WidgetExtension::calculateSize(); + if (mAutoSized) { + primary(size) = primary(mChildrenSize); + secondary(size) = std::max(secondary(size), secondary(mChildrenSize)); + } + return size; + } +} diff --git a/components/lua_ui/flex.hpp b/components/lua_ui/flex.hpp new file mode 100644 index 0000000000..9ac85213f6 --- /dev/null +++ b/components/lua_ui/flex.hpp @@ -0,0 +1,55 @@ +#ifndef OPENMW_LUAUI_FLEX +#define OPENMW_LUAUI_FLEX + +#include "widget.hpp" +#include "alignment.hpp" + +namespace LuaUi +{ + class LuaFlex : public MyGUI::Widget, public WidgetExtension + { + MYGUI_RTTI_DERIVED(LuaFlex) + + protected: + MyGUI::IntSize calculateSize() override; + void updateProperties() override; + void updateChildren() override; + MyGUI::IntSize childScalingSize() override + { + return MyGUI::IntSize(); + } + + private: + bool mHorizontal; + bool mAutoSized; + MyGUI::IntSize mChildrenSize; + Alignment mAlign; + Alignment mArrange; + + template + T& primary(MyGUI::types::TPoint& point) + { + return mHorizontal ? point.left : point.top; + } + + template + T& secondary(MyGUI::types::TPoint& point) + { + return mHorizontal ? point.top : point.left; + } + + template + T& primary(MyGUI::types::TSize& size) + { + return mHorizontal ? size.width : size.height; + } + + template + T& secondary(MyGUI::types::TSize& size) + { + return mHorizontal ? size.height : size.width; + } + }; +} + +#endif // OPENMW_LUAUI_FLEX diff --git a/components/lua_ui/util.cpp b/components/lua_ui/util.cpp index cd2cb2c077..d7e7390647 100644 --- a/components/lua_ui/util.cpp +++ b/components/lua_ui/util.cpp @@ -9,6 +9,7 @@ #include "window.hpp" #include "image.hpp" #include "container.hpp" +#include "flex.hpp" #include "element.hpp" #include "registerscriptsettings.hpp" @@ -24,8 +25,9 @@ namespace LuaUi MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("BasisSkin"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); } const std::unordered_map& widgetTypeToName() @@ -36,6 +38,7 @@ namespace LuaUi { "LuaTextEdit", "TextEdit" }, { "LuaWindow", "Window" }, { "LuaImage", "Image" }, + { "LuaFlex", "Flex" }, }; return types; } diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index 74ac8640e0..87b1636d74 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -10,7 +10,9 @@ namespace LuaUi { WidgetExtension::WidgetExtension() - : mPropagateEvents(true) + : mForcePosition(false) + , mForceSize(false) + , mPropagateEvents(true) , mLua(nullptr) , mWidget(nullptr) , mSlot(this) @@ -85,7 +87,6 @@ namespace LuaUi ext->mParent = this; ext->mTemplateChild = false; ext->widget()->attachToWidget(mSlot->widget()); - ext->updateCoord(); } void WidgetExtension::attachTemplate(WidgetExtension* ext) @@ -93,7 +94,6 @@ namespace LuaUi ext->mParent = this; ext->mTemplateChild = true; ext->widget()->attachToWidget(widget()); - ext->updateCoord(); } WidgetExtension* WidgetExtension::findDeep(std::string_view flagName) @@ -219,16 +219,30 @@ namespace LuaUi return mForcedCoord; } - void WidgetExtension::setForcedCoord(const MyGUI::IntCoord& offset) + void WidgetExtension::forceCoord(const MyGUI::IntCoord& offset) { + mForcePosition = true; + mForceSize = true; mForcedCoord = offset; } - void WidgetExtension::setForcedSize(const MyGUI::IntSize& size) + void WidgetExtension::forcePosition(const MyGUI::IntPoint& pos) { + mForcePosition = true; + mForcedCoord = pos; + } + + void WidgetExtension::forceSize(const MyGUI::IntSize& size) + { + mForceSize = true; mForcedCoord = size; } + void WidgetExtension::clearForced() { + mForcePosition = false; + mForceSize = false; + } + void WidgetExtension::updateCoord() { MyGUI::IntCoord oldCoord = mWidget->getCoord(); @@ -246,7 +260,6 @@ namespace LuaUi { mProperties = props; updateProperties(); - updateCoord(); } void WidgetExtension::updateProperties() @@ -281,9 +294,12 @@ namespace LuaUi MyGUI::IntSize WidgetExtension::calculateSize() { + if (mForceSize) + return mForcedCoord.size(); + MyGUI::IntSize pSize = parentSize(); MyGUI::IntSize newSize; - newSize = mAbsoluteCoord.size() + mForcedCoord.size(); + newSize = mAbsoluteCoord.size(); newSize.width += mRelativeCoord.width * pSize.width; newSize.height += mRelativeCoord.height * pSize.height; return newSize; @@ -291,9 +307,11 @@ namespace LuaUi MyGUI::IntPoint WidgetExtension::calculatePosition(const MyGUI::IntSize& size) { + if (mForcePosition) + return mForcedCoord.point(); MyGUI::IntSize pSize = parentSize(); MyGUI::IntPoint newPosition; - newPosition = mAbsoluteCoord.point() + mForcedCoord.point(); + newPosition = mAbsoluteCoord.point(); newPosition.left += mRelativeCoord.left * pSize.width - mAnchor.width * size.width; newPosition.top += mRelativeCoord.top * pSize.height - mAnchor.height * size.height; return newPosition; diff --git a/components/lua_ui/widget.hpp b/components/lua_ui/widget.hpp index c712d18ccd..33bf52cd78 100644 --- a/components/lua_ui/widget.hpp +++ b/components/lua_ui/widget.hpp @@ -47,8 +47,11 @@ namespace LuaUi void setExternal(sol::object external) { mExternal = external; } MyGUI::IntCoord forcedCoord(); - void setForcedCoord(const MyGUI::IntCoord& offset); - void setForcedSize(const MyGUI::IntSize& size); + void forceCoord(const MyGUI::IntCoord& offset); + void forceSize(const MyGUI::IntSize& size); + void forcePosition(const MyGUI::IntPoint& pos); + void clearForced(); + void updateCoord(); const sol::table& getLayout() { return mLayout; } @@ -65,6 +68,9 @@ namespace LuaUi mOnCoordChange = callback; } + virtual MyGUI::IntSize calculateSize(); + virtual MyGUI::IntPoint calculatePosition(const MyGUI::IntSize& size); + protected: virtual void initialize(); sol::table makeTable() const; @@ -72,8 +78,6 @@ namespace LuaUi sol::object mouseEvent(int left, int top, MyGUI::MouseButton button) const; MyGUI::IntSize parentSize(); - virtual MyGUI::IntSize calculateSize(); - virtual MyGUI::IntPoint calculatePosition(const MyGUI::IntSize& size); MyGUI::IntCoord calculateCoord(); virtual MyGUI::IntSize childScalingSize(); @@ -113,6 +117,8 @@ namespace LuaUi } } + bool mForcePosition; + bool mForceSize; // offsets the position and size, used only in C++ widget code MyGUI::IntCoord mForcedCoord; // position and size in pixels diff --git a/components/lua_ui/window.cpp b/components/lua_ui/window.cpp index 41281208d4..a9fff5ff47 100644 --- a/components/lua_ui/window.cpp +++ b/components/lua_ui/window.cpp @@ -39,8 +39,7 @@ namespace LuaUi if (mCaption) mCaption->setCaption(propertyValue("caption", std::string())); mMoveResize = MyGUI::IntCoord(); - setForcedCoord(mMoveResize); - + clearForced(); WidgetExtension::updateProperties(); } @@ -70,11 +69,8 @@ namespace LuaUi change.width *= (left - mPreviousMouse.left); change.height *= (top - mPreviousMouse.top); - mMoveResize = mMoveResize + change.size(); - setForcedCoord(mMoveResize); - // position can change based on size changes - mMoveResize = mMoveResize + change.point() + getPosition() - calculateCoord().point(); - setForcedCoord(mMoveResize); + mMoveResize = mMoveResize + change; + forceCoord(mMoveResize); updateCoord(); mPreviousMouse.left = left; diff --git a/docs/source/reference/lua-scripting/user_interface.rst b/docs/source/reference/lua-scripting/user_interface.rst index 71ad61b410..394c53e688 100644 --- a/docs/source/reference/lua-scripting/user_interface.rst +++ b/docs/source/reference/lua-scripting/user_interface.rst @@ -81,6 +81,7 @@ Widget types Text: Displays text. TextEdit: Accepts text input from the user. Image: Renders a texture. + Flex: Aligns children in a column/row Example ------- diff --git a/docs/source/reference/lua-scripting/widgets/flex.rst b/docs/source/reference/lua-scripting/widgets/flex.rst new file mode 100644 index 0000000000..359d0d4394 --- /dev/null +++ b/docs/source/reference/lua-scripting/widgets/flex.rst @@ -0,0 +1,43 @@ +Flex Widget +=========== + +Aligns its children along either a column or a row, depending on the `horizontal` property. + +Properties +---------- + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type (default value) + - description + * - horizontal + - bool (false) + - Flex aligns its children in a row if true, otherwise in a column. + * - autoSize + - bool (true) + - | If true, Flex will automatically resize to fit its contents. + | Children can't be relatively position/sized when true. + * - align + - ui.ALIGNMENT (Start) + - Where to align the children in the main axis. + * - arrange + - ui.ALIGNMETN (Start) + - How to arrange the children in the cross axis. + +External +-------- +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - name + - type (default value) + - description + * - grow + - float (0) + - | 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`.