21 #define GETTEXT_DOMAIN "wesnoth-lib"
53 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
63 #include <SDL2/SDL_timer.h>
70 #define ERR_GUI LOG_STREAM(err, log_gui)
72 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
73 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
75 #define LOG_IMPL_SCOPE_HEADER \
76 window.get_control_type() + " [" + window.id() + "] " + __func__
77 #define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
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)
102 virtual std::unique_ptr<widget>
build()
const override
113 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
114 const unsigned SHOW = debug_layout_graph::SHOW;
115 const unsigned LAYOUT = debug_layout_graph::LAYOUT;
118 const unsigned SHOW = 0;
119 const unsigned LAYOUT = 0;
129 static uint32_t delay_event_callback(
const uint32_t,
void* event)
131 SDL_PushEvent(
static_cast<SDL_Event*
>(event));
132 delete static_cast<SDL_Event*
>(event);
145 static void delay_event(
const SDL_Event& event,
const uint32_t delay)
147 SDL_AddTimer(delay, delay_event_callback,
new SDL_Event(event));
155 static bool helptip()
157 DBG_GUI_E <<
"Pushing SHOW_HELPTIP_EVENT event in queue.";
165 SDL_PushEvent(&event);
180 static manager& instance();
182 void add(window& window);
184 void remove(window& window);
186 unsigned get_id(window& window);
200 manager& manager::instance()
202 static manager window_manager;
203 return window_manager;
206 void manager::add(window& win)
216 if(itor->second == &win) {
225 unsigned manager::get_id(window& win)
227 for(
const auto& [
id, window_ptr] :
windows_) {
228 if(window_ptr == &win) {
256 , invalidate_layout_blocked_(false)
258 , automatic_placement_(definition.automatic_placement)
259 , horizontal_placement_(definition.horizontal_placement)
260 , vertical_placement_(definition.vertical_placement)
261 , maximum_width_(definition.maximum_width)
262 , maximum_height_(definition.maximum_height)
265 ,
w_(definition.width)
266 , h_(definition.height)
267 , reevaluate_best_size_(definition.reevaluate_best_size)
268 , functions_(definition.functions)
270 , helptip_(definition.helptip)
271 , click_dismiss_(definition.click_dismiss)
272 , enter_disabled_(false)
273 , escape_disabled_(false)
275 , mouse_button_state_(0)
276 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
277 , debug_layout_(new debug_layout_graph(this))
279 , event_distributor_(new event::distributor(*this, event::dispatcher::front_child))
280 , exit_hook_([] {
return true; })
282 manager::instance().add(*
this);
286 for(
const auto& [
id, fixed_width, fixed_height] : definition.linked_groups) {
287 if(!init_linked_size_group(
id, fixed_width, fixed_height)) {
288 FAIL(
VGETTEXT(
"Linked ‘$id’ group has multiple definitions.", {{
"id",
id}}));
292 const auto conf = cast_config_to<window_definition>();
296 init_grid(*conf->grid);
297 finalize(*definition.grid);
299 init_grid(*definition.grid);
302 add_to_keyboard_chain(
this);
304 connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
307 connect_signal<event::SDL_ACTIVATE>(std::bind(
310 connect_signal<event::SDL_LEFT_BUTTON_UP>(
313 std::placeholders::_2,
314 std::placeholders::_3,
315 std::placeholders::_4,
318 connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
321 std::placeholders::_2,
322 std::placeholders::_3,
323 std::placeholders::_4,
326 connect_signal<event::SDL_RIGHT_BUTTON_UP>(
329 std::placeholders::_2,
330 std::placeholders::_3,
331 std::placeholders::_4,
336 connect_signal<event::SDL_KEY_DOWN>(
340 connect_signal<event::SDL_KEY_DOWN>(std::bind(
343 connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
346 std::placeholders::_2,
347 std::placeholders::_3,
348 std::placeholders::_5),
351 connect_signal<event::MESSAGE_SHOW_HELPTIP>(
354 std::placeholders::_2,
355 std::placeholders::_3,
356 std::placeholders::_5),
359 connect_signal<event::REQUEST_PLACEMENT>(
367 [](
auto&&...) {
return helptip(); });
400 manager::instance().remove(*
this);
419 }
else if(
id ==
"cancel" ||
id ==
"quit") {
433 LOG_DP <<
"connecting " <<
id() <<
" on show_tooltip";
465 LOG_DP <<
"connecting " <<
id() <<
" on show_non_modal";
499 LOG_DP <<
"connecting " <<
id() <<
" on show";
528 if(auto_close_timeout) {
535 delay_event(event, auto_close_timeout);
544 bool mouse_button_state_initialized =
false;
552 if(!mouse_button_state_initialized) {
564 mouse_button_state_initialized =
true;
585 tb->interrupt_composition();
630 LOG_DP <<
"disconnecting " <<
id() <<
" on hide";
645 bool raw_size_changed = buf_raw.x !=
render.x || buf_raw.y !=
render.y;
646 bool draw_size_changed = buf_draw.x !=
draw.x || buf_draw.y !=
draw.y;
647 if (!raw_size_changed && !draw_size_changed) {
652 if(raw_size_changed) {
653 LOG_DP <<
"regenerating window render buffer as " <<
render;
656 if(raw_size_changed || draw_size_changed) {
657 LOG_DP <<
"updating window render buffer draw size to " <<
draw;
687 LOG_DP <<
"deferring region " << screen_region;
720 DBG_DP <<
"window::expose " << region;
756 assert(window_.invalidate_layout_blocked_);
757 window_.invalidate_layout_blocked_ =
false;
772 const bool must_be_active)
const
790 assert(fixed_width || fixed_height);
791 auto [iter, success] =
linked_size_.try_emplace(
id, fixed_width, fixed_height);
804 ERR_GUI <<
"Unknown linked group '" <<
id <<
"'; skipping";
809 if(
std::find(widgets.begin(), widgets.end(), wgt) == widgets.end()) {
810 widgets.push_back(wgt);
822 auto itor =
std::find(widgets.begin(), widgets.end(), wgt);
824 if(itor != widgets.end()) {
827 assert(
std::find(widgets.begin(), widgets.end(), wgt)
837 DBG_DP <<
"window::layout";
841 const auto conf = cast_config_to<window_definition>();
876 button* click_dismiss_button = find_widget<button>(
"click_dismiss",
false,
false);
877 if(click_dismiss_button) {
881 button* btn = find_widget<button>(
"ok",
false,
false);
884 click_dismiss_button = btn;
887 _(
"Click dismiss needs a ‘click_dismiss’ or ‘ok’ button."));
905 std::stringstream sstr;
906 sstr << __FILE__ <<
":" << __LINE__ <<
" in function '" << __func__
907 <<
"' found the following problem: Failed to size window;"
909 << maximum_width <<
',' << maximum_height <<
" screen size "
913 "which doesn’t fit on the screen."),
919 assert(click_dismiss_button);
923 *click_dismiss_button,
934 *
this, maximum_width, maximum_height);
941 std::stringstream sstr;
942 sstr << __FILE__ <<
":" << __LINE__ <<
" in function '" << __func__
943 <<
"' found the following problem: Failed to size window;"
945 << maximum_width <<
',' << maximum_height <<
" screen size "
950 "which doesn’t fit on the screen."),
961 assert(
size.x >= 0 &&
static_cast<unsigned>(
size.x) <= maximum_width
962 &&
size.y >= 0 &&
static_cast<unsigned>(
size.y) <= maximum_height);
1038 point max_size(0, 0);
1046 if(
size.x > max_size.x) {
1047 max_size.x =
size.x;
1049 if(
size.y > max_size.y) {
1050 max_size.y =
size.y;
1067 size.x = max_size.x;
1070 size.y = max_size.y;
1096 static const std::string
id =
"_window_content_grid";
1102 assert(parent_grid);
1104 if(
grid* grandparent_grid =
dynamic_cast<grid*
>(parent_grid->parent())) {
1105 grandparent_grid->swap_child(
id, std::move(
widget),
false);
1107 c->get_grid().swap_child(
id, std::move(
widget),
true);
1113 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1116 const unsigned domain)
1118 debug_layout_->generate_dot_file(
generator, domain);
1123 const unsigned maximum_width,
1124 const unsigned maximum_height)
1139 <<
" maximum size : " << maximum_width <<
','
1140 << maximum_height <<
".";
1141 if(
size.x <=
static_cast<int>(maximum_width)
1142 &&
size.y <=
static_cast<int>(maximum_height)) {
1148 if(
size.x >
static_cast<int>(maximum_width)) {
1152 if(
size.x >
static_cast<int>(maximum_width)) {
1154 <<
" Wanted width " << maximum_width
1155 <<
" resulting width " <<
size.x <<
".";
1159 <<
" Status: Resize width succeeded.";
1162 if(
size.y >
static_cast<int>(maximum_height)) {
1166 if(
size.y >
static_cast<int>(maximum_height)) {
1168 <<
" Wanted height " << maximum_height
1169 <<
" resulting height " <<
size.y <<
".";
1173 <<
" Status: Resize height succeeded.";
1176 assert(
size.x <=
static_cast<int>(maximum_width)
1177 &&
size.y <=
static_cast<int>(maximum_height));
1186 <<
" Status: Width has been modified, rerun.";
1228 if(at < 0 || at >=
static_cast<int>(
tab_order.size())) {
1237 const point& new_size)
1253 const int mouse_button_mask)
1256 <<
static_cast<unsigned>(mouse_button_mask) <<
".";
1271 const SDL_Keycode key,
1272 const SDL_Keymod mod,
1278 if(tb->is_composing()) {
1279 if(handle_tab && !
tab_order.empty() && key == SDLK_TAB) {
1280 tb->interrupt_composition();
1286 if(key == SDLK_KP_ENTER || key == SDLK_RETURN) {
1287 if (mod & (KMOD_CTRL | KMOD_ALT | KMOD_GUI | KMOD_SHIFT)) {
1301 }
else if(key == SDLK_SPACE) {
1303 }
else if(handle_tab && !
tab_order.empty() && key == SDLK_TAB) {
1308 if(mod & KMOD_SHIFT) {
1327 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1328 if(key == SDLK_F12) {
1329 debug_layout_->generate_dot_file(
"manual", debug_layout_graph::MANUAL);
1385 load_resolutions<resolution>(cfg);
1396 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_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.
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.
static const unsigned HORIZONTAL_ALIGN_RIGHT
unsigned int get_rows() const
static const unsigned VERTICAL_ALIGN_BOTTOM
static const unsigned VERTICAL_ALIGN_CENTER
static const unsigned HORIZONTAL_ALIGN_CENTER
void remove_child(const unsigned row, const unsigned col)
Removes and frees a widget in a cell.
static const unsigned VERTICAL_ALIGN_TOP
unsigned int get_cols() const
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(window &window)
~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.
virtual rect screen_location() override
The current draw location of the window, on the screen.
void update_render_textures()
Ensure render textures are valid and correct.
void show_tooltip()
Shows the window as a tooltip.
int mouse_button_state_
The state of the mouse button.
void hide()
Hides the window.
void show_non_modal()
Shows the window non modal.
std::unique_ptr< event::distributor > event_distributor_
void signal_handler_sdl_video_resize(const event::ui_event event, bool &handled, const point &new_size)
std::map< std::string, linked_size, std::less<> > linked_size_
List of the widgets, whose size are linked together.
static window * window_instance(const unsigned handle)
Returns the instance of a window.
status
The status of the window.
@ 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.
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.
show_mode show_mode_
The mode in which the window is shown.
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.
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
int show(unsigned auto_close_timeout=0)
Shows the window, running an event loop until it should close.
rect awaiting_rerender_
The part of the window (if any) currently marked for rerender.
void signal_handler_request_placement(const event::ui_event event, bool &handled)
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.
void defer_region(const rect ®ion)
Defer rendering of a particular region to next frame.
const unsigned horizontal_placement_
Sets the horizontal placement.
static retval get_retval_by_id(const std::string &id)
Gets the retval for the default buttons.
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.
std::function< bool()> exit_hook_
std::vector< rect > deferred_regions_
Parts of the window (if any) with rendering deferred to next frame.
bool hidden_
Avoid drawing the window.
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)
virtual bool expose(const rect ®ion) override
Called by draw_manager when it believes a redraw is necessary.
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.
virtual void render() override
Ensure the window's internal render buffer is up-to-date.
bool init_linked_size_group(const std::string &id, const bool fixed_width, const bool fixed_height)
Initializes a linked size group.
void draw()
Draws the window.
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.
status status_
The status of the window.
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
texture render_buffer_
The internal render buffer used by render() and expose().
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.
void set_src(const rect &r)
Set the source region of the texture used for drawing operations.
point draw_size() const
The size of the texture in draw-space.
void set_draw_size(int w, int h)
Set the intended size of the texture, in draw-space.
point get_raw_size() const
The raw internal texture size.
void clear_src()
Clear the source region.
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 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
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(std::string_view 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(...).
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
SDL_Window * get_window()
int get_pixel_scale()
Get the current active pixel scale multiplier.
void toggle_fullscreen()
Toggle fullscreen mode.
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.
rect & expand_to_cover(const SDL_Rect &r)
Minimally expand this rect to fully contain another.
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.