The Battle for Wesnoth  1.17.0-dev
generator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "gui/widgets/window.hpp"
21 #include "wml_exception.hpp"
22 
23 #include <numeric>
24 #include <cmath>
25 
26 namespace gui2
27 {
28 namespace policy
29 {
30 /***** ***** ***** ***** Minimum selection ***** ***** ***** *****/
31 
32 namespace minimum_selection
33 {
34 void one_item::set_item_shown(const unsigned index, const bool show)
35 {
36  if(show && get_selected_item_count() == 0) {
37  do_select_item(index);
38  } else if(!show && is_selected(index)) {
39  do_deselect_item(index);
40  if(get_selected_item_count() == 0) {
41  bool found_new_item = false;
42  const unsigned item_count = get_item_count();
43  const unsigned ordered_index = get_ordered_index(index);
44  // find the next shown item
45  for(unsigned i = ordered_index + 1; i < item_count; ++i) {
46  unsigned new_index = get_item_at_ordered(i);
47  if(get_item_shown(new_index)) {
48  do_select_item(new_index);
49  found_new_item = true;
50  break;
51  }
52  }
53  // fall back to finding the previous shown item
54  if(!found_new_item) {
55  for(signed i = static_cast<signed>(ordered_index) - 1; i >= 0; --i) {
56  unsigned new_index = get_item_at_ordered(static_cast<unsigned>(i));
57  if(get_item_shown(new_index)) {
58  do_select_item(new_index);
59  break;
60  }
61  }
62  }
63  // if neither search found a new item, accept that there are zero items selected
64  }
65  }
66 }
67 
68 void one_item::create_item(const unsigned index)
69 {
70  if(get_selected_item_count() == 0) {
71  do_select_item(index);
72  }
73 }
74 
75 bool one_item::deselect_item(const unsigned index)
76 {
77  if(get_selected_item_count() > 1) {
78  do_deselect_item(index);
79  return true;
80  }
81 
82  return false;
83 }
84 
85 void one_item::delete_item(const unsigned index)
86 {
87  // this needs the same logic for ensuring that at least one item is selected
88  one_item::set_item_shown(index, false);
89 }
90 
91 void no_item::set_item_shown(const unsigned index, const bool show)
92 {
93  if(!show && is_selected(index)) {
94  do_deselect_item(index);
95  }
96 }
97 
98 } // namespace minimum_selection
99 
100 /***** ***** ***** ***** Placement ***** ***** ***** *****/
101 
102 namespace placement
103 {
104 horizontal_list::horizontal_list()
105  : placed_(false)
106 {
107 }
108 
109 void horizontal_list::create_item(const unsigned /*index*/)
110 {
111  if(!placed_) {
112  return;
113  }
114 
115  /** @todo implement. */
116  assert(false);
117 }
118 
120 {
121  // The best size is the sum of the widths and the greatest height.
122  point result(0, 0);
123 
124  for(std::size_t i = 0; i < get_item_count(); ++i) {
125  if(!get_item_shown(i)) {
126  continue;
127  }
128 
129  const point best_size = item(i).get_best_size();
130 
131  result.x += best_size.x;
132 
133  if(best_size.y > result.y) {
134  result.y = best_size.y;
135  }
136  }
137 
138  return result;
139 }
140 
141 void horizontal_list::place(const point& origin, const point& size)
142 {
143  /*
144  * - Set every item to its best size.
145  * - The origin gets increased with the width of the last item.
146  * - No item should be higher as the size.
147  * - In the end the origin should be the sum or the origin and the wanted
148  * width.
149  */
150 
151  point current_origin = origin;
152 
153  for(std::size_t i = 0; i < get_item_count(); ++i) {
155  continue;
156  }
157 
158  grid& grid = item_ordered(i);
159  point best_size = grid.get_best_size();
160  assert(best_size.y <= size.y);
161 
162  // FIXME should we look at grow factors???
163  best_size.y = size.y;
164 
165  grid.place(current_origin, best_size);
166 
167  current_origin.x += best_size.x;
168  }
169 
170  if(current_origin.x != origin.x + size.x) {
171  ERR_GUI_L << "Failed to fit horizontal list to requested rect; expected right edge was " << origin.x + size.x
172  << ", actual right edge was " << current_origin.x
173  << " (left edge is " << origin.x << ")\n";
174  }
175 }
176 
178 {
179  point current_origin = origin;
180  for(std::size_t i = 0; i < get_item_count(); ++i) {
182  continue;
183  }
184 
185  grid& grid = item_ordered(i);
186  grid.set_origin(current_origin);
187 
188  current_origin.x += grid.get_width();
189  }
190 }
191 
192 void horizontal_list::set_visible_rectangle(const SDL_Rect& rectangle)
193 {
194  /*
195  * Note for most implementations this function could work only for the
196  * independent class it probably fails. Evaluate to make a generic
197  * function in the generator template class and call it from the wanted
198  * placement functions.
199  */
200  for(std::size_t i = 0; i < get_item_count(); ++i) {
201  grid& grid = item_ordered(i);
202  grid.set_visible_rectangle(rectangle);
203  }
204 }
205 
206 widget* horizontal_list::find_at(const point& coordinate, const bool must_be_active)
207 {
208  assert(get_window());
209 
210  for(std::size_t i = 0; i < get_item_count(); ++i) {
211  if(!get_item_shown(i)) {
212  continue;
213  }
214 
215  widget* widget = item(i).find_at(coordinate, must_be_active);
216 
217  if(widget) {
218  return widget;
219  }
220  }
221 
222  return nullptr;
223 }
224 
225 const widget* horizontal_list::find_at(const point& coordinate, const bool must_be_active) const
226 {
227  assert(get_window());
228 
229  for(std::size_t i = 0; i < get_item_count(); ++i) {
230  if(!get_item_shown(i)) {
231  continue;
232  }
233 
234  const widget* widget = item(i).find_at(coordinate, must_be_active);
235 
236  if(widget) {
237  return widget;
238  }
239  }
240 
241  return nullptr;
242 }
243 
244 void horizontal_list::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
245 {
246  if(get_item_count() == 0) {
247  return;
248  }
249 
250  if(get_selected_item_count() == 0) {
251  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
253  // TODO: Check if active?
254  handled = true;
256  break;
257  }
258  }
259 
260  return;
261  }
262 
263  // NOTE maybe this should only work if we can select only one item...
264  handled = true;
265 
266  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
268  continue;
269  }
270 
271  // NOTE we check the first widget to be active since grids have no
272  // active flag. This method might not be entirely reliable.
273  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
274  if(control && control->get_active()) {
276  return;
277  }
278  }
279 }
280 
281 void horizontal_list::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
282 {
283  if(get_item_count() == 0) {
284  return;
285  }
286 
287  if(get_selected_item_count() == 0) {
288  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
290  // TODO: Check if active?
291  handled = true;
293  break;
294  }
295  }
296  return;
297  }
298 
299  // NOTE maybe this should only work if we can select only one item...
300  handled = true;
301 
302  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
304  continue;
305  }
306 
307  // NOTE we check the first widget to be active since grids have no
308  // active flag. This method might not be entirely reliable.
309  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
310  if(control && control->get_active()) {
312  return;
313  }
314  }
315 }
316 
318  : placed_(false)
319 {
320 }
321 
322 void vertical_list::create_item(const unsigned /*index*/)
323 {
324  if(!placed_) {
325  return;
326  }
327 
328  /** @todo implement. */
329  assert(false);
330 }
331 
333 {
334  // The best size is the sum of the heights and the greatest width.
335  point result(0, 0);
336  for(std::size_t i = 0; i < get_item_count(); ++i) {
337  if(!get_item_shown(i)) {
338  continue;
339  }
340 
341  const point best_size = item(i).get_best_size();
342 
343  if(best_size.x > result.x) {
344  result.x = best_size.x;
345  }
346 
347  result.y += best_size.y;
348  }
349 
350  return result;
351 }
352 
353 void vertical_list::place(const point& origin, const point& size)
354 {
355  /*
356  * - Set every item to its best size.
357  * - The origin gets increased with the height of the last item.
358  * - No item should be wider as the size.
359  * - In the end the origin should be the sum or the origin and the wanted
360  * height.
361  */
362 
363  point current_origin = origin;
364  for(std::size_t i = 0; i < get_item_count(); ++i) {
366  continue;
367  }
368 
369  grid& grid = item_ordered(i);
370  point best_size = grid.get_best_size();
371  assert(best_size.x <= size.x);
372 
373  // FIXME should we look at grow factors???
374  best_size.x = size.x;
375 
376  grid.place(current_origin, best_size);
377 
378  current_origin.y += best_size.y;
379  }
380 
381  if(current_origin.y != origin.y + size.y) {
382  ERR_GUI_L << "Failed to fit vertical list to requested rect; expected bottom edge was " << origin.y + size.y
383  << ", actual bottom edge was " << current_origin.y
384  << " (top edge is " << origin.y << ")\n";
385  }
386 }
387 
388 void vertical_list::set_origin(const point& origin)
389 {
390  point current_origin = origin;
391  for(std::size_t i = 0; i < get_item_count(); ++i) {
393  continue;
394  }
395 
396  grid& grid = item_ordered(i);
397  grid.set_origin(current_origin);
398 
399  current_origin.y += grid.get_height();
400  }
401 }
402 
403 void vertical_list::set_visible_rectangle(const SDL_Rect& rectangle)
404 {
405  /*
406  * Note for most implementations this function could work only for the
407  * independent class it probably fails. Evaluate to make a generic
408  * function in the generator template class and call it from the wanted
409  * placement functions.
410  */
411  for(std::size_t i = 0; i < get_item_count(); ++i) {
412  grid& grid = item(i);
413  grid.set_visible_rectangle(rectangle);
414  }
415 }
416 
417 widget* vertical_list::find_at(const point& coordinate, const bool must_be_active)
418 {
419  assert(get_window());
420 
421  for(std::size_t i = 0; i < get_item_count(); ++i) {
422  if(!get_item_shown(i)) {
423  continue;
424  }
425 
426  widget* widget = item(i).find_at(coordinate, must_be_active);
427 
428  if(widget) {
429  return widget;
430  }
431  }
432  return nullptr;
433 }
434 
435 const widget* vertical_list::find_at(const point& coordinate, const bool must_be_active) const
436 {
437  assert(get_window());
438 
439  for(std::size_t i = 0; i < get_item_count(); ++i) {
440  if(!get_item_shown(i)) {
441  continue;
442  }
443 
444  const widget* widget = item(i).find_at(coordinate, must_be_active);
445 
446  if(widget) {
447  return widget;
448  }
449  }
450  return nullptr;
451 }
452 
453 void vertical_list::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
454 {
455  if(get_item_count() == 0) {
456  return;
457  }
458 
459  if(get_selected_item_count() == 0) {
460  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
462  // TODO: Check if active?
463  handled = true;
465  break;
466  }
467  }
468  return;
469  }
470 
471  // NOTE maybe this should only work if we can select only one item...
472  handled = true;
473 
474  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
476  continue;
477  }
478 
479  // NOTE we check the first widget to be active since grids have no
480  // active flag. This method might not be entirely reliable.
481  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
482  if(control && control->get_active()) {
484  return;
485  }
486  }
487 }
488 
489 void vertical_list::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
490 {
491  if(get_item_count() == 0) {
492  return;
493  }
494 
495  if(get_selected_item_count() == 0) {
496  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
498  // TODO: Check if active?
499  handled = true;
501  break;
502  }
503  }
504  return;
505  }
506 
507  // NOTE maybe this should only work if we can select only one item...
508  handled = true;
509 
510  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
512  continue;
513  }
514 
515  // NOTE we check the first widget to be active since grids have no
516  // active flag. This method might not be entirely reliable.
517  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
518  if(control && control->get_active()) {
520  return;
521  }
522  }
523 }
524 
526  : placed_(false) //, n_cols_(2)
527 {
528 }
529 
530 void table::create_item(const unsigned /*index*/)
531 {
532  if(!placed_) {
533  return;
534  }
535 
536  /** @todo implement. */
537  assert(false);
538 }
539 
541 {
542  /* The best size is that which minimizes the aspect ratio of the enclosing rect.
543  * We first calculate the best size of each item, then find the number of rows
544  * that minimizes the aspect ratio. We try a number of columns from 1 up to
545  * sqrt(visible_items) + 2.
546  *
547  * @todo these calculations need rethinking since the grid layout doesn't work
548  * properly as of now.
549  *
550  * - vultraz, 2017-08-25
551  */
552 
553  std::size_t n_items = get_item_count();
554  std::size_t max_cols = std::sqrt(n_items) + 2;
555 
556  std::vector<point> item_sizes;
557  for(std::size_t i = 0; i < n_items; i++) {
558  if(get_item_shown(i)) {
559  item_sizes.push_back(item(i).get_best_size());
560  }
561  }
562 
563  if(item_sizes.empty()) {
564  return point();
565  }
566 
567  std::vector<point> best_sizes(1);
568 
569  best_sizes[0] = std::accumulate(item_sizes.begin(), item_sizes.end(), point(),
570  [](point a, point b) { return point(std::max(a.x, b.x), a.y + b.y); }
571  );
572 
573  int max_xtra = std::min_element(item_sizes.begin(), item_sizes.end(),
574  [](point a, point b) { return a.x < b.x; }
575  )->x / 2;
576 
577  for(std::size_t cells_in_1st_row = 2; cells_in_1st_row <= max_cols; cells_in_1st_row++) {
578  int row_min_width = std::accumulate(item_sizes.begin(), item_sizes.begin() + cells_in_1st_row, 0,
579  [](int a, point b) { return a + b.x; }
580  );
581 
582  int row_max_width = row_min_width + max_xtra;
583  int row = 0;
584 
585  point row_size, total_size;
586 
587  for(std::size_t n = 0; n < item_sizes.size(); n++) {
588  if(row_size.x + item_sizes[n].x > row_max_width) {
589  // Start new row
590  row++;
591 
592  total_size.y += row_size.y;
593 
594  if(total_size.x < row_size.x) {
595  total_size.x = row_size.x;
596  }
597 
598  row_size = point();
599  }
600 
601  row_size.x += item_sizes[n].x;
602 
603  if(row_size.y < item_sizes[n].y) {
604  row_size.y = item_sizes[n].y;
605  }
606  }
607 
608  total_size.y += row_size.y;
609 
610  if(total_size.x < row_size.x) {
611  total_size.x = row_size.x;
612  }
613 
614  best_sizes.push_back(total_size);
615  }
616 
617  return *std::min_element(best_sizes.begin(), best_sizes.end(), [](point p1, point p2) {
618  return
619  std::max<double>(p1.x, p1.y) / std::min<double>(p1.x, p1.y) <
620  std::max<double>(p2.x, p2.y) / std::min<double>(p2.x, p2.y);
621  });
622 }
623 
624 void table::place(const point& origin, const point& size)
625 {
626  /*
627  * - Set every item to its best size.
628  * - The origin gets increased with the height of the last item.
629  * - No item should be wider as the size.
630  * - In the end the origin should be the sum of the origin and the wanted
631  * height.
632  */
633 
634  // TODO: Make sure all cells in a row are the same height
635  point current_origin = origin;
636  int row_height = 0;
637  for(std::size_t i = 0; i < get_item_count(); ++i) {
639  continue;
640  }
641 
642  grid& grid = item_ordered(i);
643  point best_size = grid.get_best_size();
644  // FIXME should we look at grow factors???
645 
646  if(current_origin.x + best_size.x > origin.x + size.x) {
647  current_origin.x = origin.x;
648  current_origin.y += row_height;
649  row_height = 0;
650  }
651 
652  grid.place(current_origin, best_size);
653 
654  current_origin.x += best_size.x;
655  if(best_size.y > row_height) {
656  row_height = best_size.y;
657  }
658  }
659 
660  // TODO: If size is wider than best_size, the matrix will take too much vertical space.
661  // This block is supposed to correct for that, but doesn't work properly.
662  // To be more specific, it requires invalidating the layout to take effect.
663  if(current_origin.y + row_height != origin.y + size.y) {
664  point better_size = size;
665  better_size.y -= current_origin.y + row_height - origin.y;
666  set_layout_size(better_size);
667  }
668 }
669 
670 void table::set_origin(const point& origin)
671 {
672  point current_origin = origin;
673  std::size_t row_height = 0;
674  for(std::size_t i = 0; i < get_item_count(); ++i) {
676  continue;
677  }
678 
679  grid& grid = item_ordered(i);
680  if(current_origin.x + grid.get_width() > origin.x + get_width()) {
681  current_origin.x = origin.x;
682  current_origin.y += row_height;
683  row_height = 0;
684  }
685 
686  grid.set_origin(current_origin);
687 
688  current_origin.x += grid.get_width();
689  if(grid.get_height() > row_height) {
690  row_height = grid.get_height();
691  }
692  }
693 }
694 
695 void table::set_visible_rectangle(const SDL_Rect& rectangle)
696 {
697  /*
698  * Note for most implementations this function could work only for the
699  * independent class it probably fails. Evaluate to make a generic
700  * function in the generator template class and call it from the wanted
701  * placement functions.
702  */
703  for(std::size_t i = 0; i < get_item_count(); ++i) {
704  grid& grid = item(i);
705  grid.set_visible_rectangle(rectangle);
706  }
707 }
708 
709 widget* table::find_at(const point& coordinate, const bool must_be_active)
710 {
711  assert(get_window());
712 
713  for(std::size_t i = 0; i < get_item_count(); ++i) {
714  if(!get_item_shown(i)) {
715  continue;
716  }
717 
718  widget* widget = item(i).find_at(coordinate, must_be_active);
719 
720  if(widget) {
721  return widget;
722  }
723  }
724  return nullptr;
725 }
726 
727 const widget* table::find_at(const point& coordinate, const bool must_be_active) const
728 {
729  assert(get_window());
730 
731  for(std::size_t i = 0; i < get_item_count(); ++i) {
732  if(!get_item_shown(i)) {
733  continue;
734  }
735 
736  const widget* widget = item(i).find_at(coordinate, must_be_active);
737 
738  if(widget) {
739  return widget;
740  }
741  }
742 
743  return nullptr;
744 }
745 
746 void table::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
747 {
748  if(get_item_count() == 0) {
749  return;
750  }
751 
752  if(get_selected_item_count() == 0) {
753  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
755  // TODO: Check if active?
756  handled = true;
758  break;
759  }
760  }
761 
762  return;
763  }
764 
765  // NOTE maybe this should only work if we can select only one item...
766  handled = true;
767 
768  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
770  continue;
771  }
772 
773  // NOTE we check the first widget to be active since grids have no
774  // active flag. This method might not be entirely reliable.
775  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
776  if(control && control->get_active()) {
778  return;
779  }
780  }
781 }
782 
783 void table::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
784 {
785  if(get_item_count() == 0) {
786  return;
787  }
788 
789  if(get_selected_item_count() == 0) {
790  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
792  // TODO: Check if active?
793  handled = true;
795  break;
796  }
797  }
798 
799  return;
800  }
801 
802  // NOTE maybe this should only work if we can select only one item...
803  handled = true;
804 
805  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
807  continue;
808  }
809 
810  // NOTE we check the first widget to be active since grids have no
811  // active flag. This method might not be entirely reliable.
812  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
813  if(control && control->get_active()) {
815  return;
816  }
817  }
818 }
819 
820 void table::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
821 {
822  if(get_item_count() == 0) {
823  return;
824  }
825 
826  if(get_selected_item_count() == 0) {
827  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
829  // TODO: Check if active?
830  handled = true;
832  break;
833  }
834  }
835 
836  return;
837  }
838 
839  // NOTE maybe this should only work if we can select only one item...
840  handled = true;
841 
842  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
844  continue;
845  }
846 
847  // NOTE we check the first widget to be active since grids have no
848  // active flag. This method might not be entirely reliable.
849  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
850  if(control && control->get_active()) {
852  return;
853  }
854  }
855 }
856 
857 void table::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
858 {
859  if(get_item_count() == 0) {
860  return;
861  }
862 
863  if(get_selected_item_count() == 0) {
864  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
866  // TODO: Check if active?
867  handled = true;
869  break;
870  }
871  }
872 
873  return;
874  }
875 
876  // NOTE maybe this should only work if we can select only one item...
877  handled = true;
878 
879  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
881  continue;
882  }
883 
884  // NOTE we check the first widget to be active since grids have no
885  // active flag. This method might not be entirely reliable.
886  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
887  if(control && control->get_active()) {
889  return;
890  }
891  }
892 }
893 
894 void independent::request_reduce_width(const unsigned maximum_width)
895 {
896  for(std::size_t i = 0; i < get_item_count(); ++i) {
897  grid& grid = item(i);
898  grid.request_reduce_width(maximum_width);
899  }
900 }
901 
902 void independent::request_reduce_height(const unsigned maximum_height)
903 {
904  for(std::size_t i = 0; i < get_item_count(); ++i) {
905  grid& grid = item(i);
906  grid.request_reduce_height(maximum_height);
907  }
908 }
909 
911 {
912  /*
913  * The best size is the combination of the greatest width and greatest
914  * height.
915  */
916  point result(0, 0);
917 
918  for(std::size_t i = 0; i < get_item_count(); ++i) {
919  const grid& grid = item(i);
920 
921  const point best_size = grid.get_best_size();
922 
923  if(best_size.x > result.x) {
924  result.x = best_size.x;
925  }
926 
927  if(best_size.y > result.y) {
928  result.y = best_size.y;
929  }
930  }
931 
932  return result;
933 }
934 
935 void independent::place(const point& origin, const point& size)
936 {
937  for(std::size_t i = 0; i < get_item_count(); ++i) {
938  grid& grid = item(i);
939  grid.place(origin, size);
940  }
941 }
942 
943 void independent::set_origin(const point& origin)
944 {
945  /*
946  * Set the origin for every item.
947  *
948  * @todo evaluate whether setting it only for the visible item is better
949  * and what the consequences are.
950  */
951  for(std::size_t i = 0; i < get_item_count(); ++i) {
952  grid& grid = item(i);
953  grid.set_origin(origin);
954  }
955 }
956 
957 widget* independent::find_at(const point& coordinate, const bool must_be_active)
958 {
959  assert(get_window());
960 
961  const int selected_item = get_selected_item();
962  if(selected_item < 0) {
963  return nullptr;
964  }
965 
966  grid& grid = item(selected_item);
967  return grid.find_at(coordinate, must_be_active);
968 }
969 
970 const widget* independent::find_at(const point& coordinate, const bool must_be_active) const
971 {
972  assert(get_window());
973 
974  const int selected_item = get_selected_item();
975  if(selected_item < 0) {
976  return nullptr;
977  }
978 
979  const grid& grid = item(selected_item);
980  return grid.find_at(coordinate, must_be_active);
981 }
982 
983 widget* independent::find(const std::string& id, const bool must_be_active)
984 {
985  for(std::size_t i = 0; i < get_item_count(); ++i) {
986  if(is_selected(i)) {
987  if(widget* widget = item(i).find(id, must_be_active)) {
988  return widget;
989  }
990  }
991  }
992 
993  return nullptr;
994 }
995 
996 const widget* independent::find(const std::string& id, const bool must_be_active) const
997 {
998  for(std::size_t i = 0; i < get_item_count(); ++i) {
999  if(is_selected(i)) {
1000  if(const widget* widget = item(i).find(id, must_be_active)) {
1001  return widget;
1002  }
1003  }
1004  }
1005 
1006  return nullptr;
1007 }
1008 
1009 void independent::set_visible_rectangle(const SDL_Rect& rectangle)
1010 {
1011  /*
1012  * Set the visible rectangle for every item.
1013  *
1014  * @todo evaluate whether setting it only for the visible item is better
1015  * and what the consequences are.
1016  */
1017  for(std::size_t i = 0; i < get_item_count(); ++i) {
1018  grid& grid = item(i);
1019  grid.set_visible_rectangle(rectangle);
1020  }
1021 }
1022 
1023 } // namespace placement
1024 
1025 /***** ***** ***** ***** Select action ***** ***** ***** *****/
1026 
1027 namespace select_action
1028 {
1029 void selection::select(grid& grid, const bool select)
1030 {
1031  selectable_item* selectable = dynamic_cast<selectable_item*>(grid.get_widget(0, 0));
1032  //the check in selection::init is not strict enouth to guaranetee this.
1033  VALIDATE(selectable, "Only toggle buttons and panels are allowed as the cells of a list definition.");
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, std::placeholders::_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, std::placeholders::_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
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:604
virtual void place(const point &, const point &) override
See widget::place.
Definition: generator.cpp:624
void handle_key_right_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:857
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:910
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:417
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:489
void set_layout_size(const point &size)
Definition: widget.cpp:335
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:453
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?
A panel is a visible container to hold multiple widgets.
Definition: panel.hpp:58
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:332
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:34
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:894
const std::string & id() const
Definition: widget.cpp:110
unsigned int get_rows() const
Definition: grid.hpp:308
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:244
virtual widget * find_at(const point &, const bool) override
See widget::find_at.
Definition: generator.cpp:709
unsigned get_height() const
Definition: widget.cpp:330
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:820
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:206
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: grid.cpp:232
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:68
void delete_item(const unsigned index)
Called just before an item is deleted.
Definition: generator.cpp:85
unsigned get_width() const
Definition: widget.cpp:325
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:935
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: generator.cpp:983
void create_item(const unsigned)
See horizontal_list::create_item().
Definition: generator.cpp:530
int x
x coordinate.
Definition: point.hpp:45
Generic file dialog.
Definition: field-fwd.hpp:23
#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:902
#define ERR_GUI_L
Definition: log.hpp:58
Base container class.
Definition: grid.hpp:31
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
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:187
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:193
Abstract base class for the generator.
Definition: generator.hpp:38
virtual void set_origin(const point &) override
See widget::set_origin.
Definition: generator.cpp:670
#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:91
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:783
void create_item(const unsigned index)
Called when an item is created.
Definition: generator.cpp:109
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:943
void init()
Initializes the GUI subsystems.
Definition: gui.cpp:36
placement
Determines how the items are placed.
Definition: generator.hpp:48
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:540
bool deselect_item(const unsigned index)
Called when the users wants to deselect an item.
Definition: generator.cpp:75
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:957
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: grid.cpp:656
unsigned int get_cols() const
Definition: grid.hpp:314
void set_visible_rectangle(const SDL_Rect &) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:695
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:1009
void create_item(const unsigned index)
See horizontal_list::create_item().
Definition: generator.cpp:322
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:480
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:177
virtual int get_selected_item() const =0
Returns the selected item.
std::size_t i
Definition: function.cpp:967
virtual void clear()=0
Deletes all items.
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:353
window * get_window()
Get the parent window.
Definition: widget.cpp:117
double g
Definition: astarsearch.cpp:65
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:141
bool placed_
Has the grid already been placed?
void handle_key_up_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:746
Holds a 2D point.
Definition: point.hpp:24
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:72
Base class for all visible items.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:119
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:587
virtual void request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
Definition: grid.cpp:310
void set_visible_rectangle(const SDL_Rect &rectangle) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:403
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: grid.cpp:643
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:388
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:281
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:192
const widget * get_widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition: grid.hpp:181
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:48
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:140