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 template<std::convertible_to<value_type> Value>
44 constexpr grid_layout_cell(
50 Value&& value) noexcept :
51 first_column(first_column),
53 last_column(last_column),
55 beyond_maximum(beyond_maximum),
58 hi_assert(first_column < last_column);
59 hi_assert(first_row < last_row);
62 constexpr void set_constraints(
box_constraints const& constraints)
noexcept
64 _constraints = constraints;
67 template<hi::axis Axis>
68 [[nodiscard]]
constexpr size_t first()
const noexcept
70 if constexpr (Axis == axis::x) {
72 }
else if constexpr (Axis == axis::y) {
75 hi_static_no_default();
79 template<hi::axis Axis>
80 [[nodiscard]]
constexpr size_t last()
const noexcept
82 if constexpr (Axis == axis::x) {
84 }
else if constexpr (Axis == axis::y) {
87 hi_static_no_default();
91 template<hi::axis Axis>
92 [[nodiscard]]
constexpr size_t span()
const noexcept
94 hi_axiom(first<Axis>() < last<Axis>());
95 return last<Axis>() - first<Axis>();
98 template<hi::axis Axis>
99 [[nodiscard]]
constexpr auto alignment()
const noexcept
101 if constexpr (Axis == axis::x) {
102 return _constraints.alignment.horizontal();
103 }
else if constexpr (Axis == axis::y) {
104 return _constraints.alignment.vertical();
106 hi_static_no_default();
110 template<hi::axis Axis>
111 [[nodiscard]]
constexpr float minimum()
const noexcept
113 if constexpr (Axis == axis::x) {
114 return _constraints.minimum.width();
115 }
else if constexpr (Axis == axis::y) {
116 return _constraints.minimum.height();
118 hi_static_no_default();
122 template<hi::axis Axis>
123 [[nodiscard]]
constexpr float preferred()
const noexcept
125 if constexpr (Axis == axis::x) {
126 return _constraints.preferred.width();
127 }
else if constexpr (Axis == axis::y) {
128 return _constraints.preferred.height();
130 hi_static_no_default();
134 template<hi::axis Axis>
135 [[nodiscard]]
constexpr float maximum()
const noexcept
137 if constexpr (Axis == axis::x) {
138 return _constraints.maximum.width();
139 }
else if constexpr (Axis == axis::y) {
140 return _constraints.maximum.height();
142 hi_static_no_default();
146 template<hi::axis Axis>
147 [[nodiscard]]
constexpr float margin_before(
bool forward)
const noexcept
149 if constexpr (Axis == axis::x) {
151 return _constraints.margins.left();
153 return _constraints.margins.right();
155 }
else if constexpr (Axis == axis::y) {
157 return _constraints.margins.bottom();
159 return _constraints.margins.top();
162 hi_static_no_default();
166 template<hi::axis Axis>
167 [[nodiscard]]
constexpr float margin_after(
bool forward)
const noexcept
169 if constexpr (Axis == axis::x) {
171 return _constraints.margins.right();
173 return _constraints.margins.left();
175 }
else if constexpr (Axis == axis::y) {
177 return _constraints.margins.top();
179 return _constraints.margins.bottom();
182 hi_static_no_default();
191class grid_layout_axis_constraints {
193 constexpr static hi::axis axis = Axis;
195 using value_type = T;
196 using alignment_type = std::conditional_t<axis == axis::y, vertical_alignment, horizontal_alignment>;
248 using iterator = constraint_vector::iterator;
249 using const_iterator = constraint_vector::const_iterator;
250 using reverse_iterator = constraint_vector::reverse_iterator;
251 using reference = constraint_vector::reference;
252 using const_reference = constraint_vector::const_reference;
254 constexpr ~grid_layout_axis_constraints() =
default;
255 constexpr grid_layout_axis_constraints() noexcept = default;
256 constexpr grid_layout_axis_constraints(grid_layout_axis_constraints const&) noexcept = default;
257 constexpr grid_layout_axis_constraints(grid_layout_axis_constraints&&) noexcept = default;
258 constexpr grid_layout_axis_constraints& operator=(grid_layout_axis_constraints const&) noexcept = default;
259 constexpr grid_layout_axis_constraints& operator=(grid_layout_axis_constraints&&) noexcept = default;
260 [[nodiscard]] constexpr friend
bool
261 operator==(grid_layout_axis_constraints const&, grid_layout_axis_constraints const&) noexcept = default;
270 constexpr grid_layout_axis_constraints(cell_vector const& cells,
size_t num,
bool forward) noexcept :
271 _constraints(num), _forward(forward)
273 for (
auto const& cell : cells) {
274 construct_simple_cell(cell);
278 for (
auto const& cell : cells) {
279 construct_span_cell(cell);
284 [[nodiscard]]
constexpr float margin_before() const noexcept
286 return empty() ? 0 : _forward ?
front().margin_before :
back().margin_before;
289 [[nodiscard]]
constexpr float margin_after() const noexcept
291 return empty() ? 0 : _forward ?
back().margin_after :
front().margin_after;
294 [[nodiscard]]
constexpr float padding_before() const noexcept
296 return empty() ? 0 : _forward ?
front().padding_before :
back().padding_before;
299 [[nodiscard]]
constexpr float padding_after() const noexcept
301 return empty() ? 0 : _forward ?
back().padding_after :
front().padding_after;
304 [[nodiscard]]
constexpr std::tuple<float, float, float> update_constraints() const noexcept
318 return constraints(cell.template first<axis>(), cell.template last<axis>());
321 [[nodiscard]]
constexpr float position(cell_type
const& cell)
const noexcept
323 return position(cell.template first<axis>(), cell.template last<axis>());
326 [[nodiscard]]
constexpr float extent(cell_type
const& cell)
const noexcept
328 return extent(cell.template first<axis>(), cell.template last<axis>());
331 [[nodiscard]]
constexpr std::optional<float> guideline(cell_type
const& cell)
const noexcept
333 if (cell.template span<axis>() == 1) {
334 return guideline(cell.template first<axis>());
362 constexpr void layout(
float new_position,
float new_extent, std::optional<float> external_guideline,
float guideline_width)
noexcept
365 for (
auto& constraint : _constraints) {
366 constraint.extent = constraint.preferred;
370 auto [total_extent, count] = layout_shrink(
begin(),
end());
371 while (total_extent > new_extent and count != 0) {
373 std::tie(total_extent, count) = layout_shrink(
begin(),
end(), total_extent - new_extent, count);
378 while (total_extent < new_extent and count != 0) {
380 std::tie(total_extent, count) = layout_expand(
begin(),
end(), new_extent - total_extent, count);
384 if (total_extent < new_extent) {
387 return item.beyond_maximum;
390 auto expand = new_extent - total_extent;
391 auto const expand_per =
std::ceil(expand / count);
393 for (
auto& constraint : _constraints) {
394 auto const expand_this =
std::min(expand_per, expand);
395 if (constraint.beyond_maximum) {
396 constraint.extent += expand_this;
397 expand -= expand_this;
405 if (total_extent < new_extent and not
empty()) {
407 front().extent += new_extent - total_extent;
411 layout_position(
begin(),
end(), new_position, guideline_width);
413 layout_position(
rbegin(),
rend(), new_position, guideline_width);
416 if (external_guideline and
size() == 1) {
419 front().guideline = *external_guideline;
425 [[nodiscard]]
constexpr size_t size() const noexcept
427 return _constraints.size();
432 [[nodiscard]]
constexpr bool empty() const noexcept
434 return _constraints.empty();
439 [[nodiscard]]
constexpr iterator
begin() noexcept
441 return _constraints.begin();
446 [[nodiscard]]
constexpr const_iterator
begin() const noexcept
448 return _constraints.begin();
453 [[nodiscard]]
constexpr const_iterator
cbegin() const noexcept
455 return _constraints.cbegin();
460 [[nodiscard]]
constexpr iterator
end() noexcept
462 return _constraints.end();
467 [[nodiscard]]
constexpr const_iterator
end() const noexcept
469 return _constraints.end();
474 [[nodiscard]]
constexpr const_iterator
cend() const noexcept
476 return _constraints.cend();
481 [[nodiscard]]
constexpr reverse_iterator
rbegin() noexcept
483 return _constraints.rbegin();
488 [[nodiscard]]
constexpr reverse_iterator
rend() noexcept
490 return _constraints.rend();
499 [[nodiscard]]
constexpr reference
operator[](
size_t index)
noexcept
501 hi_axiom(index <
size());
502 return _constraints[index];
511 [[nodiscard]]
constexpr const_reference
operator[](
size_t index)
const noexcept
513 hi_axiom(index <
size());
514 return _constraints[index];
522 [[nodiscard]]
constexpr reference
front() noexcept
524 hi_axiom(not
empty());
525 return _constraints.front();
533 [[nodiscard]]
constexpr const_reference
front() const noexcept
535 hi_axiom(not
empty());
536 return _constraints.front();
544 [[nodiscard]]
constexpr reference
back() noexcept
546 hi_axiom(not
empty());
547 return _constraints.back();
555 [[nodiscard]]
constexpr const_reference
back() const noexcept
557 hi_axiom(not
empty());
558 return _constraints.back();
570 constraint_vector _constraints = {};
574 bool _forward =
true;
593 layout_shrink(const_iterator first, const_iterator last,
float shrink = 0.0f,
size_t count = 1) noexcept
598 hi_axiom(shrink >= 0);
600 auto const shrink_per =
std::floor(shrink / count);
602 auto new_extent = 0.0f;
603 auto new_count = 0_uz;
604 for (
auto it = first_; it != last_; ++it) {
605 auto const shrink_this =
std::max({shrink_per, shrink, it->extent - it->minimum});
606 it->extent -= shrink_this;
607 shrink -= shrink_this;
610 new_extent += it->margin_before;
612 new_extent += it->extent;
614 if (it->extent > it->minimum) {
619 return {new_extent, new_count};
638 [[nodiscard]]
constexpr std::pair<float, size_t>
639 layout_expand(const_iterator first, const_iterator last,
float expand = 0.0f,
size_t count = 1) noexcept
644 hi_axiom(expand >= 0.0f);
646 auto const expand_per =
std::ceil(expand / count);
647 hi_axiom(expand_per >= 0.0f);
649 auto new_extent = 0.0f;
650 auto new_count = 0_uz;
651 for (
auto it = first_; it != last_; ++it) {
652 auto const expand_this =
std::min({expand_per, expand, it->maximum - it->extent});
653 it->extent += expand_this;
654 expand -= expand_this;
657 new_extent += it->margin_before;
659 new_extent += it->extent;
661 if (it->extent < it->maximum) {
666 return {new_extent, new_count};
669 constexpr void layout_position(
auto first,
auto last,
float start_position,
float guideline_width)
noexcept
671 auto position = start_position;
672 for (
auto it = first; it != last; ++it) {
673 it->position = position;
674 it->guideline =
make_guideline(it->alignment, position, position + it->extent, guideline_width);
676 position += it->extent;
677 position += it->margin_after;
688 constexpr void construct_simple_cell(cell_type
const& cell)
noexcept
690 inplace_max(_constraints[cell.template first<axis>()].margin_before, cell.template margin_before<axis>(_forward));
691 inplace_max(_constraints[cell.template last<axis>() - 1].margin_after, cell.template margin_after<axis>(_forward));
693 for (
auto i = cell.template first<axis>(); i != cell.template last<axis>(); ++i) {
694 _constraints[i].beyond_maximum |= cell.beyond_maximum;
697 if (cell.template span<axis>() == 1) {
698 inplace_max(_constraints[cell.template first<axis>()].alignment, cell.template
alignment<axis>());
699 inplace_max(_constraints[cell.template first<axis>()].minimum, cell.template minimum<axis>());
700 inplace_max(_constraints[cell.template first<axis>()].preferred, cell.template preferred<axis>());
701 inplace_min(_constraints[cell.template first<axis>()].maximum, cell.template maximum<axis>());
711 constexpr void construct_span_cell(cell_type
const& cell)
noexcept
715 if (cell.template span<axis>() > 1) {
716 auto const[span_minimum, span_preferred, span_maximum] =
constraints(cell);
717 if (
auto const extra = cell.template minimum<axis>() - span_minimum; extra > 0) {
718 auto const extra_per_cell =
std::floor(extra / num_cells);
719 for (
auto i = cell.template first<axis>(); i != cell.template last<axis>(); ++i) {
720 _constraints[i].minimum += extra_per_cell;
724 if (
auto const extra = cell.template preferred<axis>() - span_preferred; extra > 0) {
725 auto const extra_per_cell =
std::floor(extra / num_cells);
726 for (
auto i = cell.template first<axis>(); i != cell.template last<axis>(); ++i) {
727 _constraints[i].preferred += extra_per_cell;
731 if (
auto const extra = cell.template maximum<axis>() - span_preferred; extra < 0) {
732 auto const extra_per_cell =
std::ceil(extra / num_cells);
733 for (
auto i = cell.template first<axis>(); i != cell.template last<axis>(); ++i) {
735 _constraints[i].maximum += extra_per_cell;
745 constexpr void construct_fixup() noexcept
747 for (
auto it =
begin(); it !=
end(); ++it) {
749 if (it + 1 !=
end()) {
750 it->margin_after = (it + 1)->margin_before =
std::max(it->margin_after, (it + 1)->margin_before);
754 inplace_max(it->preferred, it->minimum);
755 inplace_max(it->maximum, it->preferred);
767 [[nodiscard]]
constexpr std::tuple<float, float, float>
constraints(const_iterator first, const_iterator last)
const noexcept
769 auto r_minimum = 0.0f;
770 auto r_preferred = 0.0f;
771 auto r_maximum = 0.0f;
772 auto r_margin = 0.0f;
775 r_minimum = first->minimum;
776 r_preferred = first->preferred;
777 r_maximum = first->maximum;
778 for (
auto it = first + 1; it != last; ++it) {
779 r_margin += it->margin_before;
780 r_minimum += it->minimum;
781 r_preferred += it->preferred;
782 r_maximum += it->maximum;
785 return {r_minimum + r_margin, r_preferred + r_margin, r_maximum + r_margin};
796 [[nodiscard]]
constexpr std::tuple<float, float, float>
constraints(
size_t first,
size_t last)
const noexcept
798 hi_axiom(first <= last);
799 hi_axiom(last <=
size());
810 [[nodiscard]]
constexpr float position(const_iterator first, const_iterator last)
const noexcept
812 hi_axiom(first != last);
814 return first->position;
816 return (last - 1)->position;
827 [[nodiscard]]
constexpr float position(
size_t first,
size_t last)
const noexcept
829 hi_axiom(first < last);
830 hi_axiom(last <=
size());
841 [[nodiscard]]
constexpr float extent(const_iterator first, const_iterator last)
const noexcept
846 for (
auto it = first + 1; it != last; ++it) {
847 r += it->margin_before;
861 [[nodiscard]]
constexpr float extent(
size_t first,
size_t last)
const noexcept
863 hi_axiom(first <= last);
864 hi_axiom(last <=
size());
868 [[nodiscard]]
constexpr std::optional<float> guideline(const_iterator it)
const noexcept
870 return it->guideline;
873 [[nodiscard]]
constexpr std::optional<float> guideline(
size_t i)
const noexcept
875 return guideline(
cbegin() + i);
889 using value_type = T;
891 using cell_type = detail::grid_layout_cell<value_type>;
893 using iterator = cell_vector::iterator;
894 using const_iterator = cell_vector::const_iterator;
895 using reference = cell_vector::reference;
896 using const_reference = cell_vector::const_reference;
904 [[nodiscard]]
constexpr friend bool operator==(
grid_layout const&,
grid_layout const&)
noexcept =
default;
906 [[nodiscard]]
constexpr bool empty()
const noexcept
908 return _cells.
empty();
911 [[nodiscard]]
constexpr size_t size()
const noexcept
913 return _cells.
size();
916 [[nodiscard]]
constexpr size_t num_columns()
const noexcept
921 [[nodiscard]]
constexpr size_t num_rows()
const noexcept
926 [[nodiscard]]
constexpr iterator begin()
noexcept
928 return _cells.
begin();
931 [[nodiscard]]
constexpr iterator end()
noexcept
936 [[nodiscard]]
constexpr const_iterator begin()
const noexcept
938 return _cells.
begin();
941 [[nodiscard]]
constexpr const_iterator end()
const noexcept
946 [[nodiscard]]
constexpr const_iterator cbegin()
const noexcept
951 [[nodiscard]]
constexpr const_iterator cend()
const noexcept
953 return _cells.
cend();
956 [[nodiscard]]
constexpr const_reference operator[](
size_t i)
const noexcept
961 [[nodiscard]]
constexpr reference operator[](
size_t i)
noexcept
974 [[nodiscard]]
constexpr bool cell_in_use(
size_t first_column,
size_t first_row,
size_t last_column,
size_t last_row)
noexcept
977 hi_axiom(first_column < last_column);
978 hi_axiom(first_row < last_row);
980 for (
auto const& cell : _cells) {
981 if (first_column >= cell.last_column) {
984 if (last_column <= cell.first_column) {
987 if (first_row >= cell.last_row) {
990 if (last_row <= cell.first_row) {
1008 template<forward_of<value_type> Value>
1010 size_t first_column,
1015 bool beyond_maximum =
false) noexcept
1018 hi_assert(first_column < last_column);
1019 hi_assert(first_row < last_row);
1020 hi_assert(not
cell_in_use(first_column, first_row, last_column, last_row));
1021 auto& r = _cells.emplace_back(first_column, first_row, last_column, last_row, beyond_maximum,
std::forward<Value>(value));
1022 update_after_insert_or_delete();
1034 template<forward_of<value_type> Value>
1035 constexpr reference
add_cell(
size_t column,
size_t row, Value&& value,
bool beyond_maximum =
false) noexcept
1040 constexpr void clear() noexcept
1043 update_after_insert_or_delete();
1046 [[nodiscard]]
constexpr box_constraints constraints(
bool left_to_right)
const noexcept
1049 _row_constraints = {_cells, num_rows(),
false};
1050 _column_constraints = {_cells, num_columns(), left_to_right};
1052 auto r = box_constraints{};
1053 std::tie(r.minimum.width(), r.preferred.width(), r.maximum.width()) = _column_constraints.update_constraints();
1054 r.margins.left() = _column_constraints.margin_before();
1055 r.margins.right() = _column_constraints.margin_after();
1057 std::tie(r.minimum.height(), r.preferred.height(), r.maximum.height()) = _row_constraints.update_constraints();
1058 r.margins.bottom() = _row_constraints.margin_after();
1059 r.margins.top() = _row_constraints.margin_before();
1062 if (num_rows() == 1 and num_columns() == 1) {
1063 return hi::alignment{_column_constraints.front().alignment, _row_constraints.front().alignment};
1064 }
else if (num_rows() == 1) {
1065 return hi::alignment{_row_constraints.front().alignment};
1066 }
else if (num_columns() == 1) {
1067 return hi::alignment{_column_constraints.front().alignment};
1069 return hi::alignment{};
1084 _column_constraints.layout(shape.x(), shape.width(), shape.centerline, 0);
1085 _row_constraints.layout(shape.y(), shape.height(), shape.baseline, baseline_adjustment);
1088 for (
auto& cell : _cells) {
1089 cell.shape.rectangle = {
1090 _column_constraints.position(cell),
1091 _row_constraints.position(cell),
1092 _column_constraints.extent(cell),
1093 _row_constraints.extent(cell)};
1094 cell.shape.centerline = _column_constraints.guideline(cell);
1095 cell.shape.baseline = _row_constraints.guideline(cell);
1100 cell_vector _cells = {};
1101 size_t _num_rows = 0;
1102 size_t _num_columns = 0;
1103 mutable detail::grid_layout_axis_constraints<axis::y, value_type> _row_constraints = {};
1104 mutable detail::grid_layout_axis_constraints<axis::x, value_type> _column_constraints = {};
1110 constexpr void sort_cells() noexcept
1112 std::sort(_cells.begin(), _cells.end(), [](cell_type
const& lhs, cell_type
const& rhs) {
1113 if (lhs.first_row != rhs.first_row) {
1114 return lhs.first_row < rhs.first_row;
1116 return lhs.first_column < rhs.first_column;
1123 constexpr void update_after_insert_or_delete() noexcept
1129 for (
auto const& cell : _cells) {
1130 inplace_max(_num_rows, cell.last_row);
1131 inplace_max(_num_columns, cell.last_column);