The Battle for Wesnoth  1.19.7+dev
grid.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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 
367 
368  DBG_GUI_L << LOG_HEADER << " Requested maximum " << maximum_height
369  << " resulting height " << size.y << ".";
370 
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.
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_view 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_view id, const bool must_be_active) const
651 {
652  return grid_implementation::find<const widget>(*this, id, must_be_active);
653 }
654 
655 bool grid::has_widget(const widget& widget) const
656 {
658  return true;
659  }
660 
661  for(const auto & child : children_)
662  {
664  return true;
665  }
666  }
667  return false;
668 }
669 
671 {
673  return false;
674  }
675 
676  for(const auto & child : children_)
677  {
678  const widget* widget = child.get_widget();
679  assert(widget);
680 
682  return true;
683  }
684  }
685  return false;
686 }
687 
689 {
690  return std::make_unique<gui2::iteration::grid>(*this);
691 }
692 
693 void grid::set_rows(const unsigned rows)
694 {
695  if(rows == rows_) {
696  return;
697  }
698 
699  set_rows_cols(rows, cols_);
700 }
701 
702 void grid::set_cols(const unsigned cols)
703 {
704  if(cols == cols_) {
705  return;
706  }
707 
708  set_rows_cols(rows_, cols);
709 }
710 
711 void grid::set_rows_cols(const unsigned rows, const unsigned cols)
712 {
713  if(rows == rows_ && cols == cols_) {
714  return;
715  }
716 
717  if(!children_.empty()) {
718  WRN_GUI_G << LOG_HEADER << " resizing a non-empty grid "
719  << " may give unexpected problems.";
720  }
721 
722  rows_ = rows;
723  cols_ = cols;
724  row_grow_factor_.resize(rows);
725  col_grow_factor_.resize(cols);
726  children_.resize(static_cast<size_t>(rows_) * cols_);
727 }
728 
730 {
732 
733  if(!widget_) {
734  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << false
735  << " returning " << border_space() << ".";
736  return border_space();
737  }
738 
739  if(widget_->get_visible() == widget::visibility::invisible) {
740  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << true
741  << " widget visible " << false << " returning 0,0.";
742  return point();
743  }
744 
745  const point best_size = widget_->get_best_size() + border_space();
746 
747  DBG_GUI_L << LOG_CHILD_HEADER << " has widget " << true
748  << " widget visible " << true << " returning " << best_size
749  << ".";
750  return best_size;
751 }
752 
754 {
755  assert(get_widget());
757  return;
758  }
759 
760  if(border_size_) {
761  if(flags_ & BORDER_TOP) {
762  origin.y += border_size_;
763  size.y -= border_size_;
764  }
765  if(flags_ & BORDER_BOTTOM) {
766  size.y -= border_size_;
767  }
768 
769  if(flags_ & BORDER_LEFT) {
770  origin.x += border_size_;
771  size.x -= border_size_;
772  }
773  if(flags_ & BORDER_RIGHT) {
774  size.x -= border_size_;
775  }
776  }
777 
778  // If size smaller or equal to best size set that size.
779  // No need to check > min size since this is what we got.
780  const point best_size = get_widget()->get_best_size();
781  if(size <= best_size) {
783  << " in best size range setting widget to " << origin << " x "
784  << size << ".";
785 
786  get_widget()->place(origin, size);
787  return;
788  }
789 
790  const styled_widget* control = dynamic_cast<const styled_widget*>(get_widget());
791  const point maximum_size = control ? control->get_config_maximum_size()
792  : point();
793 
794  if((flags_ & (HORIZONTAL_MASK | VERTICAL_MASK))
796 
797  if(maximum_size == point() || size <= maximum_size) {
798 
800  << " in maximum size range setting widget to " << origin
801  << " x " << size << ".";
802 
803  get_widget()->place(origin, size);
804  return;
805  }
806  }
807 
808  point widget_size = point(std::min(size.x, best_size.x), std::min(size.y, best_size.y));
809  point widget_orig = origin;
810 
811  const unsigned v_flag = flags_ & VERTICAL_MASK;
812 
813  if(v_flag == VERTICAL_GROW_SEND_TO_CLIENT) {
814  if(maximum_size.y) {
815  widget_size.y = std::min(size.y, maximum_size.y);
816  } else {
817  widget_size.y = size.y;
818  }
819  DBG_GUI_L << LOG_CHILD_HEADER << " vertical growing from "
820  << best_size.y << " to " << widget_size.y << ".";
821 
822  } else if(v_flag == VERTICAL_ALIGN_TOP) {
823  // Do nothing.
824 
825  DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the top.";
826 
827  } else if(v_flag == VERTICAL_ALIGN_CENTER) {
828 
829  widget_orig.y += (size.y - widget_size.y) / 2;
830  DBG_GUI_L << LOG_CHILD_HEADER << " vertically centered.";
831 
832  } else if(v_flag == VERTICAL_ALIGN_BOTTOM) {
833 
834  widget_orig.y += (size.y - widget_size.y);
835  DBG_GUI_L << LOG_CHILD_HEADER << " vertically aligned at the bottom.";
836 
837  } else {
838  ERR_GUI_L << LOG_CHILD_HEADER << " Invalid vertical alignment '"
839  << v_flag << "' specified.";
840  assert(false);
841  }
842 
843  const unsigned h_flag = flags_ & HORIZONTAL_MASK;
844 
845  if(h_flag == HORIZONTAL_GROW_SEND_TO_CLIENT) {
846  if(maximum_size.x) {
847  widget_size.x = std::min(size.x, maximum_size.x);
848  } else {
849  widget_size.x = size.x;
850  }
851  DBG_GUI_L << LOG_CHILD_HEADER << " horizontal growing from "
852  << best_size.x << " to " << widget_size.x << ".";
853 
854  } else if(h_flag == HORIZONTAL_ALIGN_LEFT) {
855  // Do nothing.
856  DBG_GUI_L << LOG_CHILD_HEADER << " horizontally aligned at the left.";
857 
858  } else if(h_flag == HORIZONTAL_ALIGN_CENTER) {
859 
860  widget_orig.x += (size.x - widget_size.x) / 2;
861  DBG_GUI_L << LOG_CHILD_HEADER << " horizontally centered.";
862 
863  } else if(h_flag == HORIZONTAL_ALIGN_RIGHT) {
864 
865  widget_orig.x += (size.x - widget_size.x);
867  << " horizontally aligned at the right.";
868 
869  } else {
870  ERR_GUI_L << LOG_CHILD_HEADER << " No horizontal alignment '" << h_flag
871  << "' specified.";
872  assert(false);
873  }
874 
875  DBG_GUI_L << LOG_CHILD_HEADER << " resize widget to " << widget_orig
876  << " x " << widget_size << ".";
877 
878  get_widget()->place(widget_orig, widget_size);
879 }
880 
881 void grid::child::layout_initialize(const bool full_initialization)
882 {
883  assert(widget_);
884 
885  if(widget_->get_visible() != widget::visibility::invisible) {
886  widget_->layout_initialize(full_initialization);
887  }
888 }
889 
890 const std::string& grid::child::id() const
891 {
892  assert(widget_);
893  return widget_->id();
894 }
895 
897 {
898  point result(0, 0);
899 
900  if(border_size_) {
901 
902  if(flags_ & BORDER_TOP)
903  result.y += border_size_;
904  if(flags_ & BORDER_BOTTOM)
905  result.y += border_size_;
906 
907  if(flags_ & BORDER_LEFT)
908  result.x += border_size_;
909  if(flags_ & BORDER_RIGHT)
910  result.x += border_size_;
911  }
912 
913  return result;
914 }
915 
917 {
918  if(!w) {
919  return nullptr;
920  }
921 
922  for(auto& child : children_) {
923  if(w == child.get_widget()) {
924  return &child;
925  }
926  }
927 
928  return nullptr;
929 }
930 
931 void grid::set_child_alignment(widget* widget, unsigned set_flag, unsigned mode_mask)
932 {
933  grid::child* cell = get_child(widget);
934  if(!cell) {
935  return;
936  }
937 
938  unsigned flags = cell->get_flags();
939 
940  if((flags & mode_mask) == HORIZONTAL_GROW_SEND_TO_CLIENT) {
941  ERR_GUI_G << "Cannot set horizontal alignment (grid cell specifies dynamic growth)";
942  return;
943  }
944 
945  if((flags & mode_mask) == VERTICAL_GROW_SEND_TO_CLIENT) {
946  ERR_GUI_G << "Cannot set vertical alignment (grid cell specifies dynamic growth)";
947  return;
948  }
949 
950  flags &= ~mode_mask;
951  flags |= set_flag;
952 
953  cell->set_flags(flags);
954 
957 }
958 
959 void grid::layout(const point& origin)
960 {
961  point orig = origin;
962  for(unsigned row = 0; row < rows_; ++row) {
963  for(unsigned col = 0; col < cols_; ++col) {
964 
965  const point size(col_width_[col], row_height_[row]);
966  DBG_GUI_L << LOG_HEADER << " set widget at " << row << ',' << col
967  << " at origin " << orig << " with size " << size
968  << ".";
969 
970  if(get_child(row, col).get_widget()) {
971  get_child(row, col).place(orig, size);
972  }
973 
974  orig.x += col_width_[col];
975  }
976  orig.y += row_height_[row];
977  orig.x = origin.x;
978  }
979 }
980 
982 {
983  // TODO: draw_manager - what is this comment talking about here? something that was removed?
984  /*
985  * The call to SDL_PumpEvents seems a bit like black magic.
986  * With the call the resizing doesn't seem to lose resize events.
987  * But when added the queue still only contains one resize event and the
988  * internal SDL queue doesn't seem to overflow (rarely more than 50 pending
989  * events).
990  * Without the call when resizing larger a black area of remains, this is
991  * the area not used for resizing the screen, this call `fixes' that.
992  */
993 
995 
996  // 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
997  for(auto & child : children_)
998  {
1000  assert(widget);
1001 
1003  continue;
1004  }
1005 
1007  continue;
1008  }
1009 
1010  // We may need to defer drawing to next frame for blur processing.
1011  if(!widget->draw_background()) {
1013  continue;
1014  }
1015 
1016  widget->draw_children();
1017 
1018  if(!widget->draw_foreground()) {
1020  continue;
1021  }
1022  }
1023 }
1024 
1026  grid& grid, const unsigned row, const unsigned maximum_height)
1027 {
1028  // The minimum height required.
1029  unsigned required_height = 0;
1030 
1031  for(std::size_t x = 0; x < grid.cols_; ++x) {
1032  grid::child& cell = grid.get_child(row, x);
1033  cell_request_reduce_height(cell, maximum_height);
1034 
1035  const point size(cell.get_best_size());
1036 
1037  if(required_height == 0 || static_cast<std::size_t>(size.y)
1038  > required_height) {
1039 
1040  required_height = size.y;
1041  }
1042  }
1043 
1044  DBG_GUI_L << LOG_IMPL_HEADER << " maximum row height " << maximum_height
1045  << " returning " << required_height << ".";
1046 
1047  return required_height;
1048 }
1049 
1051  grid& grid, const unsigned column, const unsigned maximum_width)
1052 {
1053  // The minimum width required.
1054  unsigned required_width = 0;
1055 
1056  for(std::size_t y = 0; y < grid.rows_; ++y) {
1057  grid::child& cell = grid.get_child(y, column);
1058  cell_request_reduce_width(cell, maximum_width);
1059 
1060  const point size(cell.get_best_size());
1061 
1062  if(required_width == 0 || static_cast<std::size_t>(size.x)
1063  > required_width) {
1064 
1065  required_width = size.x;
1066  }
1067  }
1068 
1069  DBG_GUI_L << LOG_IMPL_HEADER << " maximum column width " << maximum_width
1070  << " returning " << required_width << ".";
1071 
1072  return required_width;
1073 }
1074 
1075 void
1077  const unsigned maximum_height)
1078 {
1079  assert(child.widget_);
1080 
1081  if(child.widget_->get_visible() == widget::visibility::invisible) {
1082  return;
1083  }
1084 
1085  child.widget_->request_reduce_height(maximum_height
1086  - child.border_space().y);
1087 }
1088 
1089 void
1091  const unsigned maximum_width)
1092 {
1093  assert(child.widget_);
1094 
1095  if(child.widget_->get_visible() == widget::visibility::invisible) {
1096  return;
1097  }
1098 
1099  child.widget_->request_reduce_width(maximum_width - child.border_space().x);
1100 }
1101 
1102 void set_single_child(grid& grid, std::unique_ptr<widget> widget)
1103 {
1104  grid.set_rows_cols(1, 1);
1105  grid.set_child(std::move(widget),
1106  0,
1107  0,
1110  0);
1111 }
1112 
1113 } // namespace gui2
double g
Definition: astarsearch.cpp:63
Main class to show messages to the user.
Definition: message.hpp:36
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:74
Child item of the grid.
Definition: grid.hpp:324
void place(point origin, point size)
Places the widget in the cell.
Definition: grid.cpp:753
void layout_initialize(const bool full_initialization)
Forwards grid::layout_initialize to the cell.
Definition: grid.cpp:881
void set_border_size(const unsigned border_size)
Definition: grid.hpp:381
void set_widget(std::unique_ptr< widget > widget)
Acquires an owning reference to the given widget.
Definition: grid.hpp:397
void set_flags(const unsigned flags)
Definition: grid.hpp:371
const std::string & id() const
Returns the id of the widget/.
Definition: grid.cpp:890
point get_best_size() const
Returns the best size for the cell.
Definition: grid.cpp:729
const widget * get_widget() const
Definition: grid.hpp:386
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:406
point border_space() const
Returns the space needed for the border.
Definition: grid.cpp:896
unsigned get_flags() const
Definition: grid.hpp:366
bool can_wrap() const
Returns the can_wrap for the cell.
Definition: grid.hpp:358
std::unique_ptr< widget > widget_
Pointer to the widget.
Definition: grid.hpp:427
Base container class.
Definition: grid.hpp:32
void set_active(const bool active)
Activates all children.
Definition: grid.cpp:167
virtual void impl_draw_children() override
See widget::impl_draw_children.
Definition: grid.cpp:981
virtual bool has_widget(const widget &widget) const override
See widget::has_widget.
Definition: grid.cpp:655
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
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: grid.cpp:427
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
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
Definition: grid.cpp:670
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:56
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:59
const grid::child & get_child(const unsigned row, const unsigned col) const
Gets the grid child in the specified cell.
Definition: grid.hpp:524
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: grid.cpp:484
void reduce_height(const unsigned maximum_height)
Tries to reduce the height of a container.
Definition: grid.cpp:281
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
Definition: grid.cpp:608
virtual iteration::walker_ptr create_walker() override
See widget::create_walker.
Definition: grid.cpp:688
unsigned add_row(const unsigned count=1)
Adds a row to end of the grid.
Definition: grid.cpp:60
const widget * get_widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition: grid.hpp:181
void reduce_width(const unsigned maximum_width)
Tries to reduce the width of a container.
Definition: grid.cpp:203
static const unsigned HORIZONTAL_MASK
Definition: grid.hpp:60
point recalculate_best_size()
Recalculates the best size.
Definition: grid.cpp:420
std::vector< child > children_
The child items.
Definition: grid.hpp:514
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:52
static const unsigned BORDER_TOP
Definition: grid.hpp:62
std::vector< unsigned > col_width_
The column widths in the grid.
Definition: grid.hpp:500
void set_cols(const unsigned cols)
Definition: grid.cpp:702
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:51
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: grid.cpp:632
void set_rows(const unsigned rows)
Definition: grid.cpp:693
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:49
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
Definition: grid.cpp:645
static const unsigned BORDER_BOTTOM
Definition: grid.hpp:63
static const unsigned BORDER_RIGHT
Definition: grid.hpp:65
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:58
void remove_child(const unsigned row, const unsigned col)
Removes and frees a widget in a cell.
Definition: grid.cpp:145
grid(const unsigned rows=0, const unsigned cols=0)
Definition: grid.cpp:40
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:931
void layout(const point &origin)
Layouts the children in the grid.
Definition: grid.cpp:959
unsigned cols_
The number of grid columns.
Definition: grid.hpp:492
static const unsigned VERTICAL_MASK
Definition: grid.hpp:53
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:711
std::vector< unsigned > col_grow_factor_
The grow factor for all columns.
Definition: grid.hpp:506
std::vector< unsigned > row_height_
The row heights in the grid.
Definition: grid.hpp:497
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:50
static const unsigned BORDER_LEFT
Definition: grid.hpp:64
virtual ~grid()
Definition: grid.cpp:56
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
virtual void set_origin(const point &origin) override
See widget::set_origin.
Definition: grid.cpp:591
virtual void demand_reduce_height(const unsigned maximum_height) override
See widget::demand_reduce_height.
Definition: grid.cpp:374
virtual bool can_wrap() const override
See widget::can_wrap.
Definition: grid.cpp:471
virtual void layout_children() override
See widget::layout_children.
Definition: grid.cpp:623
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
Definition: grid.cpp:190
virtual void request_reduce_width(const unsigned maximum_width) override
See widget::request_reduce_width.
Definition: grid.cpp:236
std::vector< unsigned > row_grow_factor_
The grow factor for all rows.
Definition: grid.hpp:503
virtual void request_reduce_height(const unsigned maximum_height) override
See widget::request_reduce_height.
Definition: grid.cpp:314
unsigned rows_
The number of grid rows.
Definition: grid.hpp:489
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:57
virtual void demand_reduce_width(const unsigned maximum_width) override
See widget::demand_reduce_width.
Definition: grid.cpp:276
virtual void set_active(const bool active)=0
Sets the styled_widget's state.
point get_config_maximum_size() const
Gets the best size as defined in the config.
Base class for all widgets.
Definition: widget.hpp:55
void set_layout_size(const point &size)
Definition: widget.cpp:346
bool draw_foreground()
Draws the foreground of the widget.
Definition: widget.cpp:423
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:203
virtual void place(const point &origin, const point &size)
Places the widget.
Definition: widget.cpp:248
virtual bool disable_click_dismiss() const =0
Does the widget disable easy close?
virtual void layout_children()
Allows a widget to update its children.
Definition: widget.cpp:306
virtual void layout_initialize(const bool full_initialization)
How the layout engine works.
Definition: widget.cpp:177
visibility get_visible() const
Definition: widget.cpp:506
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:311
int get_x() const
Definition: widget.cpp:326
int get_y() const
Definition: widget.cpp:331
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:316
void draw_children()
Draws the children of a widget.
Definition: widget.cpp:402
bool draw_background()
Draws the background of a widget.
Definition: widget.cpp:381
virtual bool has_widget(const widget &widget) const
Does the widget contain the widget.
Definition: widget.cpp:570
virtual void set_origin(const point &origin)
Sets the origin of the widget.
Definition: widget.cpp:230
const std::string & id() const
Definition: widget.cpp:110
window * get_window()
Get the parent window.
Definition: widget.cpp:117
@ visible
The user sets the widget visible, that means:
@ invisible
The user set the widget invisible, that means:
redraw_action get_drawing_action() const
Definition: widget.cpp:511
rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:321
virtual bool can_wrap() const
Can the widget wrap.
Definition: widget.cpp:225
@ none
The widget is not visible.
virtual void set_visible_rectangle(const SDL_Rect &rectangle)
Sets the visible rectangle for a widget.
Definition: widget.cpp:451
widget * parent()
Definition: widget.cpp:170
void defer_region(const rect &region)
Defer rendering of a particular region to next frame.
Definition: window.cpp:691
This file contains the definitions for the gui2::event::message class.
std::size_t i
Definition: function.cpp:1029
int w
#define LOG_IMPL_HEADER
Definition: grid.cpp:31
#define LOG_HEADER
Definition: grid.cpp:30
#define LOG_SCOPE_HEADER
Definition: grid.cpp:29
#define LOG_CHILD_SCOPE_HEADER
Definition: grid.cpp:33
#define LOG_CHILD_HEADER
Definition: grid.cpp:35
Helper for header for the grid.
Define the common log macros for the gui toolkit.
#define DBG_GUI_L
Definition: log.hpp:55
#define ERR_GUI_G
Definition: log.hpp:44
#define ERR_GUI_L
Definition: log.hpp:58
#define WRN_GUI_G
Definition: log.hpp:43
This file contains the window object, this object is a top level container which has the event manage...
Defines the exception classes for the layout algorithm.
#define log_scope2(domain, description)
Definition: log.hpp:277
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
@ REQUEST_PLACEMENT
Definition: handler.hpp:163
std::unique_ptr< class walker_base > walker_ptr
Definition: widget.hpp:44
Generic file dialog.
void set_single_child(grid &grid, std::unique_ptr< widget > widget)
Sets the single child in a grid.
Definition: grid.cpp:1102
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:54
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
The message callbacks hold a reference to a message.
Definition: message.hpp:46
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:1050
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:1090
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:1076
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:1025
Exception thrown when the height resizing has failed.
Exception thrown when the width resizing has failed.
Holds a 2D point.
Definition: point.hpp:25
#define h