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