The Battle for Wesnoth  1.15.7+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 #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) {
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 
67 void one_item::create_item(const unsigned index)
68 {
69  if(get_selected_item_count() == 0) {
70  do_select_item(index);
71  }
72 }
73 
74 bool 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 
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
87  set_item_shown(index, false);
88 }
89 
90 void 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 
101 namespace placement
102 {
103 horizontal_list::horizontal_list()
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  int row = 0;
583 
584  point row_size, total_size;
585 
586  for(std::size_t n = 0; n < item_sizes.size(); n++) {
587  if(row_size.x + item_sizes[n].x > row_max_width) {
588  // Start new row
589  row++;
590 
591  total_size.y += row_size.y;
592 
593  if(total_size.x < row_size.x) {
594  total_size.x = row_size.x;
595  }
596 
597  row_size = point();
598  }
599 
600  row_size.x += item_sizes[n].x;
601 
602  if(row_size.y < item_sizes[n].y) {
603  row_size.y = item_sizes[n].y;
604  }
605  }
606 
607  total_size.y += row_size.y;
608 
609  if(total_size.x < row_size.x) {
610  total_size.x = row_size.x;
611  }
612 
613  best_sizes.push_back(total_size);
614  }
615 
616  return *std::min_element(best_sizes.begin(), best_sizes.end(), [](point p1, point p2) {
617  return
618  std::max<double>(p1.x, p1.y) / std::min<double>(p1.x, p1.y) <
619  std::max<double>(p2.x, p2.y) / std::min<double>(p2.x, p2.y);
620  });
621 }
622 
623 void table::place(const point& origin, const point& size)
624 {
625  /*
626  * - Set every item to its best size.
627  * - The origin gets increased with the height of the last item.
628  * - No item should be wider as the size.
629  * - In the end the origin should be the sum of the origin and the wanted
630  * height.
631  */
632 
633  // TODO: Make sure all cells in a row are the same height
634  point current_origin = origin;
635  int row_height = 0;
636  for(std::size_t i = 0; i < get_item_count(); ++i) {
638  continue;
639  }
640 
641  grid& grid = item_ordered(i);
642  point best_size = grid.get_best_size();
643  // FIXME should we look at grow factors???
644 
645  if(current_origin.x + best_size.x > origin.x + size.x) {
646  current_origin.x = origin.x;
647  current_origin.y += row_height;
648  row_height = 0;
649  }
650 
651  grid.place(current_origin, best_size);
652 
653  current_origin.x += best_size.x;
654  if(best_size.y > row_height) {
655  row_height = best_size.y;
656  }
657  }
658 
659  // TODO: If size is wider than best_size, the matrix will take too much vertical space.
660  // This block is supposed to correct for that, but doesn't work properly.
661  // To be more specific, it requires invalidating the layout to take effect.
662  if(current_origin.y + row_height != origin.y + size.y) {
663  point better_size = size;
664  better_size.y -= current_origin.y + row_height - origin.y;
665  set_layout_size(better_size);
666  }
667 }
668 
669 void table::set_origin(const point& origin)
670 {
671  point current_origin = origin;
672  std::size_t row_height = 0;
673  for(std::size_t i = 0; i < get_item_count(); ++i) {
675  continue;
676  }
677 
678  grid& grid = item_ordered(i);
679  if(current_origin.x + grid.get_width() > origin.x + get_width()) {
680  current_origin.x = origin.x;
681  current_origin.y += row_height;
682  row_height = 0;
683  }
684 
685  grid.set_origin(current_origin);
686 
687  current_origin.x += grid.get_width();
688  if(grid.get_height() > row_height) {
689  row_height = grid.get_height();
690  }
691  }
692 }
693 
694 void table::set_visible_rectangle(const SDL_Rect& rectangle)
695 {
696  /*
697  * Note for most implementations this function could work only for the
698  * independent class it probably fails. Evaluate to make a generic
699  * function in the generator template class and call it from the wanted
700  * placement functions.
701  */
702  for(std::size_t i = 0; i < get_item_count(); ++i) {
703  grid& grid = item(i);
704  grid.set_visible_rectangle(rectangle);
705  }
706 }
707 
708 widget* table::find_at(const point& coordinate, const bool must_be_active)
709 {
710  assert(get_window());
711 
712  for(std::size_t i = 0; i < get_item_count(); ++i) {
713  if(!get_item_shown(i)) {
714  continue;
715  }
716 
717  widget* widget = item(i).find_at(coordinate, must_be_active);
718 
719  if(widget) {
720  return widget;
721  }
722  }
723  return nullptr;
724 }
725 
726 const widget* table::find_at(const point& coordinate, const bool must_be_active) const
727 {
728  assert(get_window());
729 
730  for(std::size_t i = 0; i < get_item_count(); ++i) {
731  if(!get_item_shown(i)) {
732  continue;
733  }
734 
735  const widget* widget = item(i).find_at(coordinate, must_be_active);
736 
737  if(widget) {
738  return widget;
739  }
740  }
741 
742  return nullptr;
743 }
744 
745 void table::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
746 {
747  if(get_item_count() == 0) {
748  return;
749  }
750 
751  if(get_selected_item_count() == 0) {
752  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
754  // TODO: Check if active?
755  handled = true;
757  break;
758  }
759  }
760 
761  return;
762  }
763 
764  // NOTE maybe this should only work if we can select only one item...
765  handled = true;
766 
767  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
769  continue;
770  }
771 
772  // NOTE we check the first widget to be active since grids have no
773  // active flag. This method might not be entirely reliable.
774  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
775  if(control && control->get_active()) {
777  return;
778  }
779  }
780 }
781 
782 void table::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
783 {
784  if(get_item_count() == 0) {
785  return;
786  }
787 
788  if(get_selected_item_count() == 0) {
789  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
791  // TODO: Check if active?
792  handled = true;
794  break;
795  }
796  }
797 
798  return;
799  }
800 
801  // NOTE maybe this should only work if we can select only one item...
802  handled = true;
803 
804  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
806  continue;
807  }
808 
809  // NOTE we check the first widget to be active since grids have no
810  // active flag. This method might not be entirely reliable.
811  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
812  if(control && control->get_active()) {
814  return;
815  }
816  }
817 }
818 
819 void table::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
820 {
821  if(get_item_count() == 0) {
822  return;
823  }
824 
825  if(get_selected_item_count() == 0) {
826  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
828  // TODO: Check if active?
829  handled = true;
831  break;
832  }
833  }
834 
835  return;
836  }
837 
838  // NOTE maybe this should only work if we can select only one item...
839  handled = true;
840 
841  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
843  continue;
844  }
845 
846  // NOTE we check the first widget to be active since grids have no
847  // active flag. This method might not be entirely reliable.
848  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
849  if(control && control->get_active()) {
851  return;
852  }
853  }
854 }
855 
856 void table::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
857 {
858  if(get_item_count() == 0) {
859  return;
860  }
861 
862  if(get_selected_item_count() == 0) {
863  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
865  // TODO: Check if active?
866  handled = true;
868  break;
869  }
870  }
871 
872  return;
873  }
874 
875  // NOTE maybe this should only work if we can select only one item...
876  handled = true;
877 
878  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
880  continue;
881  }
882 
883  // NOTE we check the first widget to be active since grids have no
884  // active flag. This method might not be entirely reliable.
885  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
886  if(control && control->get_active()) {
888  return;
889  }
890  }
891 }
892 
893 void independent::request_reduce_width(const unsigned maximum_width)
894 {
895  for(std::size_t i = 0; i < get_item_count(); ++i) {
896  grid& grid = item(i);
897  grid.request_reduce_width(maximum_width);
898  }
899 }
900 
901 void independent::request_reduce_height(const unsigned maximum_height)
902 {
903  for(std::size_t i = 0; i < get_item_count(); ++i) {
904  grid& grid = item(i);
905  grid.request_reduce_height(maximum_height);
906  }
907 }
908 
910 {
911  /*
912  * The best size is the combination of the greatest width and greatest
913  * height.
914  */
915  point result(0, 0);
916 
917  for(std::size_t i = 0; i < get_item_count(); ++i) {
918  const grid& grid = item(i);
919 
920  const point best_size = grid.get_best_size();
921 
922  if(best_size.x > result.x) {
923  result.x = best_size.x;
924  }
925 
926  if(best_size.y > result.y) {
927  result.y = best_size.y;
928  }
929  }
930 
931  return result;
932 }
933 
934 void independent::place(const point& origin, const point& size)
935 {
936  for(std::size_t i = 0; i < get_item_count(); ++i) {
937  grid& grid = item(i);
938  grid.place(origin, size);
939  }
940 }
941 
942 void independent::set_origin(const point& origin)
943 {
944  /*
945  * Set the origin for every item.
946  *
947  * @todo evaluate whether setting it only for the visible item is better
948  * and what the consequences are.
949  */
950  for(std::size_t i = 0; i < get_item_count(); ++i) {
951  grid& grid = item(i);
952  grid.set_origin(origin);
953  }
954 }
955 
956 widget* independent::find_at(const point& coordinate, const bool must_be_active)
957 {
958  assert(get_window());
959 
960  const int selected_item = get_selected_item();
961  if(selected_item < 0) {
962  return nullptr;
963  }
964 
965  grid& grid = item(selected_item);
966  return grid.find_at(coordinate, must_be_active);
967 }
968 
969 const widget* independent::find_at(const point& coordinate, const bool must_be_active) const
970 {
971  assert(get_window());
972 
973  const int selected_item = get_selected_item();
974  if(selected_item < 0) {
975  return nullptr;
976  }
977 
978  const grid& grid = item(selected_item);
979  return grid.find_at(coordinate, must_be_active);
980 }
981 
982 widget* independent::find(const std::string& id, const bool must_be_active)
983 {
984  for(std::size_t i = 0; i < get_item_count(); ++i) {
985  if(is_selected(i)) {
986  if(widget* widget = item(i).find(id, must_be_active)) {
987  return widget;
988  }
989  }
990  }
991 
992  return nullptr;
993 }
994 
995 const widget* independent::find(const std::string& id, const bool must_be_active) const
996 {
997  for(std::size_t i = 0; i < get_item_count(); ++i) {
998  if(is_selected(i)) {
999  if(const widget* widget = item(i).find(id, must_be_active)) {
1000  return widget;
1001  }
1002  }
1003  }
1004 
1005  return nullptr;
1006 }
1007 
1008 void independent::set_visible_rectangle(const SDL_Rect& rectangle)
1009 {
1010  /*
1011  * Set the visible rectangle for every item.
1012  *
1013  * @todo evaluate whether setting it only for the visible item is better
1014  * and what the consequences are.
1015  */
1016  for(std::size_t i = 0; i < get_item_count(); ++i) {
1017  grid& grid = item(i);
1018  grid.set_visible_rectangle(rectangle);
1019  }
1020 }
1021 
1022 } // namespace placement
1023 
1024 /***** ***** ***** ***** Select action ***** ***** ***** *****/
1025 
1026 namespace select_action
1027 {
1028 void selection::select(grid& grid, const bool select)
1029 {
1030  selectable_item* selectable = dynamic_cast<selectable_item*>(grid.get_widget(0, 0));
1031  //the check in selection::init is not strict enouth to guaranetee this.
1032  VALIDATE(selectable, "Only toggle buttons and panels are allowed as the cells of a list definition.");
1033 
1034  selectable->set_value(select);
1035 }
1036 
1038  const std::map<std::string /* widget id */, string_map>& data,
1039  const std::function<void(widget&)>& callback)
1040 {
1041  for(unsigned row = 0; row < g->get_rows(); ++row) {
1042  for(unsigned col = 0; col < g->get_cols(); ++col) {
1043  widget* widget = g->get_widget(row, col);
1044  assert(widget);
1045 
1046  grid* child_grid = dynamic_cast<grid*>(widget);
1047  toggle_button* btn = dynamic_cast<toggle_button*>(widget);
1048  toggle_panel* panel = dynamic_cast<toggle_panel*>(widget);
1049 
1050  if(btn) {
1051  connect_signal_notify_modified(*btn, std::bind(callback, _1));
1052 
1053  std::map<std::string, string_map>::const_iterator itor = data.find(btn->id());
1054 
1055  if(itor == data.end()) {
1056  itor = data.find("");
1057  }
1058  if(itor != data.end()) {
1059  btn->set_members(itor->second);
1060  }
1061  } else if(panel) {
1062  connect_signal_notify_modified(*panel, std::bind(callback, _1));
1063 
1064  panel->set_child_members(data);
1065  } else if(child_grid) {
1066  init(child_grid, data, callback);
1067  } else {
1068  FAIL("Only toggle buttons and panels are allowed as the cells of a list definition.");
1069  }
1070  }
1071  }
1072 }
1073 
1075  const std::map<std::string /* widget id */, string_map>& data,
1076  const std::function<void(widget&)>& callback)
1077 {
1078  assert(!callback);
1079 
1080  for(const auto& item : data) {
1081  if(item.first.empty()) {
1082  for(unsigned row = 0; row < grid->get_rows(); ++row) {
1083  for(unsigned col = 0; col < grid->get_cols(); ++col) {
1084  if(styled_widget* control = dynamic_cast<styled_widget*>(grid->get_widget(row, col))) {
1085  control->set_members(item.second);
1086  }
1087  }
1088  }
1089  } else {
1090  styled_widget* control = dynamic_cast<styled_widget*>(grid->find(item.first, false));
1091  if(control) {
1092  control->set_members(item.second);
1093  }
1094  }
1095  }
1096 }
1097 
1098 } // namespace select_action
1099 
1100 } // namespace policy
1101 
1102 /***** ***** ***** ***** Helper macros ***** ***** ***** *****/
1103 
1104 #ifdef GENERATE_PLACEMENT
1105 static_assert(false, "GUI2/Generator: GENERATE_PLACEMENT already defined!");
1106 #else
1107 #define GENERATE_PLACEMENT \
1108  switch(placement) { \
1109  case generator_base::horizontal_list: \
1110  result = new generator<minimum, maximum, policy::placement::horizontal_list, select_action>; \
1111  break; \
1112  case generator_base::vertical_list: \
1113  result = new generator<minimum, maximum, policy::placement::vertical_list, select_action>; \
1114  break; \
1115  case generator_base::table: \
1116  result = new generator<minimum, maximum, policy::placement::table, select_action>; \
1117  break; \
1118  case generator_base::independent: \
1119  result = new generator<minimum, maximum, policy::placement::independent, select_action>; \
1120  break; \
1121  default: \
1122  assert(false); \
1123  }
1124 #endif
1125 
1126 #ifdef GENERATE_SELECT
1127 static_assert(false, "GUI2/Generator: GENERATE_SELECT already defined!");
1128 #else
1129 #define GENERATE_SELECT \
1130  if(select) { \
1131  typedef policy::select_action::selection select_action; \
1132  GENERATE_PLACEMENT \
1133  } else { \
1134  typedef policy::select_action::show select_action; \
1135  GENERATE_PLACEMENT \
1136  }
1137 #endif
1138 
1139 #ifdef GENERATE_MAXIMUM
1140 static_assert(false, "GUI2/Generator: GENERATE_MAXIMUM already defined!");
1141 #else
1142 #define GENERATE_MAXIMUM \
1143  if(has_maximum) { \
1144  typedef policy::maximum_selection::one_item maximum; \
1145  GENERATE_SELECT \
1146  } else { \
1147  typedef policy::maximum_selection::many_items maximum; \
1148  GENERATE_SELECT \
1149  }
1150 #endif
1151 
1152 #ifdef GENERATE_BODY
1153 static_assert(false, "GUI2/Generator: GENERATE_BODY already defined!");
1154 #else
1155 #define GENERATE_BODY \
1156  if(has_minimum) { \
1157  typedef policy::minimum_selection::one_item minimum; \
1158  GENERATE_MAXIMUM \
1159  } else { \
1160  typedef policy::minimum_selection::no_item minimum; \
1161  GENERATE_MAXIMUM \
1162  }
1163 #endif
1164 
1166  const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
1167 {
1168  generator_base* result = nullptr;
1169  GENERATE_BODY;
1170  return result;
1171 }
1172 
1173 /***** ***** ***** ***** Test code ***** ***** ***** *****/
1174 #if 0
1175 namespace {
1176 
1177 void pointer_test()
1178 {
1180  true, true, generator_base::horizontal_list, true);
1181 
1183  true, false, generator_base::horizontal_list, true);
1184 
1186  false, true, generator_base::horizontal_list, true);
1187 
1189  false, false, generator_base::horizontal_list, true);
1190 
1191  a->clear();
1192  b->clear();
1193  c->clear();
1194  d->clear();
1195 
1196  delete a;
1197  delete b;
1198  delete c;
1199  delete d;
1200 }
1201 
1202 void direct_test()
1203 {
1204  generator
1209  > a;
1210 
1211  generator
1212  < policy::minimum_selection::one_item
1214  , policy::placement::vertical_list
1216  > b;
1217 
1218  generator
1220  , policy::maximum_selection::one_item
1221  , policy::placement::vertical_list
1223  > c;
1224 
1225  generator
1226  < policy::minimum_selection::no_item
1227  , policy::maximum_selection::many_items
1228  , policy::placement::vertical_list
1230  > d;
1231 
1232  a.clear();
1233  b.clear();
1234  c.clear();
1235  d.clear();
1236 }
1237 
1238 } // namespace
1239 #endif
1240 
1241 } // 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
If a sort-order is being applied, maps from sorted to unsorted indicies.
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:623
void handle_key_right_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:856
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:909
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:416
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:488
void set_layout_size(const point &size)
Definition: widget.cpp:334
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:452
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:331
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:33
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:893
const std::string & id() const
Definition: widget.cpp:109
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:49
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:243
virtual widget * find_at(const point &, const bool) override
See widget::find_at.
Definition: generator.cpp:708
unsigned get_height() const
Definition: widget.cpp:329
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:819
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:205
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:67
void delete_item(const unsigned index)
Called just before an item is deleted.
Definition: generator.cpp:84
unsigned get_width() const
Definition: widget.cpp:324
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:934
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: generator.cpp:982
void create_item(const unsigned)
See horizontal_list::create_item().
Definition: generator.cpp:529
int x
x coordinate.
Definition: point.hpp:44
Generic file dialog.
Definition: field-fwd.hpp:22
#define GENERATE_BODY
Definition: generator.cpp:1155
#define b
virtual void request_reduce_height(const unsigned maximum_height) override
See horizontal_list::request_reduce_height.
Definition: generator.cpp:901
#define ERR_GUI_L
Definition: log.hpp:57
Base container class.
Definition: grid.hpp:30
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:186
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:192
Abstract base class for the generator.
Definition: generator.hpp:39
virtual void set_origin(const point &) override
See widget::set_origin.
Definition: generator.cpp:669
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
void set_item_shown(const unsigned index, const bool show)
See minimum_selection::one_item::set_item_shown().
Definition: generator.cpp:90
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:782
void create_item(const unsigned index)
Called when an item is created.
Definition: generator.cpp:108
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:942
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:539
bool deselect_item(const unsigned index)
Called when the users wants to deselect an item.
Definition: generator.cpp:74
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:956
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:694
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:1008
void create_item(const unsigned index)
See horizontal_list::create_item().
Definition: generator.cpp:321
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:176
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:352
u64 size
Definition: statement.cpp:80
window * get_window()
Get the parent window.
Definition: widget.cpp:116
double g
Definition: astarsearch.cpp:64
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 handle_key_up_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:745
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:118
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:1165
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:402
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:387
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:280
virtual unsigned get_ordered_index(unsigned index) const =0
If a sort-order is being applied, maps from unsorted to sorted indicies.
#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:191
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