26struct grid_layout_cell {
29 size_t first_column = 0;
31 size_t last_column = 0;
33 bool beyond_maximum =
false;
34 value_type value = {};
37 constexpr grid_layout_cell()
noexcept =
default;
38 constexpr grid_layout_cell(grid_layout_cell
const&)
noexcept =
default;
39 constexpr grid_layout_cell(grid_layout_cell&&)
noexcept =
default;
40 constexpr grid_layout_cell& operator=(grid_layout_cell
const&)
noexcept =
default;
41 constexpr grid_layout_cell& operator=(grid_layout_cell&&)
noexcept =
default;
43 constexpr grid_layout_cell(
49 std::convertible_to<value_type>
auto&& value) noexcept :
50 first_column(first_column),
52 last_column(last_column),
54 beyond_maximum(beyond_maximum),
55 value(hi_forward(value))
57 hi_assert(first_column < last_column);
58 hi_assert(first_row < last_row);
61 constexpr void set_constraints(
box_constraints const& constraints)
noexcept
63 _constraints = constraints;
66 template<hi::axis Axis>
67 [[nodiscard]]
constexpr size_t first()
const noexcept
69 if constexpr (Axis == axis::x) {
71 }
else if constexpr (Axis == axis::y) {
74 hi_static_no_default();
78 template<hi::axis Axis>
79 [[nodiscard]]
constexpr size_t last()
const noexcept
81 if constexpr (Axis == axis::x) {
83 }
else if constexpr (Axis == axis::y) {
86 hi_static_no_default();
90 template<hi::axis Axis>
91 [[nodiscard]]
constexpr size_t span()
const noexcept
93 hi_axiom(first<Axis>() < last<Axis>());
94 return last<Axis>() - first<Axis>();
97 template<hi::axis Axis>
98 [[nodiscard]]
constexpr auto alignment()
const noexcept
100 if constexpr (Axis == axis::x) {
101 return _constraints.alignment.horizontal();
102 }
else if constexpr (Axis == axis::y) {
103 return _constraints.alignment.vertical();
105 hi_static_no_default();
109 template<hi::axis Axis>
110 [[nodiscard]]
constexpr float minimum()
const noexcept
112 if constexpr (Axis == axis::x) {
113 return _constraints.minimum.width();
114 }
else if constexpr (Axis == axis::y) {
115 return _constraints.minimum.height();
117 hi_static_no_default();
121 template<hi::axis Axis>
122 [[nodiscard]]
constexpr float preferred()
const noexcept
124 if constexpr (Axis == axis::x) {
125 return _constraints.preferred.width();
126 }
else if constexpr (Axis == axis::y) {
127 return _constraints.preferred.height();
129 hi_static_no_default();
133 template<hi::axis Axis>
134 [[nodiscard]]
constexpr float maximum()
const noexcept
136 if constexpr (Axis == axis::x) {
137 return _constraints.maximum.width();
138 }
else if constexpr (Axis == axis::y) {
139 return _constraints.maximum.height();
141 hi_static_no_default();
145 template<hi::axis Axis>
146 [[nodiscard]]
constexpr float margin_before(
bool forward)
const noexcept
148 if constexpr (Axis == axis::x) {
150 return _constraints.margins.left();
152 return _constraints.margins.right();
154 }
else if constexpr (Axis == axis::y) {
156 return _constraints.margins.bottom();
158 return _constraints.margins.top();
161 hi_static_no_default();
165 template<hi::axis Axis>
166 [[nodiscard]]
constexpr float margin_after(
bool forward)
const noexcept
168 if constexpr (Axis == axis::x) {
170 return _constraints.margins.right();
172 return _constraints.margins.left();
174 }
else if constexpr (Axis == axis::y) {
176 return _constraints.margins.top();
178 return _constraints.margins.bottom();
181 hi_static_no_default();
185 template<hi::axis Axis>
186 [[nodiscard]]
constexpr float padding_before(
bool forward)
const noexcept
188 if constexpr (Axis == axis::x) {
190 return _constraints.padding.left();
192 return _constraints.padding.right();
194 }
else if constexpr (Axis == axis::y) {
196 return _constraints.padding.bottom();
198 return _constraints.padding.top();
201 hi_static_no_default();
205 template<hi::axis Axis>
206 [[nodiscard]]
constexpr float padding_after(
bool forward)
const noexcept
208 if constexpr (Axis == axis::x) {
210 return _constraints.padding.right();
212 return _constraints.padding.left();
214 }
else if constexpr (Axis == axis::y) {
216 return _constraints.padding.top();
218 return _constraints.padding.bottom();
221 hi_static_no_default();
230class grid_layout_axis_constraints {
232 constexpr static hi::axis axis = Axis;
234 using value_type = T;
235 using alignment_type = std::conditional_t<axis == axis::y, vertical_alignment, horizontal_alignment>;
295 using iterator = constraint_vector::iterator;
296 using const_iterator = constraint_vector::const_iterator;
297 using reverse_iterator = constraint_vector::reverse_iterator;
298 using reference = constraint_vector::reference;
299 using const_reference = constraint_vector::const_reference;
301 constexpr ~grid_layout_axis_constraints() =
default;
302 constexpr grid_layout_axis_constraints() noexcept = default;
303 constexpr grid_layout_axis_constraints(grid_layout_axis_constraints const&) noexcept = default;
304 constexpr grid_layout_axis_constraints(grid_layout_axis_constraints&&) noexcept = default;
305 constexpr grid_layout_axis_constraints& operator=(grid_layout_axis_constraints const&) noexcept = default;
306 constexpr grid_layout_axis_constraints& operator=(grid_layout_axis_constraints&&) noexcept = default;
307 [[nodiscard]] constexpr friend
bool
308 operator==(grid_layout_axis_constraints const&, grid_layout_axis_constraints const&) noexcept = default;
317 constexpr grid_layout_axis_constraints(cell_vector const& cells,
size_t num,
bool forward) noexcept :
318 _constraints(num), _forward(forward)
320 for (hilet& cell : cells) {
321 construct_simple_cell(cell);
325 for (hilet& cell : cells) {
326 construct_span_cell(cell);
331 [[nodiscard]]
constexpr float margin_before() const noexcept
333 return empty() ? 0 : _forward ?
front().margin_before :
back().margin_before;
336 [[nodiscard]]
constexpr float margin_after() const noexcept
338 return empty() ? 0 : _forward ?
back().margin_after :
front().margin_after;
341 [[nodiscard]]
constexpr float padding_before() const noexcept
343 return empty() ? 0 : _forward ?
front().padding_before :
back().padding_before;
346 [[nodiscard]]
constexpr float padding_after() const noexcept
348 return empty() ? 0 : _forward ?
back().padding_after :
front().padding_after;
351 [[nodiscard]]
constexpr std::tuple<float, float, float> update_constraints() const noexcept
365 return constraints(cell.first<axis>(), cell.last<axis>());
368 [[nodiscard]]
constexpr float position(cell_type
const& cell)
const noexcept
370 return position(cell.first<
axis>(), cell.last<
axis>());
373 [[nodiscard]]
constexpr float extent(cell_type
const& cell)
const noexcept
375 return extent(cell.first<
axis>(), cell.last<
axis>());
378 [[nodiscard]]
constexpr std::optional<float> guideline(cell_type
const& cell)
const noexcept
380 if (cell.span<
axis>() == 1) {
381 return guideline(cell.first<
axis>());
409 constexpr void layout(
float new_position,
float new_extent, std::optional<float> external_guideline,
float guideline_width)
noexcept
412 for (
auto& constraint : _constraints) {
413 constraint.extent = constraint.preferred;
417 auto [total_extent, count] = layout_shrink(
begin(),
end());
418 while (total_extent > new_extent and count != 0) {
420 std::tie(total_extent, count) = layout_shrink(
begin(),
end(), total_extent - new_extent, count);
425 while (total_extent < new_extent and count != 0) {
427 std::tie(total_extent, count) = layout_expand(
begin(),
end(), new_extent - total_extent, count);
431 if (total_extent < new_extent) {
434 return item.beyond_maximum;
437 auto expand = new_extent - total_extent;
438 hilet expand_per =
std::ceil(expand / count);
440 for (
auto& constraint : _constraints) {
441 hilet expand_this =
std::min(expand_per, expand);
442 if (constraint.beyond_maximum) {
443 constraint.extent += expand_this;
444 expand -= expand_this;
452 if (total_extent < new_extent and not
empty()) {
454 front().extent += new_extent - total_extent;
458 layout_position(
begin(),
end(), new_position, guideline_width);
460 layout_position(
rbegin(),
rend(), new_position, guideline_width);
463 if (external_guideline and
size() == 1) {
466 front().guideline = *external_guideline;
472 [[nodiscard]]
constexpr size_t size() const noexcept
474 return _constraints.size();
479 [[nodiscard]]
constexpr bool empty() const noexcept
481 return _constraints.empty();
486 [[nodiscard]]
constexpr iterator
begin() noexcept
488 return _constraints.begin();
493 [[nodiscard]]
constexpr const_iterator
begin() const noexcept
495 return _constraints.begin();
500 [[nodiscard]]
constexpr const_iterator
cbegin() const noexcept
502 return _constraints.cbegin();
507 [[nodiscard]]
constexpr iterator
end() noexcept
509 return _constraints.end();
514 [[nodiscard]]
constexpr const_iterator
end() const noexcept
516 return _constraints.end();
521 [[nodiscard]]
constexpr const_iterator
cend() const noexcept
523 return _constraints.cend();
528 [[nodiscard]]
constexpr reverse_iterator
rbegin() noexcept
530 return _constraints.rbegin();
535 [[nodiscard]]
constexpr reverse_iterator
rend() noexcept
537 return _constraints.rend();
548 hi_axiom(index <
size());
549 return _constraints[index];
558 [[nodiscard]]
constexpr const_reference
operator[](
size_t index)
const noexcept
560 hi_axiom(index <
size());
561 return _constraints[index];
571 hi_axiom(not
empty());
572 return _constraints.front();
580 [[nodiscard]]
constexpr const_reference
front() const noexcept
582 hi_axiom(not
empty());
583 return _constraints.front();
593 hi_axiom(not
empty());
594 return _constraints.back();
602 [[nodiscard]]
constexpr const_reference
back() const noexcept
604 hi_axiom(not
empty());
605 return _constraints.back();
617 constraint_vector _constraints = {};
621 bool _forward =
true;
640 layout_shrink(const_iterator first, const_iterator last,
float shrink = 0.0f,
size_t count = 1) noexcept
645 hi_axiom(shrink >= 0);
647 hilet shrink_per =
std::floor(shrink / count);
649 auto new_extent = 0.0f;
650 auto new_count = 0_uz;
651 for (
auto it = first_; it != last_; ++it) {
652 hilet shrink_this =
std::max({shrink_per, shrink, it->extent - it->minimum});
653 it->extent -= shrink_this;
654 shrink -= shrink_this;
657 new_extent += it->margin_before;
659 new_extent += it->extent;
661 if (it->extent > it->minimum) {
666 return {new_extent, new_count};
685 [[nodiscard]]
constexpr std::pair<float, size_t>
686 layout_expand(const_iterator first, const_iterator last,
float expand = 0.0f,
size_t count = 1) noexcept
691 hi_axiom(expand >= 0.0f);
693 hilet expand_per =
std::ceil(expand / count);
694 hi_axiom(expand_per >= 0.0f);
696 auto new_extent = 0.0f;
697 auto new_count = 0_uz;
698 for (
auto it = first_; it != last_; ++it) {
699 hilet expand_this =
std::min({expand_per, expand, it->maximum - it->extent});
700 it->extent += expand_this;
701 expand -= expand_this;
704 new_extent += it->margin_before;
706 new_extent += it->extent;
708 if (it->extent < it->maximum) {
713 return {new_extent, new_count};
716 constexpr void layout_position(
auto first,
auto last,
float start_position,
float guideline_width)
noexcept
718 auto position = start_position;
719 for (
auto it = first; it != last; ++it) {
720 it->position = position;
722 it->alignment, position, position + it->extent, it->padding_before, it->padding_after, guideline_width);
724 position += it->extent;
725 position += it->margin_after;
736 constexpr void construct_simple_cell(cell_type
const& cell)
noexcept
738 inplace_max(_constraints[cell.first<axis>()].margin_before, cell.margin_before<axis>(_forward));
739 inplace_max(_constraints[cell.last<axis>() - 1].margin_after, cell.margin_after<axis>(_forward));
740 inplace_max(_constraints[cell.first<axis>()].padding_before, cell.padding_before<axis>(_forward));
741 inplace_max(_constraints[cell.last<axis>() - 1].padding_after, cell.padding_after<axis>(_forward));
743 for (
auto i = cell.first<axis>(); i != cell.last<axis>(); ++i) {
744 _constraints[i].beyond_maximum |= cell.beyond_maximum;
747 if (cell.span<axis>() == 1) {
748 inplace_max(_constraints[cell.first<axis>()].alignment, cell.alignment<axis>());
749 inplace_max(_constraints[cell.first<axis>()].minimum, cell.minimum<axis>());
750 inplace_max(_constraints[cell.first<axis>()].preferred, cell.preferred<axis>());
751 inplace_min(_constraints[cell.first<axis>()].maximum, cell.maximum<axis>());
761 constexpr void construct_span_cell(cell_type
const& cell)
noexcept
765 if (cell.span<axis>() > 1) {
766 hilet[span_minimum, span_preferred, span_maximum] =
constraints(cell);
767 if (hilet extra = cell.minimum<axis>() - span_minimum; extra > 0) {
768 hilet extra_per_cell =
std::floor(extra / num_cells);
769 for (
auto i = cell.first<axis>(); i != cell.last<axis>(); ++i) {
770 _constraints[i].minimum += extra_per_cell;
774 if (hilet extra = cell.preferred<axis>() - span_preferred; extra > 0) {
775 hilet extra_per_cell =
std::floor(extra / num_cells);
776 for (
auto i = cell.first<axis>(); i != cell.last<axis>(); ++i) {
777 _constraints[i].preferred += extra_per_cell;
781 if (hilet extra = cell.maximum<axis>() - span_preferred; extra < 0) {
782 hilet extra_per_cell =
std::ceil(extra / num_cells);
783 for (
auto i = cell.first<axis>(); i != cell.last<axis>(); ++i) {
785 _constraints[i].maximum += extra_per_cell;
795 constexpr void construct_fixup() noexcept
797 for (
auto it =
begin(); it !=
end(); ++it) {
799 if (it + 1 !=
end()) {
800 it->margin_after = (it + 1)->margin_before =
std::max(it->margin_after, (it + 1)->margin_before);
804 inplace_max(it->preferred, it->minimum);
805 inplace_max(it->maximum, it->preferred);
808 if (it->padding_before + it->padding_after > it->minimum) {
809 hilet padding_diff = it->padding_after - it->padding_before;
810 hilet
middle = std::clamp(it->minimum / 2.0f + padding_diff, 0.0f, it->minimum);
811 it->padding_after =
middle;
812 it->padding_before = it->minimum -
middle;
825 [[nodiscard]]
constexpr std::tuple<float, float, float>
constraints(const_iterator first, const_iterator last)
const noexcept
827 auto r_minimum = 0.0f;
828 auto r_preferred = 0.0f;
829 auto r_maximum = 0.0f;
830 auto r_margin = 0.0f;
833 r_minimum = first->minimum;
834 r_preferred = first->preferred;
835 r_maximum = first->maximum;
836 for (
auto it = first + 1; it != last; ++it) {
837 r_margin += it->margin_before;
838 r_minimum += it->minimum;
839 r_preferred += it->preferred;
840 r_maximum += it->maximum;
843 return {r_minimum + r_margin, r_preferred + r_margin, r_maximum + r_margin};
854 [[nodiscard]]
constexpr std::tuple<float, float, float>
constraints(
size_t first,
size_t last)
const noexcept
856 hi_axiom(first <= last);
857 hi_axiom(last <=
size());
868 [[nodiscard]]
constexpr float position(const_iterator first, const_iterator last)
const noexcept
870 hi_axiom(first != last);
872 return first->position;
874 return (last - 1)->position;
885 [[nodiscard]]
constexpr float position(
size_t first,
size_t last)
const noexcept
887 hi_axiom(first < last);
888 hi_axiom(last <=
size());
899 [[nodiscard]]
constexpr float extent(const_iterator first, const_iterator last)
const noexcept
904 for (
auto it = first + 1; it != last; ++it) {
905 r += it->margin_before;
919 [[nodiscard]]
constexpr float extent(
size_t first,
size_t last)
const noexcept
921 hi_axiom(first <= last);
922 hi_axiom(last <=
size());
926 [[nodiscard]]
constexpr std::optional<float> guideline(const_iterator it)
const noexcept
928 return it->guideline;
931 [[nodiscard]]
constexpr std::optional<float> guideline(
size_t i)
const noexcept
933 return guideline(
cbegin() + i);
947 using value_type = T;
949 using cell_type = detail::grid_layout_cell<value_type>;
951 using iterator = cell_vector::iterator;
952 using const_iterator = cell_vector::const_iterator;
953 using reference = cell_vector::reference;
954 using const_reference = cell_vector::const_reference;
962 [[nodiscard]]
constexpr friend bool operator==(
grid_layout const&,
grid_layout const&)
noexcept =
default;
964 [[nodiscard]]
constexpr bool empty()
const noexcept
966 return _cells.
empty();
969 [[nodiscard]]
constexpr size_t size()
const noexcept
971 return _cells.
size();
974 [[nodiscard]]
constexpr size_t num_columns()
const noexcept
979 [[nodiscard]]
constexpr size_t num_rows()
const noexcept
984 [[nodiscard]]
constexpr iterator begin()
noexcept
986 return _cells.
begin();
989 [[nodiscard]]
constexpr iterator end()
noexcept
994 [[nodiscard]]
constexpr const_iterator begin()
const noexcept
996 return _cells.
begin();
999 [[nodiscard]]
constexpr const_iterator end()
const noexcept
1001 return _cells.
end();
1004 [[nodiscard]]
constexpr const_iterator cbegin()
const noexcept
1009 [[nodiscard]]
constexpr const_iterator cend()
const noexcept
1011 return _cells.
cend();
1014 [[nodiscard]]
constexpr const_reference operator[](
size_t i)
const noexcept
1019 [[nodiscard]]
constexpr reference operator[](
size_t i)
noexcept
1032 [[nodiscard]]
constexpr bool cell_in_use(
size_t first_column,
size_t first_row,
size_t last_column,
size_t last_row)
noexcept
1035 hi_axiom(first_column < last_column);
1036 hi_axiom(first_row < last_row);
1038 for (hilet& cell : _cells) {
1039 if (first_column >= cell.last_column) {
1042 if (last_column <= cell.first_column) {
1045 if (first_row >= cell.last_row) {
1048 if (last_row <= cell.first_row) {
1066 template<forward_of<value_type> Value>
1068 size_t first_column,
1073 bool beyond_maximum =
false) noexcept
1076 hi_assert(first_column < last_column);
1077 hi_assert(first_row < last_row);
1078 hi_assert(not
cell_in_use(first_column, first_row, last_column, last_row));
1079 auto& r = _cells.emplace_back(first_column, first_row, last_column, last_row, beyond_maximum,
std::forward<Value>(value));
1080 update_after_insert_or_delete();
1092 template<forward_of<value_type> Value>
1093 constexpr reference add_cell(
size_t column,
size_t row, Value&& value,
bool beyond_maximum =
false) noexcept
1098 constexpr void clear() noexcept
1101 update_after_insert_or_delete();
1104 [[nodiscard]]
constexpr box_constraints constraints(
bool left_to_right)
const noexcept
1107 _row_constraints = {_cells, num_rows(),
false};
1108 _column_constraints = {_cells, num_columns(), left_to_right};
1110 auto r = box_constraints{};
1111 std::tie(r.minimum.width(), r.preferred.width(), r.maximum.width()) = _column_constraints.update_constraints();
1112 r.margins.left() = _column_constraints.margin_before();
1113 r.margins.right() = _column_constraints.margin_after();
1114 r.padding.left() = _column_constraints.padding_before();
1115 r.padding.right() = _column_constraints.padding_after();
1117 std::tie(r.minimum.height(), r.preferred.height(), r.maximum.height()) = _row_constraints.update_constraints();
1118 r.margins.bottom() = _row_constraints.margin_after();
1119 r.margins.top() = _row_constraints.margin_before();
1120 r.padding.bottom() = _row_constraints.padding_after();
1121 r.padding.top() = _row_constraints.padding_before();
1124 if (num_rows() == 1 and num_columns() == 1) {
1125 return hi::alignment{_column_constraints.front().alignment, _row_constraints.front().alignment};
1126 }
else if (num_rows() == 1) {
1127 return hi::alignment{_row_constraints.front().alignment};
1128 }
else if (num_columns() == 1) {
1129 return hi::alignment{_column_constraints.front().alignment};
1131 return hi::alignment{};
1146 _column_constraints.layout(shape.x(), shape.width(), shape.centerline, 0);
1147 _row_constraints.layout(shape.y(), shape.height(), shape.baseline, baseline_adjustment);
1150 for (
auto& cell : _cells) {
1151 cell.shape.rectangle = {
1152 _column_constraints.position(cell),
1153 _row_constraints.position(cell),
1154 _column_constraints.extent(cell),
1155 _row_constraints.extent(cell)};
1156 cell.shape.centerline = _column_constraints.guideline(cell);
1157 cell.shape.baseline = _row_constraints.guideline(cell);
1162 cell_vector _cells = {};
1163 size_t _num_rows = 0;
1164 size_t _num_columns = 0;
1165 mutable detail::grid_layout_axis_constraints<axis::y, value_type> _row_constraints = {};
1166 mutable detail::grid_layout_axis_constraints<axis::x, value_type> _column_constraints = {};
1172 constexpr void sort_cells() noexcept
1174 std::sort(_cells.begin(), _cells.end(), [](cell_type
const& lhs, cell_type
const& rhs) {
1175 if (lhs.first_row != rhs.first_row) {
1176 return lhs.first_row < rhs.first_row;
1178 return lhs.first_column < rhs.first_column;
1185 constexpr void update_after_insert_or_delete() noexcept
1191 for (hilet& cell : _cells) {
1192 inplace_max(_num_rows, cell.last_row);
1193 inplace_max(_num_columns, cell.last_column);