21 #define GETTEXT_DOMAIN "wesnoth-lib"
52 #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_(false)
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 connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
292 &window::signal_handler_sdl_video_resize,
this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5));
294 connect_signal<event::SDL_ACTIVATE>(std::bind(
295 &event::distributor::initialize_state, event_distributor_.get()));
297 connect_signal<event::SDL_LEFT_BUTTON_UP>(
298 std::bind(&window::signal_handler_click_dismiss,
300 std::placeholders::_2,
301 std::placeholders::_3,
302 std::placeholders::_4,
304 event::dispatcher::front_child);
305 connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
306 std::bind(&window::signal_handler_click_dismiss,
308 std::placeholders::_2,
309 std::placeholders::_3,
310 std::placeholders::_4,
312 event::dispatcher::front_child);
313 connect_signal<event::SDL_RIGHT_BUTTON_UP>(
314 std::bind(&window::signal_handler_click_dismiss,
316 std::placeholders::_2,
317 std::placeholders::_3,
318 std::placeholders::_4,
320 event::dispatcher::front_child);
322 connect_signal<event::SDL_KEY_DOWN>(
324 &window::signal_handler_sdl_key_down,
this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6,
true),
325 event::dispatcher::back_post_child);
326 connect_signal<event::SDL_KEY_DOWN>(std::bind(
327 &window::signal_handler_sdl_key_down,
this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6,
false));
329 connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
330 std::bind(&window::signal_handler_message_show_tooltip,
332 std::placeholders::_2,
333 std::placeholders::_3,
334 std::placeholders::_5),
335 event::dispatcher::back_pre_child);
337 connect_signal<event::MESSAGE_SHOW_HELPTIP>(
338 std::bind(&window::signal_handler_message_show_helptip,
340 std::placeholders::_2,
341 std::placeholders::_3,
342 std::placeholders::_5),
343 event::dispatcher::back_pre_child);
345 connect_signal<event::REQUEST_PLACEMENT>(
347 &window::signal_handler_request_placement,
this, std::placeholders::_2, std::placeholders::_3),
348 event::dispatcher::back_pre_child);
350 connect_signal<event::CLOSE_WINDOW>(std::bind(&window::signal_handler_close_window,
this));
368 for(
unsigned row = 0; row < get_grid().get_rows(); ++row) {
369 for(
unsigned col = 0; col < get_grid().get_cols(); ++col) {
370 get_grid().remove_child(row, col);
381 if(show_mode_ == show_mode::modal) {
385 manager::instance().remove(*
this);
392 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
394 delete debug_layout_;
404 retval window::get_retval_by_id(
const std::string&
id)
410 }
else if(
id ==
"cancel" ||
id ==
"quit") {
420 if(has_linked_size_group(
lg.id)) {
426 init_linked_size_group(
lg.id,
lg.fixed_width,
lg.fixed_height);
431 const auto conf = cast_config_to<window_definition>();
435 init_grid(*conf->grid);
436 finalize(*definition.
grid);
438 init_grid(*definition.
grid);
441 add_to_keyboard_chain(
this);
444 void window::show_tooltip()
450 if(!is_connected()) {
451 LOG_DP <<
"connecting " <<
id() <<
" on show_tooltip";
457 generate_dot_file(
"show", SHOW);
459 assert(status_ == status::NEW);
461 set_mouse_behavior(event::dispatcher::mouse_behavior::none);
462 set_want_keyboard_input(
false);
473 DBG_DP <<
"show tooltip queued to " << get_rectangle();
476 void window::show_non_modal()
482 if(!is_connected()) {
483 LOG_DP <<
"connecting " <<
id() <<
" on show_non_modal";
489 generate_dot_file(
"show", SHOW);
491 assert(status_ == status::NEW);
493 set_mouse_behavior(event::dispatcher::mouse_behavior::hit);
495 show_mode_ = show_mode::modeless;
505 DBG_DP <<
"show non-modal queued to " << get_rectangle();
516 if(!is_connected()) {
517 LOG_DP <<
"connecting " <<
id() <<
" on show";
527 show_mode_ = show_mode::modal;
531 generate_dot_file(
"show", SHOW);
546 if(auto_close_timeout) {
553 delay_event(event, auto_close_timeout);
559 bool mouse_button_state_initialized =
false;
560 for(status_ = status::SHOWING; status_ != status::CLOSED;) {
564 if(!mouse_button_state_initialized) {
576 mouse_button_state_initialized =
true;
580 if(status_ == status::REQUEST_CLOSE) {
581 status_ = exit_hook_(*
this) ? status::CLOSED : status::SHOWING;
597 tb->interrupt_composition();
613 if(!this->draw_background()) {
618 defer_region(get_rectangle());
623 this->draw_children();
626 if(!this->draw_foreground()) {
627 defer_region(get_rectangle());
642 LOG_DP <<
"disconnecting " <<
id() <<
" on hide";
649 void window::update_render_textures()
655 point buf_raw = render_buffer_.get_raw_size();
656 point buf_draw = render_buffer_.draw_size();
657 bool raw_size_changed = buf_raw.x !=
render.x || buf_raw.y !=
render.y;
658 bool draw_size_changed = buf_draw.x !=
draw.x || buf_draw.y !=
draw.y;
659 if (!raw_size_changed && !draw_size_changed) {
664 if(raw_size_changed) {
665 LOG_DP <<
"regenerating window render buffer as " <<
render;
668 if(raw_size_changed || draw_size_changed) {
669 LOG_DP <<
"updating window render buffer draw size to " <<
draw;
670 render_buffer_.set_draw_size(
draw);
683 void window::queue_rerender()
685 queue_rerender(get_rectangle());
688 void window::queue_rerender(
const rect& screen_region)
692 rect local_region = screen_region.
intersect(get_rectangle());
693 local_region.
shift(-get_origin());
694 awaiting_rerender_.expand_to_cover(local_region);
697 void window::defer_region(
const rect& screen_region)
699 LOG_DP <<
"deferring region " << screen_region;
700 deferred_regions_.push_back(screen_region);
706 update_render_textures();
709 for(
auto& region : deferred_regions_) {
710 queue_redraw(region);
712 deferred_regions_.clear();
715 if (awaiting_rerender_.empty()) {
718 DBG_DP <<
"window::render() local " << awaiting_rerender_;
727 DBG_DP <<
"window::expose " << region;
738 src.
shift(-get_origin());
739 render_buffer_.set_src(src);
741 render_buffer_.clear_src();
751 return get_rectangle();
754 window::invalidate_layout_blocker::invalidate_layout_blocker(
window&
window)
763 assert(window_.invalidate_layout_blocked_);
764 window_.invalidate_layout_blocked_ =
false;
779 const bool must_be_active)
const
796 const bool fixed_width,
797 const bool fixed_height)
799 assert(fixed_width || fixed_height);
814 ERR_GUI <<
"Unknown linked group '" <<
id <<
"'; skipping";
819 if(std::find(widgets.begin(), widgets.end(), wgt) == widgets.end()) {
820 widgets.push_back(wgt);
834 = std::find(widgets.begin(), widgets.end(), wgt);
836 if(itor != widgets.end()) {
839 assert(std::find(widgets.begin(), widgets.end(), wgt)
849 DBG_DP <<
"window::layout";
853 const auto conf = cast_config_to<window_definition>();
888 button* click_dismiss_button =
nullptr;
889 if((click_dismiss_button
890 = find_widget<button>(
this,
"click_dismiss",
false,
false))) {
895 button* btn = find_widget<button>(
this,
"ok",
false,
false);
898 click_dismiss_button = btn;
901 _(
"Click dismiss needs a 'click_dismiss' or 'ok' button."));
919 std::stringstream sstr;
920 sstr << __FILE__ <<
":" << __LINE__ <<
" in function '" << __func__
921 <<
"' found the following problem: Failed to size window;"
923 << maximum_width <<
',' << maximum_height <<
" screen size "
927 "which doesn't fit on the screen."),
933 assert(click_dismiss_button);
937 *click_dismiss_button,
948 *
this, maximum_width, maximum_height);
955 std::stringstream sstr;
956 sstr << __FILE__ <<
":" << __LINE__ <<
" in function '" << __func__
957 <<
"' found the following problem: Failed to size window;"
959 << maximum_width <<
',' << maximum_height <<
" screen size "
964 "which doesn't fit on the screen."),
975 assert(
size.x >= 0 &&
static_cast<unsigned>(
size.x) <= maximum_width
976 &&
size.y >= 0 &&
static_cast<unsigned>(
size.y) <= maximum_height);
1052 point max_size(0, 0);
1060 if(
size.x > max_size.x) {
1061 max_size.x =
size.x;
1063 if(
size.y > max_size.y) {
1064 max_size.y =
size.y;
1081 size.x = max_size.x;
1084 size.y = max_size.y;
1110 static const std::string
id =
"_window_content_grid";
1115 auto* parent_grid = find_widget<grid>(&
get_grid(),
id,
true,
false);
1116 assert(parent_grid);
1118 if(
grid* grandparent_grid =
dynamic_cast<grid*
>(parent_grid->parent())) {
1119 grandparent_grid->swap_child(
id, std::move(
widget),
false);
1121 c->get_grid().swap_child(
id, std::move(
widget),
true);
1127 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1130 const unsigned domain)
1132 debug_layout_->generate_dot_file(
generator, domain);
1137 const unsigned maximum_width,
1138 const unsigned maximum_height)
1153 <<
" maximum size : " << maximum_width <<
','
1154 << maximum_height <<
".";
1155 if(
size.x <=
static_cast<int>(maximum_width)
1156 &&
size.y <=
static_cast<int>(maximum_height)) {
1162 if(
size.x >
static_cast<int>(maximum_width)) {
1166 if(
size.x >
static_cast<int>(maximum_width)) {
1168 <<
" Wanted width " << maximum_width
1169 <<
" resulting width " <<
size.x <<
".";
1173 <<
" Status: Resize width succeeded.";
1176 if(
size.y >
static_cast<int>(maximum_height)) {
1180 if(
size.y >
static_cast<int>(maximum_height)) {
1182 <<
" Wanted height " << maximum_height
1183 <<
" resulting height " <<
size.y <<
".";
1187 <<
" Status: Resize height succeeded.";
1190 assert(
size.x <=
static_cast<int>(maximum_width)
1191 &&
size.y <=
static_cast<int>(maximum_height));
1200 <<
" Status: Width has been modified, rerun.";
1242 if(at < 0 || at >=
static_cast<int>(
tab_order.size())) {
1251 const point& new_size)
1267 const int mouse_button_mask)
1270 <<
static_cast<unsigned>(mouse_button_mask) <<
".";
1285 const SDL_Keycode key,
1286 const SDL_Keymod mod,
1292 if(tb->is_composing()) {
1293 if(handle_tab && !
tab_order.empty() && key == SDLK_TAB) {
1294 tb->interrupt_composition();
1300 if(key == SDLK_KP_ENTER || key == SDLK_RETURN) {
1301 if (mod & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT)) {
1315 }
else if(key == SDLK_SPACE) {
1317 }
else if(handle_tab && !
tab_order.empty() && key == SDLK_TAB) {
1322 if(mod & KMOD_SHIFT) {
1341 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1342 if(key == SDLK_F12) {
1343 debug_layout_->generate_dot_file(
"manual", debug_layout_graph::MANUAL);
1399 load_resolutions<resolution>(cfg);
1410 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)
Euivalent 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
A panel is a visible container to hold multiple widgets.
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.
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
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Contains the SDL_Rect helper code.
This file contains the settings handling of the widget library.
virtual std::unique_ptr< widget > build() const override
Inherited from builder_widget.
std::vector< linked_group_definition > linked_groups
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.