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) 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);
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
106 void reserve(ssize_t new_size) noexcept
107 {
108 while (std::ssize(items) < new_size) {
109 items.emplace_back();
110 }
111
112 while (std::ssize(margins) < new_size + 1) {
113 margins.push_back(0.0f);
114 }
115
116 tt_axiom(margins.size() == items.size() + 1);
117 }
118
119private:
120 struct flow_layout_item {
121 constexpr flow_layout_item() noexcept : _minimum_size(0.0f), _resistance(1), offset(-1.0f), size(-1.0f) {}
122
123 constexpr flow_layout_item(flow_layout_item const &rhs) noexcept = default;
124 constexpr flow_layout_item(flow_layout_item &&rhs) noexcept = default;
125 constexpr flow_layout_item &operator=(flow_layout_item const &rhs) noexcept = default;
126 constexpr flow_layout_item &operator=(flow_layout_item &&rhs) noexcept = default;
127
128 constexpr void update(float minimum_size, ranged_int<3> resistance) noexcept
129 {
130 _minimum_size = std::max(_minimum_size, minimum_size);
131 switch (static_cast<int>(resistance)) {
132 case 0: // No resistance has lower priority than strong resistance.
133 _resistance = _resistance == 2 ? 2 : 0;
134 break;
135 case 1: // Normal resistance will not change the value.
136 break;
137 case 2: // Strong resistance overrides all
138 _resistance = 2;
139 break;
140 default:
141 tt_no_default();
142 }
143 }
144
145 [[nodiscard]] float minimum_size() const noexcept {
146 return _minimum_size;
147 }
148
149 [[nodiscard]] float maximum_size() const noexcept {
151 }
152
153 [[nodiscard]] ranged_int<3> resistance() const noexcept
154 {
155 return _resistance;
156 }
157
158 float offset;
159 float size;
160
161 private:
162 float _minimum_size;
163 ranged_int<3> _resistance;
164 };
165
166 /* The margin between the items, margin[0] is the margin
167 * before the first item. margin[items.size()] is the margin
168 * after the last item. margins.size() == items.size() + 1.
169 */
170 std::vector<float> margins;
172
173 void set_items_to_minimum_size() noexcept
174 {
175 for (auto &&item : items) {
176 item.size = std::ceil(item.minimum_size());
177 }
178 calculate_offset_and_size();
179 }
180
181 [[nodiscard]] ssize_t number_of_items_that_can_grow(ranged_int<3> resistance) const noexcept
182 {
183 auto nr_non_max = ssize_t{0};
184
185 for (auto &&item : items) {
186 if (item.resistance() == resistance && item.size < item.maximum_size()) {
187 ++nr_non_max;
188 }
189 }
190 return nr_non_max;
191 }
192
193 [[nodiscard]] ssize_t grow_items(ssize_t nr_non_max, ranged_int<3> resistance, float &extra_size) noexcept
194 {
195 ttlet extra_size_per_item = std::ceil(extra_size / nr_non_max);
196
197 nr_non_max = 0;
198 for (auto &&item : items) {
199 if (item.resistance() == resistance) {
200 auto old_size = item.size;
201
202 ttlet extra_size_this_item = std::min(extra_size, extra_size_per_item);
203
204 item.size = std::ceil(item.size + extra_size_this_item);
205 extra_size -= item.size - old_size;
206
207 if (item.size < item.maximum_size()) {
208 ++nr_non_max;
209 }
210 }
211 }
212 return nr_non_max;
213 }
214
215 void calculate_offset_and_size() noexcept
216 {
217 auto offset = 0.0f;
218 for (ssize_t i = 0; i != std::ssize(items); ++i) {
219 offset += margins[i];
220 items[i].offset = std::floor(offset);
221 offset += items[i].size;
222 }
223 }
224};
225
226}
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:106
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)