Skip to content

Commit e75866f

Browse files
committed
Merge branch 'flex-wrap-property' into 'master'
Implement Gap and Wrap Properties on Flex Widgets See merge request OpenMW/openmw!5275
2 parents 5ceff51 + 339aee7 commit e75866f

4 files changed

Lines changed: 104 additions & 46 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
8282
set(OPENMW_VERSION_MAJOR 0)
8383
set(OPENMW_VERSION_MINOR 52)
8484
set(OPENMW_VERSION_RELEASE 0)
85-
set(OPENMW_LUA_API_REVISION 131)
85+
set(OPENMW_LUA_API_REVISION 132)
8686
set(OPENMW_POSTPROCESSING_API_REVISION 5)
8787

8888
set(OPENMW_VERSION_COMMITHASH "")

components/lua_ui/flex.cpp

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ namespace LuaUi
44
{
55
void LuaFlex::updateProperties()
66
{
7+
mGap = propertyValue("gap", 0);
8+
mWrap = propertyValue("wrap", false);
79
mHorizontal = propertyValue("horizontal", false);
810
mAutoSized = propertyValue("autoSize", true);
911
mAlign = propertyValue("align", Alignment::Start);
@@ -15,22 +17,16 @@ namespace LuaUi
1517
{
1618
int alignSize(int container, int content, Alignment alignment)
1719
{
18-
int alignedPosition = 0;
20+
switch (alignment)
1921
{
20-
switch (alignment)
21-
{
22-
case Alignment::Start:
23-
alignedPosition = 0;
24-
break;
25-
case Alignment::Center:
26-
alignedPosition = (container - content) / 2;
27-
break;
28-
case Alignment::End:
29-
alignedPosition = container - content;
30-
break;
31-
}
22+
case Alignment::Start:
23+
return 0;
24+
case Alignment::Center:
25+
return (container - content) / 2;
26+
case Alignment::End:
27+
return container - content;
3228
}
33-
return alignedPosition;
29+
return 0;
3430
}
3531

3632
float getGrow(WidgetExtension* w)
@@ -41,41 +37,94 @@ namespace LuaUi
4137

4238
void LuaFlex::updateChildren()
4339
{
44-
float totalGrow = 0;
40+
const auto& flexChildren = children();
41+
MyGUI::IntSize flexSize = calculateSize();
42+
4543
MyGUI::IntSize childrenSize;
46-
for (auto* w : children())
47-
{
48-
w->clearForced();
49-
MyGUI::IntSize size = w->calculateSize();
50-
primary(childrenSize) += primary(size);
51-
secondary(childrenSize) = std::max(secondary(childrenSize), secondary(size));
52-
totalGrow += getGrow(w);
53-
}
54-
mChildrenSize = childrenSize;
44+
int currentSecondaryAxisPosition = 0;
45+
size_t widgetIndex = 0;
5546

56-
MyGUI::IntSize flexSize = calculateSize();
57-
int growSize = 0;
58-
float growFactor = 0;
59-
if (totalGrow > 0)
47+
while (widgetIndex < flexChildren.size())
6048
{
61-
growSize = primary(flexSize) - primary(childrenSize);
62-
growFactor = growSize / totalGrow;
63-
}
49+
const size_t trackStart = widgetIndex;
50+
int primaryAxisSize = 0;
51+
int primaryAxisSizeRemaining = mAutoSized ? 0 : primary(flexSize);
52+
int secondaryAxisSize = 0;
53+
float totalPrimaryGrow = 0;
6454

65-
MyGUI::IntPoint childPosition;
66-
primary(childPosition) = alignSize(primary(flexSize) - growSize, primary(childrenSize), mAlign);
67-
for (auto* w : children())
68-
{
69-
MyGUI::IntSize size = w->calculateSize();
70-
primary(size) += static_cast<int>(growFactor * getGrow(w));
71-
float stretch = std::clamp(w->externalValue("stretch", 0.0f), 0.0f, 1.0f);
72-
secondary(size) = std::max(secondary(size), static_cast<int>(stretch * secondary(flexSize)));
73-
secondary(childPosition) = alignSize(secondary(flexSize), secondary(size), mArrange);
74-
w->forcePosition(childPosition);
75-
w->forceSize(size);
76-
w->updateCoord();
77-
primary(childPosition) += primary(size);
55+
while (widgetIndex < flexChildren.size())
56+
{
57+
auto* w = flexChildren[widgetIndex];
58+
w->clearForced();
59+
60+
const MyGUI::IntSize size = w->calculateSize();
61+
const int childPrimary = primary(size);
62+
const int childSecondary = secondary(size);
63+
const bool notFirstOnTrack = (widgetIndex > trackStart);
64+
const int primaryToAdd = childPrimary + (notFirstOnTrack ? mGap : 0);
65+
66+
if (!mAutoSized && mWrap && notFirstOnTrack && primaryAxisSizeRemaining < primaryToAdd)
67+
break;
68+
69+
primaryAxisSize += primaryToAdd;
70+
if (!mAutoSized)
71+
primaryAxisSizeRemaining -= primaryToAdd;
72+
secondaryAxisSize = std::max(childSecondary, secondaryAxisSize);
73+
totalPrimaryGrow += getGrow(w);
74+
widgetIndex++;
75+
}
76+
primaryAxisSizeRemaining = std::max(0, primaryAxisSizeRemaining);
77+
78+
if (mAutoSized)
79+
{
80+
primary(childrenSize) = std::max(primaryAxisSize, primary(childrenSize));
81+
secondary(childrenSize) += secondaryAxisSize;
82+
}
83+
84+
const int primaryAxisChildrenShift
85+
= alignSize(primaryAxisSize, primaryAxisSize - primaryAxisSizeRemaining, mAlign);
86+
int currentPrimaryAxisPosition = primaryAxisChildrenShift;
87+
88+
// Keeping a constant total here ensures widgets that use grow don't use the
89+
// changing 'primaryAxisSizeRemaining' (which shrinks as we take off grown amounts
90+
const int totalPrimaryAxisSizeRemaining = primaryAxisSizeRemaining;
91+
92+
for (size_t j = trackStart; j < widgetIndex; ++j)
93+
{
94+
auto* w = flexChildren[j];
95+
MyGUI::IntPoint widgetPosition;
96+
MyGUI::IntSize widgetSize = w->calculateSize();
97+
98+
// Note: Grow is on the primary axis and stretch is "grow" on the cross/secondary axis
99+
const float widgetGrowFactor = getGrow(w);
100+
const float stretch = std::clamp(w->externalValue("stretch", 0.0f), 0.0f, 1.0f);
101+
if (widgetGrowFactor > 0)
102+
{
103+
const int pixelsToExpandBy = std::clamp(
104+
static_cast<int>(
105+
std::round((widgetGrowFactor / totalPrimaryGrow) * totalPrimaryAxisSizeRemaining)),
106+
0, primaryAxisSizeRemaining);
107+
primary(widgetSize) += pixelsToExpandBy;
108+
primaryAxisSizeRemaining -= pixelsToExpandBy;
109+
}
110+
111+
secondary(widgetSize) = std::max(secondary(widgetSize),
112+
std::clamp(static_cast<int>(std::round(stretch * secondaryAxisSize)), 0, secondaryAxisSize));
113+
114+
primary(widgetPosition) = currentPrimaryAxisPosition;
115+
currentPrimaryAxisPosition += primary(widgetSize) + mGap;
116+
secondary(widgetPosition)
117+
= currentSecondaryAxisPosition + alignSize(secondaryAxisSize, secondary(widgetSize), mArrange);
118+
119+
w->forceSize(widgetSize);
120+
w->forcePosition(widgetPosition);
121+
w->updateCoord();
122+
}
123+
124+
currentSecondaryAxisPosition += secondaryAxisSize + mGap;
78125
}
126+
127+
mChildrenSize = childrenSize;
79128
WidgetExtension::updateChildren();
80129
}
81130

@@ -108,7 +157,7 @@ namespace LuaUi
108157
const std::vector<std::string_view>& LuaFlex::allUsedProperties() const
109158
{
110159
static std::vector<std::string_view> usedProps = std::invoke([this] {
111-
std::vector<std::string_view> props = { "horizontal", "autoSize", "arrange", "align" };
160+
std::vector<std::string_view> props = { "horizontal", "autoSize", "arrange", "align", "gap", "wrap" };
112161
auto baseProps = WidgetExtension::allUsedProperties();
113162
props.insert(props.end(), baseProps.begin(), baseProps.end());
114163
return props;

components/lua_ui/flex.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ namespace LuaUi
2828
MyGUI::IntSize mChildrenSize;
2929
Alignment mAlign;
3030
Alignment mArrange;
31+
int mGap;
32+
bool mWrap;
3133

3234
template <typename T>
3335
T& primary(MyGUI::types::TPoint<T>& point) const

docs/source/reference/lua-scripting/widgets/flex.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ Properties
5959
* - arrange
6060
- ui.ALIGNMENT (Start)
6161
- How to arrange the children in the cross axis.
62+
* - gap
63+
- number (0)
64+
- The pixel gap between children in the main axis.
65+
* - wrap
66+
- bool (false)
67+
- | If true, children will wrap to the next row (or column) when they exceed the main axis size.
68+
| Only applicable when `autoSize` is `false`.
6269
6370
Events
6471
------

0 commit comments

Comments
 (0)