HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
flow_layout.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "required.hpp"
8#include "interval.hpp"
9#include "alignment.hpp"
10#include "ranged_numeric.hpp"
11#include <vector>
12#include <optional>
13
14namespace tt {
15
19public:
20 flow_layout() noexcept : margins(), items() {
21 clear();
22 }
23
24 void clear() noexcept
25 {
26 margins.clear();
27 margins.push_back(0.0f);
28 items.clear();
29 }
30
31 [[nodiscard]] size_t nr_items() const noexcept
32 {
33 return items.size();
34 }
35
36 void
37 update(ssize_t index, float extent, ranged_int<3> resistance, float margin, relative_base_line base_line) noexcept
38 {
39 tt_axiom(index >= 0);
40 tt_axiom(index < std::ssize(items));
41 tt_axiom(index + 1 < std::ssize(margins));
42 items[index].update(extent, resistance, base_line);
43 margins[index] = std::max(margins[index], margin);
44 margins[index+1] = std::max(margins[index+1], margin);
45 }
46
47 [[nodiscard]] float minimum_size() const noexcept
48 {
49 auto a = std::accumulate(margins.cbegin(), margins.cend(), 0.0f);
50 return std::accumulate(items.cbegin(), items.cend(), a, [](ttlet &acc, ttlet &item) {
51 return acc + item.minimum_size();
52 });
53 }
54
57 void set_size(float total_size) noexcept {
58 set_items_to_minimum_size();
59 auto extra_size = total_size - size();
60
61 for (ttlet resistance : ranged_int<3>::range()) {
62 auto nr_can_grow = number_of_items_that_can_grow(resistance);
63 while (extra_size >= 1.0f && nr_can_grow != 0) {
64 nr_can_grow = grow_items(nr_can_grow, resistance, extra_size);
65 }
66 }
67
68 calculate_offset_and_size();
69 }
70
74 [[nodiscard]] float size() const noexcept
75 {
76 if (items.empty()) {
77 return margins.back();
78 } else {
79 tt_axiom(items.back().offset >= 0.0f);
80 return items.back().offset + items.back().size + margins.back();
81 }
82 }
83
88 [[nodiscard]] std::pair<float, float> get_offset_and_size(ssize_t first, ssize_t last) const noexcept
89 {
90 tt_axiom(first >= 0 && first < std::ssize(items));
91 tt_axiom(last > 0 && last <= std::ssize(items));
92
93 auto offset = items[first].offset;
94 auto size = (items[last - 1].offset + items[last - 1].size) - offset;
95 return {offset, size};
96 }
97
98 [[nodiscard]] std::pair<float, float> get_offset_and_size(ssize_t index) const noexcept
99 {
100 return get_offset_and_size(index, index + 1);
101
102 }
103
104 [[nodiscard]] relative_base_line get_base_line(ssize_t index) const noexcept
105 {
106 tt_axiom(index >= 0 && index < ssize(items));
107 return items[index].base_line();
108 }
109
112 void reserve(ssize_t new_size) noexcept
113 {
114 while (std::ssize(items) < new_size) {
115 items.emplace_back();
116 }
117
118 while (std::ssize(margins) < new_size + 1) {
119 margins.push_back(0.0f);
120 }
121
122 tt_axiom(margins.size() == items.size() + 1);
123 }
124
125private:
126 struct flow_layout_item {
127 constexpr flow_layout_item() noexcept : _minimum_size(0.0f), _resistance(1), _base_line(), offset(-1.0f), size(-1.0f) {}
128
129 constexpr flow_layout_item(flow_layout_item const &rhs) noexcept = default;
130 constexpr flow_layout_item(flow_layout_item &&rhs) noexcept = default;
131 constexpr flow_layout_item &operator=(flow_layout_item const &rhs) noexcept = default;
132 constexpr flow_layout_item &operator=(flow_layout_item &&rhs) noexcept = default;
133
134 constexpr void update(float minimum_size, ranged_int<3> resistance, relative_base_line base_line) noexcept
135 {
136 _minimum_size = std::max(_minimum_size, minimum_size);
137 switch (static_cast<int>(resistance)) {
138 case 0: // No resistance has lower priority than strong resistance.
139 _resistance = _resistance == 2 ? 2 : 0;
140 break;
141 case 1: // Normal resistance will not change the value.
142 break;
143 case 2: // Strong resistance overrides all
144 _resistance = 2;
145 break;
146 default:
147 tt_no_default();
148 }
149 _base_line = std::max(_base_line, base_line);
150 }
151
152 [[nodiscard]] float minimum_size() const noexcept {
153 return _minimum_size;
154 }
155
156 [[nodiscard]] float maximum_size() const noexcept {
158 }
159
160 [[nodiscard]] ranged_int<3> resistance() const noexcept
161 {
162 return _resistance;
163 }
164
165 [[nodiscard]] relative_base_line base_line() const noexcept
166 {
167 return _base_line;
168 }
169
170 float offset;
171 float size;
172
173 private:
174 float _minimum_size;
175 ranged_int<3> _resistance;
176 relative_base_line _base_line;
177 };
178
179 /* The margin between the items, margin[0] is the margin
180 * before the first item. margin[items.size()] is the margin
181 * after the last item. margins.size() == items.size() + 1.
182 */
183 std::vector<float> margins;
185
186 void set_items_to_minimum_size() noexcept
187 {
188 for (auto &&item : items) {
189 item.size = std::ceil(item.minimum_size());
190 }
191 calculate_offset_and_size();
192 }
193
194 [[nodiscard]] ssize_t number_of_items_that_can_grow(ranged_int<3> resistance) const noexcept
195 {
196 auto nr_non_max = ssize_t{0};
197
198 for (auto &&item : items) {
199 if (item.resistance() == resistance && item.size < item.maximum_size()) {
200 ++nr_non_max;
201 }
202 }
203 return nr_non_max;
204 }
205
206 [[nodiscard]] ssize_t grow_items(ssize_t nr_non_max, ranged_int<3> resistance, float &extra_size) noexcept
207 {
208 ttlet extra_size_per_item = std::ceil(extra_size / nr_non_max);
209
210 nr_non_max = 0;
211 for (auto &&item : items) {
212 if (item.resistance() == resistance) {
213 auto old_size = item.size;
214
215 ttlet extra_size_this_item = std::min(extra_size, extra_size_per_item);
216
217 item.size = std::ceil(item.size + extra_size_this_item);
218 extra_size -= item.size - old_size;
219
220 if (item.size < item.maximum_size()) {
221 ++nr_non_max;
222 }
223 }
224 }
225 return nr_non_max;
226 }
227
228 void calculate_offset_and_size() noexcept
229 {
230 auto offset = 0.0f;
231 for (ssize_t i = 0; i != std::ssize(items); ++i) {
232 offset += margins[i];
233 items[i].offset = std::floor(offset);
234 offset += items[i].size;
235 }
236 }
237};
238
239}
Definition alignment.hpp:104
Layout algorithm.
Definition flow_layout.hpp:18
void reserve(ssize_t new_size) noexcept
Grow layout to include upto new_size of items.
Definition flow_layout.hpp:112
std::pair< float, float > get_offset_and_size(ssize_t first, ssize_t last) const noexcept
Definition flow_layout.hpp:88
float size() const noexcept
Calculate the size of the combined items in the layout.
Definition flow_layout.hpp:74
void set_size(float total_size) noexcept
Update the layout of all items based on the total size.
Definition flow_layout.hpp:57
A ranged integer.
Definition ranged_numeric.hpp:21
T accumulate(T... args)
T back(T... args)
T cbegin(T... args)
T ceil(T... args)
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T cend(T... args)
T floor(T... args)
T infinity(T... args)
T max(T... args)
T min(T... args)
T push_back(T... args)
T size(T... args)