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