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