26 class GridLayout :
public Layout
32 spk::Vector2UInt _nbCells = {0, 0};
34 static const Vector2UInt &_positionOf(
const Element &p_e)
36 const auto *ptr = std::any_cast<Vector2UInt>(&p_e.
extraData);
39 throw std::logic_error(
"GridLayout element extraData is not a Vector2UInt");
47 return (_computeMinimalSize());
50 return (_computeMinimalSize());
54 spk::Vector2UInt _baseElementSize(
const Element &p_element)
const
59 return (spk::Vector2UInt{
64 using AxisSizeContainer = std::vector<spk::Vector2UInt::value_type>;
65 struct GridSizeContainer
67 std::vector<spk::Vector2UInt::value_type> xAxisSizes;
68 std::vector<spk::Vector2UInt::value_type> yAxisSizes;
70 GridSizeContainer() =
default;
71 GridSizeContainer(
const spk::Vector2UInt &p_gridSize) :
72 xAxisSizes(p_gridSize.
x, 0),
73 yAxisSizes(p_gridSize.
y, 0)
80 AxisSizeContainer::value_type size = 0;
81 AxisSizeContainer::value_type maximal = std::numeric_limits<AxisSizeContainer::value_type>::max();
83 bool hasElement =
false;
84 bool hasDesired =
false;
92 case Layout::SizePolicy::Extend:
94 case Layout::SizePolicy::Standard:
96 case Layout::SizePolicy::Minimum:
102 return (rank(p_candidate) > rank(p_current) ? p_candidate : p_current);
105 std::vector<size_t> _collectAxisCandidates(
const std::vector<AxisState> &p_states,
Layout::SizePolicy::Value p_policy)
const
107 const AxisSizeContainer::value_type axisUnlimited = std::numeric_limits<AxisSizeContainer::value_type>::max();
108 std::vector<size_t> candidateIndices;
109 candidateIndices.reserve(p_states.size());
111 for (
size_t i = 0; i < p_states.size(); ++i)
113 const AxisState &state = p_states[i];
114 const bool canGrow = (state.maximal == axisUnlimited || state.size < state.maximal);
116 if (state.hasElement ==
true &&
117 state.hasDesired ==
false &&
118 state.policy == p_policy &&
121 candidateIndices.push_back(i);
125 return candidateIndices;
128 AxisSizeContainer::value_type _consumeAxisShare(AxisSizeContainer::value_type &p_spaceToAssign,
const std::vector<size_t> &p_candidateIndices, std::vector<AxisState> &p_states)
const
130 const AxisSizeContainer::value_type axisUnlimited = std::numeric_limits<AxisSizeContainer::value_type>::max();
131 AxisSizeContainer::value_type share = p_spaceToAssign / p_candidateIndices.size();
137 AxisSizeContainer::value_type consumed = 0;
138 for (
size_t index : p_candidateIndices)
140 if (p_spaceToAssign == 0)
145 AxisState &state = p_states[index];
146 AxisSizeContainer::value_type available = axisUnlimited;
148 if (state.maximal != axisUnlimited)
150 if (state.size >= state.maximal)
154 available = state.maximal - state.size;
162 AxisSizeContainer::value_type increment = std::min(share, available);
163 increment = std::min(increment, p_spaceToAssign);
165 state.size += increment;
166 p_spaceToAssign -= increment;
167 consumed += increment;
173 void _updateAxisState(AxisState &p_state, AxisSizeContainer::value_type p_baseSize, AxisSizeContainer::value_type p_maximalSize,
Layout::SizePolicy::Value p_value)
175 p_state.hasElement =
true;
176 p_state.size = std::max(p_state.size, p_baseSize);
177 p_state.maximal = std::min(p_state.maximal, p_maximalSize);
179 if (p_value == Layout::SizePolicy::Desired)
181 p_state.hasDesired =
true;
182 p_state.maximal = std::min(p_state.maximal, p_baseSize);
186 p_state.policy = _strongerPolicy(p_state.policy, p_value);
189 void _assignSpaceToAxis(AxisSizeContainer::value_type &p_spaceToAssign, std::vector<AxisState> &p_states,
Layout::SizePolicy::Value p_policy)
const
191 if (p_spaceToAssign == 0)
196 while (p_spaceToAssign > 0)
198 std::vector<size_t> candidateIndices = _collectAxisCandidates(p_states, p_policy);
199 if (candidateIndices.empty())
204 AxisSizeContainer::value_type consumed = _consumeAxisShare(p_spaceToAssign, candidateIndices, p_states);
212 GridSizeContainer _computeGridBaseSize()
const
214 GridSizeContainer result(_nbCells);
216 for (
size_t i = 0; i <
_elements().size(); i++)
219 const spk::Vector2UInt &elementPosition = _positionOf(element);
220 spk::Vector2UInt elementSize = _baseElementSize(element);
222 result.xAxisSizes[elementPosition.
x] = std::max(result.xAxisSizes[elementPosition.
x], elementSize.
x);
223 result.yAxisSizes[elementPosition.
y] = std::max(result.yAxisSizes[elementPosition.
y], elementSize.
y);
229 spk::Vector2UInt _computeTotalGridSize(
const GridSizeContainer &p_rawGridSize)
const
231 using AxisValue = AxisSizeContainer::value_type;
233 const auto accumulateAxis = [&](
const AxisSizeContainer &p_axisSizes,
const AxisValue p_padding) {
234 const AxisValue totalSize = std::accumulate(p_axisSizes.begin(), p_axisSizes.end(), AxisValue{0});
235 if (p_axisSizes.empty() ==
true)
240 const AxisValue paddingCount =
static_cast<AxisValue
>(
static_cast<size_t>(p_axisSizes.size() - 1) *
static_cast<size_t>(p_padding));
241 return (totalSize + paddingCount);
244 const AxisValue xSize = accumulateAxis(p_rawGridSize.xAxisSizes,
padding().x);
245 const AxisValue ySize = accumulateAxis(p_rawGridSize.yAxisSizes,
padding().y);
247 return (spk::Vector2UInt{xSize, ySize});
250 spk::Vector2UInt _computeMinimalSize()
const
252 return (_computeTotalGridSize(_computeGridBaseSize()));
288 const GridSizeContainer baseGridSize = _computeGridBaseSize();
289 spk::Vector2UInt spaceUsed = _computeTotalGridSize(baseGridSize);
291 spk::Vector2UInt spaceLeft{
292 p_extend.size.
x <= spaceUsed.
x ? 0 : (p_extend.size.
x - spaceUsed.
x),
293 p_extend.size.
y <= spaceUsed.
y ? 0 : (p_extend.size.
y - spaceUsed.
y)};
295 std::vector<AxisState> xAxisStates(_nbCells.x);
296 std::vector<AxisState> yAxisStates(_nbCells.y);
298 for (
size_t i = 0; i < xAxisStates.size(); ++i)
300 xAxisStates[i].size = baseGridSize.xAxisSizes[i];
302 for (
size_t i = 0; i < yAxisStates.size(); ++i)
304 yAxisStates[i].size = baseGridSize.yAxisSizes[i];
309 const spk::Vector2UInt position = _positionOf(element);
310 const spk::Vector2UInt elementBase = _baseElementSize(element);
311 const spk::Vector2UInt elementMaximal = element.element->sizeHint().maximal();
313 if (position.
x < xAxisStates.size())
315 _updateAxisState(xAxisStates[position.
x], elementBase.
x, elementMaximal.
x, element.sizePolicy.horizontal);
317 if (position.
y < yAxisStates.size())
319 _updateAxisState(yAxisStates[position.
y], elementBase.
y, elementMaximal.
y, element.sizePolicy.vertical);
323 _assignSpaceToAxis(spaceLeft.
x, xAxisStates, SizePolicy::Extend);
324 _assignSpaceToAxis(spaceLeft.
x, xAxisStates, SizePolicy::Standard);
326 _assignSpaceToAxis(spaceLeft.
y, yAxisStates, SizePolicy::Extend);
327 _assignSpaceToAxis(spaceLeft.
y, yAxisStates, SizePolicy::Standard);
329 std::vector<spk::Vector2Int::value_type> xAnchors(_nbCells.x);
330 std::vector<spk::Vector2Int::value_type> yAnchors(_nbCells.y);
333 for (
size_t i = 0; i < _nbCells.x; ++i)
335 xAnchors[i] = currentX;
337 if (i + 1 < _nbCells.x)
344 for (
size_t i = 0; i < _nbCells.y; ++i)
346 yAnchors[i] = currentY;
348 if (i + 1 < _nbCells.y)
356 const spk::Vector2UInt position = _positionOf(element);
357 if (position.
x >= xAxisStates.size() || position.
y >= yAxisStates.size())
362 spk::Vector2UInt elementSize{
363 xAxisStates[position.
x].size,
364 yAxisStates[position.
y].size};
366 const spk::Vector2UInt elementBase = _baseElementSize(element);
367 if (element.sizePolicy.horizontal == SizePolicy::Minimum)
369 elementSize.
x = elementBase.
x;
372 if (element.sizePolicy.vertical == SizePolicy::Minimum)
374 elementSize.
y = elementBase.
y;
377 element.element->setGeometry({{xAnchors[position.
x], yAnchors[position.
y]}, elementSize});