The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
generator.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
3 by Mark de Wever <koraq@xs4all.nl>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#define GETTEXT_DOMAIN "wesnoth-lib"
17
19
20#include "wml_exception.hpp"
21
22#include <numeric>
23#include <cmath>
24
25namespace gui2
26{
27namespace policy
28{
29/***** ***** ***** ***** Minimum selection ***** ***** ***** *****/
30
31namespace minimum_selection
32{
33void one_item::set_item_shown(const unsigned index, const bool show)
34{
35 if(show && get_selected_item_count() == 0) {
36 do_select_item(index);
37 } else if(!show && is_selected(index)) {
38 do_deselect_item(index);
39 if(get_selected_item_count() == 0) {
40 bool found_new_item = false;
41 const unsigned item_count = get_item_count();
42 const unsigned ordered_index = get_ordered_index(index);
43 // find the next shown item
44 for(unsigned i = ordered_index + 1; i < item_count; ++i) {
45 unsigned new_index = get_item_at_ordered(i);
46 if(get_item_shown(new_index)) {
47 do_select_item(new_index);
48 found_new_item = true;
49 break;
50 }
51 }
52 // fall back to finding the previous shown item
53 if(!found_new_item) {
54 for(signed i = static_cast<signed>(ordered_index) - 1; i >= 0; --i) {
55 unsigned new_index = get_item_at_ordered(static_cast<unsigned>(i));
56 if(get_item_shown(new_index)) {
57 do_select_item(new_index);
58 break;
59 }
60 }
61 }
62 // if neither search found a new item, accept that there are zero items selected
63 }
64 }
65}
66
67void one_item::create_item(const unsigned index)
68{
69 if(get_selected_item_count() == 0) {
70 do_select_item(index);
71 }
72}
73
74bool one_item::deselect_item(const unsigned index)
75{
76 if(get_selected_item_count() > 1) {
77 do_deselect_item(index);
78 return true;
79 }
80
81 return false;
82}
83
84void one_item::delete_item(const unsigned index)
85{
86 // this needs the same logic for ensuring that at least one item is selected
87 one_item::set_item_shown(index, false);
88}
89
90void no_item::set_item_shown(const unsigned index, const bool show)
91{
92 if(!show && is_selected(index)) {
93 do_deselect_item(index);
94 }
95}
96
97} // namespace minimum_selection
98
99/***** ***** ***** ***** Placement ***** ***** ***** *****/
100
101namespace placement
102{
104 : placed_(false)
105{
106}
107
108void horizontal_list::create_item(const unsigned /*index*/)
109{
110 if(!placed_) {
111 return;
112 }
113
114 /** @todo implement. */
115 assert(false);
116}
117
119{
120 // The best size is the sum of the widths and the greatest height.
121 point result(0, 0);
122
123 for(std::size_t i = 0; i < get_item_count(); ++i) {
124 if(!get_item_shown(i)) {
125 continue;
126 }
127
128 const point best_size = item(i).get_best_size();
129
130 result.x += best_size.x;
131
132 if(best_size.y > result.y) {
133 result.y = best_size.y;
134 }
135 }
136
137 return result;
138}
139
140void horizontal_list::place(const point& origin, const point& size)
141{
142 /*
143 * - Set every item to its best size.
144 * - The origin gets increased with the width of the last item.
145 * - No item should be higher as the size.
146 * - In the end the origin should be the sum or the origin and the wanted
147 * width.
148 */
149
150 point current_origin = origin;
151
152 for(std::size_t i = 0; i < get_item_count(); ++i) {
154 continue;
155 }
156
158 point best_size = grid.get_best_size();
159 assert(best_size.y <= size.y);
160
161 // FIXME should we look at grow factors???
162 best_size.y = size.y;
163
164 grid.place(current_origin, best_size);
165
166 current_origin.x += best_size.x;
167 }
168
169 if(current_origin.x != origin.x + size.x) {
170 ERR_GUI_L << "Failed to fit horizontal list to requested rect; expected right edge was " << origin.x + size.x
171 << ", actual right edge was " << current_origin.x
172 << " (left edge is " << origin.x << ")\n";
173 }
174}
175
177{
178 point current_origin = origin;
179 for(std::size_t i = 0; i < get_item_count(); ++i) {
181 continue;
182 }
183
185 grid.set_origin(current_origin);
186
187 current_origin.x += grid.get_width();
188 }
189}
190
192{
193 /*
194 * Note for most implementations this function could work only for the
195 * independent class it probably fails. Evaluate to make a generic
196 * function in the generator template class and call it from the wanted
197 * placement functions.
198 */
199 for(std::size_t i = 0; i < get_item_count(); ++i) {
201 grid.set_visible_rectangle(rectangle);
202 }
203}
204
205widget* horizontal_list::find_at(const point& coordinate, const bool must_be_active)
206{
207 assert(get_window());
208
209 for(std::size_t i = 0; i < get_item_count(); ++i) {
210 if(!get_item_shown(i)) {
211 continue;
212 }
213
214 widget* widget = item(i).find_at(coordinate, must_be_active);
215
216 if(widget) {
217 return widget;
218 }
219 }
220
221 return nullptr;
222}
223
224const widget* horizontal_list::find_at(const point& coordinate, const bool must_be_active) const
225{
226 assert(get_window());
227
228 for(std::size_t i = 0; i < get_item_count(); ++i) {
229 if(!get_item_shown(i)) {
230 continue;
231 }
232
233 const widget* widget = item(i).find_at(coordinate, must_be_active);
234
235 if(widget) {
236 return widget;
237 }
238 }
239
240 return nullptr;
241}
242
243void horizontal_list::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
244{
245 if(get_item_count() == 0) {
246 return;
247 }
248
249 if(get_selected_item_count() == 0) {
250 for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
252 // TODO: Check if active?
253 handled = true;
255 break;
256 }
257 }
258
259 return;
260 }
261
262 // NOTE maybe this should only work if we can select only one item...
263 handled = true;
264
265 for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
267 continue;
268 }
269
270 // NOTE we check the first widget to be active since grids have no
271 // active flag. This method might not be entirely reliable.
272 styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
273 if(control && control->get_active()) {
275 return;
276 }
277 }
278}
279
280void horizontal_list::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
281{
282 if(get_item_count() == 0) {
283 return;
284 }
285
286 if(get_selected_item_count() == 0) {
287 for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
289 // TODO: Check if active?
290 handled = true;
292 break;
293 }
294 }
295 return;
296 }
297
298 // NOTE maybe this should only work if we can select only one item...
299 handled = true;
300
301 for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
303 continue;
304 }
305
306 // NOTE we check the first widget to be active since grids have no
307 // active flag. This method might not be entirely reliable.
308 styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
309 if(control && control->get_active()) {
311 return;
312 }
313 }
314}
315
317 : placed_(false)
318{
319}
320
321void vertical_list::create_item(const unsigned /*index*/)
322{
323 if(!placed_) {
324 return;
325 }
326
327 /** @todo implement. */
328 assert(false);
329}
330
332{
333 // The best size is the sum of the heights and the greatest width.
334 point result(0, 0);
335 for(std::size_t i = 0; i < get_item_count(); ++i) {
336 if(!get_item_shown(i)) {
337 continue;
338 }
339
340 const point best_size = item(i).get_best_size();
341
342 if(best_size.x > result.x) {
343 result.x = best_size.x;
344 }
345
346 result.y += best_size.y;
347 }
348
349 return result;
350}
351
352void vertical_list::place(const point& origin, const point& size)
353{
354 /*
355 * - Set every item to its best size.
356 * - The origin gets increased with the height of the last item.
357 * - No item should be wider as the size.
358 * - In the end the origin should be the sum or the origin and the wanted
359 * height.
360 */
361
362 point current_origin = origin;
363 for(std::size_t i = 0; i < get_item_count(); ++i) {
365 continue;
366 }
367
369 point best_size = grid.get_best_size();
370 assert(best_size.x <= size.x);
371
372 // FIXME should we look at grow factors???
373 best_size.x = size.x;
374
375 grid.place(current_origin, best_size);
376
377 current_origin.y += best_size.y;
378 }
379
380 if(current_origin.y != origin.y + size.y) {
381 ERR_GUI_L << "Failed to fit vertical list to requested rect; expected bottom edge was " << origin.y + size.y
382 << ", actual bottom edge was " << current_origin.y
383 << " (top edge is " << origin.y << ")\n";
384 }
385}
386
388{
389 point current_origin = origin;
390 for(std::size_t i = 0; i < get_item_count(); ++i) {
392 continue;
393 }
394
396 grid.set_origin(current_origin);
397
398 current_origin.y += grid.get_height();
399 }
400}
401
403{
404 /*
405 * Note for most implementations this function could work only for the
406 * independent class it probably fails. Evaluate to make a generic
407 * function in the generator template class and call it from the wanted
408 * placement functions.
409 */
410 for(std::size_t i = 0; i < get_item_count(); ++i) {
411 grid& grid = item(i);
412 grid.set_visible_rectangle(rectangle);
413 }
414}
415
416widget* vertical_list::find_at(const point& coordinate, const bool must_be_active)
417{
418 assert(get_window());
419
420 for(std::size_t i = 0; i < get_item_count(); ++i) {
421 if(!get_item_shown(i)) {
422 continue;
423 }
424
425 widget* widget = item(i).find_at(coordinate, must_be_active);
426
427 if(widget) {
428 return widget;
429 }
430 }
431 return nullptr;
432}
433
434const widget* vertical_list::find_at(const point& coordinate, const bool must_be_active) const
435{
436 assert(get_window());
437
438 for(std::size_t i = 0; i < get_item_count(); ++i) {
439 if(!get_item_shown(i)) {
440 continue;
441 }
442
443 const widget* widget = item(i).find_at(coordinate, must_be_active);
444
445 if(widget) {
446 return widget;
447 }
448 }
449 return nullptr;
450}
451
452void vertical_list::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
453{
454 if(get_item_count() == 0) {
455 return;
456 }
457
458 if(get_selected_item_count() == 0) {
459 for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
461 // TODO: Check if active?
462 handled = true;
464 break;
465 }
466 }
467 return;
468 }
469
470 // NOTE maybe this should only work if we can select only one item...
471 handled = true;
472
473 for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
475 continue;
476 }
477
478 // NOTE we check the first widget to be active since grids have no
479 // active flag. This method might not be entirely reliable.
480 styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
481 if(control && control->get_active()) {
483 return;
484 }
485 }
486}
487
488void vertical_list::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
489{
490 if(get_item_count() == 0) {
491 return;
492 }
493
494 if(get_selected_item_count() == 0) {
495 for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
497 // TODO: Check if active?
498 handled = true;
500 break;
501 }
502 }
503 return;
504 }
505
506 // NOTE maybe this should only work if we can select only one item...
507 handled = true;
508
509 for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
511 continue;
512 }
513
514 // NOTE we check the first widget to be active since grids have no
515 // active flag. This method might not be entirely reliable.
516 styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
517 if(control && control->get_active()) {
519 return;
520 }
521 }
522}
523
525 : placed_(false) //, n_cols_(2)
526{
527}
528
529void table::create_item(const unsigned /*index*/)
530{
531 if(!placed_) {
532 return;
533 }
534
535 /** @todo implement. */
536 assert(false);
537}
538
540{
541 /* The best size is that which minimizes the aspect ratio of the enclosing rect.
542 * We first calculate the best size of each item, then find the number of rows
543 * that minimizes the aspect ratio. We try a number of columns from 1 up to
544 * sqrt(visible_items) + 2.
545 *
546 * @todo these calculations need rethinking since the grid layout doesn't work
547 * properly as of now.
548 *
549 * - vultraz, 2017-08-25
550 */
551
552 std::size_t n_items = get_item_count();
553 std::size_t max_cols = std::sqrt(n_items) + 2;
554
555 std::vector<point> item_sizes;
556 for(std::size_t i = 0; i < n_items; i++) {
557 if(get_item_shown(i)) {
558 item_sizes.push_back(item(i).get_best_size());
559 }
560 }
561
562 if(item_sizes.empty()) {
563 return point();
564 }
565
566 std::vector<point> best_sizes(1);
567
568 best_sizes[0] = std::accumulate(item_sizes.begin(), item_sizes.end(), point(),
569 [](point a, point b) { return point(std::max(a.x, b.x), a.y + b.y); }
570 );
571
572 int max_xtra = std::min_element(item_sizes.begin(), item_sizes.end(),
573 [](point a, point b) { return a.x < b.x; }
574 )->x / 2;
575
576 for(std::size_t cells_in_1st_row = 2; cells_in_1st_row <= max_cols; cells_in_1st_row++) {
577 int row_min_width = std::accumulate(item_sizes.begin(), item_sizes.begin() + cells_in_1st_row, 0,
578 [](int a, point b) { return a + b.x; }
579 );
580
581 int row_max_width = row_min_width + max_xtra;
582
583 point row_size, total_size;
584
585 for(const auto& item_size : item_sizes) {
586 if(row_size.x + item_size.x > row_max_width) {
587
588 total_size.y += row_size.y;
589
590 if(total_size.x < row_size.x) {
591 total_size.x = row_size.x;
592 }
593
594 row_size = point();
595 }
596
597 row_size.x += item_size.x;
598
599 if(row_size.y < item_size.y) {
600 row_size.y = item_size.y;
601 }
602 }
603
604 total_size.y += row_size.y;
605
606 if(total_size.x < row_size.x) {
607 total_size.x = row_size.x;
608 }
609
610 best_sizes.push_back(total_size);
611 }
612
613 return *std::min_element(best_sizes.begin(), best_sizes.end(), [](point p1, point p2) {
614 return
615 std::max<double>(p1.x, p1.y) / std::min<double>(p1.x, p1.y) <
616 std::max<double>(p2.x, p2.y) / std::min<double>(p2.x, p2.y);
617 });
618}
619
620void table::place(const point& origin, const point& size)
621{
622 /*
623 * - Set every item to its best size.
624 * - The origin gets increased with the height of the last item.
625 * - No item should be wider as the size.
626 * - In the end the origin should be the sum of the origin and the wanted
627 * height.
628 */
629
630 // TODO: Make sure all cells in a row are the same height
631 point current_origin = origin;
632 int row_height = 0;
633 for(std::size_t i = 0; i < get_item_count(); ++i) {
635 continue;
636 }
637
639 point best_size = grid.get_best_size();
640 // FIXME should we look at grow factors???
641
642 if(current_origin.x + best_size.x > origin.x + size.x) {
643 current_origin.x = origin.x;
644 current_origin.y += row_height;
645 row_height = 0;
646 }
647
648 grid.place(current_origin, best_size);
649
650 current_origin.x += best_size.x;
651 if(best_size.y > row_height) {
652 row_height = best_size.y;
653 }
654 }
655
656 // TODO: If size is wider than best_size, the matrix will take too much vertical space.
657 // This block is supposed to correct for that, but doesn't work properly.
658 // To be more specific, it requires invalidating the layout to take effect.
659 if(current_origin.y + row_height != origin.y + size.y) {
660 point better_size = size;
661 better_size.y -= current_origin.y + row_height - origin.y;
662 set_layout_size(better_size);
663 }
664}
665
666void table::set_origin(const point& origin)
667{
668 point current_origin = origin;
669 std::size_t row_height = 0;
670 for(std::size_t i = 0; i < get_item_count(); ++i) {
672 continue;
673 }
674
676 if(current_origin.x + grid.get_width() > origin.x + get_width()) {
677 current_origin.x = origin.x;
678 current_origin.y += row_height;
679 row_height = 0;
680 }
681
682 grid.set_origin(current_origin);
683
684 current_origin.x += grid.get_width();
685 if(grid.get_height() > row_height) {
686 row_height = grid.get_height();
687 }
688 }
689}
690
691void table::set_visible_rectangle(const rect& rectangle)
692{
693 /*
694 * Note for most implementations this function could work only for the
695 * independent class it probably fails. Evaluate to make a generic
696 * function in the generator template class and call it from the wanted
697 * placement functions.
698 */
699 for(std::size_t i = 0; i < get_item_count(); ++i) {
700 grid& grid = item(i);
701 grid.set_visible_rectangle(rectangle);
702 }
703}
704
705widget* table::find_at(const point& coordinate, const bool must_be_active)
706{
707 assert(get_window());
708
709 for(std::size_t i = 0; i < get_item_count(); ++i) {
710 if(!get_item_shown(i)) {
711 continue;
712 }
713
714 widget* widget = item(i).find_at(coordinate, must_be_active);
715
716 if(widget) {
717 return widget;
718 }
719 }
720 return nullptr;
721}
722
723const widget* table::find_at(const point& coordinate, const bool must_be_active) const
724{
725 assert(get_window());
726
727 for(std::size_t i = 0; i < get_item_count(); ++i) {
728 if(!get_item_shown(i)) {
729 continue;
730 }
731
732 const widget* widget = item(i).find_at(coordinate, must_be_active);
733
734 if(widget) {
735 return widget;
736 }
737 }
738
739 return nullptr;
740}
741
742void table::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
743{
744 if(get_item_count() == 0) {
745 return;
746 }
747
748 if(get_selected_item_count() == 0) {
749 for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
751 // TODO: Check if active?
752 handled = true;
754 break;
755 }
756 }
757
758 return;
759 }
760
761 // NOTE maybe this should only work if we can select only one item...
762 handled = true;
763
764 for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
766 continue;
767 }
768
769 // NOTE we check the first widget to be active since grids have no
770 // active flag. This method might not be entirely reliable.
771 styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
772 if(control && control->get_active()) {
774 return;
775 }
776 }
777}
778
779void table::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
780{
781 if(get_item_count() == 0) {
782 return;
783 }
784
785 if(get_selected_item_count() == 0) {
786 for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
788 // TODO: Check if active?
789 handled = true;
791 break;
792 }
793 }
794
795 return;
796 }
797
798 // NOTE maybe this should only work if we can select only one item...
799 handled = true;
800
801 for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
803 continue;
804 }
805
806 // NOTE we check the first widget to be active since grids have no
807 // active flag. This method might not be entirely reliable.
808 styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
809 if(control && control->get_active()) {
811 return;
812 }
813 }
814}
815
816void table::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
817{
818 if(get_item_count() == 0) {
819 return;
820 }
821
822 if(get_selected_item_count() == 0) {
823 for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
825 // TODO: Check if active?
826 handled = true;
828 break;
829 }
830 }
831
832 return;
833 }
834
835 // NOTE maybe this should only work if we can select only one item...
836 handled = true;
837
838 for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
840 continue;
841 }
842
843 // NOTE we check the first widget to be active since grids have no
844 // active flag. This method might not be entirely reliable.
845 styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
846 if(control && control->get_active()) {
848 return;
849 }
850 }
851}
852
853void table::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
854{
855 if(get_item_count() == 0) {
856 return;
857 }
858
859 if(get_selected_item_count() == 0) {
860 for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
862 // TODO: Check if active?
863 handled = true;
865 break;
866 }
867 }
868
869 return;
870 }
871
872 // NOTE maybe this should only work if we can select only one item...
873 handled = true;
874
875 for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
877 continue;
878 }
879
880 // NOTE we check the first widget to be active since grids have no
881 // active flag. This method might not be entirely reliable.
882 styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
883 if(control && control->get_active()) {
885 return;
886 }
887 }
888}
889
890void independent::request_reduce_width(const unsigned maximum_width)
891{
892 for(std::size_t i = 0; i < get_item_count(); ++i) {
893 grid& grid = item(i);
894 grid.request_reduce_width(maximum_width);
895 }
896}
897
898void independent::request_reduce_height(const unsigned maximum_height)
899{
900 for(std::size_t i = 0; i < get_item_count(); ++i) {
901 grid& grid = item(i);
902 grid.request_reduce_height(maximum_height);
903 }
904}
905
907{
908 /*
909 * The best size is the combination of the greatest width and greatest
910 * height.
911 */
912 point result(0, 0);
913
914 for(std::size_t i = 0; i < get_item_count(); ++i) {
915 const grid& grid = item(i);
916
917 const point best_size = grid.get_best_size();
918
919 if(best_size.x > result.x) {
920 result.x = best_size.x;
921 }
922
923 if(best_size.y > result.y) {
924 result.y = best_size.y;
925 }
926 }
927
928 return result;
929}
930
931void independent::place(const point& origin, const point& size)
932{
933 for(std::size_t i = 0; i < get_item_count(); ++i) {
934 grid& grid = item(i);
935 grid.place(origin, size);
936 }
937}
938
940{
941 /*
942 * Set the origin for every item.
943 *
944 * @todo evaluate whether setting it only for the visible item is better
945 * and what the consequences are.
946 */
947 for(std::size_t i = 0; i < get_item_count(); ++i) {
948 grid& grid = item(i);
949 grid.set_origin(origin);
950 }
951}
952
953widget* independent::find_at(const point& coordinate, const bool must_be_active)
954{
955 assert(get_window());
956
957 const int selected_item = get_selected_item();
958 if(selected_item < 0) {
959 return nullptr;
960 }
961
962 grid& grid = item(selected_item);
963 return grid.find_at(coordinate, must_be_active);
964}
965
966const widget* independent::find_at(const point& coordinate, const bool must_be_active) const
967{
968 assert(get_window());
969
970 const int selected_item = get_selected_item();
971 if(selected_item < 0) {
972 return nullptr;
973 }
974
975 const grid& grid = item(selected_item);
976 return grid.find_at(coordinate, must_be_active);
977}
978
979widget* independent::find(const std::string_view id, const bool must_be_active)
980{
981 for(std::size_t i = 0; i < get_item_count(); ++i) {
982 if(is_selected(i)) {
983 if(widget* widget = item(i).find(id, must_be_active)) {
984 return widget;
985 }
986 }
987 }
988
989 return nullptr;
990}
991
992const widget* independent::find(const std::string_view id, const bool must_be_active) const
993{
994 for(std::size_t i = 0; i < get_item_count(); ++i) {
995 if(is_selected(i)) {
996 if(const widget* widget = item(i).find(id, must_be_active)) {
997 return widget;
998 }
999 }
1000 }
1001
1002 return nullptr;
1003}
1004
1006{
1007 /*
1008 * Set the visible rectangle for every item.
1009 *
1010 * @todo evaluate whether setting it only for the visible item is better
1011 * and what the consequences are.
1012 */
1013 for(std::size_t i = 0; i < get_item_count(); ++i) {
1014 grid& grid = item(i);
1015 grid.set_visible_rectangle(rectangle);
1016 }
1017}
1018
1019} // namespace placement
1020
1021/***** ***** ***** ***** Select action ***** ***** ***** *****/
1022
1023namespace select_action
1024{
1025void selection::select(grid& grid, const bool select)
1026{
1027 selectable_item* selectable = dynamic_cast<selectable_item*>(grid.get_widget(0, 0));
1028 //the check in selection::init is not strict enouth to guaranetee this.
1029 VALIDATE(selectable, "Only toggle buttons and panels are allowed as the cells of a list definition.");
1030
1031 selectable->set_value(select);
1032}
1033
1035 const widget_data& data,
1036 const std::function<void(widget&)>& callback)
1037{
1038 for(unsigned row = 0; row < g->get_rows(); ++row) {
1039 for(unsigned col = 0; col < g->get_cols(); ++col) {
1040 widget* widget = g->get_widget(row, col);
1041 assert(widget);
1042
1043 grid* child_grid = dynamic_cast<grid*>(widget);
1044 toggle_button* btn = dynamic_cast<toggle_button*>(widget);
1045 toggle_panel* panel = dynamic_cast<toggle_panel*>(widget);
1046
1047 if(btn) {
1048 connect_signal_notify_modified(*btn, std::bind(callback, std::placeholders::_1));
1049
1050 widget_data::const_iterator itor = data.find(btn->id());
1051
1052 if(itor == data.end()) {
1053 itor = data.find("");
1054 }
1055 if(itor != data.end()) {
1056 btn->set_members(itor->second);
1057 }
1058 } else if(panel) {
1059 connect_signal_notify_modified(*panel, std::bind(callback, std::placeholders::_1));
1060
1061 panel->set_child_members(data);
1062 } else if(child_grid) {
1063 init(child_grid, data, callback);
1064 } else {
1065 FAIL("In widget '" + widget->id() + "': only toggle buttons and panels are allowed as the cells of a list definition.");
1066 }
1067 }
1068 }
1069}
1070
1072 const widget_data& data,
1073 const std::function<void(widget&)>& /*callback*/)
1074{
1075 for(const auto& item : data) {
1076 if(item.first.empty()) {
1077 for(unsigned row = 0; row < grid->get_rows(); ++row) {
1078 for(unsigned col = 0; col < grid->get_cols(); ++col) {
1079 if(styled_widget* control = dynamic_cast<styled_widget*>(grid->get_widget(row, col))) {
1080 control->set_members(item.second);
1081 }
1082 }
1083 }
1084 } else {
1085 styled_widget* control = dynamic_cast<styled_widget*>(grid->find(item.first, false));
1086 if(control) {
1087 control->set_members(item.second);
1088 }
1089 }
1090 }
1091}
1092
1093} // namespace select_action
1094
1095} // namespace policy
1096
1097/***** ***** ***** ***** Helper macros ***** ***** ***** *****/
1098
1099#ifdef GENERATE_PLACEMENT
1100static_assert(false, "GUI2/Generator: GENERATE_PLACEMENT already defined!");
1101#else
1102#define GENERATE_PLACEMENT \
1103 switch(placement) { \
1104 case generator_base::horizontal_list: \
1105 result = std::make_unique<generator<minimum, maximum, policy::placement::horizontal_list, select_action>>(); \
1106 break; \
1107 case generator_base::vertical_list: \
1108 result = std::make_unique<generator<minimum, maximum, policy::placement::vertical_list, select_action>>(); \
1109 break; \
1110 case generator_base::table: \
1111 result = std::make_unique<generator<minimum, maximum, policy::placement::table, select_action>>(); \
1112 break; \
1113 case generator_base::independent: \
1114 result = std::make_unique<generator<minimum, maximum, policy::placement::independent, select_action>>(); \
1115 break; \
1116 default: \
1117 assert(false); \
1118 }
1119#endif
1120
1121#ifdef GENERATE_SELECT
1122static_assert(false, "GUI2/Generator: GENERATE_SELECT already defined!");
1123#else
1124#define GENERATE_SELECT \
1125 if(select) { \
1126 typedef policy::select_action::selection select_action; \
1127 GENERATE_PLACEMENT \
1128 } else { \
1129 typedef policy::select_action::show select_action; \
1130 GENERATE_PLACEMENT \
1131 }
1132#endif
1133
1134#ifdef GENERATE_MAXIMUM
1135static_assert(false, "GUI2/Generator: GENERATE_MAXIMUM already defined!");
1136#else
1137#define GENERATE_MAXIMUM \
1138 if(has_maximum) { \
1139 typedef policy::maximum_selection::one_item maximum; \
1140 GENERATE_SELECT \
1141 } else { \
1142 typedef policy::maximum_selection::many_items maximum; \
1143 GENERATE_SELECT \
1144 }
1145#endif
1146
1147#ifdef GENERATE_BODY
1148static_assert(false, "GUI2/Generator: GENERATE_BODY already defined!");
1149#else
1150#define GENERATE_BODY \
1151 if(has_minimum) { \
1152 typedef policy::minimum_selection::one_item minimum; \
1153 GENERATE_MAXIMUM \
1154 } else { \
1155 typedef policy::minimum_selection::no_item minimum; \
1156 GENERATE_MAXIMUM \
1157 }
1158#endif
1159
1160std::unique_ptr<generator_base> generator_base::build(
1161 const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
1162{
1163 std::unique_ptr<generator_base> result = nullptr;
1165 return result;
1166}
1167
1168/***** ***** ***** ***** Test code ***** ***** ***** *****/
1169#if 0
1170namespace {
1171
1172void pointer_test()
1173{
1174 auto a = generator_base::build(
1175 true, true, generator_base::horizontal_list, true);
1176
1177 auto b = generator_base::build(
1178 true, false, generator_base::horizontal_list, true);
1179
1180 auto c = generator_base::build(
1181 false, true, generator_base::horizontal_list, true);
1182
1183 auto d = generator_base::build(
1184 false, false, generator_base::horizontal_list, true);
1185
1186 a->clear();
1187 b->clear();
1188 c->clear();
1189 d->clear();
1190}
1191
1192void direct_test()
1193{
1194 generator
1195 < policy::minimum_selection::one_item
1196 , policy::maximum_selection::one_item
1197 , policy::placement::vertical_list
1198 , policy::select_action::selection
1199 > a;
1200
1201 generator
1202 < policy::minimum_selection::one_item
1203 , policy::maximum_selection::many_items
1204 , policy::placement::vertical_list
1205 , policy::select_action::selection
1206 > b;
1207
1208 generator
1209 < policy::minimum_selection::no_item
1210 , policy::maximum_selection::one_item
1211 , policy::placement::vertical_list
1212 , policy::select_action::selection
1213 > c;
1214
1215 generator
1216 < policy::minimum_selection::no_item
1217 , policy::maximum_selection::many_items
1218 , policy::placement::vertical_list
1219 , policy::select_action::selection
1220 > d;
1221
1222 a.clear();
1223 b.clear();
1224 c.clear();
1225 d.clear();
1226}
1227
1228} // namespace
1229#endif
1230
1231} // namespace gui2
virtual unsigned get_ordered_index(unsigned index) const =0
If a sort-order is being applied, maps from unsorted to sorted indicies.
virtual unsigned get_selected_item_count() const =0
Returns the number of selected items.
virtual grid & item(const unsigned index)=0
Gets the grid of an item.
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
virtual unsigned get_item_count() const =0
Returns the number of items.
static std::unique_ptr< generator_base > build(const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
Create a new generator.
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
virtual bool get_item_shown(const unsigned index) const =0
Returns whether the item is shown.
placement
Determines how the items are placed.
Definition generator.hpp:48
virtual unsigned get_item_at_ordered(unsigned index_ordered) const =0
If a sort-order is being applied, maps from sorted to unsorted indices.
virtual grid & item_ordered(const unsigned index)=0
Gets the grid of an item.
virtual void do_select_item(const unsigned index)=0
Selects a not selected item.
virtual void do_deselect_item(const unsigned index)=0
Deselects a selected item.
virtual int get_selected_item() const =0
Returns the selected item.
Base container class.
Definition grid.hpp:32
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition grid.cpp:484
unsigned int get_rows() const
Definition grid.hpp:302
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition grid.cpp:632
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
Definition grid.cpp:645
const widget * get_widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition grid.hpp:181
unsigned int get_cols() const
Definition grid.hpp:308
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition grid.cpp:591
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition grid.cpp:236
virtual void set_visible_rectangle(const rect &rectangle) override
See widget::set_visible_rectangle.
Definition grid.cpp:608
virtual void request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
Definition grid.cpp:314
Small abstract helper class.
virtual void set_value(unsigned value, bool fire_event=false)=0
Select the styled_widget.
virtual void set_members(const widget_item &data)
Sets the members of the styled_widget.
virtual bool get_active() const =0
Gets the active state of the styled_widget.
void set_members(const widget_item &data) override
See styled_widget::set_members.
Base class for all widgets.
Definition widget.hpp:55
void set_layout_size(const point &size)
Definition widget.cpp:346
point get_best_size() const
Gets the best size for the widget.
Definition widget.cpp:203
unsigned get_width() const
Definition widget.cpp:336
unsigned get_height() const
Definition widget.cpp:341
const std::string & id() const
Definition widget.cpp:110
window * get_window()
Get the parent window.
Definition widget.cpp:117
#define GENERATE_BODY
#define ERR_GUI_L
Definition log.hpp:58
Generic file dialog.
void init()
Initializes the GUI subsystems.
Definition gui.cpp:108
std::map< std::string, widget_item > widget_data
Definition widget.hpp:36
std::string_view data
Definition picture.cpp:188
void set_item_shown(const unsigned index, const bool show)
See minimum_selection::one_item::set_item_shown().
Definition generator.cpp:90
void delete_item(const unsigned index)
Called just before an item is deleted.
Definition generator.cpp:84
bool deselect_item(const unsigned index)
Called when the users wants to deselect an item.
Definition generator.cpp:74
void create_item(const unsigned index)
Called when an item is created.
Definition generator.cpp:67
void set_item_shown(const unsigned index, const bool show)
Called when an item is shown or hidden.
Definition generator.cpp:33
void set_visible_rectangle(const rect &rectangle) override
Sets the visible rectangle of the generator.
virtual void set_origin(const point &origin) override
See widget::set_origin.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
void create_item(const unsigned index)
Called when an item is created.
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
virtual void place(const point &origin, const point &size) override
See widget::place.
bool placed_
Has the grid already been placed?
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
void set_visible_rectangle(const rect &rectangle) override
See horizontal_list::set_visible_rectangle().
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
virtual void request_reduce_height(const unsigned maximum_height) override
See horizontal_list::request_reduce_height.
virtual void place(const point &origin, const point &size) override
See widget::place.
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
virtual void set_origin(const point &origin) override
See widget::set_origin.
virtual void place(const point &, const point &) override
See widget::place.
bool placed_
Has the grid already been placed?
void set_visible_rectangle(const rect &) override
See horizontal_list::set_visible_rectangle().
void handle_key_up_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
void handle_key_right_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
virtual void set_origin(const point &) override
See widget::set_origin.
void handle_key_down_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
void handle_key_left_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
virtual widget * find_at(const point &, const bool) override
See widget::find_at.
void create_item(const unsigned)
See horizontal_list::create_item().
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
virtual void set_origin(const point &origin) override
See widget::set_origin.
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
void create_item(const unsigned index)
See horizontal_list::create_item().
virtual void place(const point &origin, const point &size) override
See widget::place.
void set_visible_rectangle(const rect &rectangle) override
See horizontal_list::set_visible_rectangle().
bool placed_
Has the grid already been placed?
virtual point calculate_best_size() const override
See widget::calculate_best_size.
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
void init(grid *grid, const widget_data &data, const std::function< void(widget &)> &callback)
Helper function to initialize a grid.
void select(grid &grid, const bool select)
void init(grid *grid, const widget_data &data, const std::function< void(widget &)> &callback)
Helper function to initialize a grid.
Holds a 2D point.
Definition point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define FAIL(message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define i
#define g
#define d
#define c
#define b