The Battle for Wesnoth  1.17.0-dev
grid.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 
22 #include "gui/core/log.hpp"
25 #include "gui/widgets/window.hpp"
26 
27 #include <numeric>
28 
29 #define LOG_SCOPE_HEADER "grid [" + id() + "] " + __func__
30 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
31 #define LOG_IMPL_HEADER "grid [" + grid.id() + "] " + __func__ + ':'
32 
33 #define LOG_CHILD_SCOPE_HEADER \
34  "grid::child [" + (widget_ ? widget_->id() : "-") + "] " + __func__
35 #define LOG_CHILD_HEADER LOG_CHILD_SCOPE_HEADER + ':'
36 
37 namespace gui2
38 {
39 
40 grid::grid(const unsigned rows, const unsigned cols)
41  : rows_(rows)
42  , cols_(cols)
43  , row_height_()
44  , col_width_()
45  , row_grow_factor_(rows)
46  , col_grow_factor_(cols)
47  , children_(rows * cols)
48 {
49  connect_signal<event::REQUEST_PLACEMENT>(
50  std::bind(&grid::request_placement, this,
51  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
53 }
54 
56 {
57 }
58 
59 unsigned grid::add_row(const unsigned count)
60 {
61  assert(count);
62 
63  // FIXME the warning in set_rows_cols should be killed.
64 
65  unsigned result = rows_;
66  set_rows_cols(rows_ + count, cols_);
67  return result;
68 }
69 
71  const unsigned row,
72  const unsigned col,
73  const unsigned flags,
74  const unsigned border_size)
75 {
76  assert(row < rows_ && col < cols_);
77  assert(flags & VERTICAL_MASK);
78  assert(flags & HORIZONTAL_MASK);
79 
80  child& cell = get_child(row, col);
81 
82  // clear old child if any
83  if(cell.get_widget()) {
84  // free a child when overwriting it
85  WRN_GUI_G << LOG_HEADER << " child '" << cell.id() << "' at cell '"
86  << row << ',' << col << "' will be replaced.\n";
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.get_widget()) {
94  // make sure the new child is valid before deferring
95  cell.get_widget()->set_parent(this);
96  }
97 }
98 
99 std::unique_ptr<widget> grid::swap_child(const std::string& id,
100  widget* w,
101  const bool recurse,
102  widget* new_parent)
103 {
104  assert(w);
105 
106  for(auto & child : children_)
107  {
108  if(child.id() != id) {
109 
110  if(recurse) {
111  // decent in the nested grids.
112  grid* g = dynamic_cast<grid*>(child.get_widget());
113  if(g) {
114 
115  std::unique_ptr<widget> old = g->swap_child(id, w, true);
116  if(old) {
117  return old;
118  }
119  }
120  }
121 
122  continue;
123  }
124 
125  // Free widget from cell and validate.
126  std::unique_ptr<widget> old = child.free_widget();
127  assert(old);
128 
129  old->set_parent(new_parent);
130 
131  w->set_parent(this);
132  w->set_visible(old->get_visible());
133 
134  child.set_widget(w);
135  return old;
136  }
137 
138  return nullptr;
139 }
140 
141 void grid::remove_child(const unsigned row, const unsigned col)
142 {
143  assert(row < rows_ && col < cols_);
144 
145  child& cell = get_child(row, col);
146  cell.set_widget(nullptr);
147 }
148 
149 void grid::remove_child(const std::string& id, const bool find_all)
150 {
151  for(auto & child : children_)
152  {
153  if(child.id() == id) {
154  child.set_widget(nullptr);
155 
156  if(!find_all) {
157  break;
158  }
159  }
160  }
161 }
162 
163 void grid::set_active(const bool active)
164 {
165  for(auto & child : children_)
166  {
167 
169  if(!widget) {
170  continue;
171  }
172 
173  grid* g = dynamic_cast<grid*>(widget);
174  if(g) {
175  g->set_active(active);
176  continue;
177  }
178 
179  styled_widget* control = dynamic_cast<styled_widget*>(widget);
180  if(control) {
181  control->set_active(active);
182  }
183  }
184 }
185 
186 void grid::layout_initialize(const bool full_initialization)
187 {
188  // Inherited.
189  widget::layout_initialize(full_initialization);
190 
191  // Clear child caches.
192  for(auto & child : children_)
193  {
194 
195  child.layout_initialize(full_initialization);
196  }
197 }
198 
199 void grid::reduce_width(const unsigned maximum_width)
200 {
201  /***** ***** ***** ***** INIT ***** ***** ***** *****/
203  DBG_GUI_L << LOG_HEADER << " maximum width " << maximum_width << ".\n";
204 
206  if(size.x <= static_cast<int>(maximum_width)) {
207  DBG_GUI_L << LOG_HEADER << " Already fits.\n";
208  return;
209  }
210 
211  /***** ***** ***** ***** Request resize ***** ***** ***** *****/
212 
213  request_reduce_width(maximum_width);
214 
215  size = get_best_size();
216  if(size.x <= static_cast<int>(maximum_width)) {
217  DBG_GUI_L << LOG_HEADER << " Resize request honored.\n";
218  return;
219  }
220 
221  /***** ***** ***** ***** Demand resize ***** ***** ***** *****/
222 
223  /** @todo Implement. */
224 
225  /***** ***** ***** ***** Acknowledge failure ***** ***** ***** *****/
226 
227  DBG_GUI_L << LOG_HEADER << " Resizing failed.\n";
228 
230 }
231 
232 void grid::request_reduce_width(const unsigned maximum_width)
233 {
235  if(size.x <= static_cast<int>(maximum_width)) {
236  /** @todo this point shouldn't be reached, find out why it does. */
237  return;
238  }
239 
240  const unsigned too_wide = size.x - maximum_width;
241  unsigned reduced = 0;
242  for(std::size_t col = 0; col < cols_; ++col) {
243  if(too_wide - reduced >= col_width_[col]) {
244  DBG_GUI_L << LOG_HEADER << " column " << col
245  << " is too small to be reduced.\n";
246  continue;
247  }
248 
249  const unsigned wanted_width = col_width_[col] - (too_wide - reduced);
250  const unsigned width
252  *this, col, wanted_width);
253 
254  if(width < col_width_[col]) {
255  unsigned reduction = col_width_[col] - width;
256 
257  DBG_GUI_L << LOG_HEADER << " reduced " << reduction
258  << " pixels for column " << col << ".\n";
259 
260  size.x -= reduction;
261  reduced += reduction;
262  }
263 
264  if(size.x <= static_cast<int>(maximum_width)) {
265  break;
266  }
267  }
268 
270 }
271 
272 void grid::demand_reduce_width(const unsigned /*maximum_width*/)
273 {
274  /** @todo Implement. */
275 }
276 
277 void grid::reduce_height(const unsigned maximum_height)
278 {
279  /***** ***** ***** ***** INIT ***** ***** ***** *****/
281  DBG_GUI_L << LOG_HEADER << " maximum height " << maximum_height << ".\n";
282 
284  if(size.y <= static_cast<int>(maximum_height)) {
285  DBG_GUI_L << LOG_HEADER << " Already fits.\n";
286  return;
287  }
288 
289  /***** ***** ***** ***** Request resize ***** ***** ***** *****/
290 
291  request_reduce_height(maximum_height);
292 
293  size = get_best_size();
294  if(size.y <= static_cast<int>(maximum_height)) {
295  DBG_GUI_L << LOG_HEADER << " Resize request honored.\n";
296  return;
297  }
298 
299  /***** ***** ***** ***** Demand resize ***** ***** ***** *****/
300 
301  /** @todo Implement. */
302 
303  /***** ***** ***** ***** Acknowledge failure ***** ***** ***** *****/
304 
305  DBG_GUI_L << LOG_HEADER << " Resizing failed.\n";
306 
308 }
309 
310 void grid::request_reduce_height(const unsigned maximum_height)
311 {
313  if(size.y <= static_cast<int>(maximum_height)) {
314  /** @todo this point shouldn't be reached, find out why it does. */
315  return;
316  }
317 
318  const unsigned too_high = size.y - maximum_height;
319  unsigned reduced = 0;
320  for(std::size_t row = 0; row < rows_; ++row) {
321  unsigned wanted_height = row_height_[row] - (too_high - reduced);
322  /**
323  * @todo Improve this code.
324  *
325  * Now we try every item to be reduced, maybe items need a flag whether
326  * or not to try to reduce and also evaluate whether the force
327  * reduction is still needed.
328  */
329  if(too_high - reduced >= row_height_[row]) {
330  DBG_GUI_L << LOG_HEADER << " row " << row << " height "
331  << row_height_[row] << " want to reduce " << too_high
332  << " is too small to be reduced fully try 1 pixel.\n";
333 
334  wanted_height = 1;
335  }
336 
337  /* Reducing the height of a widget causes the widget to save its new size
338  in widget::layout_size_. After that, get_best_size() will return that
339  size and not the originally calculated optimal size.
340  Thus, it's perfectly correct that grid::calculate_best_size() that we
341  call later calls get_best_size() for child widgets as if size reduction
342  had never happened. */
343  const unsigned height = grid_implementation::row_request_reduce_height(
344  *this, row, wanted_height);
345 
346  if(height < row_height_[row]) {
347  unsigned reduction = row_height_[row] - height;
348 
349  DBG_GUI_L << LOG_HEADER << " row " << row << " height "
350  << row_height_[row] << " want to reduce " << too_high
351  << " reduced " << reduction << " pixels.\n";
352 
353  size.y -= reduction;
354  reduced += reduction;
355  }
356 
357  if(size.y <= static_cast<int>(maximum_height)) {
358  break;
359  }
360  }
361 
362  size = calculate_best_size();
363 
364  DBG_GUI_L << LOG_HEADER << " Requested maximum " << maximum_height
365  << " resulting height " << size.y << ".\n";
366 
367  set_layout_size(size);
368 }
369 
370 void grid::demand_reduce_height(const unsigned /*maximum_height*/)
371 {
372  /** @todo Implement. */
373 }
374 
375 void grid::request_placement(dispatcher&, const event::ui_event, bool& handled, bool&)
376 {
377  if (get_window()->invalidate_layout_blocked()) {
378  handled = true;
379  return;
380  }
381 
382  point size = get_size();
383  point best_size = calculate_best_size();
384  if(size.x >= best_size.x && size.y >= best_size.y) {
385  place(get_origin(), size);
386  handled = true;
387  return;
388  }
389 
391 
392  if(size.y >= best_size.y) {
393  // We have enough space in the Y direction, but not in the X direction.
394  // Try wrapping the content.
395  request_reduce_width(size.x);
396  best_size = get_best_size();
397 
398  if(size.x >= best_size.x && size.y >= best_size.y) {
399  // Wrapping succeeded, we still fit vertically.
400  place(get_origin(), size);
401  handled = true;
402  return;
403  } else {
404  // Wrapping failed, we no longer fit.
405  // Reset the sizes of child widgets.
406  layout_initialize(true);
407  }
408  }
409 
410  /*
411  Not enough space.
412  Let the event flow higher up.
413  This is a pre-event handler, so the event flows upwards. */
414 }
415 
417 {
418  point best_size = calculate_best_size();
419  set_layout_size(best_size);
420  return best_size;
421 }
422 
424 {
426 
427  // Reset the cached values.
428  row_height_.clear();
429  row_height_.resize(rows_, 0);
430  col_width_.clear();
431  col_width_.resize(cols_, 0);
432 
433  // First get the sizes for all items.
434  for(unsigned row = 0; row < rows_; ++row) {
435  for(unsigned col = 0; col < cols_; ++col) {
436 
437  const point size = get_child(row, col).get_best_size();
438 
439  if(size.x > static_cast<int>(col_width_[col])) {
440  col_width_[col] = size.x;
441  }
442 
443  if(size.y > static_cast<int>(row_height_[row])) {
444  row_height_[row] = size.y;
445  }
446  }
447  }
448 
449  for(unsigned row = 0; row < rows_; ++row) {
450  DBG_GUI_L << LOG_HEADER << " the row_height_ for row " << row
451  << " will be " << row_height_[row] << ".\n";
452  }
453 
454  for(unsigned col = 0; col < cols_; ++col) {
455  DBG_GUI_L << LOG_HEADER << " the col_width_ for column " << col
456  << " will be " << col_width_[col] << ".\n";
457  }
458 
459  const point result(
460  std::accumulate(col_width_.begin(), col_width_.end(), 0),
461  std::accumulate(row_height_.begin(), row_height_.end(), 0));
462 
463  DBG_GUI_L << LOG_HEADER << " returning " << result << ".\n";
464  return result;
465 }
466 
467 bool grid::can_wrap() const
468 {
469  for(const auto & child : children_)
470  {
471  if(child.can_wrap()) {
472  return true;
473  }
474  }
475 
476  // Inherited.
477  return widget::can_wrap();
478 }
479 
480 void grid::place(const point& origin, const point& size)
481 {
483 
484  /***** INIT *****/
485 
486  widget::place(origin, size);
487 
488  if(!rows_ || !cols_) {
489  return;
490  }
491 
492  // call the calculate so the size cache gets updated.
493  const point best_size = calculate_best_size();
494 
495  assert(row_height_.size() == rows_);
496  assert(col_width_.size() == cols_);
497  assert(row_grow_factor_.size() == rows_);
498  assert(col_grow_factor_.size() == cols_);
499 
500  DBG_GUI_L << LOG_HEADER << " best size " << best_size << " available size "
501  << size << ".\n";
502 
503  /***** BEST_SIZE *****/
504 
505  if(best_size == size) {
506  layout(origin);
507  return;
508  }
509 
510  if(best_size.x > size.x || best_size.y > size.y) {
511  // The assertion below fails quite often so try to give as much information as possible.
512  std::stringstream out;
513  out << " Failed to place a grid, we have " << size << " space but we need " << best_size << " space.";
514  out << " This happened at a grid with the id '" << id() << "'";
515  widget* pw = parent();
516  while(pw != nullptr) {
517  out << " in a '" << typeid(*pw).name() << "' with the id '" << pw->id() << "'";
518  pw = pw->parent();
519  }
520  ERR_GUI_L << LOG_HEADER << out.str() << ".\n";
521 
522  return;
523  }
524 
525  /***** GROW *****/
526 
527  // expand it.
528  if(size.x > best_size.x) {
529  const unsigned w = size.x - best_size.x;
530  unsigned w_size = std::accumulate(
531  col_grow_factor_.begin(), col_grow_factor_.end(), 0);
532 
533  DBG_GUI_L << LOG_HEADER << " extra width " << w
534  << " will be divided amount " << w_size << " units in "
535  << cols_ << " columns.\n";
536 
537  if(w_size == 0) {
538  // If all sizes are 0 reset them to 1
539  for(auto & val : col_grow_factor_)
540  {
541  val = 1;
542  }
543  w_size = cols_;
544  }
545  // We might have a bit 'extra' if the division doesn't fix exactly
546  // but we ignore that part for now.
547  const unsigned w_normal = w / w_size;
548  for(unsigned i = 0; i < cols_; ++i) {
549  col_width_[i] += w_normal * col_grow_factor_[i];
550  DBG_GUI_L << LOG_HEADER << " column " << i
551  << " with grow factor " << col_grow_factor_[i]
552  << " set width to " << col_width_[i] << ".\n";
553  }
554  }
555 
556  if(size.y > best_size.y) {
557  const unsigned h = size.y - best_size.y;
558  unsigned h_size = std::accumulate(
559  row_grow_factor_.begin(), row_grow_factor_.end(), 0);
560  DBG_GUI_L << LOG_HEADER << " extra height " << h
561  << " will be divided amount " << h_size << " units in "
562  << rows_ << " rows.\n";
563 
564  if(h_size == 0) {
565  // If all sizes are 0 reset them to 1
566  for(auto & val : row_grow_factor_)
567  {
568  val = 1;
569  }
570  h_size = rows_;
571  }
572  // We might have a bit 'extra' if the division doesn't fix exactly
573  // but we ignore that part for now.
574  const unsigned h_normal = h / h_size;
575  for(unsigned i = 0; i < rows_; ++i) {
576  row_height_[i] += h_normal * row_grow_factor_[i];
577  DBG_GUI_L << LOG_HEADER << " row " << i << " with grow factor "
578  << row_grow_factor_[i] << " set height to "
579  << row_height_[i] << ".\n";
580  }
581  }
582 
583  layout(origin);
584  return;
585 }
586 
587 void grid::set_origin(const point& origin)
588 {
589  const point movement {origin.x - get_x(), origin.y - get_y()};
590 
591  // Inherited.
592  widget::set_origin(origin);
593 
594  for(auto & child : children_)
595  {
596 
598  assert(widget);
599 
600  widget->set_origin(point(widget->get_x() + movement.x, widget->get_y() + movement.y));
601  }
602 }
603 
604 void grid::set_visible_rectangle(const SDL_Rect& rectangle)
605 {
606  // Inherited.
608 
609  for(auto & child : children_)
610  {
611 
613  assert(widget);
614 
615  widget->set_visible_rectangle(rectangle);
616  }
617 }
618 
620 {
621  for(auto & child : children_)
622  {
623  assert(child.get_widget());
625  }
626 }
627 
629  const std::vector<widget*>& call_stack)
630 {
631  assert(!call_stack.empty() && call_stack.back() == this);
632 
633  for(auto & child : children_)
634  {
635 
636  assert(child.get_widget());
637 
638  std::vector<widget*> child_call_stack = call_stack;
639  child.get_widget()->populate_dirty_list(caller, child_call_stack);
640  }
641 }
642 
643 widget* grid::find_at(const point& coordinate, const bool must_be_active)
644 {
645  return grid_implementation::find_at<widget>(
646  *this, coordinate, must_be_active);
647 }
648 
650  const bool must_be_active) const
651 {
652  return grid_implementation::find_at<const widget>(
653  *this, coordinate, must_be_active);
654 }
655 
656 widget* grid::find(const std::string& id, const bool must_be_active)
657 {
658  return grid_implementation::find<widget>(*this, id, must_be_active);
659 }
660 
661 const widget* grid::find(const std::string& id, const bool must_be_active)
662  const
663 {
664  return grid_implementation::find<const widget>(*this, id, must_be_active);
665 }
666 
667 bool grid::has_widget(const widget& widget) const
668 {
669  if(widget::has_widget(widget)) {
670  return true;
671  }
672 
673  for(const auto & child : children_)
674  {
675  if(child.get_widget()->has_widget(widget)) {
676  return true;
677  }
678  }
679  return false;
680 }
681 
683 {
685  return false;
686  }
687 
688  for(const auto & child : children_)
689  {
690  const widget* widget = child.get_widget();
691  assert(widget);
692 
693  if(widget->disable_click_dismiss()) {
694  return true;
695  }
696  }
697  return false;
698 }
699 
701 {
702  return new gui2::iteration::grid(*this);
703 }
704 
705 void grid::set_rows(const unsigned rows)
706 {
707  if(rows == rows_) {
708  return;
709  }
710 
711  set_rows_cols(rows, cols_);
712 }
713 
714 void grid::set_cols(const unsigned cols)
715 {
716  if(cols == cols_) {
717  return;
718  }
719 
720  set_rows_cols(rows_, cols);
721 }
722 
723 void grid::set_rows_cols(const unsigned rows, const unsigned cols)
724 {
725  if(rows == rows_ && cols == cols_) {
726  return;
727  }
728 
729  if(!children_.empty()) {
730  WRN_GUI_G << LOG_HEADER << " resizing a non-empty grid "
731  << " may give unexpected problems.\n";
732  }
733 
734  rows_ = rows;
735  cols_ = cols;
736  row_grow_factor_.resize(rows);
737  col_grow_factor_.resize(cols);
738  children_.resize(rows_ * cols_);
739 }
740 
742 {
744 
745  if(!widget_) {
746  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << false
747  << " returning " << border_space() << ".\n";
748  return border_space();
749  }
750 
751  if(widget_->get_visible() == widget::visibility::invisible) {
752  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << true
753  << " widget visible " << false << " returning 0,0"
754  << ".\n";
755  return point();
756  }
757 
758  const point best_size = widget_->get_best_size() + border_space();
759 
760  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << true
761  << " widget visible " << true << " returning " << best_size
762  << ".\n";
763  return best_size;
764 }
765 
767 {
768  assert(get_widget());
770  return;
771  }
772 
773  if(border_size_) {
774  if(flags_ & BORDER_TOP) {
775  origin.y += border_size_;
776  size.y -= border_size_;
777  }
778  if(flags_ & BORDER_BOTTOM) {
779  size.y -= border_size_;
780  }
781 
782  if(flags_ & BORDER_LEFT) {
783  origin.x += border_size_;
784  size.x -= border_size_;
785  }
786  if(flags_ & BORDER_RIGHT) {
787  size.x -= border_size_;
788  }
789  }
790 
791  // If size smaller or equal to best size set that size.
792  // No need to check > min size since this is what we got.
793  const point best_size = get_widget()->get_best_size();
794  if(size <= best_size) {
796  << " in best size range setting widget to " << origin << " x "
797  << size << ".\n";
798 
799  get_widget()->place(origin, size);
800  return;
801  }
802 
803  const styled_widget* control = dynamic_cast<const styled_widget*>(get_widget());
804  const point maximum_size = control ? control->get_config_maximum_size()
805  : point();
806 
807  if((flags_ & (HORIZONTAL_MASK | VERTICAL_MASK))
809 
810  if(maximum_size == point() || size <= maximum_size) {
811 
813  << " in maximum size range setting widget to " << origin
814  << " x " << size << ".\n";
815 
816  get_widget()->place(origin, size);
817  return;
818  }
819  }
820 
821  point widget_size = point(std::min(size.x, best_size.x), std::min(size.y, best_size.y));
822  point widget_orig = origin;
823 
824  const unsigned v_flag = flags_ & VERTICAL_MASK;
825 
826  if(v_flag == VERTICAL_GROW_SEND_TO_CLIENT) {
827  if(maximum_size.y) {
828  widget_size.y = std::min(size.y, maximum_size.y);
829  } else {
830  widget_size.y = size.y;
831  }
832  DBG_GUI_L << LOG_CHILD_HEADER << " vertical growing from "
833  << best_size.y << " to " << widget_size.y << ".\n";
834 
835  } else if(v_flag == VERTICAL_ALIGN_TOP) {
836  // Do nothing.
837 
838  DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the top.\n";
839 
840  } else if(v_flag == VERTICAL_ALIGN_CENTER) {
841 
842  widget_orig.y += (size.y - widget_size.y) / 2;
843  DBG_GUI_L << LOG_CHILD_HEADER << " vertically centered.\n";
844 
845  } else if(v_flag == VERTICAL_ALIGN_BOTTOM) {
846 
847  widget_orig.y += (size.y - widget_size.y);
848  DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the bottom.\n";
849 
850  } else {
851  ERR_GUI_L << LOG_CHILD_HEADER << " Invalid vertical alignment '"
852  << v_flag << "' specified.\n";
853  assert(false);
854  }
855 
856  const unsigned h_flag = flags_ & HORIZONTAL_MASK;
857 
858  if(h_flag == HORIZONTAL_GROW_SEND_TO_CLIENT) {
859  if(maximum_size.x) {
860  widget_size.x = std::min(size.x, maximum_size.x);
861  } else {
862  widget_size.x = size.x;
863  }
864  DBG_GUI_L << LOG_CHILD_HEADER << " horizontal growing from "
865  << best_size.x << " to " << widget_size.x << ".\n";
866 
867  } else if(h_flag == HORIZONTAL_ALIGN_LEFT) {
868  // Do nothing.
869  DBG_GUI_L << LOG_CHILD_HEADER << " horizontally aligned at the left.\n";
870 
871  } else if(h_flag == HORIZONTAL_ALIGN_CENTER) {
872 
873  widget_orig.x += (size.x - widget_size.x) / 2;
874  DBG_GUI_L << LOG_CHILD_HEADER << " horizontally centered.\n";
875 
876  } else if(h_flag == HORIZONTAL_ALIGN_RIGHT) {
877 
878  widget_orig.x += (size.x - widget_size.x);
880  << " horizontally aligned at the right.\n";
881 
882  } else {
883  ERR_GUI_L << LOG_CHILD_HEADER << " No horizontal alignment '" << h_flag
884  << "' specified.\n";
885  assert(false);
886  }
887 
888  DBG_GUI_L << LOG_CHILD_HEADER << " resize widget to " << widget_orig
889  << " x " << widget_size << ".\n";
890 
891  get_widget()->place(widget_orig, widget_size);
892 }
893 
894 void grid::child::layout_initialize(const bool full_initialization)
895 {
896  assert(widget_);
897 
898  if(widget_->get_visible() != widget::visibility::invisible) {
899  widget_->layout_initialize(full_initialization);
900  }
901 }
902 
903 const std::string& grid::child::id() const
904 {
905  assert(widget_);
906  return widget_->id();
907 }
908 
910 {
911  point result(0, 0);
912 
913  if(border_size_) {
914 
915  if(flags_ & BORDER_TOP)
916  result.y += border_size_;
917  if(flags_ & BORDER_BOTTOM)
918  result.y += border_size_;
919 
920  if(flags_ & BORDER_LEFT)
921  result.x += border_size_;
922  if(flags_ & BORDER_RIGHT)
923  result.x += border_size_;
924  }
925 
926  return result;
927 }
928 
930 {
931  if(!w) {
932  return nullptr;
933  }
934 
935  for(auto& child : children_) {
936  if(w == child.get_widget()) {
937  return &child;
938  }
939  }
940 
941  return nullptr;
942 }
943 
944 void grid::set_child_alignment(widget* widget, unsigned set_flag, unsigned mode_mask)
945 {
946  grid::child* cell = get_child(widget);
947  if(!cell) {
948  return;
949  }
950 
951  unsigned flags = cell->get_flags();
952 
953  if((flags & mode_mask) == HORIZONTAL_GROW_SEND_TO_CLIENT) {
954  ERR_GUI_G << "Cannot set horizontal alignment (grid cell specifies dynamic growth)" << std::endl;
955  return;
956  }
957 
958  if((flags & mode_mask) == VERTICAL_GROW_SEND_TO_CLIENT) {
959  ERR_GUI_G << "Cannot set vertical alignment (grid cell specifies dynamic growth)" << std::endl;
960  return;
961  }
962 
963  flags &= ~mode_mask;
964  flags |= set_flag;
965 
966  cell->set_flags(flags);
967 
969  fire(event::REQUEST_PLACEMENT, *this, message);
970 }
971 
972 void grid::layout(const point& origin)
973 {
974  point orig = origin;
975  for(unsigned row = 0; row < rows_; ++row) {
976  for(unsigned col = 0; col < cols_; ++col) {
977 
978  const point size(col_width_[col], row_height_[row]);
979  DBG_GUI_L << LOG_HEADER << " set widget at " << row << ',' << col
980  << " at origin " << orig << " with size " << size
981  << ".\n";
982 
983  if(get_child(row, col).get_widget()) {
984  get_child(row, col).place(orig, size);
985  }
986 
987  orig.x += col_width_[col];
988  }
989  orig.y += row_height_[row];
990  orig.x = origin.x;
991  }
992 }
993 
994 void grid::impl_draw_children(surface& frame_buffer, int x_offset, int y_offset)
995 {
996  /*
997  * The call to SDL_PumpEvents seems a bit like black magic.
998  * With the call the resizing doesn't seem to lose resize events.
999  * But when added the queue still only contains one resize event and the
1000  * internal SDL queue doesn't seem to overflow (rarely more than 50 pending
1001  * events).
1002  * Without the call when resizing larger a black area of remains, this is
1003  * the area not used for resizing the screen, this call `fixes' that.
1004  */
1005 
1007  set_is_dirty(false);
1008 
1009  for(auto & child : children_)
1010  {
1011 
1013  assert(widget);
1014 
1015  if(widget->get_visible() != widget::visibility::visible) {
1016  continue;
1017  }
1018 
1020  continue;
1021  }
1022 
1023  widget->draw_background(frame_buffer, x_offset, y_offset);
1024  widget->draw_children(frame_buffer, x_offset, y_offset);
1025  widget->draw_foreground(frame_buffer, x_offset, y_offset);
1026  widget->set_is_dirty(false);
1027  }
1028 }
1029 
1031  grid& grid, const unsigned row, const unsigned maximum_height)
1032 {
1033  // The minimum height required.
1034  unsigned required_height = 0;
1035 
1036  for(std::size_t x = 0; x < grid.cols_; ++x) {
1037  grid::child& cell = grid.get_child(row, x);
1038  cell_request_reduce_height(cell, maximum_height);
1039 
1040  const point size(cell.get_best_size());
1041 
1042  if(required_height == 0 || static_cast<std::size_t>(size.y)
1043  > required_height) {
1044 
1045  required_height = size.y;
1046  }
1047  }
1048 
1049  DBG_GUI_L << LOG_IMPL_HEADER << " maximum row height " << maximum_height
1050  << " returning " << required_height << ".\n";
1051 
1052  return required_height;
1053 }
1054 
1056  grid& grid, const unsigned column, const unsigned maximum_width)
1057 {
1058  // The minimum width required.
1059  unsigned required_width = 0;
1060 
1061  for(std::size_t y = 0; y < grid.rows_; ++y) {
1062  grid::child& cell = grid.get_child(y, column);
1063  cell_request_reduce_width(cell, maximum_width);
1064 
1065  const point size(cell.get_best_size());
1066 
1067  if(required_width == 0 || static_cast<std::size_t>(size.x)
1068  > required_width) {
1069 
1070  required_width = size.x;
1071  }
1072  }
1073 
1074  DBG_GUI_L << LOG_IMPL_HEADER << " maximum column width " << maximum_width
1075  << " returning " << required_width << ".\n";
1076 
1077  return required_width;
1078 }
1079 
1080 void
1082  const unsigned maximum_height)
1083 {
1084  assert(child.widget_);
1085 
1086  if(child.widget_->get_visible() == widget::visibility::invisible) {
1087  return;
1088  }
1089 
1090  child.widget_->request_reduce_height(maximum_height
1091  - child.border_space().y);
1092 }
1093 
1094 void
1096  const unsigned maximum_width)
1097 {
1098  assert(child.widget_);
1099 
1100  if(child.widget_->get_visible() == widget::visibility::invisible) {
1101  return;
1102  }
1103 
1104  child.widget_->request_reduce_width(maximum_width - child.border_space().x);
1105 }
1106 
1108 {
1109  grid.set_rows_cols(1, 1);
1110  grid.set_child(widget,
1111  0,
1112  0,
1115  0);
1116 }
1117 
1118 } // namespace gui2
Define the common log macros for the gui toolkit.
unsigned get_flags() const
Definition: grid.hpp:372
Defines the exception classes for the layout algorithm.
static const unsigned BORDER_TOP
Definition: grid.hpp:62
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
Definition: grid.cpp:604
int get_x() const
Definition: widget.cpp:315
void set_parent(widget *parent)
Definition: widget.cpp:155
#define DBG_GUI_L
Definition: log.hpp:55
virtual void layout_initialize(const bool full_initialization)
How the layout engine works.
Definition: widget.cpp:167
void set_layout_size(const point &size)
Definition: widget.cpp:335
std::vector< unsigned > row_grow_factor_
The grow factor for all rows.
Definition: grid.hpp:505
virtual void demand_reduce_width(const unsigned maximum_width) override
See widget::demand_reduce_width.
Definition: grid.cpp:272
virtual bool can_wrap() const override
See widget::can_wrap.
Definition: grid.cpp:467
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
Definition: grid.cpp:682
visibility get_visible() const
Definition: widget.cpp:503
Main class to show messages to the user.
Definition: message.hpp:35
std::unique_ptr< widget > widget_
Pointer to the widget.
Definition: grid.hpp:432
unsigned cols_
The number of grid columns.
Definition: grid.hpp:494
Child item of the grid.
Definition: grid.hpp:329
const std::string & id() const
Definition: widget.cpp:110
virtual bool has_widget(const widget &widget) const override
See widget::has_widget.
Definition: grid.cpp:667
Exception thrown when the height resizing has failed.
This file contains the window object, this object is a top level container which has the event manage...
redraw_action get_drawing_action() const
Definition: widget.cpp:508
Base class for all widgets.
Definition: widget.hpp:49
virtual void set_visible_rectangle(const SDL_Rect &rectangle)
Sets the visible rectangle for a widget.
Definition: widget.cpp:453
#define ERR_GUI_G
Definition: log.hpp:44
#define LOG_IMPL_HEADER
Definition: grid.cpp:31
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:54
#define h
static const unsigned HORIZONTAL_MASK
Definition: grid.hpp:60
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: grid.cpp:232
point recalculate_best_size()
Recalculates the best size.
Definition: grid.cpp:416
int x
x coordinate.
Definition: point.hpp:45
Generic file dialog.
Definition: field-fwd.hpp:23
unsigned rows_
The number of grid rows.
Definition: grid.hpp:491
The message callbacks hold a reference to a message.
Definition: message.hpp:45
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
Definition: grid.cpp:186
#define ERR_GUI_L
Definition: log.hpp:58
static void cell_request_reduce_height(grid::child &child, const unsigned maximum_height)
Helper function to do the resizing of a widget.
Definition: grid.cpp:1081
Base container class.
Definition: grid.hpp:31
const grid::child & get_child(const unsigned row, const unsigned col) const
Gets the grid child in the specified cell.
Definition: grid.hpp:526
void populate_dirty_list(window &caller, std::vector< widget *> &call_stack)
Adds a widget to the dirty list if it is dirty.
Definition: widget.cpp:417
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
Request to place the widget.
Definition: handler.hpp:95
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:193
virtual void place(const point &origin, const point &size)
Places the widget.
Definition: widget.cpp:238
#define LOG_CHILD_SCOPE_HEADER
Definition: grid.cpp:33
void set_visible(const visibility visible)
Definition: widget.cpp:476
void set_flags(const unsigned flags)
Definition: grid.hpp:377
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: grid.cpp:423
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:466
virtual void set_origin(const point &origin)
Sets the origin of the widget.
Definition: widget.cpp:221
widget * parent()
Definition: widget.cpp:160
The walker abstract base class.
Definition: walker.hpp:27
The widget is not visible.
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:59
static const unsigned BORDER_BOTTOM
Definition: grid.hpp:63
const std::string & id() const
Returns the id of the widget/.
Definition: grid.cpp:903
static thread_local std::deque< std::string > call_stack
For printing error messages when WFL parsing or evaluation fails, this contains the names of the WFL ...
Definition: function.cpp:47
std::vector< unsigned > col_grow_factor_
The grow factor for all columns.
Definition: grid.hpp:508
virtual ~grid()
Definition: grid.cpp:55
virtual bool has_widget(const widget &widget) const
Does the widget contain the widget.
Definition: widget.cpp:595
This file contains the definitions for the gui2::event::message class.
std::vector< unsigned > row_height_
The row heights in the grid.
Definition: grid.hpp:499
std::unique_ptr< widget > free_widget()
Releases widget from ownership by this child and returns it in the form of a new unique_ptr.
Definition: grid.hpp:411
void draw_background(surface &frame_buffer, int x_offset, int y_offset)
Draws the background of a widget.
Definition: widget.cpp:370
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: grid.cpp:656
Exception thrown when the width resizing has failed.
#define log_scope2(domain, description)
Definition: log.hpp:219
void layout(const point &origin)
Layouts the children in the grid.
Definition: grid.cpp:972
int get_y() const
Definition: widget.cpp:320
void request_placement(dispatcher &dispatcher, const event::ui_event event, bool &handled, bool &halt)
Attempts to lay out the grid without laying out the entire window.
Definition: grid.cpp:375
void place(point origin, point size)
Places the widget in the cell.
Definition: grid.cpp:766
std::vector< child > children_
The child items.
Definition: grid.hpp:516
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: grid.cpp:480
void set_child(widget *widget, const unsigned row, const unsigned col, const unsigned flags, const unsigned border_size)
Sets a child in the grid.
Definition: grid.cpp:70
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:50
std::size_t i
Definition: function.cpp:967
#define WRN_GUI_G
Definition: log.hpp:43
virtual iteration::walker_base * create_walker() override
See widget::create_walker.
Definition: grid.cpp:700
static const unsigned BORDER_RIGHT
Definition: grid.hpp:65
unsigned add_row(const unsigned count=1)
Adds a row to end of the grid.
Definition: grid.cpp:59
The user set the widget invisible, that means:
#define LOG_SCOPE_HEADER
Definition: grid.cpp:29
window * get_window()
Get the parent window.
Definition: widget.cpp:117
void reduce_width(const unsigned maximum_width)
Tries to reduce the width of a container.
Definition: grid.cpp:199
void set_widget(widget *widget)
Definition: grid.hpp:402
double g
Definition: astarsearch.cpp:65
void layout_initialize(const bool full_initialization)
Forwards grid::layout_initialize to the cell.
Definition: grid.cpp:894
static unsigned row_request_reduce_height(grid &grid, const unsigned row, const unsigned maximum_height)
Helper function to do the resizing of a row.
Definition: grid.cpp:1030
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:51
bool can_wrap() const
Returns the can_wrap for the cell.
Definition: grid.hpp:364
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:58
std::vector< unsigned > col_width_
The column widths in the grid.
Definition: grid.hpp:502
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
Definition: grid.cpp:628
Holds a 2D point.
Definition: point.hpp:24
static const unsigned VERTICAL_MASK
Definition: grid.hpp:53
void set_active(const bool active)
Activates all children.
Definition: grid.cpp:163
static unsigned column_request_reduce_width(grid &grid, const unsigned column, const unsigned maximum_width)
Helper function to do the resizing of a column.
Definition: grid.cpp:1055
virtual void layout_children()
Allows a widget to update its children.
Definition: widget.cpp:295
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:56
int w
Base class for all visible items.
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:49
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_single_child(grid &grid, widget *widget)
Sets the single child in a grid.
Definition: grid.cpp:1107
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:723
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: grid.cpp:643
bool grid()
Definition: general.cpp:525
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:52
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:305
void set_cols(const unsigned cols)
Definition: grid.cpp:714
The user sets the widget visible, that means:
void remove_child(const unsigned row, const unsigned col)
Removes and frees a widget in a cell.
Definition: grid.cpp:141
std::unique_ptr< widget > swap_child(const std::string &id, widget *w, const bool recurse, widget *new_parent=nullptr)
Exchanges a child in the grid.
Definition: grid.cpp:99
void set_child_alignment(widget *widget, unsigned set_flag, unsigned mode_mask)
Modifies the widget alignment data of a child cell containing a specific widget.
Definition: grid.cpp:944
virtual void set_active(const bool active)=0
Sets the styled_widget&#39;s state.
virtual void demand_reduce_height(const unsigned maximum_height) override
See widget::demand_reduce_height.
Definition: grid.cpp:370
virtual void layout_children() override
See widget::layout_children.
Definition: grid.cpp:619
Helper for header for the grid.
#define LOG_CHILD_HEADER
Definition: grid.cpp:35
void draw_foreground(surface &frame_buffer, int x_offset, int y_offset)
Draws the foreground of the widget.
Definition: widget.cpp:402
void set_border_size(const unsigned border_size)
Definition: grid.hpp:387
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:69
virtual bool disable_click_dismiss() const =0
Does the widget disable easy close?
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:57
void set_rows(const unsigned rows)
Definition: grid.cpp:705
void draw_children(surface &frame_buffer, int x_offset, int y_offset)
Draws the children of a widget.
Definition: widget.cpp:387
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
virtual bool can_wrap() const
Can the widget wrap.
Definition: widget.cpp:216
point border_space() const
Returns the space needed for the border.
Definition: grid.cpp:909
const widget * get_widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition: grid.hpp:181
const widget * get_widget() const
Definition: grid.hpp:392
static const unsigned BORDER_LEFT
Definition: grid.hpp:64
grid(const unsigned rows=0, const unsigned cols=0)
Definition: grid.cpp:40
int y
y coordinate.
Definition: point.hpp:48
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
point get_best_size() const
Returns the best size for the cell.
Definition: grid.cpp:741
void reduce_height(const unsigned maximum_height)
Tries to reduce the height of a container.
Definition: grid.cpp:277
#define LOG_HEADER
Definition: grid.cpp:30
static void cell_request_reduce_width(grid::child &child, const unsigned maximum_width)
Helper function to do the resizing of a widget.
Definition: grid.cpp:1095
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:300
ui_event
The event send to the dispatcher.
Definition: handler.hpp:48
point get_config_maximum_size() const
Gets the best size as defined in the config.
virtual void impl_draw_children(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_children.
Definition: grid.cpp:994