Project Alice
Loading...
Searching...
No Matches
table.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <vector>
5#include <string>
6#include <functional>
9
10namespace table {
11
15};
16
21};
22
24 switch(order) {
31 default:
33 }
34}
35
36struct sort_data {
39};
40
41template<typename item_type>
42void tooltip_fallback (sys::state& state, ui::element_base* container, text::columnar_layout& contents, const item_type& a, std::string fallback) {
43 auto box = text::open_layout_box(contents, 0);
44 text::localised_format_box(state, contents, box, std::string_view(fallback));
45 text::close_layout_box(contents, box);
46}
47
48template<typename item_type>
49struct column {
50 bool sortable = true;
51 std::string header = "???";
52 std::function<bool(sys::state& state, ui::element_base* container, const item_type & a, const item_type & b)> compare;
53 std::function<std::string(sys::state& state, ui::element_base* container, const item_type& a)> view;
54 std::function<void(
55 sys::state& state,
56 ui::element_base* container,
57 text::columnar_layout& contents,
58 const item_type& a,
59 std::string fallback
60 )> update_tooltip = tooltip_fallback<item_type>;
61 std::string cell_definition_string="thin_cell_number";
62 std::string header_definition_string="thin_cell_number";
63 bool has_tooltip = false;
64};
65
66template<typename item_type>
67struct data {
68 std::vector<column<item_type>> columns;
69 std::vector<sort_data> sort_priority;
70 std::vector<item_type> data;
71
73 if(!columns[column_index].sortable) {
74 return;
75 }
76
77 uint8_t offset = 0;
78 bool offset_found = false;
79 sort_order current_order = sort_order::undefined;
80 for(uint8_t i = 0; i < sort_priority.size(); i++) {
81 if(sort_priority[i].sorted_index == column_index) {
82 offset_found = true;
83 offset = i;
84 current_order = sort_priority[i].order;
85 }
86 }
87
88 if(offset_found) {
89 auto begin = sort_priority.begin();
90 sort_priority.erase(begin + offset);
91 }
92
93 sort_data new_sort_data = {};
94 new_sort_data.order = toggle_sort_order(current_order);
95 new_sort_data.sorted_index = column_index;
96 sort_priority.push_back(new_sort_data);
97 }
98
100 for(auto& order : sort_priority) {
101 if(order.order == sort_order::ascending) {
103 data.begin(),
104 data.end(),
105 [&](const item_type& a, const item_type& b) {
106 return columns[order.sorted_index].compare(state, container, a, b);
107 }
108 );
109 } else {
111 data.begin(),
112 data.end(),
113 [&](const item_type& a, const item_type& b) {
114 return columns[order.sorted_index].compare(state, container, b, a);
115 }
116 );
117 }
118 }
119 }
120};
121
124};
125
126template<typename item_type>
128public:
130 std::string name;
131
132 sort_button(uint8_t in_column, std::string in_name) {
133 column = in_column;
134 name = in_name;
135 }
136
137 void button_action(sys::state& state) noexcept override {
138 sort_signal signal = { };
139 signal.column = column;
140 send(state, parent, signal);
141 }
142
143 ui::tooltip_behavior has_tooltip(sys::state& state) noexcept override {
145 }
146
147 void update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept override {
148 auto box = text::open_layout_box(contents, 0);
149 text::localised_format_box(state, contents, box, std::string_view(name));
150 text::close_layout_box(contents, box);
151 }
152};
153
154template<typename item_type>
158 std::string text_to_set;
160};
161
165};
166
170};
171
174};
175
177 bool has_tooltip = false;
179};
180
185};
186
187template<typename item_type>
189public:
193
194 entry(uint8_t in_row, uint8_t in_column) {
195 row = in_row;
196 column = in_column;
197 }
198
199 void on_update(sys::state& state) noexcept override {
200 table_signal<item_type> signal{ };
201 signal.column = column;
202 signal.row = row;
203 auto response = send_and_retrieve<table_signal<item_type>>(state, parent, signal);
204 set_button_text(state, response.text_to_set);
205 }
206
207 virtual ui::message_result on_mouse_move(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override {
208 auto result = ui::button_element_base::on_mouse_move(state, x, y, mods);
209
210 table_signal_cell_hover signal = { };
211 signal.column = column;
212 signal.row = row;
213
214 send<table_signal_cell_hover>(state, parent, signal);
215 return result;
216 }
217
218 void button_action(sys::state& state) noexcept override {
219 table_signal_cell_click signal = { };
220 signal.column = column;
221 signal.row = row;
222
223 send<table_signal_cell_click>(state, parent, signal);
224 }
225
226 ui::message_result test_mouse(sys::state& state, int32_t x, int32_t y, ui::mouse_probe_type t) noexcept override {
228 }
229
230 ui::message_result on_scroll(sys::state& state, int32_t x, int32_t y, float amount, sys::key_modifiers mods) noexcept override {
231 table_signal_scroll signal{ };
232 signal.scroll_amount = amount;
233 send<table_signal_scroll>(state, parent, signal);
234
236 }
237
238 ui::tooltip_behavior has_tooltip(sys::state& state) noexcept override {
239 table_has_tooltip_signal signal = { };
240 signal.column = column;
241 signal = send_and_retrieve<table_has_tooltip_signal>(state, parent, signal);
242 if(signal.has_tooltip) {
244 } else {
246 }
247 }
248
249 void update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept override {
251 signal.row = row;
252 signal.column = column;
253 signal.tooltip_layout = &contents;
254 send<table_tooltip_callback_signal>(state, parent, signal);
255 }
256};
257
258
259template<typename item_type>
260class body : public ui::container_base {
261protected:
263 public:
264 void on_value_change(sys::state& state, int32_t v) noexcept override {
265 static_cast<body*>(parent)->update(state);
266 };
267 };
268public:
270 std::vector<entry<item_type>*> cells{};
271 int32_t scroll_pos;
273 uint16_t rows;
275
276 body(uint8_t in_columns, uint8_t in_rows) {
277 scroll_pos = 0;
278 columns = in_columns;
279 rows = in_rows;
280 scroll_impulse = 0;
281 }
282
283 void update(sys::state& state) {
284 auto rows_visible = cells.size() / columns;
285 auto content_off_screen = int32_t(rows) - int32_t(rows_visible);
286
288 if(content_off_screen <= 0) {
289 list_scrollbar->set_visible(state, false);
290 scroll_pos = 0;
291 } else {
292 list_scrollbar->change_settings(state, ui::mutable_scrollbar_settings{ 0, content_off_screen, 0, 0, false });
293 list_scrollbar->set_visible(state, true);
294 scroll_pos = std::min((int32_t)scroll_pos, content_off_screen);
295 }
296
297 for(size_t current_row = 0; current_row < rows_visible; current_row++) {
298 for(uint8_t current_column = 0; current_column < columns; current_column++) {
299 auto cell_index = current_row * columns + current_column;
300 if(current_row >= rows) {
301 cells[cell_index]->set_visible(state, false);
302 } else {
303 cells[cell_index]->set_visible(state, true);
304 cells[cell_index]->on_update(state);
305 }
306 }
307 }
308 }
309
310 void on_create(sys::state& state) noexcept override {
312
313 auto ptr = ui::make_element_by_type<_scrollbar>(state, "standardlistbox_slider");
314 list_scrollbar = static_cast<_scrollbar*>(ptr.get());
315 add_child_to_front(std::move(ptr));
317 update(state);
318 }
319
320 ui::message_result get(sys::state& state, Cyto::Any& payload) noexcept override {
321 if(payload.holds_type<table_signal_scroll>()) {
322 table_signal_scroll signal = any_cast<table_signal_scroll>(payload);
323 auto amount = signal.scroll_amount;
324 auto rows_visible = cells.size() / columns;
325 auto content_off_screen = int32_t(rows - rows_visible);
326 if(content_off_screen > 0) {
327 // for the sake of smooth scrolling:
328 scroll_impulse += amount > 0 ? 1 : -1;
329 } else {
330 scroll_impulse = 0.f;
331 }
332 }
333 return ui::container_base::get(state, payload);
334 }
335
336 ui::tooltip_behavior has_tooltip(sys::state& state) noexcept override {
338 }
339};
340
341template<typename item_type>
343public:
345 std::string body_def;
347 std::vector<uint16_t> widths;
348 uint16_t row_height;
349
352 bool column_is_hovered = false;
353
354 std::function<void(sys::state& state, ui::element_base* container, const item_type& a)> row_callback;
355
356 display(std::string body_definition, std::vector<column<item_type>> columns) {
357 content.columns = columns;
358 body_def = body_definition;
359 row_callback = [](sys::state& state, ui::element_base* container, const item_type& a) {
360 return;
361 };
362 }
363
364 std::unique_ptr<element_base> make_child(sys::state& state, std::string_view name, dcon::gui_def_id id) noexcept override {
365 if(name == body_def) {
366 auto ptr_body = ui::make_element_by_type<body<item_type>>(
367 state,
368 id,
369 (uint8_t)content.columns.size(), (uint8_t)0
370 );
371 table_body = static_cast<body<item_type>*>(ptr_body.get());
372
373 // generate cells
374 int16_t offset_y = 0;
375 int16_t offset_x = 0;
376 row_height = 0;
377 uint8_t row = 0;
378 while(offset_y + row_height <= base_data.size.y - 40) {
379 for(uint8_t column_index = 0; column_index < content.columns.size(); column_index++) {
380 auto& column = content.columns[column_index];
381 auto ptr = ui::make_element_by_type<entry<item_type>>(
382 state,
384 row, column_index
385 );
386 ptr->base_data.position.x += offset_x;
387 ptr->base_data.position.y += offset_y;
388 table_body->cells.push_back(static_cast<entry<item_type>*>(ptr.get()));
389 offset_x += ptr->base_data.size.x;
390 row_height = std::max<uint16_t>(row_height, (uint16_t)(ptr->base_data.size.y));
391 table_body->add_child_to_front(std::move(ptr));
392 }
393
394 offset_x = 0;
395 offset_y = offset_y + row_height;
396 row++;
397 }
398 return ptr_body;
399 }
400 return nullptr;
401 }
402
403 void on_create(sys::state& state) noexcept override {
404 window_element_base::on_create(state);
405
406 int16_t offset_x = 0;
407 for(uint8_t column_index = 0; column_index < content.columns.size(); column_index++) {
408 auto& column = content.columns[column_index];
409 auto def = state.ui_state.defs_by_name.find(
410 state.lookup_key(column.header_definition_string)
411 )->second.definition;
412 auto ptr = ui::make_element_by_type<sort_button<item_type>>(
413 state,
414 def,
415 column_index,
417 );
418 ptr->base_data.position.x += offset_x;
419 ptr->set_button_text(state, text::produce_simple_string(state, column.header));
420
421 widths.push_back(ptr->base_data.size.x);
422 offset_x += ptr->base_data.size.x;
423 add_child_to_front(std::move(ptr));
424 }
425 }
426
427 ui::message_result get(sys::state& state, Cyto::Any& payload) noexcept override {
428 if(payload.holds_type<table_signal<item_type>>()) {
429 table_signal<item_type> signal = any_cast<table_signal<item_type>>(payload);
430 auto index = signal.column;
431 if((uint16_t)signal.row + (uint16_t)(table_body->scroll_pos) < (uint16_t)content.data.size()) {
432 auto raw_data = content.data[signal.row + table_body->scroll_pos];
433 signal.text_to_set = content.columns[index].view(state, this, raw_data);
434 } else {
435 signal.text_to_set = "???";
436 }
437
439 if(signal.column == hovered_column) {
440 signal.is_hovered = true;
441 }
442 }
443
444 payload.emplace<table_signal<item_type>>(signal);
446 } else if(payload.holds_type<sort_signal>()) {
447 sort_signal signal = any_cast<sort_signal>(payload);
448 content.toggle_sort_column_by_index(signal.column);
449 on_update(state);
451 } else if(payload.holds_type<table_signal_cell_hover>()) {
452 table_signal_cell_hover signal = any_cast<table_signal_cell_hover>(payload);
453 hovered_column = signal.column;
454 hovered_row = signal.row;
456 } else if(payload.holds_type<table_signal_cell_click>()) {
457 table_signal_cell_click signal = any_cast<table_signal_cell_click>(payload);
458 if((uint16_t)signal.row + (uint16_t)(table_body->scroll_pos) < (uint16_t)content.data.size()) {
459 row_callback(state, this, content.data[signal.row + table_body->scroll_pos]);
460 }
462 } else if(payload.holds_type<table_has_tooltip_signal>()) {
463 table_has_tooltip_signal signal = any_cast<table_has_tooltip_signal>(payload);
464 signal.has_tooltip = content.columns[signal.column].has_tooltip;
465 payload.emplace<table_has_tooltip_signal>(signal);
467 } else if(payload.holds_type<table_tooltip_callback_signal>()) {
468 table_tooltip_callback_signal signal = any_cast<table_tooltip_callback_signal>(payload);
469 content.columns[signal.column].update_tooltip(
470 state,
471 this,
472 *(signal.tooltip_layout),
473 content.data[signal.row],
474 content.columns[signal.column].header
475 );
477 }
478 return ui::container_base::get(state, payload);
479 }
480
481 void on_update(sys::state& state) noexcept override {
482 content.update_rows_order(state, this);
483 if(table_body != nullptr) {
484 table_body->rows = (uint16_t)content.data.size();
485 table_body->update(state);
486 }
487 }
488
489 void render(sys::state& state, int32_t x, int32_t y) noexcept override {
490 //smooth scolling
491 constexpr float dt = 0.99f;
492
493 float impulse = table_body->scroll_impulse;
494 if(impulse > dt) {
495 table_body->scroll_impulse -= dt;
496 } else if (impulse < -dt) {
497 table_body->scroll_impulse += dt;
498 } else {
499 table_body->scroll_impulse = 0;
500 }
501
502 // if we jumped to another step: make a step
503 if(std::floor(impulse) != std::floor(table_body->scroll_impulse)) {
504 table_body->list_scrollbar->update_raw_value(state, table_body->list_scrollbar->raw_value() + (impulse < 0 ? 1 : -1));
505 state.ui_state.last_tooltip = nullptr; //force update of tooltip
506 table_body->update(state);
507 }
508
509
510 float tinted_column_x = (float)x;
511 for(uint8_t i = 0; i < hovered_column; i++) {
512 float width = (float)(widths[i]);
513 tinted_column_x += width;
514 }
515 float tinted_column_width = (float)(widths[hovered_column]);
516
517 float tinted_column_y = (float)y;
518 float tinted_column_height = (float)(base_data.size.y);
519
520 auto rtl = state.world.locale_get_native_rtl(
521 state.font_collection.get_current_locale()
522 );
523
525 tinted_column_x, tinted_column_y,
526 tinted_column_width, tinted_column_height,
527 0.9f, 0.8f, 0.8f,
528 base_data.get_rotation(), false,
529 rtl
530 );
531
532 float hovered_row_x = float(x);
533 float hovered_row_width = float(base_data.size.x);
534
535 float hovered_row_height = float(row_height);
536 float hovered_row_y = float(y) + float(table_body->base_data.position.y) + row_height * hovered_row;
537
539 hovered_row_x, hovered_row_y,
540 hovered_row_width, hovered_row_height,
541 0.9f, 0.8f, 0.8f,
542 base_data.get_rotation(), false,
543 rtl
544 );
545
547 }
548};
549
550}
void on_value_change(sys::state &state, int32_t v) noexcept override
Definition: table.hpp:264
body(uint8_t in_columns, uint8_t in_rows)
Definition: table.hpp:276
void update(sys::state &state)
Definition: table.hpp:283
uint8_t columns
Definition: table.hpp:272
float scroll_impulse
Definition: table.hpp:274
_scrollbar * list_scrollbar
Definition: table.hpp:269
ui::message_result get(sys::state &state, Cyto::Any &payload) noexcept override
Definition: table.hpp:320
uint16_t rows
Definition: table.hpp:273
ui::tooltip_behavior has_tooltip(sys::state &state) noexcept override
Definition: table.hpp:336
std::vector< entry< item_type > * > cells
Definition: table.hpp:270
int32_t scroll_pos
Definition: table.hpp:271
void on_create(sys::state &state) noexcept override
Definition: table.hpp:310
ui::message_result get(sys::state &state, Cyto::Any &payload) noexcept override
Definition: table.hpp:427
std::unique_ptr< element_base > make_child(sys::state &state, std::string_view name, dcon::gui_def_id id) noexcept override
Definition: table.hpp:364
std::string body_def
Definition: table.hpp:345
body< item_type > * table_body
Definition: table.hpp:344
uint8_t hovered_column
Definition: table.hpp:350
std::vector< uint16_t > widths
Definition: table.hpp:347
std::function< void(sys::state &state, ui::element_base *container, const item_type &a)> row_callback
Definition: table.hpp:354
uint8_t hovered_row
Definition: table.hpp:351
void on_create(sys::state &state) noexcept override
Definition: table.hpp:403
void on_update(sys::state &state) noexcept override
Definition: table.hpp:481
void render(sys::state &state, int32_t x, int32_t y) noexcept override
Definition: table.hpp:489
data< item_type > content
Definition: table.hpp:346
uint16_t row_height
Definition: table.hpp:348
display(std::string body_definition, std::vector< column< item_type > > columns)
Definition: table.hpp:356
bool column_is_hovered
Definition: table.hpp:352
void on_update(sys::state &state) noexcept override
Definition: table.hpp:199
uint8_t column
Definition: table.hpp:190
void button_action(sys::state &state) noexcept override
Definition: table.hpp:218
entry(uint8_t in_row, uint8_t in_column)
Definition: table.hpp:194
uint8_t row
Definition: table.hpp:191
virtual ui::message_result on_mouse_move(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override
Definition: table.hpp:207
void update_tooltip(sys::state &state, int32_t x, int32_t y, text::columnar_layout &contents) noexcept override
Definition: table.hpp:249
ui::message_result test_mouse(sys::state &state, int32_t x, int32_t y, ui::mouse_probe_type t) noexcept override
Definition: table.hpp:226
bool is_hovered
Definition: table.hpp:192
ui::tooltip_behavior has_tooltip(sys::state &state) noexcept override
Definition: table.hpp:238
ui::message_result on_scroll(sys::state &state, int32_t x, int32_t y, float amount, sys::key_modifiers mods) noexcept override
Definition: table.hpp:230
ui::tooltip_behavior has_tooltip(sys::state &state) noexcept override
Definition: table.hpp:143
uint8_t column
Definition: table.hpp:129
std::string name
Definition: table.hpp:130
sort_button(uint8_t in_column, std::string in_name)
Definition: table.hpp:132
void update_tooltip(sys::state &state, int32_t x, int32_t y, text::columnar_layout &contents) noexcept override
Definition: table.hpp:147
void button_action(sys::state &state) noexcept override
Definition: table.hpp:137
void set_button_text(sys::state &state, std::string const &new_text)
void add_child_to_front(std::unique_ptr< element_base > child) noexcept final
element_base * parent
virtual void render(sys::state &state, int32_t x, int32_t y) noexcept
virtual message_result get(sys::state &state, Cyto::Any &payload) noexcept
virtual message_result on_mouse_move(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept
virtual void on_create(sys::state &state) noexcept
element_data base_data
void set_visible(sys::state &state, bool vis)
void change_settings(sys::state &state, mutable_scrollbar_settings const &settings_s)
int32_t raw_value() const
void render_tinted_rect(sys::state const &state, float x, float y, float width, float height, float r, float g, float b, ui::rotation rot, bool flipped, bool rtl)
key_modifiers
Definition: constants.hpp:156
void merge_sort(IT first, IT end, CMP const &cmp) noexcept
Definition: table.hpp:10
column_type
Definition: table.hpp:12
@ button
Definition: table.hpp:13
@ text
Definition: table.hpp:14
void tooltip_fallback(sys::state &state, ui::element_base *container, text::columnar_layout &contents, const item_type &a, std::string fallback)
Definition: table.hpp:42
sort_order
Definition: table.hpp:17
@ undefined
Definition: table.hpp:18
@ ascending
Definition: table.hpp:19
@ descending
Definition: table.hpp:20
sort_order toggle_sort_order(const sort_order order)
Definition: table.hpp:23
layout_box open_layout_box(layout_base &dest, int32_t indent)
Definition: text.cpp:1823
void localised_format_box(sys::state &state, layout_base &dest, layout_box &box, std::string_view key, text::substitution_map const &sub)
Definition: text.cpp:1904
std::string produce_simple_string(sys::state const &state, dcon::text_key id)
Definition: text.cpp:617
void close_layout_box(columnar_layout &dest, layout_box &box)
Definition: text.cpp:1831
tooltip_behavior
message_result
uchar uint8_t
Holds important data about the game world, state, and other data regarding windowing,...
std::string header
Definition: table.hpp:51
std::function< bool(sys::state &state, ui::element_base *container, const item_type &a, const item_type &b)> compare
Definition: table.hpp:52
std::function< std::string(sys::state &state, ui::element_base *container, const item_type &a)> view
Definition: table.hpp:53
bool sortable
Definition: table.hpp:50
bool has_tooltip
Definition: table.hpp:63
std::function< void(sys::state &state, ui::element_base *container, text::columnar_layout &contents, const item_type &a, std::string fallback)> update_tooltip
Definition: table.hpp:60
std::string header_definition_string
Definition: table.hpp:62
std::string cell_definition_string
Definition: table.hpp:61
std::vector< sort_data > sort_priority
Definition: table.hpp:69
std::vector< item_type > data
Definition: table.hpp:70
std::vector< column< item_type > > columns
Definition: table.hpp:68
void update_rows_order(sys::state &state, ui::element_base *container)
Definition: table.hpp:99
void toggle_sort_column_by_index(uint8_t column_index)
Definition: table.hpp:72
uint8_t sorted_index
Definition: table.hpp:38
sort_order order
Definition: table.hpp:37
uint8_t column
Definition: table.hpp:123
std::string text_to_set
Definition: table.hpp:158
text::columnar_layout * tooltip_layout
Definition: table.hpp:182
rotation get_rotation() const