The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
grid.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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"
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
37namespace gui2
38{
39
40grid::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
60unsigned 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
71void 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
101std::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
145void 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
153void 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
167void 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
190void 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
203void grid::reduce_width(const unsigned maximum_width)
204{
205 /***** ***** ***** ***** INIT ***** ***** ***** *****/
207 DBG_GUI_L << LOG_HEADER << " maximum width " << maximum_width << ".";
208
209 point size = get_best_size();
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
236void grid::request_reduce_width(const unsigned maximum_width)
237{
238 point size = get_best_size();
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
276void grid::demand_reduce_width(const unsigned /*maximum_width*/)
277{
278 /** @todo Implement. */
279}
280
281void grid::reduce_height(const unsigned maximum_height)
282{
283 /***** ***** ***** ***** INIT ***** ***** ***** *****/
285 DBG_GUI_L << LOG_HEADER << " maximum height " << maximum_height << ".";
286
287 point size = get_best_size();
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
314void grid::request_reduce_height(const unsigned maximum_height)
315{
316 point size = get_best_size();
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. */
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
374void grid::demand_reduce_height(const unsigned /*maximum_height*/)
375{
376 /** @todo Implement. */
377}
378
379void 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
471bool 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
484void 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
591void 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
608void grid::set_visible_rectangle(const 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
632widget* 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
638const widget* grid::find_at(const point& coordinate,
639 const bool must_be_active) const
640{
641 return grid_implementation::find_at<const widget>(
642 *this, coordinate, must_be_active);
643}
644
645widget* 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
650const 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
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
693void grid::set_rows(const unsigned rows)
694{
695 if(rows == rows_) {
696 return;
697 }
698
699 set_rows_cols(rows, cols_);
700}
701
702void grid::set_cols(const unsigned cols)
703{
704 if(cols == cols_) {
705 return;
706 }
707
708 set_rows_cols(rows_, cols);
709}
710
711void 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<std::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
881void 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
890const 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
931void grid::set_child_alignment(widget* widget, unsigned set_flag, unsigned mode_mask)
932{
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
959void 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
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
1075void
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
1089void
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
1102void 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
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.
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
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
const widget * get_widget() const
Definition grid.hpp:386
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
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 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
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 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
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
const widget * get_widget(const unsigned row, const unsigned col) const
Returns the widget in the selected cell.
Definition grid.hpp:181
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 set_visible_rectangle(const rect &rectangle) override
See widget::set_visible_rectangle.
Definition grid.cpp:608
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
virtual void set_visible_rectangle(const rect &rectangle)
Sets the visible rectangle for a widget.
Definition widget.cpp:451
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.
widget * parent()
Definition widget.cpp:170
void defer_region(const rect &region)
Defer rendering of a particular region to next frame.
Definition window.cpp:684
This file contains the definitions for the gui2::event::message class.
#define LOG_HEADER
#define LOG_IMPL_HEADER
Definition grid.cpp:31
#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
#define LOG_SCOPE_HEADER
Definition button.cpp:34
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:276
ui_event
The event sent to the dispatcher.
Definition handler.hpp:115
@ REQUEST_PLACEMENT
Definition handler.hpp:182
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
int w
Definition pathfind.cpp:188
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
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
#define i
#define g
#define h