21 #define GETTEXT_DOMAIN "wesnoth-lib"
53 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
69 #define ERR_GUI LOG_STREAM(err, log_gui)
71 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
72 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
74 #define LOG_IMPL_SCOPE_HEADER \
75 window.get_control_type() + " [" + window.id() + "] " + __func__
76 #define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
79 #define DBG_DP LOG_STREAM(debug, log_display)
80 #define LOG_DP LOG_STREAM(info, log_display)
81 #define WRN_DP LOG_STREAM(warn, log_display)
101 virtual std::unique_ptr<widget>
build()
const override
112 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
113 const unsigned SHOW = debug_layout_graph::SHOW;
114 const unsigned LAYOUT = debug_layout_graph::LAYOUT;
117 const unsigned SHOW = 0;
118 const unsigned LAYOUT = 0;
128 static uint32_t delay_event_callback(
const uint32_t,
void* event)
130 SDL_PushEvent(
static_cast<SDL_Event*
>(event));
131 delete static_cast<SDL_Event*
>(event);
144 static void delay_event(
const SDL_Event& event,
const uint32_t delay)
146 SDL_AddTimer(delay, delay_event_callback,
new SDL_Event(event));
154 static void helptip()
156 DBG_GUI_E <<
"Pushing SHOW_HELPTIP_EVENT event in queue.";
164 SDL_PushEvent(&event);
178 static manager& instance();
180 void add(window& window);
182 void remove(window& window);
184 unsigned get_id(window& window);
198 manager& manager::instance()
200 static manager window_manager;
201 return window_manager;
204 void manager::add(window& win)
217 if(itor->second == &win) {
225 unsigned manager::get_id(window& win)
231 if(itor->second == &win) {
261 , invalidate_layout_blocked_(false)
263 , automatic_placement_(definition.automatic_placement)
264 , horizontal_placement_(definition.horizontal_placement)
265 , vertical_placement_(definition.vertical_placement)
266 , maximum_width_(definition.maximum_width)
267 , maximum_height_(definition.maximum_height)
270 ,
w_(definition.width)
271 , h_(definition.height)
272 , reevaluate_best_size_(definition.reevaluate_best_size)
273 , functions_(definition.functions)
275 , helptip_(definition.helptip)
276 , click_dismiss_(definition.click_dismiss)
277 , enter_disabled_(false)
278 , escape_disabled_(false)
280 , mouse_button_state_(0)
281 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
282 , debug_layout_(new debug_layout_graph(this))
284 , event_distributor_(new event::distributor(*this, event::dispatcher::front_child))
285 , exit_hook_([](
window&)->bool { return true; })
287 manager::instance().add(*
this);
291 for(
const auto&
lg : definition.linked_groups) {
292 if(has_linked_size_group(
lg.id)) {
293 FAIL(
VGETTEXT(
"Linked ‘$id’ group has multiple definitions.", {{
"id",
lg.id}}));
296 init_linked_size_group(
lg.id,
lg.fixed_width,
lg.fixed_height);
299 const auto conf = cast_config_to<window_definition>();
303 init_grid(*conf->grid);
304 finalize(*definition.grid);
306 init_grid(*definition.grid);
309 add_to_keyboard_chain(
this);
311 connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
312 &window::signal_handler_sdl_video_resize,
this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5));
314 connect_signal<event::SDL_ACTIVATE>(std::bind(
315 &event::distributor::initialize_state, event_distributor_.get()));
317 connect_signal<event::SDL_LEFT_BUTTON_UP>(
318 std::bind(&window::signal_handler_click_dismiss,
320 std::placeholders::_2,
321 std::placeholders::_3,
322 std::placeholders::_4,
324 event::dispatcher::front_child);
325 connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
326 std::bind(&window::signal_handler_click_dismiss,
328 std::placeholders::_2,
329 std::placeholders::_3,
330 std::placeholders::_4,
332 event::dispatcher::front_child);
333 connect_signal<event::SDL_RIGHT_BUTTON_UP>(
334 std::bind(&window::signal_handler_click_dismiss,
336 std::placeholders::_2,
337 std::placeholders::_3,
338 std::placeholders::_4,
340 event::dispatcher::front_child);
343 connect_signal<event::SDL_KEY_DOWN>(
345 &window::signal_handler_sdl_key_down,
this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6,
false),
346 event::dispatcher::back_post_child);
347 connect_signal<event::SDL_KEY_DOWN>(std::bind(
348 &window::signal_handler_sdl_key_down,
this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6,
true));
350 connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
351 std::bind(&window::signal_handler_message_show_tooltip,
353 std::placeholders::_2,
354 std::placeholders::_3,
355 std::placeholders::_5),
356 event::dispatcher::back_pre_child);
358 connect_signal<event::MESSAGE_SHOW_HELPTIP>(
359 std::bind(&window::signal_handler_message_show_helptip,
361 std::placeholders::_2,
362 std::placeholders::_3,
363 std::placeholders::_5),
364 event::dispatcher::back_pre_child);
366 connect_signal<event::REQUEST_PLACEMENT>(
368 &window::signal_handler_request_placement,
this, std::placeholders::_2, std::placeholders::_3),
369 event::dispatcher::back_pre_child);
371 connect_signal<event::CLOSE_WINDOW>(std::bind(&window::signal_handler_close_window,
this));
389 for(
unsigned row = 0; row < get_grid().get_rows(); ++row) {
390 for(
unsigned col = 0; col < get_grid().get_cols(); ++col) {
391 get_grid().remove_child(row, col);
402 if(show_mode_ == show_mode::modal) {
406 manager::instance().remove(*
this);
419 retval window::get_retval_by_id(
const std::string&
id)
425 }
else if(
id ==
"cancel" ||
id ==
"quit") {
432 void window::show_tooltip()
438 if(!is_connected()) {
439 LOG_DP <<
"connecting " <<
id() <<
" on show_tooltip";
445 generate_dot_file(
"show", SHOW);
447 assert(status_ == status::NEW);
449 set_mouse_behavior(event::dispatcher::mouse_behavior::none);
450 set_want_keyboard_input(
false);
461 DBG_DP <<
"show tooltip queued to " << get_rectangle();
464 void window::show_non_modal()
470 if(!is_connected()) {
471 LOG_DP <<
"connecting " <<
id() <<
" on show_non_modal";
477 generate_dot_file(
"show", SHOW);
479 assert(status_ == status::NEW);
481 set_mouse_behavior(event::dispatcher::mouse_behavior::hit);
483 show_mode_ = show_mode::modeless;
493 DBG_DP <<
"show non-modal queued to " << get_rectangle();
504 if(!is_connected()) {
505 LOG_DP <<
"connecting " <<
id() <<
" on show";
515 show_mode_ = show_mode::modal;
519 generate_dot_file(
"show", SHOW);
534 if(auto_close_timeout) {
541 delay_event(event, auto_close_timeout);
550 bool mouse_button_state_initialized =
false;
551 mouse_button_state_ = std::numeric_limits<uint32_t>::max();
554 for(status_ = status::SHOWING; status_ != status::CLOSED;) {
558 if(!mouse_button_state_initialized) {
570 mouse_button_state_initialized =
true;
574 if(status_ == status::REQUEST_CLOSE) {
575 status_ = exit_hook_(*
this) ? status::CLOSED : status::SHOWING;
591 tb->interrupt_composition();
607 if(!this->draw_background()) {
612 defer_region(get_rectangle());
617 this->draw_children();
620 if(!this->draw_foreground()) {
621 defer_region(get_rectangle());
636 LOG_DP <<
"disconnecting " <<
id() <<
" on hide";
643 void window::update_render_textures()
649 point buf_raw = render_buffer_.get_raw_size();
650 point buf_draw = render_buffer_.draw_size();
651 bool raw_size_changed = buf_raw.x !=
render.x || buf_raw.y !=
render.y;
652 bool draw_size_changed = buf_draw.x !=
draw.x || buf_draw.y !=
draw.y;
653 if (!raw_size_changed && !draw_size_changed) {
658 if(raw_size_changed) {
659 LOG_DP <<
"regenerating window render buffer as " <<
render;
662 if(raw_size_changed || draw_size_changed) {
663 LOG_DP <<
"updating window render buffer draw size to " <<
draw;
664 render_buffer_.set_draw_size(
draw);
677 void window::queue_rerender()
679 queue_rerender(get_rectangle());
682 void window::queue_rerender(
const rect& screen_region)
686 rect local_region = screen_region.
intersect(get_rectangle());
687 local_region.
shift(-get_origin());
688 awaiting_rerender_.expand_to_cover(local_region);
691 void window::defer_region(
const rect& screen_region)
693 LOG_DP <<
"deferring region " << screen_region;
694 deferred_regions_.push_back(screen_region);
700 update_render_textures();
703 for(
auto& region : deferred_regions_) {
704 queue_redraw(region);
706 deferred_regions_.clear();
709 if (awaiting_rerender_.empty()) {
712 DBG_DP <<
"window::render() local " << awaiting_rerender_;
721 DBG_DP <<
"window::expose " << region;
733 render_buffer_.set_src(
src);
735 render_buffer_.clear_src();
745 return get_rectangle();
748 window::invalidate_layout_blocker::invalidate_layout_blocker(
window&
window)
757 assert(window_.invalidate_layout_blocked_);
758 window_.invalidate_layout_blocked_ =
false;
773 const bool must_be_active)
const
790 const bool fixed_width,
791 const bool fixed_height)
793 assert(fixed_width || fixed_height);
808 ERR_GUI <<
"Unknown linked group '" <<
id <<
"'; skipping";
813 if(std::find(widgets.begin(), widgets.end(), wgt) == widgets.end()) {
814 widgets.push_back(wgt);
828 = std::find(widgets.begin(), widgets.end(), wgt);
830 if(itor != widgets.end()) {
833 assert(std::find(widgets.begin(), widgets.end(), wgt)
843 DBG_DP <<
"window::layout";
847 const auto conf = cast_config_to<window_definition>();
882 button* click_dismiss_button = find_widget<button>(
"click_dismiss",
false,
false);
883 if(click_dismiss_button) {
887 button* btn = find_widget<button>(
"ok",
false,
false);
890 click_dismiss_button = btn;
893 _(
"Click dismiss needs a ‘click_dismiss’ or ‘ok’ button."));
911 std::stringstream sstr;
912 sstr << __FILE__ <<
":" << __LINE__ <<
" in function '" << __func__
913 <<
"' found the following problem: Failed to size window;"
915 << maximum_width <<
',' << maximum_height <<
" screen size "
919 "which doesn’t fit on the screen."),
925 assert(click_dismiss_button);
929 *click_dismiss_button,
940 *
this, maximum_width, maximum_height);
947 std::stringstream sstr;
948 sstr << __FILE__ <<
":" << __LINE__ <<
" in function '" << __func__
949 <<
"' found the following problem: Failed to size window;"
951 << maximum_width <<
',' << maximum_height <<
" screen size "
956 "which doesn’t fit on the screen."),
967 assert(
size.x >= 0 &&
static_cast<unsigned>(
size.x) <= maximum_width
968 &&
size.y >= 0 &&
static_cast<unsigned>(
size.y) <= maximum_height);
1044 point max_size(0, 0);
1052 if(
size.x > max_size.x) {
1053 max_size.x =
size.x;
1055 if(
size.y > max_size.y) {
1056 max_size.y =
size.y;
1073 size.x = max_size.x;
1076 size.y = max_size.y;
1102 static const std::string
id =
"_window_content_grid";
1108 assert(parent_grid);
1110 if(
grid* grandparent_grid =
dynamic_cast<grid*
>(parent_grid->parent())) {
1111 grandparent_grid->swap_child(
id, std::move(
widget),
false);
1113 c->get_grid().swap_child(
id, std::move(
widget),
true);
1119 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1122 const unsigned domain)
1124 debug_layout_->generate_dot_file(
generator, domain);
1129 const unsigned maximum_width,
1130 const unsigned maximum_height)
1145 <<
" maximum size : " << maximum_width <<
','
1146 << maximum_height <<
".";
1147 if(
size.x <=
static_cast<int>(maximum_width)
1148 &&
size.y <=
static_cast<int>(maximum_height)) {
1154 if(
size.x >
static_cast<int>(maximum_width)) {
1158 if(
size.x >
static_cast<int>(maximum_width)) {
1160 <<
" Wanted width " << maximum_width
1161 <<
" resulting width " <<
size.x <<
".";
1165 <<
" Status: Resize width succeeded.";
1168 if(
size.y >
static_cast<int>(maximum_height)) {
1172 if(
size.y >
static_cast<int>(maximum_height)) {
1174 <<
" Wanted height " << maximum_height
1175 <<
" resulting height " <<
size.y <<
".";
1179 <<
" Status: Resize height succeeded.";
1182 assert(
size.x <=
static_cast<int>(maximum_width)
1183 &&
size.y <=
static_cast<int>(maximum_height));
1192 <<
" Status: Width has been modified, rerun.";
1234 if(at < 0 || at >=
static_cast<int>(
tab_order.size())) {
1243 const point& new_size)
1259 const int mouse_button_mask)
1262 <<
static_cast<unsigned>(mouse_button_mask) <<
".";
1277 const SDL_Keycode key,
1278 const SDL_Keymod mod,
1284 if(tb->is_composing()) {
1285 if(handle_tab && !
tab_order.empty() && key == SDLK_TAB) {
1286 tb->interrupt_composition();
1292 if(key == SDLK_KP_ENTER || key == SDLK_RETURN) {
1293 if (mod & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT)) {
1307 }
else if(key == SDLK_SPACE) {
1309 }
else if(handle_tab && !
tab_order.empty() && key == SDLK_TAB) {
1314 if(mod & KMOD_SHIFT) {
1333 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1334 if(key == SDLK_F12) {
1335 debug_layout_->generate_dot_file(
"manual", debug_layout_graph::MANUAL);
1391 load_resolutions<resolution>(cfg);
1402 grid = std::make_shared<builder_grid>(*child);
A config object defines a single node in a WML file, with access to child nodes.
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
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 &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.
Basic template class to generate new items.
static const unsigned HORIZONTAL_ALIGN_RIGHT
static const unsigned VERTICAL_ALIGN_BOTTOM
static const unsigned VERTICAL_ALIGN_CENTER
static const unsigned HORIZONTAL_ALIGN_CENTER
static const unsigned VERTICAL_ALIGN_TOP
static const unsigned HORIZONTAL_ALIGN_LEFT
builder_window(const config &cfg)
virtual std::unique_ptr< widget > build() const override
Abstract base class for text items.
~invalidate_layout_blocker()
base class of top level items, the only item which needs to store the final canvases to draw on.
bool need_layout_
When set the form needs a full layout redraw cycle.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
const unsigned vertical_placement_
Sets the vertical placement.
bool invalidate_layout_blocked_
Is invalidate_layout blocked, see invalidate_layout_blocker.
typed_formula< bool > reevaluate_best_size_
The formula to determine whether the size is good.
typed_formula< unsigned > maximum_width_
The maximum width if automatic_placement_ is true.
void remove_from_keyboard_chain(widget *widget)
Remove the widget from the keyboard chain.
void keyboard_capture(widget *widget)
void invalidate_layout()
Updates the size of the window.
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
void signal_handler_message_show_tooltip(const event::ui_event event, bool &handled, const event::message &message)
void mouse_capture(const bool capture=true)
typed_formula< unsigned > maximum_height_
The maximum height if automatic_placement_ is true.
int mouse_button_state_
The state of the mouse button.
std::unique_ptr< event::distributor > event_distributor_
void signal_handler_sdl_video_resize(const event::ui_event event, bool &handled, const point &new_size)
status
The status of the window.
builder_window::window_resolution::tooltip_info tooltip_
The settings for the tooltip.
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key, const SDL_Keymod mod, bool handle_tab)
virtual void layout() override
Lays out the window.
void signal_handler_message_show_helptip(const event::ui_event event, bool &handled, const event::message &message)
void layout_linked_widgets()
Layouts the linked widgets.
std::vector< widget * > tab_order
List of widgets in the tabbing order.
typed_formula< unsigned > h_
The formula to calculate the height of the dialog.
void signal_handler_request_placement(const event::ui_event event, bool &handled)
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
bool escape_disabled_
Disable the escape key see our setter for more info.
bool does_click_dismiss() const
Does the window close easily?
void signal_handler_close_window()
bool click_dismiss_
Do we want to have easy close behavior?
void add_linked_widget(const std::string &id, widget *widget)
Adds a widget to a linked size group.
const unsigned horizontal_placement_
Sets the horizontal placement.
void add_to_tab_order(widget *widget, int at=-1)
Add the widget to the tabbing order.
const bool automatic_placement_
Do we wish to place the widget automatically?
void finalize(const builder_grid &content_grid)
Finishes the initialization of the grid.
builder_window::window_resolution::tooltip_info helptip_
The settings for the helptip.
bool click_dismiss(const int mouse_button_mask)
Handles a mouse click event for dismissing the dialog.
typed_formula< unsigned > x_
The formula to calculate the x value of the dialog.
typed_formula< unsigned > y_
The formula to calculate the y value of the dialog.
void generate_dot_file(const std::string &, const unsigned)
wfl::map_formula_callable variables_
The variables of the canvas.
void remove_linked_widget(const std::string &id, const widget *widget)
Removes a widget from a linked size group.
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'.
bool enter_disabled_
Disable the enter key see our setter for more info.
void init_linked_size_group(const std::string &id, const bool fixed_width, const bool fixed_height)
Initializes a linked size group.
typed_formula< unsigned > w_
The formula to calculate the width of the dialog.
wfl::function_symbol_table functions_
The formula definitions available for the calculation formulas.
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
std::map< std::string, linked_size > linked_size_
List of the widgets, whose size are linked together.
bool has_linked_size_group(const std::string &id)
Is the linked size group defined for this window?
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definitions for the interface to Wesnoth Markup Language (WML).
This file contains the definitions for the gui2::event::message class.
Contains the event distributor.
Drawing functions, for drawing things on the screen.
#define CLOSE_WINDOW_EVENT
#define SHOW_HELPTIP_EVENT
static std::string _(const char *str)
Define the common log macros for the gui toolkit.
std::string tooltip
Shown when hovering over an entry in the filter's drop-down list.
std::string id
Text to match against addon_info.tags()
Defines the exception classes for the layout algorithm.
Standard logging facilities (interface).
#define log_scope2(domain, description)
render_target_setter set_render_target(const texture &t)
Set the given texture as the active render target.
clip_setter override_clip(const SDL_Rect &clip)
Override the clipping area.
void clear()
Clear the current render target.
void blit(const texture &tex, const SDL_Rect &dst)
Draws a texture, or part of a texture, at the given location.
::rect get_clip()
Get the current clipping area, in draw coordinates.
void draw()
Trigger a draw cycle.
void pump_and_draw()
pump() then immediately draw()
void pump()
Process all events currently in the queue.
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
void remove()
Removes a tip.
ui_event
The event sent to the dispatcher.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
void init_mouse_location()
Initializes the location of the mouse.
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
unsigned gamemap_width
The size of the map area, if not available equal to the screen size.
void get_screen_size_variables(wfl::map_formula_callable &variable)
Gets a formula object with the screen size.
static bool is_active(const widget *wgt)
lg::log_domain log_gui_draw("gui/draw")
point get_mouse_position()
Returns the current mouse position.
retval
Default window/dialog return values.
@ OK
Dialog was closed with the OK button.
@ AUTO_CLOSE
The dialog was closed automatically as its timeout had been reached.
@ CANCEL
Dialog was closed with the CANCEL button.
@ NONE
Default, unset return value.
lg::log_domain log_gui_layout("gui/layout")
std::shared_ptr< halo_record > handle
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
Contains the implementation details for lexical_cast and shouldn't be used directly.
static std::string at(const std::string &file, int line)
uint32_t get_mouse_button_mask()
Returns the current mouse button mask.
constexpr const SDL_Rect empty_rect
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
SDL_Window * get_window()
int get_pixel_scale()
Get the current active pixel scale multiplier.
void toggle_fullscreen()
Toggle fullscreen mode.
std::string::const_iterator iterator
Contains the SDL_Rect helper code.
This file contains the settings handling of the widget library.
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.
const SDL_Rect source_rect
The size of the entity requesting to show a helptip.
const point location
The location where to show the helptip.
const std::string message
The message to show on the helptip.
The message callbacks hold a reference to a message.
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.
int height
The current height of all widgets in the group, -1 if the height is not linked.
int width
The current width of all widgets in the group, -1 if the width is not linked.
std::vector< widget * > widgets
The widgets linked.
resolution(const config &cfg)
window_definition(const config &cfg)
static void layout(window &window, const unsigned maximum_width, const unsigned maximum_height)
Layouts the window.
An abstract description of a rectangle with integer coordinates.
bool empty() const
False if both w and h are > 0, true otherwise.
void clip(const SDL_Rect &r)
Clip this rectangle by the given rectangle.
void shift(const point &p)
Shift the rectangle by the given relative position.
rect intersect(const SDL_Rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
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 VALIDATE(cond, message)
The macro to use for the validation of WML.