The Battle for Wesnoth  1.17.17+dev
generator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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) {
38  } else if(!show && is_selected(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) {
72  }
73 }
74 
75 bool one_item::deselect_item(const unsigned index)
76 {
77  if(get_selected_item_count() > 1) {
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
89 }
90 
91 void no_item::set_item_shown(const unsigned index, const bool show)
92 {
93  if(!show && is_selected(index)) {
95  }
96 }
97 
98 } // namespace minimum_selection
99 
100 /***** ***** ***** ***** Placement ***** ***** ***** *****/
101 
102 namespace placement
103 {
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 
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 
589  total_size.y += row_size.y;
590 
591  if(total_size.x < row_size.x) {
592  total_size.x = row_size.x;
593  }
594 
595  row_size = point();
596  }
597 
598  row_size.x += item_sizes[n].x;
599 
600  if(row_size.y < item_sizes[n].y) {
601  row_size.y = item_sizes[n].y;
602  }
603  }
604 
605  total_size.y += row_size.y;
606 
607  if(total_size.x < row_size.x) {
608  total_size.x = row_size.x;
609  }
610 
611  best_sizes.push_back(total_size);
612  }
613 
614  return *std::min_element(best_sizes.begin(), best_sizes.end(), [](point p1, point p2) {
615  return
616  std::max<double>(p1.x, p1.y) / std::min<double>(p1.x, p1.y) <
617  std::max<double>(p2.x, p2.y) / std::min<double>(p2.x, p2.y);
618  });
619 }
620 
621 void table::place(const point& origin, const point& size)
622 {
623  /*
624  * - Set every item to its best size.
625  * - The origin gets increased with the height of the last item.
626  * - No item should be wider as the size.
627  * - In the end the origin should be the sum of the origin and the wanted
628  * height.
629  */
630 
631  // TODO: Make sure all cells in a row are the same height
632  point current_origin = origin;
633  int row_height = 0;
634  for(std::size_t i = 0; i < get_item_count(); ++i) {
636  continue;
637  }
638 
639  grid& grid = item_ordered(i);
640  point best_size = grid.get_best_size();
641  // FIXME should we look at grow factors???
642 
643  if(current_origin.x + best_size.x > origin.x + size.x) {
644  current_origin.x = origin.x;
645  current_origin.y += row_height;
646  row_height = 0;
647  }
648 
649  grid.place(current_origin, best_size);
650 
651  current_origin.x += best_size.x;
652  if(best_size.y > row_height) {
653  row_height = best_size.y;
654  }
655  }
656 
657  // TODO: If size is wider than best_size, the matrix will take too much vertical space.
658  // This block is supposed to correct for that, but doesn't work properly.
659  // To be more specific, it requires invalidating the layout to take effect.
660  if(current_origin.y + row_height != origin.y + size.y) {
661  point better_size = size;
662  better_size.y -= current_origin.y + row_height - origin.y;
663  set_layout_size(better_size);
664  }
665 }
666 
667 void table::set_origin(const point& origin)
668 {
669  point current_origin = origin;
670  std::size_t row_height = 0;
671  for(std::size_t i = 0; i < get_item_count(); ++i) {
673  continue;
674  }
675 
676  grid& grid = item_ordered(i);
677  if(current_origin.x + grid.get_width() > origin.x + get_width()) {
678  current_origin.x = origin.x;
679  current_origin.y += row_height;
680  row_height = 0;
681  }
682 
683  grid.set_origin(current_origin);
684 
685  current_origin.x += grid.get_width();
686  if(grid.get_height() > row_height) {
687  row_height = grid.get_height();
688  }
689  }
690 }
691 
692 void table::set_visible_rectangle(const SDL_Rect& rectangle)
693 {
694  /*
695  * Note for most implementations this function could work only for the
696  * independent class it probably fails. Evaluate to make a generic
697  * function in the generator template class and call it from the wanted
698  * placement functions.
699  */
700  for(std::size_t i = 0; i < get_item_count(); ++i) {
701  grid& grid = item(i);
702  grid.set_visible_rectangle(rectangle);
703  }
704 }
705 
706 widget* table::find_at(const point& coordinate, const bool must_be_active)
707 {
708  assert(get_window());
709 
710  for(std::size_t i = 0; i < get_item_count(); ++i) {
711  if(!get_item_shown(i)) {
712  continue;
713  }
714 
715  widget* widget = item(i).find_at(coordinate, must_be_active);
716 
717  if(widget) {
718  return widget;
719  }
720  }
721  return nullptr;
722 }
723 
724 const widget* table::find_at(const point& coordinate, const bool must_be_active) const
725 {
726  assert(get_window());
727 
728  for(std::size_t i = 0; i < get_item_count(); ++i) {
729  if(!get_item_shown(i)) {
730  continue;
731  }
732 
733  const widget* widget = item(i).find_at(coordinate, must_be_active);
734 
735  if(widget) {
736  return widget;
737  }
738  }
739 
740  return nullptr;
741 }
742 
743 void table::handle_key_up_arrow(SDL_Keymod /*modifier*/, bool& handled)
744 {
745  if(get_item_count() == 0) {
746  return;
747  }
748 
749  if(get_selected_item_count() == 0) {
750  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
752  // TODO: Check if active?
753  handled = true;
755  break;
756  }
757  }
758 
759  return;
760  }
761 
762  // NOTE maybe this should only work if we can select only one item...
763  handled = true;
764 
765  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
767  continue;
768  }
769 
770  // NOTE we check the first widget to be active since grids have no
771  // active flag. This method might not be entirely reliable.
772  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
773  if(control && control->get_active()) {
775  return;
776  }
777  }
778 }
779 
780 void table::handle_key_down_arrow(SDL_Keymod /*modifier*/, bool& handled)
781 {
782  if(get_item_count() == 0) {
783  return;
784  }
785 
786  if(get_selected_item_count() == 0) {
787  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
789  // TODO: Check if active?
790  handled = true;
792  break;
793  }
794  }
795 
796  return;
797  }
798 
799  // NOTE maybe this should only work if we can select only one item...
800  handled = true;
801 
802  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
804  continue;
805  }
806 
807  // NOTE we check the first widget to be active since grids have no
808  // active flag. This method might not be entirely reliable.
809  styled_widget* control = dynamic_cast<styled_widget*>(item_ordered(i).get_widget(0, 0));
810  if(control && control->get_active()) {
812  return;
813  }
814  }
815 }
816 
817 void table::handle_key_left_arrow(SDL_Keymod /*modifier*/, bool& handled)
818 {
819  if(get_item_count() == 0) {
820  return;
821  }
822 
823  if(get_selected_item_count() == 0) {
824  for(int i = get_ordered_index(get_item_count() - 1); i >= 0; i--) {
826  // TODO: Check if active?
827  handled = true;
829  break;
830  }
831  }
832 
833  return;
834  }
835 
836  // NOTE maybe this should only work if we can select only one item...
837  handled = true;
838 
839  for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
841  continue;
842  }
843 
844  // NOTE we check the first widget to be active since grids have no
845  // active flag. This method might not be entirely reliable.
846  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
847  if(control && control->get_active()) {
849  return;
850  }
851  }
852 }
853 
854 void table::handle_key_right_arrow(SDL_Keymod /*modifier*/, bool& handled)
855 {
856  if(get_item_count() == 0) {
857  return;
858  }
859 
860  if(get_selected_item_count() == 0) {
861  for(std::size_t i = get_ordered_index(0); i < get_item_count(); i++) {
863  // TODO: Check if active?
864  handled = true;
866  break;
867  }
868  }
869 
870  return;
871  }
872 
873  // NOTE maybe this should only work if we can select only one item...
874  handled = true;
875 
876  for(std::size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
878  continue;
879  }
880 
881  // NOTE we check the first widget to be active since grids have no
882  // active flag. This method might not be entirely reliable.
883  styled_widget* control = dynamic_cast<styled_widget*>(item(get_item_at_ordered(i)).get_widget(0, 0));
884  if(control && control->get_active()) {
886  return;
887  }
888  }
889 }
890 
891 void independent::request_reduce_width(const unsigned maximum_width)
892 {
893  for(std::size_t i = 0; i < get_item_count(); ++i) {
894  grid& grid = item(i);
895  grid.request_reduce_width(maximum_width);
896  }
897 }
898 
899 void independent::request_reduce_height(const unsigned maximum_height)
900 {
901  for(std::size_t i = 0; i < get_item_count(); ++i) {
902  grid& grid = item(i);
903  grid.request_reduce_height(maximum_height);
904  }
905 }
906 
908 {
909  /*
910  * The best size is the combination of the greatest width and greatest
911  * height.
912  */
913  point result(0, 0);
914 
915  for(std::size_t i = 0; i < get_item_count(); ++i) {
916  const grid& grid = item(i);
917 
918  const point best_size = grid.get_best_size();
919 
920  if(best_size.x > result.x) {
921  result.x = best_size.x;
922  }
923 
924  if(best_size.y > result.y) {
925  result.y = best_size.y;
926  }
927  }
928 
929  return result;
930 }
931 
932 void independent::place(const point& origin, const point& size)
933 {
934  for(std::size_t i = 0; i < get_item_count(); ++i) {
935  grid& grid = item(i);
936  grid.place(origin, size);
937  }
938 }
939 
940 void independent::set_origin(const point& origin)
941 {
942  /*
943  * Set the origin for every item.
944  *
945  * @todo evaluate whether setting it only for the visible item is better
946  * and what the consequences are.
947  */
948  for(std::size_t i = 0; i < get_item_count(); ++i) {
949  grid& grid = item(i);
950  grid.set_origin(origin);
951  }
952 }
953 
954 widget* independent::find_at(const point& coordinate, const bool must_be_active)
955 {
956  assert(get_window());
957 
958  const int selected_item = get_selected_item();
959  if(selected_item < 0) {
960  return nullptr;
961  }
962 
963  grid& grid = item(selected_item);
964  return grid.find_at(coordinate, must_be_active);
965 }
966 
967 const widget* independent::find_at(const point& coordinate, const bool must_be_active) const
968 {
969  assert(get_window());
970 
971  const int selected_item = get_selected_item();
972  if(selected_item < 0) {
973  return nullptr;
974  }
975 
976  const grid& grid = item(selected_item);
977  return grid.find_at(coordinate, must_be_active);
978 }
979 
980 widget* independent::find(const std::string& id, const bool must_be_active)
981 {
982  for(std::size_t i = 0; i < get_item_count(); ++i) {
983  if(is_selected(i)) {
984  if(widget* widget = item(i).find(id, must_be_active)) {
985  return widget;
986  }
987  }
988  }
989 
990  return nullptr;
991 }
992 
993 const widget* independent::find(const std::string& id, const bool must_be_active) const
994 {
995  for(std::size_t i = 0; i < get_item_count(); ++i) {
996  if(is_selected(i)) {
997  if(const widget* widget = item(i).find(id, must_be_active)) {
998  return widget;
999  }
1000  }
1001  }
1002 
1003  return nullptr;
1004 }
1005 
1006 void independent::set_visible_rectangle(const SDL_Rect& rectangle)
1007 {
1008  /*
1009  * Set the visible rectangle for every item.
1010  *
1011  * @todo evaluate whether setting it only for the visible item is better
1012  * and what the consequences are.
1013  */
1014  for(std::size_t i = 0; i < get_item_count(); ++i) {
1015  grid& grid = item(i);
1016  grid.set_visible_rectangle(rectangle);
1017  }
1018 }
1019 
1020 } // namespace placement
1021 
1022 /***** ***** ***** ***** Select action ***** ***** ***** *****/
1023 
1024 namespace select_action
1025 {
1026 void selection::select(grid& grid, const bool select)
1027 {
1028  selectable_item* selectable = dynamic_cast<selectable_item*>(grid.get_widget(0, 0));
1029  //the check in selection::init is not strict enouth to guaranetee this.
1030  VALIDATE(selectable, "Only toggle buttons and panels are allowed as the cells of a list definition.");
1031 
1032  selectable->set_value(select);
1033 }
1034 
1036  const widget_data& 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, std::placeholders::_1));
1050 
1051  widget_data::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, std::placeholders::_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 widget_data& 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 = std::make_unique<generator<minimum, maximum, policy::placement::horizontal_list, select_action>>(); \
1109  break; \
1110  case generator_base::vertical_list: \
1111  result = std::make_unique<generator<minimum, maximum, policy::placement::vertical_list, select_action>>(); \
1112  break; \
1113  case generator_base::table: \
1114  result = std::make_unique<generator<minimum, maximum, policy::placement::table, select_action>>(); \
1115  break; \
1116  case generator_base::independent: \
1117  result = std::make_unique<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 
1163 std::unique_ptr<generator_base> generator_base::build(
1164  const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
1165 {
1166  std::unique_ptr<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
1203  < policy::minimum_selection::one_item
1204  , policy::maximum_selection::one_item
1205  , policy::placement::vertical_list
1206  , policy::select_action::selection
1207  > a;
1208 
1209  generator
1210  < policy::minimum_selection::one_item
1211  , policy::maximum_selection::many_items
1212  , policy::placement::vertical_list
1213  , policy::select_action::selection
1214  > b;
1215 
1216  generator
1217  < policy::minimum_selection::no_item
1218  , policy::maximum_selection::one_item
1219  , policy::placement::vertical_list
1220  , policy::select_action::selection
1221  > c;
1222 
1223  generator
1224  < policy::minimum_selection::no_item
1225  , policy::maximum_selection::many_items
1226  , policy::placement::vertical_list
1227  , policy::select_action::selection
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
double g
Definition: astarsearch.cpp:65
Abstract base class for the generator.
Definition: generator.hpp:40
virtual unsigned get_ordered_index(unsigned index) const =0
If a sort-order is being applied, maps from unsorted to sorted indicies.
virtual unsigned get_selected_item_count() const =0
Returns the number of selected items.
virtual grid & item_ordered(const unsigned index)=0
Gets the grid of an item.
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
virtual grid & item(const unsigned index)=0
Gets the grid of an item.
virtual unsigned get_item_count() const =0
Returns the number of items.
static std::unique_ptr< generator_base > build(const bool has_minimum, const bool has_maximum, const placement placement, const bool select)
Create a new generator.
Definition: generator.cpp:1163
virtual bool is_selected(const unsigned index) const =0
Returns whether the item is selected.
virtual bool get_item_shown(const unsigned index) const =0
Returns whether the item is shown.
placement
Determines how the items are placed.
Definition: generator.hpp:49
virtual unsigned get_item_at_ordered(unsigned index_ordered) const =0
If a sort-order is being applied, maps from sorted to unsorted indicies.
virtual void do_select_item(const unsigned index)=0
Selects a not selected item.
virtual void do_deselect_item(const unsigned index)=0
Deselects a selected item.
virtual int get_selected_item() const =0
Returns the selected item.
Base container class.
Definition: grid.hpp:32
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: grid.cpp:484
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
Definition: grid.cpp:608
const widget * get_widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition: grid.hpp:181
unsigned int get_rows() const
Definition: grid.hpp:303
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: grid.cpp:632
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: grid.cpp:645
unsigned int get_cols() const
Definition: grid.hpp:309
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: grid.cpp:591
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: grid.cpp:236
virtual void request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
Definition: grid.cpp:314
A panel is a visible container to hold multiple widgets.
Definition: panel.hpp:59
Small abstract helper class.
virtual void set_value(unsigned value, bool fire_event=false)=0
Select the styled_widget.
Base class for all visible items.
virtual void set_members(const widget_item &data)
Sets the members of the styled_widget.
virtual bool get_active() const =0
Gets the active state of the styled_widget.
Class for a toggle button.
void set_members(const widget_item &data) override
See styled_widget::set_members.
Class for a toggle button.
Base class for all widgets.
Definition: widget.hpp:54
void set_layout_size(const point &size)
Definition: widget.cpp:336
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:194
unsigned get_width() const
Definition: widget.cpp:326
unsigned get_height() const
Definition: widget.cpp:331
const std::string & id() const
Definition: widget.cpp:111
window * get_window()
Get the parent window.
Definition: widget.cpp:118
std::size_t i
Definition: function.cpp:968
#define GENERATE_BODY
Definition: generator.cpp:1153
#define ERR_GUI_L
Definition: log.hpp:58
This file contains the window object, this object is a top level container which has the event manage...
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:193
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:81
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:205
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:61
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::string_view data
Definition: picture.cpp:199
void set_item_shown(const unsigned index, const bool show)
See minimum_selection::one_item::set_item_shown().
Definition: generator.cpp:91
void delete_item(const unsigned index)
Called just before an item is deleted.
Definition: generator.cpp:85
bool deselect_item(const unsigned index)
Called when the users wants to deselect an item.
Definition: generator.cpp:75
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
void set_item_shown(const unsigned index, const bool show)
Called when an item is shown or hidden.
Definition: generator.cpp:34
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:177
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:119
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:244
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:281
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 set_visible_rectangle(const SDL_Rect &rectangle) override
Sets the visible rectangle of the generator.
Definition: generator.cpp:192
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:206
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:954
void set_visible_rectangle(const SDL_Rect &rectangle) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:1006
virtual void request_reduce_height(const unsigned maximum_height) override
See horizontal_list::request_reduce_height.
Definition: generator.cpp:899
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:932
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: generator.cpp:891
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:907
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: generator.cpp:980
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:940
virtual void place(const point &, const point &) override
See widget::place.
Definition: generator.cpp:621
bool placed_
Has the grid already been placed?
void handle_key_up_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:743
void handle_key_right_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:854
virtual void set_origin(const point &) override
See widget::set_origin.
Definition: generator.cpp:667
void handle_key_down_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:780
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:540
void handle_key_left_arrow(SDL_Keymod, bool &) override
Inherited from generator_base.
Definition: generator.cpp:817
virtual widget * find_at(const point &, const bool) override
See widget::find_at.
Definition: generator.cpp:706
void set_visible_rectangle(const SDL_Rect &) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:692
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: generator.cpp:417
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: generator.cpp:388
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:453
virtual grid & create_item(const int index, const builder_grid &list_builder, const widget_item &item_data, const std::function< void(widget &)> &callback)=0
Creates a new item.
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: generator.cpp:353
bool placed_
Has the grid already been placed?
void set_visible_rectangle(const SDL_Rect &rectangle) override
See horizontal_list::set_visible_rectangle().
Definition: generator.cpp:403
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: generator.cpp:332
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from generator_base.
Definition: generator.cpp:489
void init(grid *grid, const widget_data &data, const std::function< void(widget &)> &callback)
Helper function to initialize a grid.
Definition: generator.cpp:1035
void select(grid &grid, const bool select)
Definition: generator.cpp:1026
void init(grid *grid, const widget_data &data, const std::function< void(widget &)> &callback)
Helper function to initialize a grid.
Definition: generator.cpp:1072
Holds a 2D point.
Definition: point.hpp:25
mock_char c
static map_location::DIRECTION n
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define FAIL(message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define d
#define a
#define b