The Battle for Wesnoth  1.19.0-dev
generator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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 
25 namespace gui2
26 {
27 namespace policy
28 {
29 /***** ***** ***** ***** Minimum selection ***** ***** ***** *****/
30 
31 namespace minimum_selection
32 {
33 void one_item::set_item_shown(const unsigned index, const bool show)
34 {
35  if(show && get_selected_item_count() == 0) {
37  } else if(!show && is_selected(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 
67 void one_item::create_item(const unsigned index)
68 {
69  if(get_selected_item_count() == 0) {
71  }
72 }
73 
74 bool one_item::deselect_item(const unsigned index)
75 {
76  if(get_selected_item_count() > 1) {
78  return true;
79  }
80 
81  return false;
82 }
83 
84 void one_item::delete_item(const unsigned index)
85 {
86  // this needs the same logic for ensuring that at least one item is selected
88 }
89 
90 void no_item::set_item_shown(const unsigned index, const bool show)
91 {
92  if(!show && is_selected(index)) {
94  }
95 }
96 
97 } // namespace minimum_selection
98 
99 /***** ***** ***** ***** Placement ***** ***** ***** *****/
100 
101 namespace placement
102 {
104  : placed_(false)
105 {
106 }
107 
108 void 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 
140 void 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 
157  grid& grid = item_ordered(i);
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 
184  grid& grid = item_ordered(i);
185  grid.set_origin(current_origin);
186 
187  current_origin.x += grid.get_width();
188  }
189 }
190 
191 void horizontal_list::set_visible_rectangle(const SDL_Rect& rectangle)
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) {
200  grid& grid = item_ordered(i);
201  grid.set_visible_rectangle(rectangle);
202  }
203 }
204 
205 widget* 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 
224 const 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 
243 void 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 
280 void 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 
321 void 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 
352 void 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 
368  grid& grid = item_ordered(i);
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 
387 void vertical_list::set_origin(const point& origin)
388 {
389  point current_origin = origin;
390  for(std::size_t i = 0; i < get_item_count(); ++i) {
392  continue;
393  }
394 
395  grid& grid = item_ordered(i);
396  grid.set_origin(current_origin);
397 
398  current_origin.y += grid.get_height();
399  }
400 }
401 
402 void vertical_list::set_visible_rectangle(const SDL_Rect& rectangle)
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 
416 widget* 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 
434 const 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 
452 void 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 
488 void 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 
529 void 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(std::size_t n = 0; n < item_sizes.size(); n++) {
586  if(row_size.x + item_sizes[n].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_sizes[n].x;
598 
599  if(row_size.y < item_sizes[n].y) {
600  row_size.y = item_sizes[n].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 
620 void 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 
638  grid& grid = item_ordered(i);
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 
666 void 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 
675  grid& grid = item_ordered(i);
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 
691 void table::set_visible_rectangle(const SDL_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 
705 widget* 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 
723 const 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 
742 void 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 
779 void 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 
816 void 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 
853 void 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 
890 void 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 
898 void 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 
931 void 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 
939 void independent::set_origin(const point& origin)
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 
953 widget* 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 
966 const 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 
979 widget* independent::find(const std::string& 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 
992 const widget* independent::find(const std::string& 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 
1005 void independent::set_visible_rectangle(const SDL_Rect& rectangle)
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 
1023 namespace select_action
1024 {
1025 void 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("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  assert(!callback);
1076 
1077  for(const auto& item : data) {
1078  if(item.first.empty()) {
1079  for(unsigned row = 0; row < grid->get_rows(); ++row) {
1080  for(unsigned col = 0; col < grid->get_cols(); ++col) {
1081  if(styled_widget* control = dynamic_cast<styled_widget*>(grid->get_widget(row, col))) {
1082  control->set_members(item.second);
1083  }
1084  }
1085  }
1086  } else {
1087  styled_widget* control = dynamic_cast<styled_widget*>(grid->find(item.first, false));
1088  if(control) {
1089  control->set_members(item.second);
1090  }
1091  }
1092  }
1093 }
1094 
1095 } // namespace select_action
1096 
1097 } // namespace policy
1098 
1099 /***** ***** ***** ***** Helper macros ***** ***** ***** *****/
1100 
1101 #ifdef GENERATE_PLACEMENT
1102 static_assert(false, "GUI2/Generator: GENERATE_PLACEMENT already defined!");
1103 #else
1104 #define GENERATE_PLACEMENT \
1105  switch(placement) { \
1106  case generator_base::horizontal_list: \
1107  result = std::make_unique<generator<minimum, maximum, policy::placement::horizontal_list, select_action>>(); \
1108  break; \
1109  case generator_base::vertical_list: \
1110  result = std::make_unique<generator<minimum, maximum, policy::placement::vertical_list, select_action>>(); \
1111  break; \
1112  case generator_base::table: \
1113  result = std::make_unique<generator<minimum, maximum, policy::placement::table, select_action>>(); \
1114  break; \
1115  case generator_base::independent: \
1116  result = std::make_unique<generator<minimum, maximum, policy::placement::independent, select_action>>(); \
1117  break; \
1118  default: \
1119  assert(false); \
1120  }
1121 #endif
1122 
1123 #ifdef GENERATE_SELECT
1124 static_assert(false, "GUI2/Generator: GENERATE_SELECT already defined!");
1125 #else
1126 #define GENERATE_SELECT \
1127  if(select) { \
1128  typedef policy::select_action::selection select_action; \
1129  GENERATE_PLACEMENT \
1130  } else { \
1131  typedef policy::select_action::show select_action; \
1132  GENERATE_PLACEMENT \
1133  }
1134 #endif
1135 
1136 #ifdef GENERATE_MAXIMUM
1137 static_assert(false, "GUI2/Generator: GENERATE_MAXIMUM already defined!");
1138 #else
1139 #define GENERATE_MAXIMUM \
1140  if(has_maximum) { \
1141  typedef policy::maximum_selection::one_item maximum; \
1142  GENERATE_SELECT \
1143  } else { \
1144  typedef policy::maximum_selection::many_items maximum; \
1145  GENERATE_SELECT \
1146  }
1147 #endif
1148 
1149 #ifdef GENERATE_BODY
1150 static_assert(false, "GUI2/Generator: GENERATE_BODY already defined!");
1151 #else
1152 #define GENERATE_BODY \
1153  if(has_minimum) { \
1154  typedef policy::minimum_selection::one_item minimum; \
1155  GENERATE_MAXIMUM \
1156  } else { \
1157  typedef policy::minimum_selection::no_item minimum; \
1158  GENERATE_MAXIMUM \
1159  }
1160 #endif
1161 
1162 std::unique_ptr<generator_base> generator_base::build(
1163  const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
1164 {
1165  std::unique_ptr<generator_base> result = nullptr;
1166  GENERATE_BODY;
1167  return result;
1168 }
1169 
1170 /***** ***** ***** ***** Test code ***** ***** ***** *****/
1171 #if 0
1172 namespace {
1173 
1174 void pointer_test()
1175 {
1177  true, true, generator_base::horizontal_list, true);
1178 
1180  true, false, generator_base::horizontal_list, true);
1181 
1183  false, true, generator_base::horizontal_list, true);
1184 
1186  false, false, generator_base::horizontal_list, true);
1187 
1188  a->clear();
1189  b->clear();
1190  c->clear();
1191  d->clear();
1192 
1193  delete a;
1194  delete b;
1195  delete c;
1196  delete d;
1197 }
1198 
1199 void direct_test()
1200 {
1201  generator
1202  < policy::minimum_selection::one_item
1203  , policy::maximum_selection::one_item
1204  , policy::placement::vertical_list
1205  , policy::select_action::selection
1206  > a;
1207 
1208  generator
1209  < policy::minimum_selection::one_item
1210  , policy::maximum_selection::many_items
1211  , policy::placement::vertical_list
1212  , policy::select_action::selection
1213  > b;
1214 
1215  generator
1216  < policy::minimum_selection::no_item
1217  , policy::maximum_selection::one_item
1218  , policy::placement::vertical_list
1219  , policy::select_action::selection
1220  > c;
1221 
1222  generator
1223  < policy::minimum_selection::no_item
1224  , policy::maximum_selection::many_items
1225  , policy::placement::vertical_list
1226  , policy::select_action::selection
1227  > d;
1228 
1229  a.clear();
1230  b.clear();
1231  c.clear();
1232  d.clear();
1233 }
1234 
1235 } // namespace
1236 #endif
1237 
1238 } // namespace gui2
double g
Definition: astarsearch.cpp:63
Abstract base class for the generator.
Definition: generator.hpp:39
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_ordered(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 grid & item(const unsigned index)=0
Gets the grid of 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.
Definition: generator.cpp:1162
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 indicies.
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
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
Definition: grid.cpp:608
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_rows() const
Definition: grid.hpp:303
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 &id, const bool must_be_active) override
See widget::find.
Definition: grid.cpp:645
unsigned int get_cols() const
Definition: grid.hpp:309
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 request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
Definition: grid.cpp:314
A panel is a visible container to hold multiple widgets.
Definition: panel.hpp:55
Small abstract helper class.
virtual void set_value(unsigned value, bool fire_event=false)=0
Select the styled_widget.
Base class for all visible items.
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.
Class for a toggle button.
void set_members(const widget_item &data) override
See styled_widget::set_members.
Class for a toggle button.
Base class for all widgets.
Definition: widget.hpp:53
void set_layout_size(const point &size)
Definition: widget.cpp:337
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:193
unsigned get_width() const
Definition: widget.cpp:327
unsigned get_height() const
Definition: widget.cpp:332
const std::string & id() const
Definition: widget.cpp:110
window * get_window()
Get the parent window.
Definition: widget.cpp:117
std::size_t i
Definition: function.cpp:968
#define GENERATE_BODY
Definition: generator.cpp:1152
#define ERR_GUI_L
Definition: log.hpp:58
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:79
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:34
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string_view data
Definition: picture.cpp:194
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
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
void set_item_shown(const unsigned index, const bool show)
Called when an item is shown or hidden.
Definition: generator.cpp:33
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:176
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:118
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:243
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:280
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:140
bool placed_
Has the grid already been placed?
void set_visible_rectangle(const SDL_Rect &rectangle) override
Sets the visible rectangle of the generator.
Definition: generator.cpp:191
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:205
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:953
void set_visible_rectangle(const SDL_Rect &rectangle) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:1005
virtual void request_reduce_height(const unsigned maximum_height) override
See horizontal_list::request_reduce_height.
Definition: generator.cpp:898
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:931
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: generator.cpp:890
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:906
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: generator.cpp:979
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:939
virtual void place(const point &, const point &) override
See widget::place.
Definition: generator.cpp:620
bool placed_
Has the grid already been placed?
void handle_key_up_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:742
void handle_key_right_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:853
virtual void set_origin(const point &) override
See widget::set_origin.
Definition: generator.cpp:666
void handle_key_down_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:779
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:539
void handle_key_left_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:816
virtual widget * find_at(const point &, const bool) override
See widget::find_at.
Definition: generator.cpp:705
void set_visible_rectangle(const SDL_Rect &) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:691
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:416
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:387
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:452
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:352
bool placed_
Has the grid already been placed?
void set_visible_rectangle(const SDL_Rect &rectangle) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:402
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:331
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:488
void init(grid *grid, const widget_data &data, const std::function< void(widget &)> &callback)
Helper function to initialize a grid.
Definition: generator.cpp:1034
void select(grid &grid, const bool select)
Definition: generator.cpp:1025
void init(grid *grid, const widget_data &data, const std::function< void(widget &)> &callback)
Helper function to initialize a grid.
Definition: generator.cpp:1071
Holds a 2D point.
Definition: point.hpp:25
mock_char c
static map_location::DIRECTION n
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 d
#define a
#define b