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, 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 text::columnar_layout& contents,
57 const item_type& a,
58 std::string fallback
59 )> update_tooltip = tooltip_fallback<item_type>;
60 std::string cell_definition_string="thin_cell_number";
61 std::string header_definition_string="thin_cell_number";
62 bool has_tooltip = false;
63};
64
65template<typename item_type>
66struct data {
67 std::vector<column<item_type>> columns;
68 std::vector<sort_data> sort_priority;
69 std::vector<item_type> data;
70
72 if(!columns[column_index].sortable) {
73 return;
74 }
75
76 uint8_t offset = 0;
77 bool offset_found = false;
78 sort_order current_order = sort_order::undefined;
79 for(uint8_t i = 0; i < sort_priority.size(); i++) {
80 if(sort_priority[i].sorted_index == column_index) {
81 offset_found = true;
82 offset = i;
83 current_order = sort_priority[i].order;
84 }
85 }
86
87 if(offset_found) {
88 auto begin = sort_priority.begin();
89 sort_priority.erase(begin + offset);
90 }
91
92 sort_data new_sort_data = {};
93 new_sort_data.order = toggle_sort_order(current_order);
94 new_sort_data.sorted_index = column_index;
95 sort_priority.push_back(new_sort_data);
96 }
97
99 for(auto& order : sort_priority) {
100 if(order.order == sort_order::ascending) {
101 std::sort(
102 data.begin(),
103 data.end(),
104 [&](const item_type& a, const item_type& b) {
105 return columns[order.sorted_index].compare(state, container, a, b);
106 }
107 );
108 } else {
109 std::sort(
110 data.begin(),
111 data.end(),
112 [&](const item_type& a, const item_type& b) {
113 return columns[order.sorted_index].compare(state, container, b, a);
114 }
115 );
116 }
117 }
118 }
119};
120
123};
124
125template<typename item_type>
127public:
129 std::string name;
130
131 sort_button(uint8_t in_column, std::string in_name) {
132 column = in_column;
133 name = in_name;
134 }
135
136 void button_action(sys::state& state) noexcept override {
137 sort_signal signal = { };
138 signal.column = column;
139 send(state, parent, signal);
140 }
141
142 ui::tooltip_behavior has_tooltip(sys::state& state) noexcept override {
144 }
145
146 void update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept override {
147 auto box = text::open_layout_box(contents, 0);
148 text::localised_format_box(state, contents, box, std::string_view(name));
149 text::close_layout_box(contents, box);
150 }
151};
152
153template<typename item_type>
157 std::string text_to_set;
159};
160
164};
165
169};
170
173};
174
176 bool has_tooltip = false;
178};
179
184};
185
186template<typename item_type>
188public:
192
193 entry(uint8_t in_row, uint8_t in_column) {
194 row = in_row;
195 column = in_column;
196 }
197
198 void on_update(sys::state& state) noexcept override {
199 table_signal<item_type> signal{ };
200 signal.column = column;
201 signal.row = row;
202 auto response = send_and_retrieve<table_signal<item_type>>(state, parent, signal);
203 set_button_text(state, response.text_to_set);
204 }
205
206 virtual ui::message_result on_mouse_move(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override {
207 auto result = ui::button_element_base::on_mouse_move(state, x, y, mods);
208
209 table_signal_cell_hover signal = { };
210 signal.column = column;
211 signal.row = row;
212
213 send<table_signal_cell_hover>(state, parent, signal);
214 return result;
215 }
216
217 void button_action(sys::state& state) noexcept override {
218 table_signal_cell_click signal = { };
219 signal.column = column;
220 signal.row = row;
221
222 send<table_signal_cell_click>(state, parent, signal);
223 }
224
225 ui::message_result test_mouse(sys::state& state, int32_t x, int32_t y, ui::mouse_probe_type t) noexcept override {
227 }
228
229 ui::message_result on_scroll(sys::state& state, int32_t x, int32_t y, float amount, sys::key_modifiers mods) noexcept override {
230 table_signal_scroll signal{ };
231 signal.scroll_amount = amount;
232 send<table_signal_scroll>(state, parent, signal);
233
235 }
236
237 ui::tooltip_behavior has_tooltip(sys::state& state) noexcept override {
238 table_has_tooltip_signal signal = { };
239 signal.column = column;
240 signal = send_and_retrieve<table_has_tooltip_signal>(state, parent, signal);
241 if(signal.has_tooltip) {
243 } else {
245 }
246 }
247
248 void update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept override {
250 signal.row = row;
251 signal.column = column;
252 signal.tooltip_layout = &contents;
253 send<table_tooltip_callback_signal>(state, parent, signal);
254 }
255};
256
257
258template<typename item_type>
259class body : public ui::container_base {
260protected:
262 public:
263 void on_value_change(sys::state& state, int32_t v) noexcept override {
264 static_cast<body*>(parent)->update(state);
265 };
266 };
267public:
269 std::vector<entry<item_type>*> cells{};
270 int32_t scroll_pos;
272 uint16_t rows;
274
275 body(uint8_t in_columns, uint8_t in_rows) {
276 scroll_pos = 0;
277 columns = in_columns;
278 rows = in_rows;
279 scroll_impulse = 0;
280 }
281
282 void update(sys::state& state) {
283 auto rows_visible = cells.size() / columns;
284 auto content_off_screen = int32_t(rows) - int32_t(rows_visible);
285
287 if(content_off_screen <= 0) {
288 list_scrollbar->set_visible(state, false);
289 scroll_pos = 0;
290 } else {
291 list_scrollbar->change_settings(state, ui::mutable_scrollbar_settings{ 0, content_off_screen, 0, 0, false });
292 list_scrollbar->set_visible(state, true);
293 scroll_pos = std::min((int32_t)scroll_pos, content_off_screen);
294 }
295
296 for(size_t current_row = 0; current_row < rows_visible; current_row++) {
297 for(uint8_t current_column = 0; current_column < columns; current_column++) {
298 auto cell_index = current_row * columns + current_column;
299 if(current_row > rows) {
300 cells[cell_index]->set_visible(state, false);
301 } else {
302 cells[cell_index]->set_visible(state, true);
303 cells[cell_index]->on_update(state);
304 }
305 }
306 }
307 }
308
309 void on_create(sys::state& state) noexcept override {
311
312 auto ptr = ui::make_element_by_type<_scrollbar>(state, "standardlistbox_slider");
313 list_scrollbar = static_cast<_scrollbar*>(ptr.get());
314 add_child_to_front(std::move(ptr));
316 update(state);
317 }
318
319 ui::message_result get(sys::state& state, Cyto::Any& payload) noexcept override {
320 if(payload.holds_type<table_signal_scroll>()) {
321 table_signal_scroll signal = any_cast<table_signal_scroll>(payload);
322 auto amount = signal.scroll_amount;
323 auto rows_visible = cells.size() / columns;
324 auto content_off_screen = int32_t(rows - rows_visible);
325 if(content_off_screen > 0) {
326 // for the sake of smooth scrolling:
327 scroll_impulse += amount > 0 ? 1 : -1;
328 } else {
329 scroll_impulse = 0.f;
330 }
331 }
332 return ui::container_base::get(state, payload);
333 }
334
335 ui::tooltip_behavior has_tooltip(sys::state& state) noexcept override {
337 }
338};
339
340template<typename item_type>
342public:
344 std::string body_def;
346 std::vector<uint16_t> widths;
347 uint16_t row_height;
348
351 bool column_is_hovered = false;
352
353 std::function<void(sys::state& state, ui::element_base* container, const item_type& a)> row_callback;
354
355 display(std::string body_definition, std::vector<column<item_type>> columns) {
356 content.columns = columns;
357 body_def = body_definition;
358 row_callback = [](sys::state& state, ui::element_base* container, const item_type& a) {
359 return;
360 };
361 }
362
363 std::unique_ptr<element_base> make_child(sys::state& state, std::string_view name, dcon::gui_def_id id) noexcept override {
364 if(name == body_def) {
365 auto ptr_body = ui::make_element_by_type<body<item_type>>(
366 state,
367 id,
368 (uint8_t)content.columns.size(), (uint8_t)0
369 );
370 table_body = static_cast<body<item_type>*>(ptr_body.get());
371
372 // generate cells
373 int16_t offset_y = 0;
374 int16_t offset_x = 0;
375 row_height = 0;
376 uint8_t row = 0;
377 while(offset_y + row_height <= base_data.size.y - 40) {
378 for(uint8_t column_index = 0; column_index < content.columns.size(); column_index++) {
379 auto& column = content.columns[column_index];
380 auto ptr = ui::make_element_by_type<entry<item_type>>(
381 state,
383 row, column_index
384 );
385 ptr->base_data.position.x += offset_x;
386 ptr->base_data.position.y += offset_y;
387 table_body->cells.push_back(static_cast<entry<item_type>*>(ptr.get()));
388 offset_x += ptr->base_data.size.x;
389 row_height = std::max<uint16_t>(row_height, (uint16_t)(ptr->base_data.size.y));
390 table_body->add_child_to_front(std::move(ptr));
391 }
392
393 offset_x = 0;
394 offset_y = offset_y + row_height;
395 row++;
396 }
397 return ptr_body;
398 }
399 return nullptr;
400 }
401
402 void on_create(sys::state& state) noexcept override {
403 window_element_base::on_create(state);
404
405 int16_t offset_x = 0;
406 for(uint8_t column_index = 0; column_index < content.columns.size(); column_index++) {
407 auto& column = content.columns[column_index];
408 auto def = state.ui_state.defs_by_name.find(
409 state.lookup_key(column.header_definition_string)
410 )->second.definition;
411 auto ptr = ui::make_element_by_type<sort_button<item_type>>(
412 state,
413 def,
414 column_index,
416 );
417 ptr->base_data.position.x += offset_x;
418 ptr->set_button_text(state, text::produce_simple_string(state, column.header));
419
420 widths.push_back(ptr->base_data.size.x);
421 offset_x += ptr->base_data.size.x;
422 add_child_to_front(std::move(ptr));
423 }
424 }
425
426 ui::message_result get(sys::state& state, Cyto::Any& payload) noexcept override {
427 if(payload.holds_type<table_signal<item_type>>()) {
428 table_signal<item_type> signal = any_cast<table_signal<item_type>>(payload);
429 auto index = signal.column;
430 if((uint16_t)signal.row + (uint16_t)(table_body->scroll_pos) < (uint16_t)content.data.size()) {
431 auto raw_data = content.data[signal.row + table_body->scroll_pos];
432 signal.text_to_set = content.columns[index].view(state, this, raw_data);
433 } else {
434 signal.text_to_set = "???";
435 }
436
438 if(signal.column == hovered_column) {
439 signal.is_hovered = true;
440 }
441 }
442
443 payload.emplace<table_signal<item_type>>(signal);
445 } else if(payload.holds_type<sort_signal>()) {
446 sort_signal signal = any_cast<sort_signal>(payload);
447 content.toggle_sort_column_by_index(signal.column);
448 on_update(state);
450 } else if(payload.holds_type<table_signal_cell_hover>()) {
451 table_signal_cell_hover signal = any_cast<table_signal_cell_hover>(payload);
452 hovered_column = signal.column;
453 hovered_row = signal.row;
455 } else if(payload.holds_type<table_signal_cell_click>()) {
456 table_signal_cell_click signal = any_cast<table_signal_cell_click>(payload);
457 if((uint16_t)signal.row + (uint16_t)(table_body->scroll_pos) < (uint16_t)content.data.size()) {
458 row_callback(state, this, content.data[signal.row + table_body->scroll_pos]);
459 }
461 } else if(payload.holds_type<table_has_tooltip_signal>()) {
462 table_has_tooltip_signal signal = any_cast<table_has_tooltip_signal>(payload);
463 signal.has_tooltip = content.columns[signal.column].has_tooltip;
464 payload.emplace<table_has_tooltip_signal>(signal);
466 } else if(payload.holds_type<table_tooltip_callback_signal>()) {
467 table_tooltip_callback_signal signal = any_cast<table_tooltip_callback_signal>(payload);
468 content.columns[signal.column].update_tooltip(
469 state,
470 *(signal.tooltip_layout),
471 content.data[signal.row],
472 content.columns[signal.column].header
473 );
475 }
476 return ui::container_base::get(state, payload);
477 }
478
479 void on_update(sys::state& state) noexcept override {
480 content.update_rows_order(state, this);
481 if(table_body != nullptr) {
482 table_body->rows = (uint16_t)content.data.size();
483 table_body->update(state);
484 }
485 }
486
487 void render(sys::state& state, int32_t x, int32_t y) noexcept override {
488 //smooth scolling
489 constexpr float dt = 0.99f;
490
491 float impulse = table_body->scroll_impulse;
492 if(impulse > dt) {
493 table_body->scroll_impulse -= dt;
494 } else if (impulse < -dt) {
495 table_body->scroll_impulse += dt;
496 } else {
497 table_body->scroll_impulse = 0;
498 }
499
500 // if we jumped to another step: make a step
501 if(std::floor(impulse) != std::floor(table_body->scroll_impulse)) {
502 table_body->list_scrollbar->update_raw_value(state, table_body->list_scrollbar->raw_value() + (impulse < 0 ? 1 : -1));
503 state.ui_state.last_tooltip = nullptr; //force update of tooltip
504 table_body->update(state);
505 }
506
507
508 float tinted_column_x = (float)x;
509 for(uint8_t i = 0; i < hovered_column; i++) {
510 float width = (float)(widths[i]);
511 tinted_column_x += width;
512 }
513 float tinted_column_width = (float)(widths[hovered_column]);
514
515 float tinted_column_y = (float)y;
516 float tinted_column_height = (float)(base_data.size.y);
517
518 auto rtl = state.world.locale_get_native_rtl(
519 state.font_collection.get_current_locale()
520 );
521
523 tinted_column_x, tinted_column_y,
524 tinted_column_width, tinted_column_height,
525 0.9f, 0.8f, 0.8f,
526 base_data.get_rotation(), false,
527 rtl
528 );
529
530 float hovered_row_x = float(x);
531 float hovered_row_width = float(base_data.size.x);
532
533 float hovered_row_height = float(row_height);
534 float hovered_row_y = float(y) + float(table_body->base_data.position.y) + row_height * hovered_row;
535
537 hovered_row_x, hovered_row_y,
538 hovered_row_width, hovered_row_height,
539 0.9f, 0.8f, 0.8f,
540 base_data.get_rotation(), false,
541 rtl
542 );
543
545 }
546};
547
548}
void on_value_change(sys::state &state, int32_t v) noexcept override
Definition: table.hpp:263
body(uint8_t in_columns, uint8_t in_rows)
Definition: table.hpp:275
void update(sys::state &state)
Definition: table.hpp:282
uint8_t columns
Definition: table.hpp:271
float scroll_impulse
Definition: table.hpp:273
_scrollbar * list_scrollbar
Definition: table.hpp:268
ui::message_result get(sys::state &state, Cyto::Any &payload) noexcept override
Definition: table.hpp:319
uint16_t rows
Definition: table.hpp:272
ui::tooltip_behavior has_tooltip(sys::state &state) noexcept override
Definition: table.hpp:335
std::vector< entry< item_type > * > cells
Definition: table.hpp:269
int32_t scroll_pos
Definition: table.hpp:270
void on_create(sys::state &state) noexcept override
Definition: table.hpp:309
ui::message_result get(sys::state &state, Cyto::Any &payload) noexcept override
Definition: table.hpp:426
std::unique_ptr< element_base > make_child(sys::state &state, std::string_view name, dcon::gui_def_id id) noexcept override
Definition: table.hpp:363
std::string body_def
Definition: table.hpp:344
body< item_type > * table_body
Definition: table.hpp:343
uint8_t hovered_column
Definition: table.hpp:349
std::vector< uint16_t > widths
Definition: table.hpp:346
std::function< void(sys::state &state, ui::element_base *container, const item_type &a)> row_callback
Definition: table.hpp:353
uint8_t hovered_row
Definition: table.hpp:350
void on_create(sys::state &state) noexcept override
Definition: table.hpp:402
void on_update(sys::state &state) noexcept override
Definition: table.hpp:479
void render(sys::state &state, int32_t x, int32_t y) noexcept override
Definition: table.hpp:487
data< item_type > content
Definition: table.hpp:345
uint16_t row_height
Definition: table.hpp:347
display(std::string body_definition, std::vector< column< item_type > > columns)
Definition: table.hpp:355
bool column_is_hovered
Definition: table.hpp:351
void on_update(sys::state &state) noexcept override
Definition: table.hpp:198
uint8_t column
Definition: table.hpp:189
void button_action(sys::state &state) noexcept override
Definition: table.hpp:217
entry(uint8_t in_row, uint8_t in_column)
Definition: table.hpp:193
uint8_t row
Definition: table.hpp:190
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:206
void update_tooltip(sys::state &state, int32_t x, int32_t y, text::columnar_layout &contents) noexcept override
Definition: table.hpp:248
ui::message_result test_mouse(sys::state &state, int32_t x, int32_t y, ui::mouse_probe_type t) noexcept override
Definition: table.hpp:225
bool is_hovered
Definition: table.hpp:191
ui::tooltip_behavior has_tooltip(sys::state &state) noexcept override
Definition: table.hpp:237
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:229
ui::tooltip_behavior has_tooltip(sys::state &state) noexcept override
Definition: table.hpp:142
uint8_t column
Definition: table.hpp:128
std::string name
Definition: table.hpp:129
sort_button(uint8_t in_column, std::string in_name)
Definition: table.hpp:131
void update_tooltip(sys::state &state, int32_t x, int32_t y, text::columnar_layout &contents) noexcept override
Definition: table.hpp:146
void button_action(sys::state &state) noexcept override
Definition: table.hpp:136
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
Definition: table.hpp:10
void tooltip_fallback(sys::state &state, text::columnar_layout &contents, const item_type &a, std::string fallback)
Definition: table.hpp:42
column_type
Definition: table.hpp:12
@ button
Definition: table.hpp:13
@ text
Definition: table.hpp:14
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:1799
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:1880
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:1807
tooltip_behavior
message_result
uchar uint8_t
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
std::function< void(sys::state &state, text::columnar_layout &contents, const item_type &a, std::string fallback)> update_tooltip
Definition: table.hpp:59
bool has_tooltip
Definition: table.hpp:62
std::string header_definition_string
Definition: table.hpp:61
std::string cell_definition_string
Definition: table.hpp:60
std::vector< sort_data > sort_priority
Definition: table.hpp:68
std::vector< item_type > data
Definition: table.hpp:69
std::vector< column< item_type > > columns
Definition: table.hpp:67
void update_rows_order(sys::state &state, ui::element_base *container)
Definition: table.hpp:98
void toggle_sort_column_by_index(uint8_t column_index)
Definition: table.hpp:71
uint8_t sorted_index
Definition: table.hpp:38
sort_order order
Definition: table.hpp:37
uint8_t column
Definition: table.hpp:122
std::string text_to_set
Definition: table.hpp:157
text::columnar_layout * tooltip_layout
Definition: table.hpp:181
rotation get_rotation() const