The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
window.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2007 - 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/**
17 * @file
18 * Implementation of window.hpp.
19 */
20
21#define GETTEXT_DOMAIN "wesnoth-lib"
22
24
25#include "config.hpp"
26#include "draw.hpp"
27#include "events.hpp"
28#include "formula/callable.hpp"
30#include "gettext.hpp"
31#include "log.hpp"
32#include "wml_exception.hpp"
33
38#include "gui/core/log.hpp"
40#include "sdl/point.hpp"
47#include "gui/widgets/grid.hpp"
49#include "gui/widgets/panel.hpp"
53#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
54#include "gui/widgets/debug.hpp"
55#endif
56#include "sdl/rect.hpp"
57#include "sdl/texture.hpp"
58#include "formula/variant.hpp"
59#include "video.hpp" // only for toggle_fullscreen
60#include "sdl/userevent.hpp"
61#include "sdl/input.hpp" // get_mouse_button_mask
62
63#include <SDL3/SDL_timer.h>
64
65#include <algorithm>
66#include <functional>
67
68
69static lg::log_domain log_gui("gui/layout");
70#define ERR_GUI LOG_STREAM(err, log_gui)
71
72#define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
73#define LOG_HEADER LOG_SCOPE_HEADER + ':'
74
75#define LOG_IMPL_SCOPE_HEADER \
76 window.get_control_type() + " [" + window.id() + "] " + __func__
77#define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
78
79static lg::log_domain log_display("display");
80#define DBG_DP LOG_STREAM(debug, log_display)
81#define LOG_DP LOG_STREAM(info, log_display)
82#define WRN_DP LOG_STREAM(warn, log_display)
83
84namespace gui2
85{
86
87// ------------ WIDGET -----------{
88
89namespace implementation
90{
91/** @todo See whether this hack can be removed. */
92// Needed to fix a compiler error in REGISTER_WIDGET.
94{
95public:
99
101
102 virtual std::unique_ptr<widget> build() const override
103 {
104 return nullptr;
105 }
106};
107
108} // namespace implementation
109REGISTER_WIDGET(window)
110
111namespace
112{
113#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
114const unsigned SHOW = debug_layout_graph::SHOW;
115const unsigned LAYOUT = debug_layout_graph::LAYOUT;
116#else
117// values are irrelavant when DEBUG_WINDOW_LAYOUT_GRAPHS is not defined.
118const unsigned SHOW = 0;
119const unsigned LAYOUT = 0;
120#endif
121
122/**
123 * SDL_AddTimer() callback for delay_event.
124 *
125 * @param event The event to push in the event queue.
126 *
127 * @return The new timer interval (always 0).
128 */
129static uint32_t delay_event_callback(void* event, SDL_TimerID, uint32_t)
130{
131 SDL_PushEvent(static_cast<SDL_Event*>(event));
132 delete static_cast<SDL_Event*>(event);
133 return 0;
134}
135
136/**
137 * Allows an event to be delayed a certain amount of time.
138 *
139 * @note the delay is the minimum time, after the time has passed the event
140 * will be pushed in the SDL event queue, so it might delay more.
141 *
142 * @param event The event to delay.
143 * @param delay The number of ms to delay the event.
144 */
145static void delay_event(const SDL_Event& event, const uint32_t delay)
146{
147 SDL_AddTimer(delay, delay_event_callback, new SDL_Event(event));
148}
149
150/**
151 * Adds a SHOW_HELPTIP event to the SDL event queue.
152 *
153 * The event is used to show the helptip for the currently focused widget.
154 */
155static bool helptip()
156{
157 DBG_GUI_E << "Pushing SHOW_HELPTIP_EVENT event in queue.";
158
159 SDL_Event event;
161
162 event.type = SHOW_HELPTIP_EVENT;
163 event.user = data;
164
165 SDL_PushEvent(&event);
166 return true;
167}
168
169/**
170 * Small helper class to get an unique id for every window instance.
171 *
172 * This is used to send event to the proper window, this allows windows to post
173 * messages to themselves and let them delay for a certain amount of time.
174 */
175class manager
176{
177 manager();
178
179public:
180 static manager& instance();
181
182 void add(window& window);
183
184 void remove(window& window);
185
186 unsigned get_id(window& window);
187
188 window* get_window(const unsigned id);
189
190private:
191 // The number of active window should be rather small
192 // so keep it simple and don't add a reverse lookup map.
193 std::map<unsigned, window*> windows_;
194};
195
196manager::manager() : windows_()
197{
198}
199
200manager& manager::instance()
201{
202 static manager window_manager;
203 return window_manager;
204}
205
206void manager::add(window& win)
207{
208 static unsigned id;
209 ++id;
210 windows_[id] = &win;
211}
212
213void manager::remove(window& win)
214{
215 for(auto itor = windows_.begin(); itor != windows_.end(); ++itor) {
216 if(itor->second == &win) {
217 windows_.erase(itor);
218 return;
219 }
220 }
221
222 assert(false);
223}
224
225unsigned manager::get_id(window& win)
226{
227 for(const auto& [id, window_ptr] : windows_) {
228 if(window_ptr == &win) {
229 return id;
230 }
231 }
232
233 assert(false);
234 return 0;
235}
236
237window* manager::get_window(const unsigned id)
238{
239 if(auto itor = windows_.find(id); itor != windows_.end()) {
240 return itor->second;
241 } else {
242 return nullptr;
243 }
244}
245
246} // namespace
247
248window::window(const builder_window::window_resolution& definition)
249 : panel(implementation::builder_window(::config {"definition", definition.definition}), type())
250 , status_(status::NEW)
251 , show_mode_(show_mode::none)
252 , retval_(retval::NONE)
253 , need_layout_(true)
254 , variables_()
255 , invalidate_layout_blocked_(false)
256 , hidden_(true)
257 , automatic_placement_(definition.automatic_placement)
258 , horizontal_placement_(definition.horizontal_placement)
259 , vertical_placement_(definition.vertical_placement)
260 , maximum_width_(definition.maximum_width)
261 , maximum_height_(definition.maximum_height)
262 , x_(definition.x)
263 , y_(definition.y)
264 , w_(definition.width)
265 , h_(definition.height)
266 , reevaluate_best_size_(definition.reevaluate_best_size)
267 , functions_(definition.functions)
268 , tooltip_(definition.tooltip)
269 , helptip_(definition.helptip)
270 , click_dismiss_(definition.click_dismiss)
271 , enter_disabled_(false)
272 , escape_disabled_(false)
273 , linked_size_()
274 , mouse_button_state_(0) /**< Needs to be initialized in @ref show. */
275#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
276 , debug_layout_(new debug_layout_graph(this))
277#endif
278 , event_distributor_(new event::distributor(*this, event::dispatcher::front_child))
279 , exit_hook_([] { return true; })
280{
281 manager::instance().add(*this);
282
283 connect();
284
285 for(const auto& [id, fixed_width, fixed_height] : definition.linked_groups) {
286 if(!init_linked_size_group(id, fixed_width, fixed_height)) {
287 FAIL(VGETTEXT("Linked ‘$id’ group has multiple definitions.", {{"id", id}}));
288 }
289 }
290
291 const auto conf = cast_config_to<window_definition>();
292 assert(conf);
293
294 if(conf->grid) {
295 init_grid(*conf->grid);
296 finalize(*definition.grid);
297 } else {
298 init_grid(*definition.grid);
299 }
300
301 add_to_keyboard_chain(this);
302
303 connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
304 &window::signal_handler_sdl_video_resize, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5));
305
306 connect_signal<event::SDL_ACTIVATE>(std::bind(
307 &event::distributor::initialize_state, event_distributor_.get()));
308
309 connect_signal<event::SDL_LEFT_BUTTON_UP>(
311 this,
312 std::placeholders::_2,
313 std::placeholders::_3,
314 std::placeholders::_4,
315 SDL_BUTTON_LMASK),
317 connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
319 this,
320 std::placeholders::_2,
321 std::placeholders::_3,
322 std::placeholders::_4,
323 SDL_BUTTON_MMASK),
325 connect_signal<event::SDL_RIGHT_BUTTON_UP>(
327 this,
328 std::placeholders::_2,
329 std::placeholders::_3,
330 std::placeholders::_4,
331 SDL_BUTTON_RMASK),
333
334 // FIXME investigate why this handler is being called twice and if this is actually needed
335 connect_signal<event::SDL_KEY_DOWN>(
336 std::bind(
337 &window::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6, false),
339 connect_signal<event::SDL_KEY_DOWN>(std::bind(
340 &window::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6, true));
341
342 connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
344 this,
345 std::placeholders::_2,
346 std::placeholders::_3,
347 std::placeholders::_5),
349
350 connect_signal<event::MESSAGE_SHOW_HELPTIP>(
352 this,
353 std::placeholders::_2,
354 std::placeholders::_3,
355 std::placeholders::_5),
357
358 connect_signal<event::REQUEST_PLACEMENT>(
359 std::bind(
360 &window::signal_handler_request_placement, this, std::placeholders::_2, std::placeholders::_3),
362
363 connect_signal<event::CLOSE_WINDOW>(std::bind(&window::signal_handler_close_window, this));
364
365 register_hotkey(hotkey::GLOBAL__HELPTIP,
366 [](auto&&...) { return helptip(); });
367
368 /** @todo: should eventually become part of global hotkey handling. */
369 register_hotkey(hotkey::HOTKEY_FULLSCREEN,
370 [](auto&&...) { video::toggle_fullscreen(); return true; });
371}
372
374{
375 /*
376 * We need to delete our children here instead of waiting for the grid to
377 * automatically do it. The reason is when the grid deletes its children
378 * they will try to unregister them self from the linked widget list. At
379 * this point the member of window are destroyed and we enter UB. (For
380 * some reason the bug didn't trigger on g++ but it does on MSVC.
381 */
382 for(unsigned row = 0; row < get_grid().get_rows(); ++row) {
383 for(unsigned col = 0; col < get_grid().get_cols(); ++col) {
384 get_grid().remove_child(row, col);
385 }
386 }
387
388 /*
389 * The tip needs to be closed if the window closes and the window is
390 * not a tip. If we don't do that the tip will unrender in the next
391 * window and cause drawing glitches.
392 * Another issue is that on smallgui and an MP game the tooltip not
393 * unrendered properly can capture the mouse and make playing impossible.
394 */
397 }
398
399 manager::instance().remove(*this);
400
401 // If we are currently shown, then queue an undraw.
402 if(!hidden_) {
403 queue_redraw();
404 }
405}
406
407window* window::window_instance(const unsigned handle)
408{
409 return manager::instance().get_window(handle);
410}
411
412retval window::get_retval_by_id(const std::string& id)
413{
414 // Note it might change to a map later depending on the number
415 // of items.
416 if(id == "ok") {
417 return retval::OK;
418 } else if(id == "cancel" || id == "quit") {
419 return retval::CANCEL;
420 } else {
421 return retval::NONE;
422 }
423}
424
425void window::show_tooltip(/*const unsigned auto_close_timeout*/)
426{
427 // Unhide in any case.
428 hidden_ = false;
429
430 // Connect to the event handler, if not yet connected.
431 if(!is_connected()) {
432 LOG_DP << "connecting " << id() << " on show_tooltip";
433 connect();
434 }
435
436 log_scope2(log_gui_draw, "Window: show as tooltip.");
437
438 generate_dot_file("show", SHOW);
439
440 assert(status_ == status::NEW);
441
444
446
447 /*
448 * Before show has been called, some functions might have done some testing
449 * on the window and called layout, which can give glitches. So
450 * reinvalidate the window to avoid those glitches.
451 */
453 queue_redraw();
454 DBG_DP << "show tooltip queued to " << get_rectangle();
455}
456
457void window::show_non_modal(/*const unsigned auto_close_timeout*/)
458{
459 // Unhide in any case.
460 hidden_ = false;
461
462 // Connect to the event handler, if not yet connected.
463 if(!is_connected()) {
464 LOG_DP << "connecting " << id() << " on show_non_modal";
465 connect();
466 }
467
468 log_scope2(log_gui_draw, "Window: show non modal.");
469
470 generate_dot_file("show", SHOW);
471
472 assert(status_ == status::NEW);
473
475
477
478 /*
479 * Before show has been called, some functions might have done some testing
480 * on the window and called layout, which can give glitches. So
481 * reinvalidate the window to avoid those glitches.
482 */
484 queue_redraw();
485
486 DBG_DP << "show non-modal queued to " << get_rectangle();
487
489}
490
491int window::show(const unsigned auto_close_timeout)
492{
493 // Unhide in any case.
494 hidden_ = false;
495
496 // Connect to the event handler, if not yet connected.
497 if(!is_connected()) {
498 LOG_DP << "connecting " << id() << " on show";
499 connect();
500 }
501
502 /*
503 * Removes the old tip if one shown. The show_tip doesn't remove
504 * the tip, since it's the tip.
505 */
507
509
511
512 generate_dot_file("show", SHOW);
513
514 //assert(status_ == status::NEW);
515
516 /*
517 * Before show has been called, some functions might have done some testing
518 * on the window and called layout, which can give glitches. So
519 * reinvalidate the window to avoid those glitches.
520 */
522 queue_redraw();
523
524 // Make sure we display at least once in all cases.
525 events::draw();
526
527 if(auto_close_timeout) {
528 SDL_Event event;
529 sdl::UserEvent data(CLOSE_WINDOW_EVENT, manager::instance().get_id(*this));
530
531 event.type = CLOSE_WINDOW_EVENT;
532 event.user = data;
533
534 delay_event(event, auto_close_timeout);
535 }
536
537 try
538 {
539 // According to the comment in the next loop, we need to pump() once
540 // before we know which mouse buttons are down. Assume they're all
541 // down, otherwise there's a race condition when the MOUSE_UP gets
542 // processed in the first pump(), which immediately closes the window.
543 bool mouse_button_state_initialized = false;
544 mouse_button_state_ = std::numeric_limits<uint32_t>::max();
545
546 // Start our loop, drawing will happen here as well.
548 // Process and handle all pending events.
549 events::pump();
550
551 if(!mouse_button_state_initialized) {
552 /*
553 * The state must be initialize when showing the dialog.
554 * However when initialized before this point there were random
555 * errors. This only happened when the 'click' was done fast; a
556 * slower click worked properly.
557 *
558 * So it seems the events need to be processed before SDL can
559 * return the proper button state. When initializing here all
560 * works fine.
561 */
563 mouse_button_state_initialized = true;
564 }
565
566 // See if we should close.
569 }
570
571 // Update the display. This will rate limit to vsync.
572 events::draw();
573 }
574 }
575 catch(...)
576 {
577 // TODO: is this even necessary? What are we catching?
578 DBG_DP << "Caught general exception in show(): " << utils::get_unknown_exception_type();
579 hide();
580 throw;
581 }
582
583 if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
584 tb->interrupt_composition();
585 }
586
587 // The window may be kept around to be re-shown later. Hide it for now.
588 hide();
589
590 return retval_;
591}
592
594{
595 if(hidden_) {
596 return;
597 }
598
599 // Draw background.
600 if(!this->draw_background()) {
601 // We may need to blur the background behind the window,
602 // but at this point it hasn't been rendered yet.
603 // We thus defer rendering to next frame so we can snapshot what
604 // is underneath the window without drawing over it.
606 return;
607 }
608
609 // Draw children.
610 this->draw_children();
611
612 // Draw foreground.
613 if(!this->draw_foreground()) {
615 }
616
617 return;
618}
619
621{
622 // Queue a redraw of the region if we were shown.
623 if(!hidden_) {
624 queue_redraw();
625 }
626
627 // Disconnect from the event handler so we stop receiving events.
628 if(is_connected()) {
629 LOG_DP << "disconnecting " << id() << " on hide";
630 disconnect();
631 }
632
633 hidden_ = true;
634}
635
637{
638 point draw = get_size();
640
641 // Check that the render buffer size is correct.
643 point buf_draw = render_buffer_.draw_size();
644 bool raw_size_changed = buf_raw.x != render.x || buf_raw.y != render.y;
645 bool draw_size_changed = buf_draw.x != draw.x || buf_draw.y != draw.y;
646 if (!raw_size_changed && !draw_size_changed) {
647 // buffers are fine
648 return;
649 }
650
651 if(raw_size_changed) {
652 LOG_DP << "regenerating window render buffer as " << render;
653 render_buffer_ = texture(render.x, render.y, SDL_TEXTUREACCESS_TARGET);
654 }
655 if(raw_size_changed || draw_size_changed) {
656 LOG_DP << "updating window render buffer draw size to " << draw;
658 }
659
660 // Clear the entire texture.
661 {
663 draw::clear();
664 }
665
666 // Rerender everything.
668}
669
674
675void window::queue_rerender(const rect& screen_region)
676{
677 // More than one region updating per-frame should be rare.
678 // Just rerender the minimal area that covers everything.
679 rect local_region = screen_region.intersect(get_rectangle());
680 local_region.shift(-get_origin());
682}
683
684void window::defer_region(const rect& screen_region)
685{
686 LOG_DP << "deferring region " << screen_region;
687 deferred_regions_.push_back(screen_region);
688}
689
691{
692 // Ensure our render texture is correctly sized.
694
695 // Mark regions that were previously deferred for rerender and repaint.
696 for(auto& region : deferred_regions_) {
697 queue_redraw(region);
698 }
699 deferred_regions_.clear();
700
701 // Render the portion of the window awaiting rerender (if any).
703 return;
704 }
705
706 DBG_DP << "window::render() local " << awaiting_rerender_;
707 auto target_setter = draw::set_render_target(render_buffer_);
708 auto clip_setter = draw::override_clip(awaiting_rerender_);
709
710 // Clear the to-be-rendered area unconditionally
711 draw::clear();
712
713 draw();
715}
716
717bool window::expose(const rect& region)
718{
719 DBG_DP << "window::expose " << region;
720
721 // Calculate the destination region we need to draw.
722 rect dst = get_rectangle().intersect(region);
724 if (dst.empty()) {
725 return false;
726 }
727
728 // Blit from the pre-rendered buffer.
729 rect src = dst;
730 src.shift(-get_origin());
734
735 return true;
736}
737
739{
740 if(hidden_) {
741 return {0,0,0,0};
742 }
743 return get_rectangle();
744}
745
752
754{
755 assert(window_.invalidate_layout_blocked_);
756 window_.invalidate_layout_blocked_ = false;
757}
758
760{
762 need_layout_ = true;
763 }
764}
765widget* window::find_at(const point& coordinate, const bool must_be_active)
766{
767 return panel::find_at(coordinate, must_be_active);
768}
769
770const widget* window::find_at(const point& coordinate,
771 const bool must_be_active) const
772{
773 return panel::find_at(coordinate, must_be_active);
774}
775
776widget* window::find(const std::string_view id, const bool must_be_active)
777{
778 return container_base::find(id, must_be_active);
779}
780
781const widget* window::find(const std::string_view id, const bool must_be_active)
782 const
783{
784 return container_base::find(id, must_be_active);
785}
786
787bool window::init_linked_size_group(const std::string& id, const bool fixed_width, const bool fixed_height)
788{
789 assert(fixed_width || fixed_height);
790 auto [iter, success] = linked_size_.try_emplace(id, fixed_width, fixed_height);
791 return success;
792}
793
794bool window::has_linked_size_group(const std::string& id)
795{
796 return linked_size_.find(id) != linked_size_.end();
797}
798
799void window::add_linked_widget(const std::string& id, widget* wgt)
800{
801 assert(wgt);
802 if(!has_linked_size_group(id)) {
803 ERR_GUI << "Unknown linked group '" << id << "'; skipping";
804 return;
805 }
806
807 std::vector<widget*>& widgets = linked_size_[id].widgets;
808 if(!utils::contains(widgets, wgt)) {
809 widgets.push_back(wgt);
810 }
811}
812
813void window::remove_linked_widget(const std::string& id, const widget* wgt)
814{
815 assert(wgt);
816 if(!has_linked_size_group(id)) {
817 return;
818 }
819
820 std::vector<widget*>& widgets = linked_size_[id].widgets;
821 auto itor = std::find(widgets.begin(), widgets.end(), wgt);
822
823 if(itor != widgets.end()) {
824 widgets.erase(itor);
825 assert(!utils::contains(widgets, wgt));
826 }
827}
828
830{
831 if(!need_layout_) {
832 return;
833 }
834 DBG_DP << "window::layout";
835
836 /***** Initialize. *****/
837
838 const auto conf = cast_config_to<window_definition>();
839 assert(conf);
840
842
843 point size = get_best_size();
844 const point mouse = get_mouse_position();
845
846 variables_.add("mouse_x", wfl::variant(mouse.x));
847 variables_.add("mouse_y", wfl::variant(mouse.y));
848 variables_.add("window_width", wfl::variant(0));
849 variables_.add("window_height", wfl::variant(0));
850 variables_.add("best_window_width", wfl::variant(size.x));
851 variables_.add("best_window_height", wfl::variant(size.y));
852 variables_.add("size_request_mode", wfl::variant("maximum"));
853
855
856 unsigned int maximum_width = maximum_width_(variables_, &functions_);
857 unsigned int maximum_height = maximum_height_(variables_, &functions_);
858
860 if(maximum_width == 0 || maximum_width > settings::screen_width) {
861 maximum_width = settings::screen_width;
862 }
863
864 if(maximum_height == 0 || maximum_height > settings::screen_height) {
865 maximum_height = settings::screen_height;
866 }
867 } else {
868 maximum_width = w_(variables_, &functions_);
869 maximum_height = h_(variables_, &functions_);
870 }
871
872 /***** Handle click dismiss status. *****/
873 button* click_dismiss_button = find_widget<button>("click_dismiss", false, false);
874 if(click_dismiss_button) {
875 click_dismiss_button->set_visible(widget::visibility::invisible);
876 }
877 if(click_dismiss_) {
878 button* btn = find_widget<button>("ok", false, false);
879 if(btn) {
881 click_dismiss_button = btn;
882 }
883 VALIDATE(click_dismiss_button,
884 _("Click dismiss needs a ‘click_dismiss’ or ‘ok’ button."));
885 }
886
887 /***** Layout. *****/
888 layout_initialize(true);
889 generate_dot_file("layout_initialize", LAYOUT);
890
892
893 try
894 {
895 window_implementation::layout(*this, maximum_width, maximum_height);
896 }
898 {
899 /** @todo implement the scrollbars on the window. */
900
901 std::stringstream sstr;
902 sstr << __FILE__ << ":" << __LINE__ << " in function ‘" << __func__
903 << "’ found the following problem: Failed to size window with id ‘" << id()
904 << "’; wanted size " << get_best_size() << ", available size ("
905 << maximum_width << ',' << maximum_height << "), screen size ("
907
908 throw wml_exception(
909 _("Failed to show a dialog, which doesn’t fit on the screen."),
910 sstr.str(),
912 }
913
914 /****** Validate click dismiss status. *****/
916 assert(click_dismiss_button);
917 click_dismiss_button->set_visible(widget::visibility::visible);
918
919 connect_signal_mouse_left_click(
920 *click_dismiss_button,
921 std::bind(&window::set_retval, this, retval::OK, true));
922
923 layout_initialize(true);
924 generate_dot_file("layout_initialize", LAYOUT);
925
927
928 try
929 {
931 *this, maximum_width, maximum_height);
932 }
934 {
935 /** @todo implement the scrollbars on the window. */
936
937 std::stringstream sstr;
938 sstr << __FILE__ << ":" << __LINE__ << " in function ‘" << __func__
939 << "’ found the following problem: Failed to size window with id ‘" << id()
940 << "’; wanted size " << get_best_size() << ", available size ("
941 << maximum_width << ',' << maximum_height << "), screen size ("
943
944 throw wml_exception(
945 _("Failed to show a dialog, which doesn’t fit on the screen."),
946 sstr.str(),
948 }
949 }
950
951 /***** Get the best location for the window *****/
952 size = get_best_size();
953 /* Although 0-size windows might not seem valid/useful, there are
954 a handful of windows that request 0 size just to get a position
955 chosen via the code below, so at least for now allow them:
956 */
957 assert(size.x >= 0 && static_cast<unsigned>(size.x) <= maximum_width
958 && size.y >= 0 && static_cast<unsigned>(size.y) <= maximum_height);
959
960 point origin(0, 0);
961
963
964 switch(horizontal_placement_) {
966 // Do nothing
967 break;
969 origin.x = (settings::screen_width - size.x) / 2;
970 break;
972 origin.x = settings::screen_width - size.x;
973 break;
974 default:
975 assert(false);
976 }
977 switch(vertical_placement_) {
979 // Do nothing
980 break;
982 origin.y = (settings::screen_height - size.y) / 2;
983 break;
985 origin.y = settings::screen_height - size.y;
986 break;
987 default:
988 assert(false);
989 }
990 } else {
991
992 variables_.add("window_width", wfl::variant(size.x));
993 variables_.add("window_height", wfl::variant(size.y));
994
996 layout_initialize(true);
997
1001
1002 size = get_best_size();
1003 variables_.add("window_width", wfl::variant(size.x));
1004 variables_.add("window_height", wfl::variant(size.y));
1005 }
1006
1007 variables_.add("size_request_mode", wfl::variant("size"));
1008
1009 size.x = w_(variables_, &functions_);
1010 size.y = h_(variables_, &functions_);
1011
1012 variables_.add("window_width", wfl::variant(size.x));
1013 variables_.add("window_height", wfl::variant(size.y));
1014
1015 origin.x = x_(variables_, &functions_);
1016 origin.y = y_(variables_, &functions_);
1017 }
1018
1019 /***** Set the window size *****/
1020 place(origin, size);
1021
1022 generate_dot_file("layout_finished", LAYOUT);
1023 need_layout_ = false;
1024
1026}
1027
1029{
1030 // evaluate the group sizes
1031 for(auto & linked_size : linked_size_)
1032 {
1033
1034 point max_size(0, 0);
1035
1036 // Determine the maximum size.
1037 for(auto widget : linked_size.second.widgets)
1038 {
1039
1040 const point size = widget->get_best_size();
1041
1042 if(size.x > max_size.x) {
1043 max_size.x = size.x;
1044 }
1045 if(size.y > max_size.y) {
1046 max_size.y = size.y;
1047 }
1048 }
1049 if(linked_size.second.width != -1) {
1050 linked_size.second.width = max_size.x;
1051 }
1052 if(linked_size.second.height != -1) {
1053 linked_size.second.height = max_size.y;
1054 }
1055
1056 // Set the maximum size.
1057 for(auto widget : linked_size.second.widgets)
1058 {
1059
1060 point size = widget->get_best_size();
1061
1062 if(linked_size.second.width != -1) {
1063 size.x = max_size.x;
1064 }
1065 if(linked_size.second.height != -1) {
1066 size.y = max_size.y;
1067 }
1068
1069 widget->set_layout_size(size);
1070 }
1071 }
1072}
1073
1074bool window::click_dismiss(const int mouse_button_mask)
1075{
1076 if(does_click_dismiss()) {
1077 if((mouse_button_state_ & mouse_button_mask) == 0) {
1079 } else {
1080 mouse_button_state_ &= ~mouse_button_mask;
1081 }
1082 return true;
1083 }
1084 return false;
1085}
1086
1087void window::finalize(const builder_grid& content_grid)
1088{
1089 auto widget = content_grid.build();
1090 assert(widget);
1091
1092 static const std::string id = "_window_content_grid";
1093
1094 // Make sure the new child has same id.
1095 widget->set_id(id);
1096
1097 auto* parent_grid = get_grid().find_widget<grid>(id, true, false);
1098 assert(parent_grid);
1099
1100 if(grid* grandparent_grid = dynamic_cast<grid*>(parent_grid->parent())) {
1101 grandparent_grid->swap_child(id, std::move(widget), false);
1102 } else if(container_base* c = dynamic_cast<container_base*>(parent_grid->parent())) {
1103 c->get_grid().swap_child(id, std::move(widget), true);
1104 } else {
1105 assert(false);
1106 }
1107}
1108
1109#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1110
1111void window::generate_dot_file(const std::string& generator,
1112 const unsigned domain)
1113{
1114 debug_layout_->generate_dot_file(generator, domain);
1115}
1116#endif
1117
1119 const unsigned maximum_width,
1120 const unsigned maximum_height)
1121{
1123
1124 /*
1125 * For now we return the status, need to test later whether this can
1126 * entirely be converted to an exception based system as in 'promised' on
1127 * the algorithm page.
1128 */
1129
1130 try
1131 {
1132 point size = window.get_best_size();
1133
1134 DBG_GUI_L << LOG_IMPL_HEADER << " best size : " << size
1135 << " maximum size : " << maximum_width << ','
1136 << maximum_height << ".";
1137 if(size.x <= static_cast<int>(maximum_width)
1138 && size.y <= static_cast<int>(maximum_height)) {
1139
1140 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Fits, nothing to do.";
1141 return;
1142 }
1143
1144 if(size.x > static_cast<int>(maximum_width)) {
1145 window.reduce_width(maximum_width);
1146
1147 size = window.get_best_size();
1148 if(size.x > static_cast<int>(maximum_width)) {
1149 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize width failed."
1150 << " Wanted width " << maximum_width
1151 << " resulting width " << size.x << ".";
1153 }
1155 << " Status: Resize width succeeded.";
1156 }
1157
1158 if(size.y > static_cast<int>(maximum_height)) {
1159 window.reduce_height(maximum_height);
1160
1161 size = window.get_best_size();
1162 if(size.y > static_cast<int>(maximum_height)) {
1163 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize height failed."
1164 << " Wanted height " << maximum_height
1165 << " resulting height " << size.y << ".";
1167 }
1169 << " Status: Resize height succeeded.";
1170 }
1171
1172 assert(size.x <= static_cast<int>(maximum_width)
1173 && size.y <= static_cast<int>(maximum_height));
1174
1175
1176 DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resizing succeeded.";
1177 return;
1178 }
1180 {
1182 << " Status: Width has been modified, rerun.";
1183
1186 layout(window, maximum_width, maximum_height);
1187 return;
1188 }
1189}
1190
1191void window::mouse_capture(const bool capture)
1192{
1193 assert(event_distributor_);
1194 event_distributor_->capture_mouse(capture);
1195}
1196
1198{
1199 assert(event_distributor_);
1200#ifndef __ANDROID__
1201 event_distributor_->keyboard_capture(widget);
1202#endif
1203}
1204
1206{
1207 assert(event_distributor_);
1208 event_distributor_->keyboard_capture(widget);
1209}
1210
1212{
1213 assert(event_distributor_);
1214 event_distributor_->keyboard_add_to_chain(widget);
1215}
1216
1218{
1219 assert(event_distributor_);
1220 event_distributor_->keyboard_remove_from_chain(widget);
1221}
1222
1224{
1226 return;
1227 }
1228 assert(event_distributor_);
1229 if(tab_order.empty() && !event_distributor_->keyboard_focus()) {
1231 }
1232 if(at < 0 || at >= static_cast<int>(tab_order.size())) {
1233 tab_order.push_back(widget);
1234 } else {
1235 tab_order.insert(tab_order.begin() + at, widget);
1236 }
1237}
1238
1240 bool& handled,
1241 const point& new_size)
1242{
1243 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1244
1247 settings::screen_width = new_size.x;
1248 settings::screen_height = new_size.y;
1250
1251 handled = true;
1252}
1253
1255 bool& handled,
1256 bool& halt,
1257 const int mouse_button_mask)
1258{
1259 DBG_GUI_E << LOG_HEADER << ' ' << event << " mouse_button_mask "
1260 << static_cast<unsigned>(mouse_button_mask) << ".";
1261
1262 handled = halt = click_dismiss(mouse_button_mask);
1263}
1264
1265static bool is_active(const widget* wgt)
1266{
1267 if(const styled_widget* control = dynamic_cast<const styled_widget*>(wgt)) {
1268 return control->get_active() && control->get_visible() == widget::visibility::visible;
1269 }
1270 return false;
1271}
1272
1274 bool& handled,
1275 const SDL_Keycode key,
1276 const SDL_Keymod mod,
1277 bool handle_tab)
1278{
1279 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1280
1281 if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
1282 if(tb->is_composing()) {
1283 if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1284 tb->interrupt_composition();
1285 } else {
1286 return;
1287 }
1288 }
1289 }
1290 if(key == SDLK_KP_ENTER || key == SDLK_RETURN) {
1291 if (mod & (SDL_KMOD_CTRL | SDL_KMOD_ALT | SDL_KMOD_GUI | SDL_KMOD_SHIFT)) {
1292 // Don't handle if modifier is pressed
1293 handled = false;
1294 } else {
1295 // Trigger window OK button only if Enter enabled,
1296 // otherwise pass handling to widget
1297 if (!enter_disabled_) {
1299 handled = true;
1300 }
1301 }
1302 } else if((key == SDLK_ESCAPE || key == SDLK_AC_BACK) && !escape_disabled_) {
1304 handled = true;
1305 } else if(key == SDLK_SPACE) {
1306 handled = click_dismiss(0);
1307 } else if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1308 assert(event_distributor_);
1309 widget* focus = event_distributor_->keyboard_focus();
1310 auto iter = std::find(tab_order.begin(), tab_order.end(), focus);
1311 do {
1312 if(mod & SDL_KMOD_SHIFT) {
1313 if(iter == tab_order.begin()) {
1314 iter = tab_order.end();
1315 }
1316 iter--;
1317 } else {
1318 if(iter == tab_order.end()) {
1319 iter = tab_order.begin();
1320 } else {
1321 iter++;
1322 if(iter == tab_order.end()) {
1323 iter = tab_order.begin();
1324 }
1325 }
1326 }
1327 } while(!is_active(*iter));
1328 keyboard_capture(*iter);
1329 handled = true;
1330 }
1331#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1332 if(key == SDLK_F12) {
1333 debug_layout_->generate_dot_file("manual", debug_layout_graph::MANUAL);
1334 handled = true;
1335 }
1336#endif
1337}
1338
1340 bool& handled,
1341 const event::message& message)
1342{
1343 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1344
1345 const event::message_show_tooltip& request
1346 = dynamic_cast<const event::message_show_tooltip&>(message);
1347
1348 dialogs::tip::show(tooltip_.id, request.message, request.location, request.source_rect);
1349
1350 handled = true;
1351}
1352
1354 bool& handled,
1355 const event::message& message)
1356{
1357 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1358
1359 const event::message_show_helptip& request
1360 = dynamic_cast<const event::message_show_helptip&>(message);
1361
1362 dialogs::tip::show(helptip_.id, request.message, request.location, request.source_rect);
1363
1364 handled = true;
1365}
1366
1368 bool& handled)
1369{
1370 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1371
1373
1374 handled = true;
1375}
1376
1381
1382// }---------- DEFINITION ---------{
1383
1386{
1387 DBG_GUI_P << "Parsing window " << id;
1388
1389 load_resolutions<resolution>(cfg);
1390}
1391
1393 : panel_definition::resolution(cfg), grid(nullptr)
1394{
1395 auto child = cfg.optional_child("grid");
1396 // VALIDATE(child, _("No grid defined."));
1397
1398 /** @todo Evaluate whether the grid should become mandatory. */
1399 if(child) {
1400 grid = std::make_shared<builder_grid>(*child);
1401 }
1402}
1403
1404// }------------ END --------------
1405
1406} // namespace gui2
1407
1408
1409/**
1410 * @page layout_algorithm Layout algorithm
1411 *
1412 * @section introduction-layout_algorithm Introduction
1413 *
1414 * This page describes how the layout engine for the dialogs works. First
1415 * a global overview of some terms used in this document.
1416 *
1417 * - @ref gui2::widget "Widget"; Any item which can be used in the widget
1418 * toolkit. Not all widgets are visible. In general widgets cannot be
1419 * sized directly, but this is controlled by a window. A widget has an
1420 * internal size cache and if the value in the cache is not equal to 0,0
1421 * that value is its best size. This value gets set when the widget can
1422 * honor a resize request. It will be set with the value which honors
1423 * the request.
1424 *
1425 * - @ref gui2::grid "Grid"; A grid is an invisible container which holds
1426 * one or more widgets. Several widgets have a grid in them to hold
1427 * multiple widgets eg panels and windows.
1428 *
1429 * - @ref gui2::grid::child "Grid cell"; Every widget which is in a grid is
1430 * put in a grid cell. These cells also hold the information about the gaps
1431 * between widgets the behavior on growing etc. All grid cells must have a
1432 * widget inside them.
1433 *
1434 * - @ref gui2::window "Window"; A window is a top level item which has a
1435 * grid with its children. The window handles the sizing of the window and
1436 * makes sure everything fits.
1437 *
1438 * - @ref gui2::window::linked_size "Shared size group"; A shared size
1439 * group is a number of widgets which share width and or height. These
1440 * widgets are handled separately in the layout algorithm. All grid cells
1441 * width such a widget will get the same height and or width and these
1442 * widgets won't be resized when there's not enough size. To be sure that
1443 * these widgets don't cause trouble for the layout algorithm, they must be
1444 * in a container with scrollbars so there will always be a way to properly
1445 * layout them. The engine must enforce this restriction so the shared
1446 * layout property must be set by the engine after validation.
1447 *
1448 * - All visible grid cells; A grid cell is visible when the widget inside
1449 * of it doesn't have the state visibility::invisible. Widgets which have the
1450 * state visibility::hidden are sized properly since when they become
1451 * visibility::visible the layout shouldn't be invalidated. A grid cell
1452 * that's invisible has size 0,0.
1453 *
1454 * - All resizable grid cells; A grid cell is resizable under the following
1455 * conditions:
1456 * - The widget is visibility::visible.
1457 * - The widget is not in a shared size group.
1458 *
1459 * There are two layout algorithms with a different purpose.
1460 *
1461 * - The Window algorithm; this algorithm's goal is it to make sure all grid
1462 * cells fit in the window. Sizing the grid cells depends on the widget
1463 * size as well, but this algorithm only sizes the grid cells and doesn't
1464 * handle the widgets inside them.
1465 *
1466 * - The Grid algorithm; after the Window algorithm made sure that all grid
1467 * cells fit this algorithm makes sure the widgets are put in the optimal
1468 * state in their grid cell.
1469 *
1470 * @section layout_algorithm_window Window
1471 *
1472 * Here is the algorithm used to layout the window:
1473 *
1474 * - Perform a full initialization
1475 * (@ref gui2::widget::layout_initialize (full_initialization = true)):
1476 * - Clear the internal best size cache for all widgets.
1477 * - For widgets with scrollbars hide them unless the
1478 * @ref gui2::scrollbar_container::scrollbar_mode "scrollbar_mode" is
1479 * ALWAYS_VISIBLE or AUTO_VISIBLE.
1480 * - Handle shared sizes:
1481 * - Height and width:
1482 * - Get the best size for all widgets that share height and width.
1483 * - Set the maximum of width and height as best size for all these
1484 * widgets.
1485 * - Width only:
1486 * - Get the best width for all widgets which share their width.
1487 * - Set the maximum width for all widgets, but keep their own height.
1488 * - Height only:
1489 * - Get the best height for all widgets which share their height.
1490 * - Set the maximum height for all widgets, but keep their own width.
1491 * - Start layout loop:
1492 * - Get best size.
1493 * - If width <= maximum_width && height <= maximum_height we're done.
1494 * - If width > maximum_width, optimize the width:
1495 * - For every grid cell in a grid row there will be a resize request
1496 * (@ref gui2::grid::reduce_width):
1497 * - Sort the widgets in the row on the resize priority.
1498 * - Loop through this priority queue until the row fits
1499 * - If priority != 0 try to share the extra width else all
1500 * widgets are tried to reduce the full size.
1501 * - Try to shrink the widgets by either wrapping or using a
1502 * scrollbar (@ref gui2::widget::request_reduce_width).
1503 * - If the row fits in the wanted width this row is done.
1504 * - Else try the next priority.
1505 * - All priorities done and the width still doesn't fit.
1506 * - Loop through this priority queue until the row fits.
1507 * - If priority != 0:
1508 * - try to share the extra width
1509 * -Else:
1510 * - All widgets are tried to reduce the full size.
1511 * - Try to shrink the widgets by sizing them smaller as really
1512 * wanted (@ref gui2::widget::demand_reduce_width).
1513 * For labels, buttons etc. they get ellipsized.
1514 * - If the row fits in the wanted width this row is done.
1515 * - Else try the next priority.
1516 * - All priorities done and the width still doesn't fit.
1517 * - Throw a layout width doesn't fit exception.
1518 * - If height > maximum_height, optimize the height
1519 * (@ref gui2::grid::reduce_height):
1520 * - For every grid cell in a grid column there will be a resize request:
1521 * - Sort the widgets in the column on the resize priority.
1522 * - Loop through this priority queue until the column fits:
1523 * - If priority != 0 try to share the extra height else all
1524 * widgets are tried to reduce the full size.
1525 * - Try to shrink the widgets by using a scrollbar
1526 * (@ref gui2::widget::request_reduce_height).
1527 * - If succeeded for a widget the width is influenced and the
1528 * width might be invalid.
1529 * - Throw a width modified exception.
1530 * - If the column fits in the wanted height this column is done.
1531 * - Else try the next priority.
1532 * - All priorities done and the height still doesn't fit.
1533 * - Loop through this priority queue until the column fits.
1534 * - If priority != 0 try to share the extra height else all
1535 * widgets are tried to reduce the full size.
1536 * - Try to shrink the widgets by sizing them smaller as really
1537 * wanted (@ref gui2::widget::demand_reduce_width).
1538 * For labels, buttons etc. they get ellipsized .
1539 * - If the column fits in the wanted height this column is done.
1540 * - Else try the next priority.
1541 * - All priorities done and the height still doesn't fit.
1542 * - Throw a layout height doesn't fit exception.
1543 * - End layout loop.
1544 *
1545 * - Catch @ref gui2::layout_exception_width_modified "width modified":
1546 * - Goto relayout.
1547 *
1548 * - Catch
1549 * @ref gui2::layout_exception_width_resize_failed "width resize failed":
1550 * - If the window has a horizontal scrollbar which isn't shown but can be
1551 * shown.
1552 * - Show the scrollbar.
1553 * - goto relayout.
1554 * - Else show a layout failure message.
1555 *
1556 * - Catch
1557 * @ref gui2::layout_exception_height_resize_failed "height resize failed":
1558 * - If the window has a vertical scrollbar which isn't shown but can be
1559 * shown:
1560 * - Show the scrollbar.
1561 * - goto relayout.
1562 * - Else:
1563 * - show a layout failure message.
1564 *
1565 * - Relayout:
1566 * - Initialize all widgets
1567 * (@ref gui2::widget::layout_initialize (full_initialization = false))
1568 * - Handle shared sizes, since the reinitialization resets that state.
1569 * - Goto start layout loop.
1570 *
1571 * @section grid Grid
1572 *
1573 * This section will be documented later.
1574 */
#define LOG_DP
std::size_t w_
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
Simple push button.
Definition button.hpp:36
A generic container base class.
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
void reduce_height(const unsigned maximum_height)
Tries to reduce the height of a container.
const grid & get_grid() const
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
void reduce_width(const unsigned maximum_width)
Tries to reduce the width of a container.
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
virtual void place(const point &origin, const point &size) override
See widget::place.
Main class to show messages to the user.
Definition message.hpp:36
At the moment two kinds of tips are known:
Definition tooltip.cpp:41
void connect()
Connects the dispatcher to the event handler.
void set_mouse_behavior(const mouse_behavior mouse_behavior)
void disconnect()
Disconnects the dispatcher from the event handler.
void set_want_keyboard_input(const bool want_keyboard_input)
bool is_connected() const
Return whether the dispatcher is currently connected.
void initialize_state()
Initializes the state of the keyboard and mouse.
Basic template class to generate new items.
Base container class.
Definition grid.hpp:32
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition grid.hpp:59
unsigned int get_rows() const
Definition grid.hpp:302
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition grid.hpp:52
static const unsigned VERTICAL_ALIGN_CENTER
Definition grid.hpp:51
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
static const unsigned VERTICAL_ALIGN_TOP
Definition grid.hpp:50
unsigned int get_cols() const
Definition grid.hpp:308
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition grid.hpp:57
builder_window(const config &cfg)
Definition window.cpp:96
virtual std::unique_ptr< widget > build() const override
Definition window.cpp:102
Abstract base class for text items.
Base class for all widgets.
Definition widget.hpp:55
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition widget.hpp:747
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
void set_visible(const visibility visible)
Definition widget.cpp:479
void set_id(const std::string &id)
Definition widget.cpp:98
void queue_redraw()
Indicates that this widget should be redrawn.
Definition widget.cpp:464
point get_origin() const
Returns the screen origin of the widget.
Definition widget.cpp:311
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
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:
rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition widget.cpp:321
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition window.hpp:59
bool need_layout_
When set the form needs a full layout redraw cycle.
Definition window.hpp:478
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition window.hpp:387
void capture_and_show_keyboard(widget *widget)
Definition window.cpp:1205
const unsigned vertical_placement_
Sets the vertical placement.
Definition window.hpp:506
bool invalidate_layout_blocked_
Is invalidate_layout blocked, see invalidate_layout_blocker.
Definition window.hpp:484
typed_formula< bool > reevaluate_best_size_
The formula to determine whether the size is good.
Definition window.hpp:527
typed_formula< unsigned > maximum_width_
The maximum width if automatic_placement_ is true.
Definition window.hpp:509
void remove_from_keyboard_chain(widget *widget)
Remove the widget from the keyboard chain.
Definition window.cpp:1217
void keyboard_capture(widget *widget)
Definition window.cpp:1197
void invalidate_layout()
Updates the size of the window.
Definition window.cpp:759
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition window.cpp:1211
void signal_handler_message_show_tooltip(const event::ui_event event, bool &handled, const event::message &message)
Definition window.cpp:1339
void mouse_capture(const bool capture=true)
Definition window.cpp:1191
typed_formula< unsigned > maximum_height_
The maximum height if automatic_placement_ is true.
Definition window.hpp:512
virtual rect screen_location() override
The current draw location of the window, on the screen.
Definition window.cpp:738
void update_render_textures()
Ensure render textures are valid and correct.
Definition window.cpp:636
void show_tooltip()
Shows the window as a tooltip.
Definition window.cpp:425
int mouse_button_state_
The state of the mouse button.
Definition window.hpp:628
void hide()
Hides the window.
Definition window.cpp:620
void show_non_modal()
Shows the window non modal.
Definition window.cpp:457
std::unique_ptr< event::distributor > event_distributor_
Definition window.hpp:659
void signal_handler_sdl_video_resize(const event::ui_event event, bool &handled, const point &new_size)
Definition window.cpp:1239
virtual ~window()
Definition window.cpp:373
std::map< std::string, linked_size, std::less<> > linked_size_
List of the widgets, whose size are linked together.
Definition window.hpp:588
static window * window_instance(const unsigned handle)
Returns the instance of a window.
Definition window.cpp:407
status
The status of the window.
Definition window.hpp:203
@ CLOSED
The window has been closed.
@ NEW
The window is new and not yet shown.
@ SHOWING
The window is being shown.
@ REQUEST_CLOSE
The window has been requested to be closed but still needs to evaluate the request.
builder_window::window_resolution::tooltip_info tooltip_
The settings for the tooltip.
Definition window.hpp:533
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key, const SDL_Keymod mod, bool handle_tab)
Definition window.cpp:1273
virtual void layout() override
Lays out the window.
Definition window.cpp:829
void signal_handler_message_show_helptip(const event::ui_event event, bool &handled, const event::message &message)
Definition window.cpp:1353
void layout_linked_widgets()
Layouts the linked widgets.
Definition window.cpp:1028
show_mode show_mode_
The mode in which the window is shown.
Definition window.hpp:467
std::vector< widget * > tab_order
List of widgets in the tabbing order.
Definition window.hpp:591
typed_formula< unsigned > h_
The formula to calculate the height of the dialog.
Definition window.hpp:524
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
Definition window.cpp:776
int show(unsigned auto_close_timeout=0)
Shows the window, running an event loop until it should close.
Definition window.cpp:491
rect awaiting_rerender_
The part of the window (if any) currently marked for rerender.
Definition window.hpp:163
void signal_handler_request_placement(const event::ui_event event, bool &handled)
Definition window.cpp:1367
bool escape_disabled_
Disable the escape key see our setter for more info.
Definition window.hpp:561
bool does_click_dismiss() const
Does the window close easily?
Definition window.hpp:300
void signal_handler_close_window()
Definition window.cpp:1377
bool click_dismiss_
Do we want to have easy close behavior?
Definition window.hpp:555
void add_linked_widget(const std::string &id, widget *widget)
Adds a widget to a linked size group.
Definition window.cpp:799
void defer_region(const rect &region)
Defer rendering of a particular region to next frame.
Definition window.cpp:684
const unsigned horizontal_placement_
Sets the horizontal placement.
Definition window.hpp:498
void queue_rerender()
Definition window.cpp:670
static retval get_retval_by_id(const std::string &id)
Gets the retval for the default buttons.
Definition window.cpp:412
void add_to_tab_order(widget *widget, int at=-1)
Add the widget to the tabbing order.
Definition window.cpp:1223
const bool automatic_placement_
Do we wish to place the widget automatically?
Definition window.hpp:490
void finalize(const builder_grid &content_grid)
Finishes the initialization of the grid.
Definition window.cpp:1087
builder_window::window_resolution::tooltip_info helptip_
The settings for the helptip.
Definition window.hpp:536
std::function< bool()> exit_hook_
Definition window.hpp:745
std::vector< rect > deferred_regions_
Parts of the window (if any) with rendering deferred to next frame.
Definition window.hpp:166
bool hidden_
Avoid drawing the window.
Definition window.hpp:487
bool click_dismiss(const int mouse_button_mask)
Handles a mouse click event for dismissing the dialog.
Definition window.cpp:1074
typed_formula< unsigned > x_
The formula to calculate the x value of the dialog.
Definition window.hpp:515
typed_formula< unsigned > y_
The formula to calculate the y value of the dialog.
Definition window.hpp:518
void generate_dot_file(const std::string &, const unsigned)
Definition window.hpp:654
virtual bool expose(const rect &region) override
Called by draw_manager when it believes a redraw is necessary.
Definition window.cpp:717
wfl::map_formula_callable variables_
The variables of the canvas.
Definition window.hpp:481
void remove_linked_widget(const std::string &id, const widget *widget)
Removes a widget from a linked size group.
Definition window.cpp:813
void signal_handler_click_dismiss(const event::ui_event event, bool &handled, bool &halt, const int mouse_button_mask)
The handler for the click dismiss mouse 'event'.
Definition window.cpp:1254
bool enter_disabled_
Disable the enter key see our setter for more info.
Definition window.hpp:558
virtual void render() override
Ensure the window's internal render buffer is up-to-date.
Definition window.cpp:690
bool init_linked_size_group(const std::string &id, const bool fixed_width, const bool fixed_height)
Initializes a linked size group.
Definition window.cpp:787
void draw()
Draws the window.
Definition window.cpp:593
typed_formula< unsigned > w_
The formula to calculate the width of the dialog.
Definition window.hpp:521
wfl::function_symbol_table functions_
The formula definitions available for the calculation formulas.
Definition window.hpp:530
status status_
The status of the window.
Definition window.hpp:460
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition window.cpp:765
texture render_buffer_
The internal render buffer used by render() and expose().
Definition window.hpp:160
bool has_linked_size_group(const std::string &id)
Is the linked size group defined for this window?
Definition window.cpp:794
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition texture.hpp:33
void set_src(const rect &r)
Set the source region of the texture used for drawing operations.
Definition texture.cpp:122
point draw_size() const
The size of the texture in draw-space.
Definition texture.hpp:120
void set_draw_size(int w, int h)
Set the intended size of the texture, in draw-space.
Definition texture.hpp:129
point get_raw_size() const
The raw internal texture size.
Definition texture.cpp:109
void clear_src()
Clear the source region.
Definition texture.hpp:165
map_formula_callable & add(const std::string &key, const variant &value)
Definition callable.hpp:249
Definitions for the interface to Wesnoth Markup Language (WML).
This file contains the definitions for the gui2::event::message class.
#define DBG_DP
Definition display.cpp:79
#define LOG_HEADER
Contains the event distributor.
Drawing functions, for drawing things on the screen.
#define CLOSE_WINDOW_EVENT
Definition events.hpp:32
#define SHOW_HELPTIP_EVENT
Definition events.hpp:33
const config * cfg
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
static std::string _(const char *str)
Definition gettext.hpp:100
#define LOG_IMPL_HEADER
Definition grid.cpp:31
Define the common log macros for the gui toolkit.
#define DBG_GUI_L
Definition log.hpp:55
#define DBG_GUI_P
Definition log.hpp:66
#define DBG_GUI_E
Definition log.hpp:35
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
#define LOG_SCOPE_HEADER
Definition button.cpp:34
#define LOG_IMPL_SCOPE_HEADER
Definition window.cpp:75
std::map< unsigned, window * > windows_
Definition window.cpp:193
static lg::log_domain log_display("display")
static lg::log_domain log_gui("gui/layout")
This file contains the window object, this object is a top level container which has the event manage...
Contains functions for cleanly handling SDL input.
Defines the exception classes for the layout algorithm.
Standard logging facilities (interface).
#define log_scope2(domain, description)
Definition log.hpp:276
#define ERR_GUI
Definition draw.hpp:43
render_target_setter set_render_target(const texture &t)
Set the given texture as the active render target.
Definition draw.cpp:757
clip_setter override_clip(const ::rect &clip)
Override the clipping area.
Definition draw.cpp:590
void clear()
Clear the current render target.
Definition draw.cpp:52
::rect get_clip()
Get the current clipping area, in draw coordinates.
Definition draw.cpp:615
void blit(const texture &tex, const ::rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition draw.cpp:409
void draw()
Trigger a draw cycle.
Definition events.cpp:712
void pump_and_draw()
pump() then immediately draw()
Definition events.hpp:156
void pump()
Process all events currently in the queue.
Definition events.cpp:480
void remove()
Removes a tip.
Definition tooltip.cpp:94
void show(const std::string &window_id, const t_string &message, const point &mouse, const rect &source_rect)
Shows a tip.
Definition tooltip.cpp:64
ui_event
The event sent to the dispatcher.
Definition handler.hpp:115
void init_mouse_location()
Initializes the location of the mouse.
Definition handler.cpp:891
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
Definition settings.cpp:27
unsigned screen_height
Definition settings.cpp:28
unsigned gamemap_height
Definition settings.cpp:32
unsigned gamemap_width
The size of the map area, if not available equal to the screen size.
Definition settings.cpp:31
Generic file dialog.
static bool is_active(const widget *wgt)
Definition window.cpp:1265
lg::log_domain log_gui_draw("gui/draw")
Definition log.hpp:28
point get_mouse_position()
Returns the current mouse position.
Definition helper.cpp:167
wfl::map_formula_callable get_screen_size_variables()
Gets a formula object with the screen size.
Definition helper.cpp:159
retval
Default window/dialog return values.
Definition retval.hpp:30
@ OK
Dialog was closed with the OK button.
Definition retval.hpp:35
@ AUTO_CLOSE
The dialog was closed automatically as its timeout had been reached.
Definition retval.hpp:41
@ CANCEL
Dialog was closed with the CANCEL button.
Definition retval.hpp:38
@ NONE
Default, unset return value.
Definition retval.hpp:32
lg::log_domain log_gui_layout("gui/layout")
Definition log.hpp:54
Contains the implementation details for lexical_cast and shouldn't be used directly.
uint32_t get_mouse_button_mask()
Returns the current mouse button mask.
Definition input.cpp:44
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition general.cpp:23
SDL_Window * get_window()
Definition video.cpp:712
int get_pixel_scale()
Get the current active pixel scale multiplier.
Definition video.cpp:480
void toggle_fullscreen()
Toggle fullscreen mode.
Definition video.cpp:847
std::string_view data
Definition picture.cpp:188
Contains the SDL_Rect helper code.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
This file contains the settings handling of the widget library.
const std::size_t max_size
Definition sound.cpp:145
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
virtual std::unique_ptr< widget > build() const override
Inherited from builder_widget.
The message for MESSAGE_SHOW_HELPTIP.
Definition message.hpp:77
const point location
The location where to show the helptip.
Definition message.hpp:87
const std::string message
The message to show on the helptip.
Definition message.hpp:84
const rect source_rect
The size of the entity requesting to show a helptip.
Definition message.hpp:90
The message for MESSAGE_SHOW_TOOLTIP.
Definition message.hpp:59
const point location
The location where to show the tooltip.
Definition message.hpp:69
const std::string message
The message to show on the tooltip.
Definition message.hpp:66
const rect source_rect
The size of the entity requesting to show a tooltip.
Definition message.hpp:72
The message callbacks hold a reference to a message.
Definition message.hpp:46
virtual std::unique_ptr< widget > build() const=0
Exception thrown when the height resizing has failed.
Basic exception when the layout doesn't fit.
Exception thrown when the width has been modified during resizing.
Exception thrown when the width resizing has failed.
Helper struct to force widgets the have the same size.
Definition window.hpp:571
int height
The current height of all widgets in the group, -1 if the height is not linked.
Definition window.hpp:584
int width
The current width of all widgets in the group, -1 if the width is not linked.
Definition window.hpp:581
std::vector< widget * > widgets
The widgets linked.
Definition window.hpp:578
window_definition(const config &cfg)
Definition window.cpp:1384
static void layout(window &window, const unsigned maximum_width, const unsigned maximum_height)
Layouts the window.
Definition window.cpp:1118
Holds a 2D point.
Definition point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
void clip(const rect &r)
Clip this rectangle by the given rectangle.
Definition rect.cpp:101
bool empty() const
False if both w and h are > 0, true otherwise.
Definition rect.cpp:49
rect intersect(const rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
Definition rect.cpp:92
void shift(const point &p)
Shift the rectangle by the given relative position.
Definition rect.cpp:106
rect & expand_to_cover(const rect &r)
Minimally expand this rect to fully contain another.
Definition rect.cpp:86
Helper class, don't construct this directly.
Helper for header for the window.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define FAIL(message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define c