Skip to content

Commit 0e10f1b

Browse files
committed
Merge branch 'flex-wrap-autosize-regressions' into 'master'
Flex Wrap/Gap With AutoSize Regressions Closes #9124 See merge request OpenMW/openmw!5334
2 parents b6b04ca + ea0b427 commit 0e10f1b

3 files changed

Lines changed: 115 additions & 42 deletions

File tree

components/lua_ui/flex.cpp

Lines changed: 106 additions & 42 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,109 @@ namespace LuaUi
4137

4238
void LuaFlex::updateChildren()
4339
{
44-
float totalGrow = 0;
40+
const auto& flexChildren = children();
41+
4542
MyGUI::IntSize childrenSize;
46-
for (auto* w : children())
43+
if (mAutoSized)
4744
{
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);
45+
int measuredPrimary = 0;
46+
int measuredSecondary = 0;
47+
bool isFirst = true;
48+
for (auto* w : flexChildren)
49+
{
50+
w->clearForced();
51+
const MyGUI::IntSize size = w->calculateSize();
52+
measuredPrimary += primary(size) + (isFirst ? 0 : mGap);
53+
measuredSecondary = std::max(measuredSecondary, secondary(size));
54+
isFirst = false;
55+
}
56+
primary(childrenSize) = measuredPrimary;
57+
secondary(childrenSize) = measuredSecondary;
58+
mChildrenSize = childrenSize;
5359
}
54-
mChildrenSize = childrenSize;
5560

5661
MyGUI::IntSize flexSize = calculateSize();
57-
int growSize = 0;
58-
float growFactor = 0;
59-
if (totalGrow > 0)
60-
{
61-
growSize = primary(flexSize) - primary(childrenSize);
62-
growFactor = growSize / totalGrow;
63-
}
6462

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

@@ -108,7 +172,7 @@ namespace LuaUi
108172
const std::vector<std::string_view>& LuaFlex::allUsedProperties() const
109173
{
110174
static std::vector<std::string_view> usedProps = std::invoke([this] {
111-
std::vector<std::string_view> props = { "horizontal", "autoSize", "arrange", "align" };
175+
std::vector<std::string_view> props = { "horizontal", "autoSize", "arrange", "align", "gap", "wrap" };
112176
auto baseProps = WidgetExtension::allUsedProperties();
113177
props.insert(props.end(), baseProps.begin(), baseProps.end());
114178
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)