The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
grid.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by Mark de Wever <[email protected]>
3  Part of the Battle for Wesnoth Project http://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 
20 #include "gui/core/log.hpp"
22 #include "gui/widgets/control.hpp"
23 
24 #include <numeric>
25 
26 #define LOG_SCOPE_HEADER "tgrid [" + id() + "] " + __func__
27 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
28 #define LOG_IMPL_HEADER "tgrid [" + grid.id() + "] " + __func__ + ':'
29 
30 #define LOG_CHILD_SCOPE_HEADER \
31  "tgrid::tchild [" + (widget_ ? widget_->id() : "-") + "] " + __func__
32 #define LOG_CHILD_HEADER LOG_CHILD_SCOPE_HEADER + ':'
33 
34 namespace gui2
35 {
36 
37 tgrid::tgrid(const unsigned rows, const unsigned cols)
38  : rows_(rows)
39  , cols_(cols)
40  , row_height_()
41  , col_width_()
42  , row_grow_factor_(rows)
43  , col_grow_factor_(cols)
44  , children_(rows * cols)
45 {
46 }
47 
49 {
50  // Delete the children in this destructor since resizing a vector copies the
51  // children and thus frees the child prematurely.
52  for(auto & child : children_)
53  {
54  delete child.widget();
55  }
56 }
57 
58 unsigned tgrid::add_row(const unsigned count)
59 {
60  assert(count);
61 
62  // FIXME the warning in set_rows_cols should be killed.
63 
64  unsigned result = rows_;
65  set_rows_cols(rows_ + count, cols_);
66  return result;
67 }
68 
70  const unsigned row,
71  const unsigned col,
72  const unsigned flags,
73  const unsigned border_size)
74 {
75  assert(row < rows_ && col < cols_);
76  assert(flags & VERTICAL_MASK);
77  assert(flags & HORIZONTAL_MASK);
78 
79  tchild& cell = child(row, col);
80 
81  // clear old child if any
82  if(cell.widget()) {
83  // free a child when overwriting it
84  WRN_GUI_G << LOG_HEADER << " child '" << cell.id() << "' at cell '"
85  << row << ',' << col << "' will be replaced.\n";
86  delete cell.widget();
87  }
88 
89  // copy data
90  cell.set_flags(flags);
91  cell.set_border_size(border_size);
92  cell.set_widget(widget);
93  if(cell.widget()) {
94  // make sure the new child is valid before deferring
95  cell.widget()->set_parent(this);
96  }
97 }
98 
100  twidget* widget,
101  const bool recurse,
102  twidget* new_parent)
103 {
104  assert(widget);
105 
106  for(auto & child : children_)
107  {
108  if(child.id() != id) {
109 
110  if(recurse) {
111  // decent in the nested grids.
112  tgrid* grid = dynamic_cast<tgrid*>(child.widget());
113  if(grid) {
114 
115  twidget* old = grid->swap_child(id, widget, true);
116  if(old) {
117  return old;
118  }
119  }
120  }
121 
122  continue;
123  }
124 
125  // When find the widget there should be a widget.
126  twidget* old = child.widget();
127  assert(old);
128  old->set_parent(new_parent);
129 
130  widget->set_parent(this);
131  widget->set_visible(old->get_visible());
132  child.set_widget(widget);
133 
134  return old;
135  }
136 
137  return nullptr;
138 }
139 
140 void tgrid::remove_child(const unsigned row, const unsigned col)
141 {
142  assert(row < rows_ && col < cols_);
143 
144  tchild& cell = child(row, col);
145 
146  if(cell.widget()) {
147  delete cell.widget();
148  }
149  cell.set_widget(nullptr);
150 }
151 
152 void tgrid::remove_child(const std::string& id, const bool find_all)
153 {
154  for(auto & child : children_)
155  {
156 
157  if(child.id() == id) {
158  delete child.widget();
159  child.set_widget(nullptr);
160 
161  if(!find_all) {
162  break;
163  }
164  }
165  }
166 }
167 
168 void tgrid::set_active(const bool active)
169 {
170  for(auto & child : children_)
171  {
172 
173  twidget* widget = child.widget();
174  if(!widget) {
175  continue;
176  }
177 
178  tgrid* grid = dynamic_cast<tgrid*>(widget);
179  if(grid) {
180  grid->set_active(active);
181  continue;
182  }
183 
184  tcontrol* control = dynamic_cast<tcontrol*>(widget);
185  if(control) {
186  control->set_active(active);
187  }
188  }
189 }
190 
191 void tgrid::layout_initialise(const bool full_initialisation)
192 {
193  // Inherited.
194  twidget::layout_initialise(full_initialisation);
195 
196  // Clear child caches.
197  for(auto & child : children_)
198  {
199 
200  child.layout_initialise(full_initialisation);
201  }
202 }
203 
204 void tgrid::reduce_width(const unsigned maximum_width)
205 {
206  /***** ***** ***** ***** INIT ***** ***** ***** *****/
208  DBG_GUI_L << LOG_HEADER << " maximum width " << maximum_width << ".\n";
209 
211  if(size.x <= static_cast<int>(maximum_width)) {
212  DBG_GUI_L << LOG_HEADER << " Already fits.\n";
213  return;
214  }
215 
216  /***** ***** ***** ***** Request resize ***** ***** ***** *****/
217 
218  request_reduce_width(maximum_width);
219 
220  size = get_best_size();
221  if(size.x <= static_cast<int>(maximum_width)) {
222  DBG_GUI_L << LOG_HEADER << " Resize request honored.\n";
223  return;
224  }
225 
226  /***** ***** ***** ***** Demand resize ***** ***** ***** *****/
227 
228  /** @todo Implement. */
229 
230  /***** ***** ***** ***** Acknowlegde failure ***** ***** ***** *****/
231 
232  DBG_GUI_L << LOG_HEADER << " Resizing failed.\n";
233 
235 }
236 
237 void tgrid::request_reduce_width(const unsigned maximum_width)
238 {
240  if(size.x <= static_cast<int>(maximum_width)) {
241  /** @todo this point shouldn't be reached, find out why it does. */
242  return;
243  }
244 
245  const unsigned too_wide = size.x - maximum_width;
246  unsigned reduced = 0;
247  for(size_t col = 0; col < cols_; ++col) {
248  if(too_wide - reduced >= col_width_[col]) {
249  DBG_GUI_L << LOG_HEADER << " column " << col
250  << " is too small to be reduced.\n";
251  continue;
252  }
253 
254  const unsigned wanted_width = col_width_[col] - (too_wide - reduced);
255  const unsigned width
257  *this, col, wanted_width);
258 
259  if(width < col_width_[col]) {
260  DBG_GUI_L << LOG_HEADER << " reduced " << col_width_[col] - width
261  << " pixels for column " << col << ".\n";
262 
263  size.x -= col_width_[col] - width;
264  col_width_[col] = width;
265  }
266 
267  if(size.x <= static_cast<int>(maximum_width)) {
268  break;
269  }
270  }
271 
273 }
274 
275 void tgrid::demand_reduce_width(const unsigned /*maximum_width*/)
276 {
277  /** @todo Implement. */
278 }
279 
280 void tgrid::reduce_height(const unsigned maximum_height)
281 {
282  /***** ***** ***** ***** INIT ***** ***** ***** *****/
284  DBG_GUI_L << LOG_HEADER << " maximum height " << maximum_height << ".\n";
285 
287  if(size.y <= static_cast<int>(maximum_height)) {
288  DBG_GUI_L << LOG_HEADER << " Already fits.\n";
289  return;
290  }
291 
292  /***** ***** ***** ***** Request resize ***** ***** ***** *****/
293 
294  request_reduce_height(maximum_height);
295 
296  size = get_best_size();
297  if(size.y <= static_cast<int>(maximum_height)) {
298  DBG_GUI_L << LOG_HEADER << " Resize request honored.\n";
299  return;
300  }
301 
302  /***** ***** ***** ***** Demand resize ***** ***** ***** *****/
303 
304  /** @todo Implement. */
305 
306  /***** ***** ***** ***** Acknowlegde failure ***** ***** ***** *****/
307 
308  DBG_GUI_L << LOG_HEADER << " Resizing failed.\n";
309 
311 }
312 
313 void tgrid::request_reduce_height(const unsigned maximum_height)
314 {
316  if(size.y <= static_cast<int>(maximum_height)) {
317  /** @todo this point shouldn't be reached, find out why it does. */
318  return;
319  }
320 
321  const unsigned too_high = size.y - maximum_height;
322  unsigned reduced = 0;
323  for(size_t row = 0; row < rows_; ++row) {
324  unsigned wanted_height = row_height_[row] - (too_high - reduced);
325  /**
326  * @todo Improve this code.
327  *
328  * Now we try every item to be reduced, maybe items need a flag whether
329  * or not to try to reduce and also evaluate whether the force
330  * reduction is still needed.
331  */
332  if(too_high - reduced >= row_height_[row]) {
333  DBG_GUI_L << LOG_HEADER << " row " << row << " height "
334  << row_height_[row] << " want to reduce " << too_high
335  << " is too small to be reduced fully try 1 pixel.\n";
336 
337  wanted_height = 1;
338  }
339 
341  *this, row, wanted_height);
342 
343  if(height < row_height_[row]) {
344  DBG_GUI_L << LOG_HEADER << " row " << row << " height "
345  << row_height_[row] << " want to reduce " << too_high
346  << " reduced " << row_height_[row] - height
347  << " pixels.\n";
348 
349  size.y -= row_height_[row] - height;
351  }
352 
353  if(size.y <= static_cast<int>(maximum_height)) {
354  break;
355  }
356  }
357 
358  size = calculate_best_size();
359 
360  DBG_GUI_L << LOG_HEADER << " Requested maximum " << maximum_height
361  << " resulting height " << size.y << ".\n";
362 
363  set_layout_size(size);
364 }
365 
366 void tgrid::demand_reduce_height(const unsigned /*maximum_height*/)
367 {
368  /** @todo Implement. */
369 }
370 
372 {
373  tpoint best_size = calculate_best_size();
374  set_layout_size(best_size);
375  return best_size;
376 }
377 
379 {
381 
382  // Reset the cached values.
383  row_height_.clear();
384  row_height_.resize(rows_, 0);
385  col_width_.clear();
386  col_width_.resize(cols_, 0);
387 
388  // First get the sizes for all items.
389  for(unsigned row = 0; row < rows_; ++row) {
390  for(unsigned col = 0; col < cols_; ++col) {
391 
392  const tpoint size = child(row, col).get_best_size();
393 
394  if(size.x > static_cast<int>(col_width_[col])) {
395  col_width_[col] = size.x;
396  }
397 
398  if(size.y > static_cast<int>(row_height_[row])) {
399  row_height_[row] = size.y;
400  }
401  }
402  }
403 
404  for(unsigned row = 0; row < rows_; ++row) {
405  DBG_GUI_L << LOG_HEADER << " the row_height_ for row " << row
406  << " will be " << row_height_[row] << ".\n";
407  }
408 
409  for(unsigned col = 0; col < cols_; ++col) {
410  DBG_GUI_L << LOG_HEADER << " the col_width_ for column " << col
411  << " will be " << col_width_[col] << ".\n";
412  }
413 
414  const tpoint result(
415  std::accumulate(col_width_.begin(), col_width_.end(), 0),
416  std::accumulate(row_height_.begin(), row_height_.end(), 0));
417 
418  DBG_GUI_L << LOG_HEADER << " returning " << result << ".\n";
419  return result;
420 }
421 
422 bool tgrid::can_wrap() const
423 {
424  for(const auto & child : children_)
425  {
426  if(child.can_wrap()) {
427  return true;
428  }
429  }
430 
431  // Inherited.
432  return twidget::can_wrap();
433 }
434 
435 void tgrid::place(const tpoint& origin, const tpoint& size)
436 {
438 
439  /***** INIT *****/
440 
441  twidget::place(origin, size);
442 
443  if(!rows_ || !cols_) {
444  return;
445  }
446 
447  // call the calculate so the size cache gets updated.
448  const tpoint best_size = calculate_best_size();
449 
450  assert(row_height_.size() == rows_);
451  assert(col_width_.size() == cols_);
452  assert(row_grow_factor_.size() == rows_);
453  assert(col_grow_factor_.size() == cols_);
454 
455  DBG_GUI_L << LOG_HEADER << " best size " << best_size << " available size "
456  << size << ".\n";
457 
458  /***** BEST_SIZE *****/
459 
460  if(best_size == size) {
461  layout(origin);
462  return;
463  }
464 
465  if(best_size.x > size.x || best_size.y > size.y) {
466  // The assertion below fails quite often so try to give as much information as possible.
467  std::stringstream out;
468  out << " Failed to place a grid, we have " << size << " space but we need " << best_size << " space.";
469  out << " This happened at a grid with the id '" << id() << "'";
470  twidget* pw = parent();
471  while(pw != nullptr) {
472  out << " in a '" << typeid(*pw).name() << "' with the id '" << pw->id() << "'";
473  pw = pw->parent();
474  }
475  ERR_GUI_L << LOG_HEADER << out.str() << ".\n";
476 
477  return;
478  }
479 
480  /***** GROW *****/
481 
482  // expand it.
483  if(size.x > best_size.x) {
484  const unsigned w = size.x - best_size.x;
485  unsigned w_size = std::accumulate(
486  col_grow_factor_.begin(), col_grow_factor_.end(), 0);
487 
488  DBG_GUI_L << LOG_HEADER << " extra width " << w
489  << " will be divided amount " << w_size << " units in "
490  << cols_ << " columns.\n";
491 
492  if(w_size == 0) {
493  // If all sizes are 0 reset them to 1
494  for(auto & val : col_grow_factor_)
495  {
496  val = 1;
497  }
498  w_size = cols_;
499  }
500  // We might have a bit 'extra' if the division doesn't fix exactly
501  // but we ignore that part for now.
502  const unsigned w_normal = w / w_size;
503  for(unsigned i = 0; i < cols_; ++i) {
504  col_width_[i] += w_normal * col_grow_factor_[i];
505  DBG_GUI_L << LOG_HEADER << " column " << i
506  << " with grow factor " << col_grow_factor_[i]
507  << " set width to " << col_width_[i] << ".\n";
508  }
509  }
510 
511  if(size.y > best_size.y) {
512  const unsigned h = size.y - best_size.y;
513  unsigned h_size = std::accumulate(
514  row_grow_factor_.begin(), row_grow_factor_.end(), 0);
515  DBG_GUI_L << LOG_HEADER << " extra height " << h
516  << " will be divided amount " << h_size << " units in "
517  << rows_ << " rows.\n";
518 
519  if(h_size == 0) {
520  // If all sizes are 0 reset them to 1
521  for(auto & val : row_grow_factor_)
522  {
523  val = 1;
524  }
525  h_size = rows_;
526  }
527  // We might have a bit 'extra' if the division doesn't fix exactly
528  // but we ignore that part for now.
529  const unsigned h_normal = h / h_size;
530  for(unsigned i = 0; i < rows_; ++i) {
531  row_height_[i] += h_normal * row_grow_factor_[i];
532  DBG_GUI_L << LOG_HEADER << " row " << i << " with grow factor "
533  << row_grow_factor_[i] << " set height to "
534  << row_height_[i] << ".\n";
535  }
536  }
537 
538  layout(origin);
539  return;
540 }
541 
542 void tgrid::set_origin(const tpoint& origin)
543 {
544  const tpoint movement = tpoint(origin.x - get_x(), origin.y - get_y());
545 
546  // Inherited.
547  twidget::set_origin(origin);
548 
549  for(auto & child : children_)
550  {
551 
552  twidget* widget = child.widget();
553  assert(widget);
554 
555  widget->set_origin(tpoint(widget->get_x() + movement.x,
556  widget->get_y() + movement.y));
557  }
558 }
559 
560 void tgrid::set_visible_rectangle(const SDL_Rect& rectangle)
561 {
562  // Inherited.
564 
565  for(auto & child : children_)
566  {
567 
568  twidget* widget = child.widget();
569  assert(widget);
570 
571  widget->set_visible_rectangle(rectangle);
572  }
573 }
574 
576 {
577  for(auto & child : children_)
578  {
579  assert(child.widget());
581  }
582 }
583 
585  const std::vector<twidget*>& call_stack)
586 {
587  assert(!call_stack.empty() && call_stack.back() == this);
588 
589  for(auto & child : children_)
590  {
591 
592  assert(child.widget());
593 
594  std::vector<twidget*> child_call_stack = call_stack;
595  child.widget()->populate_dirty_list(caller, child_call_stack);
596  }
597 }
598 
599 twidget* tgrid::find_at(const tpoint& coordinate, const bool must_be_active)
600 {
601  return tgrid_implementation::find_at<twidget>(
602  *this, coordinate, must_be_active);
603 }
604 
605 const twidget* tgrid::find_at(const tpoint& coordinate,
606  const bool must_be_active) const
607 {
608  return tgrid_implementation::find_at<const twidget>(
609  *this, coordinate, must_be_active);
610 }
611 
612 twidget* tgrid::find(const std::string& id, const bool must_be_active)
613 {
614  return tgrid_implementation::find<twidget>(*this, id, must_be_active);
615 }
616 
617 const twidget* tgrid::find(const std::string& id, const bool must_be_active)
618  const
619 {
620  return tgrid_implementation::find<const twidget>(*this, id, must_be_active);
621 }
622 
623 bool tgrid::has_widget(const twidget& widget) const
624 {
625  if(twidget::has_widget(widget)) {
626  return true;
627  }
628 
629  for(const auto & child : children_)
630  {
631  if(child.widget()->has_widget(widget)) {
632  return true;
633  }
634  }
635  return false;
636 }
637 
639 {
641  return false;
642  }
643 
644  for(const auto & child : children_)
645  {
646  const twidget* widget = child.widget();
647  assert(widget);
648 
649  if(widget->disable_click_dismiss()) {
650  return true;
651  }
652  }
653  return false;
654 }
655 
657 {
658  return new gui2::iterator::tgrid(*this);
659 }
660 
661 void tgrid::set_rows(const unsigned rows)
662 {
663  if(rows == rows_) {
664  return;
665  }
666 
667  set_rows_cols(rows, cols_);
668 }
669 
670 void tgrid::set_cols(const unsigned cols)
671 {
672  if(cols == cols_) {
673  return;
674  }
675 
676  set_rows_cols(rows_, cols);
677 }
678 
679 void tgrid::set_rows_cols(const unsigned rows, const unsigned cols)
680 {
681  if(rows == rows_ && cols == cols_) {
682  return;
683  }
684 
685  if(!children_.empty()) {
686  WRN_GUI_G << LOG_HEADER << " resizing a non-empty grid "
687  << " may give unexpected problems.\n";
688  }
689 
690  rows_ = rows;
691  cols_ = cols;
692  row_grow_factor_.resize(rows);
693  col_grow_factor_.resize(cols);
694  children_.resize(rows_ * cols_);
695 }
696 
698 {
700 
701  if(!widget_) {
702  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << false
703  << " returning " << border_space() << ".\n";
704  return border_space();
705  }
706 
708  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << true
709  << " widget visible " << false << " returning 0,0"
710  << ".\n";
711  return tpoint(0, 0);
712  }
713 
714  const tpoint best_size = widget_->get_best_size() + border_space();
715 
716  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << true
717  << " widget visible " << true << " returning " << best_size
718  << ".\n";
719  return best_size;
720 }
721 
723 {
724  assert(widget());
726  return;
727  }
728 
729  if(border_size_) {
730  if(flags_ & BORDER_TOP) {
731  origin.y += border_size_;
732  size.y -= border_size_;
733  }
734  if(flags_ & BORDER_BOTTOM) {
735  size.y -= border_size_;
736  }
737 
738  if(flags_ & BORDER_LEFT) {
739  origin.x += border_size_;
740  size.x -= border_size_;
741  }
742  if(flags_ & BORDER_RIGHT) {
743  size.x -= border_size_;
744  }
745  }
746 
747  // If size smaller or equal to best size set that size.
748  // No need to check > min size since this is what we got.
749  const tpoint best_size = widget()->get_best_size();
750  if(size <= best_size) {
752  << " in best size range setting widget to " << origin << " x "
753  << size << ".\n";
754 
755  widget()->place(origin, size);
756  return;
757  }
758 
759  const tcontrol* control = dynamic_cast<const tcontrol*>(widget());
760  const tpoint maximum_size = control ? control->get_config_maximum_size()
761  : tpoint(0, 0);
762 
763  if((flags_ & (HORIZONTAL_MASK | VERTICAL_MASK))
765 
766  if(maximum_size == tpoint(0, 0) || size <= maximum_size) {
767 
769  << " in maximum size range setting widget to " << origin
770  << " x " << size << ".\n";
771 
772  widget()->place(origin, size);
773  return;
774  }
775  }
776 
777  tpoint widget_size = tpoint(std::min(size.x, best_size.x),
778  std::min(size.y, best_size.y));
779  tpoint widget_orig = origin;
780 
781  const unsigned v_flag = flags_ & VERTICAL_MASK;
782 
783  if(v_flag == VERTICAL_GROW_SEND_TO_CLIENT) {
784  if(maximum_size.y) {
785  widget_size.y = std::min(size.y, maximum_size.y);
786  } else {
787  widget_size.y = size.y;
788  }
789  DBG_GUI_L << LOG_CHILD_HEADER << " vertical growing from "
790  << best_size.y << " to " << widget_size.y << ".\n";
791 
792  } else if(v_flag == VERTICAL_ALIGN_TOP) {
793  // Do nothing.
794 
795  DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the top.\n";
796 
797  } else if(v_flag == VERTICAL_ALIGN_CENTER) {
798 
799  widget_orig.y += (size.y - widget_size.y) / 2;
800  DBG_GUI_L << LOG_CHILD_HEADER << " vertically centered.\n";
801 
802  } else if(v_flag == VERTICAL_ALIGN_BOTTOM) {
803 
804  widget_orig.y += (size.y - widget_size.y);
805  DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the bottom.\n";
806 
807  } else {
808  ERR_GUI_L << LOG_CHILD_HEADER << " Invalid vertical alignment '"
809  << v_flag << "' specified.\n";
810  assert(false);
811  }
812 
813  const unsigned h_flag = flags_ & HORIZONTAL_MASK;
814 
815  if(h_flag == HORIZONTAL_GROW_SEND_TO_CLIENT) {
816  if(maximum_size.x) {
817  widget_size.x = std::min(size.x, maximum_size.x);
818  } else {
819  widget_size.x = size.x;
820  }
821  DBG_GUI_L << LOG_CHILD_HEADER << " horizontal growing from "
822  << best_size.x << " to " << widget_size.x << ".\n";
823 
824  } else if(h_flag == HORIZONTAL_ALIGN_LEFT) {
825  // Do nothing.
826  DBG_GUI_L << LOG_CHILD_HEADER << " horizontally aligned at the left.\n";
827 
828  } else if(h_flag == HORIZONTAL_ALIGN_CENTER) {
829 
830  widget_orig.x += (size.x - widget_size.x) / 2;
831  DBG_GUI_L << LOG_CHILD_HEADER << " horizontally centered.\n";
832 
833  } else if(h_flag == HORIZONTAL_ALIGN_RIGHT) {
834 
835  widget_orig.x += (size.x - widget_size.x);
837  << " horizontally aligned at the right.\n";
838 
839  } else {
840  ERR_GUI_L << LOG_CHILD_HEADER << " No horizontal alignment '" << h_flag
841  << "' specified.\n";
842  assert(false);
843  }
844 
845  DBG_GUI_L << LOG_CHILD_HEADER << " resize widget to " << widget_orig
846  << " x " << widget_size << ".\n";
847 
848  widget()->place(widget_orig, widget_size);
849 }
850 
851 void tgrid::tchild::layout_initialise(const bool full_initialisation)
852 {
853  assert(widget_);
854 
855  if(widget_->get_visible() != twidget::tvisible::invisible) {
856  widget_->layout_initialise(full_initialisation);
857  }
858 }
859 
861 {
862  assert(widget_);
863  return widget_->id();
864 }
865 
867 {
868  tpoint result(0, 0);
869 
870  if(border_size_) {
871 
872  if(flags_ & BORDER_TOP)
873  result.y += border_size_;
874  if(flags_ & BORDER_BOTTOM)
875  result.y += border_size_;
876 
877  if(flags_ & BORDER_LEFT)
878  result.x += border_size_;
879  if(flags_ & BORDER_RIGHT)
880  result.x += border_size_;
881  }
882 
883  return result;
884 }
885 
886 void tgrid::layout(const tpoint& origin)
887 {
888  tpoint orig = origin;
889  for(unsigned row = 0; row < rows_; ++row) {
890  for(unsigned col = 0; col < cols_; ++col) {
891 
892  const tpoint size(col_width_[col], row_height_[row]);
893  DBG_GUI_L << LOG_HEADER << " set widget at " << row << ',' << col
894  << " at origin " << orig << " with size " << size
895  << ".\n";
896 
897  if(child(row, col).widget()) {
898  child(row, col).place(orig, size);
899  }
900 
901  orig.x += col_width_[col];
902  }
903  orig.y += row_height_[row];
904  orig.x = origin.x;
905  }
906 }
907 
908 void tgrid::impl_draw_children(surface& frame_buffer, int x_offset, int y_offset)
909 {
910  /*
911  * The call to SDL_PumpEvents seems a bit like black magic.
912  * With the call the resizing doesn't seem to lose resize events.
913  * But when added the queue still only contains one resize event and the
914  * internal SDL queue doesn't seem to overflow (rarely more than 50 pending
915  * events).
916  * Without the call when resizing larger a black area of remains, this is
917  * the area not used for resizing the screen, this call `fixes' that.
918  */
919 
921  set_is_dirty(false);
922 
923  for(auto & child : children_)
924  {
925 
926  twidget* widget = child.widget();
927  assert(widget);
928 
929  if(widget->get_visible() != twidget::tvisible::visible) {
930  continue;
931  }
932 
934  continue;
935  }
936 
937  widget->draw_background(frame_buffer, x_offset, y_offset);
938  widget->draw_children(frame_buffer, x_offset, y_offset);
939  widget->draw_foreground(frame_buffer, x_offset, y_offset);
940  widget->set_is_dirty(false);
941  }
942 }
943 
945  tgrid& grid, const unsigned row, const unsigned maximum_height)
946 {
947  // The minimum height required.
948  unsigned required_height = 0;
949 
950  for(size_t x = 0; x < grid.cols_; ++x) {
951  tgrid::tchild& cell = grid.child(row, x);
952  cell_request_reduce_height(cell, maximum_height);
953 
954  const tpoint size(cell.get_best_size());
955 
956  if(required_height == 0 || static_cast<size_t>(size.y)
957  > required_height) {
958 
959  required_height = size.y;
960  }
961  }
962 
963  DBG_GUI_L << LOG_IMPL_HEADER << " maximum row height " << maximum_height
964  << " returning " << required_height << ".\n";
965 
966  return required_height;
967 }
968 
970  tgrid& grid, const unsigned column, const unsigned maximum_width)
971 {
972  // The minimum width required.
973  unsigned required_width = 0;
974 
975  for(size_t y = 0; y < grid.rows_; ++y) {
976  tgrid::tchild& cell = grid.child(y, column);
977  cell_request_reduce_width(cell, maximum_width);
978 
979  const tpoint size(cell.get_best_size());
980 
981  if(required_width == 0 || static_cast<size_t>(size.x)
982  > required_width) {
983 
984  required_width = size.x;
985  }
986  }
987 
988  DBG_GUI_L << LOG_IMPL_HEADER << " maximum column width " << maximum_width
989  << " returning " << required_width << ".\n";
990 
991  return required_width;
992 }
993 
994 void
996  const unsigned maximum_height)
997 {
998  assert(child.widget_);
999 
1001  return;
1002  }
1003 
1004  child.widget_->request_reduce_height(maximum_height
1005  - child.border_space().y);
1006 }
1007 
1008 void
1010  const unsigned maximum_width)
1011 {
1012  assert(child.widget_);
1013 
1015  return;
1016  }
1017 
1018  child.widget_->request_reduce_width(maximum_width - child.border_space().x);
1019 }
1020 
1022 {
1023  grid.set_rows_cols(1, 1);
1024  grid.set_child(widget,
1025  0,
1026  0,
1029  0);
1030 }
1031 
1032 } // namespace gui2
1033 
1034 
1035 /*WIKI
1036  * @page = GUILayout
1037  *
1038  * {{Autogenerated}}
1039  *
1040  * = Abstract =
1041  *
1042  * In the widget library the placement and sizes of elements is determined by
1043  * a grid. Therefore most widgets have no fixed size.
1044  *
1045  *
1046  * = Theory =
1047  *
1048  * We have two examples for the addon dialog, the first example the lower
1049  * buttons are in one grid, that means if the remove button gets wider
1050  * (due to translations) the connect button (4.1 - 2.2) will be aligned
1051  * to the left of the remove button. In the second example the connect
1052  * button will be partial underneath the remove button.
1053  *
1054  * A grid exists of x rows and y columns for all rows the number of columns
1055  * needs to be the same, there is no column (nor row) span. If spanning is
1056  * required place a nested grid to do so. In the examples every row has 1 column
1057  * but rows 3, 4 (and in the second 5) have a nested grid to add more elements
1058  * per row.
1059  *
1060  * In the grid every cell needs to have a widget, if no widget is wanted place
1061  * the special widget ''spacer''. This is a non-visible item which normally
1062  * shouldn't have a size. It is possible to give a spacer a size as well but
1063  * that is discussed elsewhere.
1064  *
1065  * Every row and column has a ''grow_factor'', since all columns in a grid are
1066  * aligned only the columns in the first row need to define their grow factor.
1067  * The grow factor is used to determine with the extra size available in a
1068  * dialog. The algorithm determines the extra size work like this:
1069  *
1070  * * determine the extra size
1071  * * determine the sum of the grow factors
1072  * * if this sum is 0 set the grow factor for every item to 1 and sum to sum of items.
1073  * * divide the extra size with the sum of grow factors
1074  * * for every item multiply the grow factor with the division value
1075  *
1076  * eg
1077  * extra size 100
1078  * grow factors 1, 1, 2, 1
1079  * sum 5
1080  * division 100 / 5 = 20
1081  * extra sizes 20, 20, 40, 20
1082  *
1083  * Since we force the factors to 1 if all zero it's not possible to have non
1084  * growing cells. This can be solved by adding an extra cell with a spacer and a
1085  * grow factor of 1. This is used for the buttons in the examples.
1086  *
1087  * Every cell has a ''border_size'' and ''border'' the ''border_size'' is the
1088  * number of pixels in the cell which aren't available for the widget. This is
1089  * used to make sure the items in different cells aren't put side to side. With
1090  * ''border'' it can be determined which sides get the border. So a border is
1091  * either 0 or ''border_size''.
1092  *
1093  * If the widget doesn't grow when there's more space available the alignment
1094  * determines where in the cell the widget is placed.
1095  *
1096  * == Examples ==
1097  *
1098  * |---------------------------------------|
1099  * | 1.1 |
1100  * |---------------------------------------|
1101  * | 2.1 |
1102  * |---------------------------------------|
1103  * | |-----------------------------------| |
1104  * | | 3.1 - 1.1 | 3.1 - 1.2 | |
1105  * | |-----------------------------------| |
1106  * |---------------------------------------|
1107  * | |-----------------------------------| |
1108  * | | 4.1 - 1.1 | 4.1 - 1.2 | 4.1 - 1.3 | |
1109  * | |-----------------------------------| |
1110  * | | 4.1 - 2.1 | 4.1 - 2.2 | 4.1 - 2.3 | |
1111  * | |-----------------------------------| |
1112  * |---------------------------------------|
1113  *
1114  *
1115  * 1.1 label : title
1116  * 2.1 label : description
1117  * 3.1 - 1.1 label : server
1118  * 3.1 - 1.2 text box : server to connect to
1119  * 4.1 - 1.1 spacer
1120  * 4.1 - 1.2 spacer
1121  * 4.1 - 1.3 button : remove addon
1122  * 4.2 - 2.1 spacer
1123  * 4.2 - 2.2 button : connect
1124  * 4.2 - 2.3 button : cancel
1125  *
1126  *
1127  * |---------------------------------------|
1128  * | 1.1 |
1129  * |---------------------------------------|
1130  * | 2.1 |
1131  * |---------------------------------------|
1132  * | |-----------------------------------| |
1133  * | | 3.1 - 1.1 | 3.1 - 1.2 | |
1134  * | |-----------------------------------| |
1135  * |---------------------------------------|
1136  * | |-----------------------------------| |
1137  * | | 4.1 - 1.1 | 4.1 - 1.2 | |
1138  * | |-----------------------------------| |
1139  * |---------------------------------------|
1140  * | |-----------------------------------| |
1141  * | | 5.1 - 1.1 | 5.1 - 1.2 | 5.1 - 2.3 | |
1142  * | |-----------------------------------| |
1143  * |---------------------------------------|
1144  *
1145  *
1146  * 1.1 label : title
1147  * 2.1 label : description
1148  * 3.1 - 1.1 label : server
1149  * 3.1 - 1.2 text box : server to connect to
1150  * 4.1 - 1.1 spacer
1151  * 4.1 - 1.2 button : remove addon
1152  * 5.2 - 1.1 spacer
1153  * 5.2 - 1.2 button : connect
1154  * 5.2 - 1.3 button : cancel
1155  *
1156  * = Praxis =
1157  *
1158  * This is the code needed to create the skeleton for the structure the extra
1159  * flags are omitted.
1160  *
1161  * [grid]
1162  * [row]
1163  * [column]
1164  * [label]
1165  * # 1.1
1166  * [/label]
1167  * [/column]
1168  * [/row]
1169  * [row]
1170  * [column]
1171  * [label]
1172  * # 2.1
1173  * [/label]
1174  * [/column]
1175  * [/row]
1176  * [row]
1177  * [column]
1178  * [grid]
1179  * [row]
1180  * [column]
1181  * [label]
1182  * # 3.1 - 1.1
1183  * [/label]
1184  * [/column]
1185  * [column]
1186  * [text_box]
1187  * # 3.1 - 1.2
1188  * [/text_box]
1189  * [/column]
1190  * [/row]
1191  * [/grid]
1192  * [/column]
1193  * [/row]
1194  * [row]
1195  * [column]
1196  * [grid]
1197  * [row]
1198  * [column]
1199  * [spacer]
1200  * # 4.1 - 1.1
1201  * [/spacer]
1202  * [/column]
1203  * [column]
1204  * [spacer]
1205  * # 4.1 - 1.2
1206  * [/spacer]
1207  * [/column]
1208  * [column]
1209  * [button]
1210  * # 4.1 - 1.3
1211  * [/button]
1212  * [/column]
1213  * [/row]
1214  * [row]
1215  * [column]
1216  * [spacer]
1217  * # 4.1 - 2.1
1218  * [/spacer]
1219  * [/column]
1220  * [column]
1221  * [button]
1222  * # 4.1 - 2.2
1223  * [/button]
1224  * [/column]
1225  * [column]
1226  * [button]
1227  * # 4.1 - 2.3
1228  * [/button]
1229  * [/column]
1230  * [/row]
1231  * [/grid]
1232  * [/column]
1233  * [/row]
1234  * [/grid]
1235  *
1236  *
1237  * [[Category: WML Reference]]
1238  * [[Category: GUI WML Reference]]
1239  */
Define the common log macros for the gui toolkit.
void set_single_child(tgrid &grid, twidget *widget)
Sets the single child in a grid.
Definition: grid.cpp:1021
Defines the exception classes for the layout algorithm.
void remove_child(const unsigned row, const unsigned col)
Removes and frees a widget in a cell.
Definition: grid.cpp:140
unsigned cols_
The number of grid columns.
Definition: grid.hpp:454
void reduce_height(const unsigned maximum_height)
Tries to reduce the height of a container.
Definition: grid.cpp:280
void draw_foreground(surface &frame_buffer, int x_offset, int y_offset)
Draws the foreground of the widget.
Definition: widget.cpp:371
static const unsigned BORDER_BOTTOM
Definition: grid.hpp:56
#define DBG_GUI_L
Definition: log.hpp:58
void set_parent(twidget *parent)
Definition: widget.cpp:150
virtual void demand_reduce_height(const unsigned maximum_height) override
See twidget::demand_reduce_height.
Definition: grid.cpp:366
virtual bool can_wrap() const
Can the widget wrap.
Definition: widget.cpp:211
virtual void layout_children() override
See twidget::layout_children.
Definition: grid.cpp:575
static const unsigned HORIZONTAL_MASK
Definition: grid.hpp:53
tgrid(const unsigned rows=0, const unsigned cols=0)
Definition: grid.cpp:37
Base container class.
Definition: grid.hpp:29
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:52
virtual bool disable_click_dismiss() const =0
Does the widget disable easy close?
tvisible::scoped_enum get_visible() const
Definition: widget.cpp:471
The widget is not visible.
Definition: widget.hpp:142
virtual void impl_draw_children(surface &frame_buffer, int x_offset, int y_offset) override
See twidget::impl_draw_children.
Definition: grid.cpp:908
#define LOG_IMPL_HEADER
Definition: grid.cpp:28
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:41
GLuint const GLfloat * val
Definition: glew.h:2614
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:57
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: grid.cpp:435
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:48
tpoint recalculate_best_size()
Recalculates the best size.
Definition: grid.cpp:371
bool disable_click_dismiss() const override
See twidget::disable_click_dismiss.
Definition: grid.cpp:638
static const unsigned BORDER_RIGHT
Definition: grid.hpp:58
int get_y() const
Definition: widget.cpp:289
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
virtual void set_origin(const tpoint &origin) override
See twidget::set_origin.
Definition: grid.cpp:542
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:435
tpoint get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:188
static const unsigned BORDER_TOP
Definition: grid.hpp:55
virtual void layout_initialise(const bool full_initialisation)
How the layout engine works.
Definition: widget.cpp:162
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
std::vector< unsigned > row_height_
The row heights in the grid.
Definition: grid.hpp:459
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
void place(tpoint origin, tpoint size)
Places the widget in the cell.
Definition: grid.cpp:722
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:50
tredraw_action::scoped_enum get_drawing_action() const
Definition: widget.cpp:476
twidget * parent()
Definition: widget.cpp:155
virtual ~tgrid()
Definition: grid.cpp:48
void layout_initialise(const bool full_initialisation)
Forwards tgrid::layout_initialise to the cell.
Definition: grid.cpp:851
The user set the widget invisible, that means:
Definition: widget.hpp:103
virtual void request_reduce_width(const unsigned maximum_width)=0
Tries to reduce the width of a widget.
#define ERR_GUI_L
Definition: log.hpp:61
virtual iterator::twalker_ * create_walker() override
See twidget::create_walker.
Definition: grid.cpp:656
virtual void request_reduce_height(const unsigned maximum_height) override
See twidget::request_reduce_height.
Definition: grid.cpp:313
virtual void set_active(const bool active)=0
Sets the control's state.
const tchild & child(const unsigned row, const unsigned col) const
Definition: grid.hpp:477
GLuint64EXT * result
Definition: glew.h:10727
const twidget * widget() const
Definition: grid.hpp:362
tpoint get_config_maximum_size() const
Gets the best size as defined in the config.
Definition: control.cpp:169
unsigned rows_
The number of grid rows.
Definition: grid.hpp:451
#define LOG_CHILD_SCOPE_HEADER
Definition: grid.cpp:30
int y
y coordinate.
Definition: point.hpp:34
void set_layout_size(const tpoint &size)
Definition: widget.cpp:304
void populate_dirty_list(twindow &caller, std::vector< twidget * > &call_stack)
Adds a widget to the dirty list if it is dirty.
Definition: widget.cpp:386
std::vector< unsigned > row_grow_factor_
The grow factor for all rows.
Definition: grid.hpp:465
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
static unsigned row_request_reduce_height(tgrid &grid, const unsigned row, const unsigned maximum_height)
Helper function to do the resizing of a row.
Definition: grid.cpp:944
void layout(const tpoint &origin)
Layouts the children in the grid.
Definition: grid.cpp:886
void set_rows(const unsigned rows)
Definition: grid.cpp:661
void draw_children(surface &frame_buffer, int x_offset, int y_offset)
Draws the children of a widget.
Definition: widget.cpp:356
void set_active(const bool active)
Activates all children.
Definition: grid.cpp:168
virtual void request_reduce_width(const unsigned maximum_width) override
See twidget::request_reduce_width.
Definition: grid.cpp:237
int get_x() const
Definition: widget.cpp:284
#define log_scope2(domain, description)
Definition: log.hpp:186
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:42
virtual void place(const tpoint &origin, const tpoint &size)
Places the widget.
Definition: widget.cpp:233
void draw_background(surface &frame_buffer, int x_offset, int y_offset)
Draws the background of a widget.
Definition: widget.cpp:339
Exception thrown when the width resizing has failed.
std::vector< unsigned > col_grow_factor_
The grow factor for all columns.
Definition: grid.hpp:468
GLuint GLuint GLsizei count
Definition: glew.h:1221
cl_event GLbitfield flags
Definition: glew.h:3070
virtual bool has_widget(const twidget &widget) const override
See twidget::has_widget.
Definition: grid.cpp:623
int x
x coordinate.
Definition: point.hpp:31
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See twidget::set_visible_rectangle.
Definition: grid.cpp:560
GLenum GLenum GLvoid GLvoid * column
Definition: glew.h:3805
void set_border_size(const unsigned border_size)
Definition: grid.hpp:357
The user sets the widget visible, that means:
Definition: widget.hpp:79
#define WRN_GUI_G
Definition: log.hpp:43
#define LOG_SCOPE_HEADER
Definition: grid.cpp:26
virtual bool has_widget(const twidget &widget) const
Does the widget contain the widget.
Definition: widget.cpp:569
virtual bool can_wrap() const override
See twidget::can_wrap.
Definition: grid.cpp:422
const std::string & id() const
Definition: widget.cpp:109
static void cell_request_reduce_width(tgrid::tchild &child, const unsigned maximum_width)
Helper function to do the resizing of a widget.
Definition: grid.cpp:1009
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
twidget * swap_child(const std::string &id, twidget *widget, const bool recurse, twidget *new_parent=nullptr)
Exchanges a child in the grid.
Definition: grid.cpp:99
tpoint border_space() const
Returns the space needed for the border.
Definition: grid.cpp:866
Holds a 2D point.
Definition: point.hpp:24
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
virtual tpoint calculate_best_size() const override
See twidget::calculate_best_size.
Definition: grid.cpp:378
A walker for a gui2::tgrid.
Definition: walker_grid.hpp:29
virtual void demand_reduce_width(const unsigned maximum_width) override
See twidget::demand_reduce_width.
Definition: grid.cpp:275
Child item of the grid.
Definition: grid.hpp:309
virtual void request_reduce_height(const unsigned maximum_height)
Tries to reduce the height of a widget.
Definition: widget.cpp:178
unsigned add_row(const unsigned count=1)
Adds a row to end of the grid.
Definition: grid.cpp:58
virtual void set_visible_rectangle(const SDL_Rect &rectangle)
Sets the visible rectangle for a widget.
Definition: widget.cpp:422
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:43
bool can_wrap() const
Returns the can_wrap for the cell.
Definition: grid.hpp:336
std::vector< tchild > children_
The child items.
Definition: grid.hpp:476
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
Base class for all visible items.
Definition: control.hpp:34
void set_widget(twidget *widget)
Definition: grid.hpp:371
GLsizeiptr size
Definition: glew.h:1649
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:679
GLenum GLenum GLvoid * row
Definition: glew.h:3805
virtual void set_origin(const tpoint &origin)
Sets the origin of the widget.
Definition: widget.cpp:216
void set_cols(const unsigned cols)
Definition: grid.cpp:670
Helper for header for the grid.
const std::string & id() const
Returns the id of the widget/.
Definition: grid.cpp:860
static unsigned column_request_reduce_width(tgrid &grid, const unsigned column, const unsigned maximum_width)
Helper function to do the resizing of a column.
Definition: grid.cpp:969
#define LOG_CHILD_HEADER
Definition: grid.cpp:32
Base class for all widgets.
Definition: widget.hpp:49
twidget * widget_
Pointer to the widget.
Definition: grid.hpp:392
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:51
void set_flags(const unsigned flags)
Definition: grid.hpp:348
virtual void layout_initialise(const bool full_initialisation) override
See twidget::layout_initialise.
Definition: grid.cpp:191
twidget * find(const std::string &id, const bool must_be_active) override
See twidget::find.
Definition: grid.cpp:612
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1220
static const unsigned VERTICAL_MASK
Definition: grid.hpp:45
void set_child(twidget *widget, const unsigned row, const unsigned col, const unsigned flags, const unsigned border_size)
Sets a child in the grid.
Definition: grid.cpp:69
const twidget * widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition: grid.hpp:176
The walker abstract base class.
Definition: walker.hpp:27
void reduce_width(const unsigned maximum_width)
Tries to reduce the width of a container.
Definition: grid.cpp:204
static void cell_request_reduce_height(tgrid::tchild &child, const unsigned maximum_height)
Helper function to do the resizing of a widget.
Definition: grid.cpp:995
tpoint get_best_size() const
Returns the best size for the cell.
Definition: grid.cpp:697
Exception thrown when the height resizing has failed.
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:44
virtual void child_populate_dirty_list(twindow &caller, const std::vector< twidget * > &call_stack) override
See twidget::child_populate_dirty_list.
Definition: grid.cpp:584
void set_visible(const tvisible::scoped_enum visible)
Definition: widget.cpp:445
virtual void layout_children()
Allows a widget to update its children.
Definition: widget.cpp:264
GLsizei const GLcharARB ** string
Definition: glew.h:4503
virtual twidget * find_at(const tpoint &coordinate, const bool must_be_active) override
See twidget::find_at.
Definition: grid.cpp:599
#define LOG_HEADER
Definition: grid.cpp:27
std::vector< unsigned > col_width_
The column widths in the grid.
Definition: grid.hpp:462
static const unsigned BORDER_LEFT
Definition: grid.hpp:57