HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
cell_address.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#include "required.hpp"
6#include "cast.hpp"
7#include "exception.hpp"
8#include "alignment.hpp"
9
10#pragma once
11
12namespace tt {
13
14template<arrangement Arrangement>
16 bool is_absolute;
17 bool is_opposite;
18 int8_t alignment;
19 int8_t span;
20 int16_t index;
21
22 [[nodiscard]] constexpr cell_address_axis() noexcept :
23 is_absolute(false), is_opposite(false), alignment(0), span(1), index(0) {}
24
29 [[nodiscard]] ssize_t begin(ssize_t size) const noexcept
30 {
31 tt_axiom(is_absolute);
32 tt_axiom(span >= 1);
33 tt_axiom(index >= 0);
34
35 ttlet index_ = (size == 0 || !is_opposite) ? index : size - index - span;
36
37 tt_axiom(index_ >= 0 && index_ < size);
38 return index_;
39 }
40
45 [[nodiscard]] ssize_t end(ssize_t size) const noexcept
46 {
47 tt_axiom(is_absolute);
48 tt_axiom(span >= 1);
49 tt_axiom(index >= 0);
50
51 ttlet index_span = (size == 0 || !is_opposite) ? index + span : size - index;
52
53 tt_axiom(index_span >= 1 && index_span <= size);
54 return index_span;
55 }
56
61 [[nodiscard]] ssize_t aligned_to(ssize_t size) const noexcept
62 {
63 tt_axiom(alignment >= 0 && alignment < span);
64
65 ttlet aligned_to_ = begin(size) + alignment;
66
67 tt_axiom(aligned_to_ >= 0 && aligned_to_ < size);
68 return aligned_to_;
69 }
70
71 [[nodiscard]] constexpr bool operator==(cell_address_axis const &rhs) const noexcept
72 {
73 tt_axiom(span >= 1);
74 tt_axiom(alignment >= 0);
75 tt_axiom(rhs.span >= 1);
76 tt_axiom(rhs.alignment >= 0);
77
78 return
79 is_absolute == rhs.is_absolute &&
80 is_opposite == rhs.is_opposite &&
81 index == rhs.index &&
82 span == rhs.span &&
83 alignment == rhs.alignment;
84 }
85
86
87 [[nodiscard]] friend constexpr cell_address_axis
88 operator*(cell_address_axis const &lhs, cell_address_axis const &rhs) noexcept
89 {
90 cell_address_axis r;
91
92 tt_axiom(lhs.span >= 1);
93 r.span = lhs.span;
94
95 tt_axiom(lhs.alignment >= 0);
96 r.alignment = lhs.alignment;
97
98 if (lhs.is_absolute) {
99 r.is_absolute = true;
100 r.is_opposite = lhs.is_opposite;
101 r.index = lhs.index;
102
103 } else {
104 r.is_absolute = rhs.is_absolute;
105 r.is_opposite = rhs.is_opposite;
106 if (lhs.is_opposite == rhs.is_opposite) {
107 r.index = rhs.index + lhs.index;
108 } else {
109 r.index = rhs.index - lhs.index;
110 }
111 }
112
113 return r;
114 }
115
116 [[nodiscard]] friend std::string to_string(cell_address_axis const &rhs) noexcept
117 {
118 auto r = std::string{};
119
120 auto axis = Arrangement == arrangement::row ? (rhs.is_opposite ? 'T' : 'B') : (rhs.is_opposite ? 'R' : 'L');
121
122 if (!rhs.is_absolute) {
123 if (rhs.index != 0) {
124 r += fmt::format("{}{:+}", axis, rhs.index);
125 }
126 } else {
127 r += fmt::format("{}{}", axis, rhs.index);
128 }
129
130 tt_axiom(rhs.span >= 1);
131 if (rhs.span != 1) {
132 if (std::ssize(r) == 0) {
133 r += axis;
134 }
135 r += fmt::format(":{}", rhs.span);
136
137 tt_axiom(rhs.alignment >= 0);
138 if (rhs.alignment != 0) {
139 r += fmt::format(":{}", rhs.alignment);
140 }
141 }
142
143 return r;
144 }
145
146 friend struct cell_address;
147};
148
152
153 [[nodiscard]] constexpr cell_address() noexcept : row(), column() {}
154
162 [[nodiscard]] constexpr cell_address(char const *str) : row(), column()
163 {
164 enum class state_t { Idle, Coord, Number };
165 enum class part_t { Coord, Span, Alignment };
166
167 char axis = 0;
168 part_t part = part_t::Coord;
169 bool is_absolute = false;
170 bool is_positive = true;
171 int value = 0;
172
173 auto state = state_t::Idle;
174 char c = 0;
175 do {
176 c = *str;
177 auto consume = true;
178
179 switch (state) {
180 case state_t::Idle:
181 value = 0;
182 is_positive = true;
183 part = part_t::Coord;
184 is_absolute = true;
185
186 switch (c) {
187 case 'L':
188 case 'l':
189 state = state_t::Coord;
190 axis = 'L';
191 break;
192 case 'R':
193 case 'r':
194 state = state_t::Coord;
195 axis = 'R';
196 break;
197 case 'B':
198 case 'b':
199 state = state_t::Coord;
200 axis = 'B';
201 break;
202 case 'T':
203 case 't':
204 state = state_t::Coord;
205 axis = 'T';
206 break;
207 case ' ': break;
208 case 0:
209 // End of the string. Don't consume the nul.
210 consume = false;
211 break;
212 default:
213 throw parse_error("Unexpected character");
214 }
215 break;
216
217 case state_t::Coord:
218 switch (c) {
219 case '+':
220 state = state_t::Number;
221 is_absolute = false;
222 is_positive = true;
223 break;
224 case '-':
225 state = state_t::Number;
226 is_absolute = false;
227 is_positive = false;
228 break;
229 case ':':
230 state = state_t::Number;
231 part = part_t::Span;
232 break;
233 case ' ': break;
234 default:
235 // This is already the first digit, switch to the next state
236 // without consuming this character.
237 state = state_t::Number;
238 consume = false;
239 }
240 break;
241
242 case state_t::Number:
243 if (c >= '0' && c <= '9') {
244 value *= 10;
245 value += static_cast<int>(c - '0');
246
247 } else {
248 if (!is_positive) {
249 value = -value;
250 }
251
252 // The first non-digit character (including '\0') is the new
253 // command or the end of the string. Switch the Idle and
254 // don't consume the character.
255 ttlet is_row = axis == 'B' || axis == 'T';
256 ttlet is_opposite = axis == 'R' || axis == 'T';
257 switch (part) {
258 case part_t::Coord:
259 if (is_row) {
260 row.index = narrow_cast<int16_t>(value);
261 row.is_opposite = is_opposite;
262 row.is_absolute = is_absolute;
263 } else {
264 column.index = narrow_cast<int16_t>(value);
265 column.is_opposite = is_opposite;
266 column.is_absolute = is_absolute;
267 }
268 break;
269
270 case part_t::Span:
271 if (is_row) {
272 row.span = narrow_cast<int8_t>(value);
273 row.is_opposite = is_opposite;
274 } else {
275 column.span = narrow_cast<int8_t>(value);
276 column.is_opposite = is_opposite;
277 }
278 break;
279
280 case part_t::Alignment:
281 if (is_row) {
282 row.alignment = narrow_cast<int8_t>(value);
283 } else {
284 column.alignment = narrow_cast<int8_t>(value);
285 }
286 break;
287
288 default: tt_no_default();
289 }
290
291 if (c == ':') {
292 switch (part) {
293 case part_t::Coord:
294 // A ':' after a coord means a span, parse the next number.
295 value = 0;
296 part = part_t::Span;
297 is_positive = true;
298 state = state_t::Number;
299 break;
300
301 case part_t::Span:
302 // A ':' after a span means an alignment, parse the next number.
303 value = 0;
304 part = part_t::Alignment;
305 is_positive = true;
306 state = state_t::Number;
307 break;
308
309 case part_t::Alignment:
310 throw parse_error("Unexpected third ':'");
311
312 default: tt_no_default();
313 }
314
315 } else {
316 // Any other character means we start over, do not consume this character.
317 consume = false;
318 state = state_t::Idle;
319 }
320 }
321 break;
322
323 default: tt_no_default();
324 }
325
326 if (consume) {
327 ++str;
328 }
329 } while (c != 0);
330 }
331
334 constexpr cell_address &operator*=(cell_address const &rhs) noexcept
335 {
336 *this = rhs * *this;
337 return *this;
338 }
339
340 [[nodiscard]] constexpr bool operator==(cell_address const &rhs) const noexcept
341 {
342 return this->row == rhs.row && this->column == rhs.column;
343 }
344
347 [[nodiscard]] friend constexpr cell_address operator*(cell_address const &lhs, cell_address const &rhs) noexcept
348 {
349 cell_address r;
350 r.row = lhs.row * rhs.row;
351 r.column = lhs.column * rhs.column;
352 return r;
353 }
354
355
356 [[nodiscard]] friend std::string to_string(cell_address const &rhs) noexcept
357 {
358 return to_string(rhs.column) + to_string(rhs.row);
359 }
360
361 friend std::ostream &operator<<(std::ostream &lhs, cell_address const &rhs)
362 {
363 return lhs << to_string(rhs);
364 }
365};
366
367[[nodiscard]] constexpr cell_address operator"" _ca(char const *str, size_t str_len) noexcept
368{
369 return cell_address{str};
370}
371
372} // namespace tt
Definition cell_address.hpp:15
ssize_t end(ssize_t size) const noexcept
Find one beyond the end of the cell.
Definition cell_address.hpp:45
ssize_t begin(ssize_t size) const noexcept
Find the begin of the cell.
Definition cell_address.hpp:29
ssize_t aligned_to(ssize_t size) const noexcept
Find on which cell this aligns to.
Definition cell_address.hpp:61
Definition cell_address.hpp:149
friend constexpr cell_address operator*(cell_address const &lhs, cell_address const &rhs) noexcept
Transform rhs by lhs.
Definition cell_address.hpp:347
constexpr cell_address(char const *str)
Parse a cell position.
Definition cell_address.hpp:162
constexpr cell_address & operator*=(cell_address const &rhs) noexcept
Transform lhs/this by rhs.
Definition cell_address.hpp:334
Exception thrown during parsing on an error.
Definition exception.hpp:21