The Battle for Wesnoth  1.19.7+dev
modal_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "cursor.hpp"
21 #include "events.hpp"
22 #include "gui/auxiliary/field.hpp"
23 #include "gui/core/gui_definition.hpp" // get_window_builder
27 #include "video.hpp"
28 
29 static lg::log_domain log_display("display");
30 #define DBG_DP LOG_STREAM(debug, log_display)
31 #define WRN_DP LOG_STREAM(warn, log_display)
32 
33 namespace gui2::dialogs
34 {
35 
36 modal_dialog::modal_dialog(const std::string& window_id)
37  : window(get_window_builder(window_id))
38  , retval_(retval::NONE)
39  , always_save_fields_(false)
40  , fields_()
41  , focus_()
42  , allow_plugin_skip_(true)
43  , show_even_without_video_(false)
44 {
46 }
47 
49 {
50 }
51 
52 namespace {
53  struct window_stack_handler {
54  window_stack_handler(window* win) : local_window(win) {
56  }
57  ~window_stack_handler() {
59  }
60  window* local_window;
61  };
62 }
63 
64 bool modal_dialog::show(const unsigned auto_close_time)
65 {
67  DBG_DP << "modal_dialog::show denied";
68  if(!allow_plugin_skip_) {
69  return false;
70  }
71 
73  if (pm && pm->any_running())
74  {
75  plugins_context pc("Dialog");
76  pc.set_callback("skip_dialog", [this](const config&) { retval_ = retval::OK; }, false);
77  pc.set_callback("quit", [](const config&) {}, false);
78  pc.play_slice();
79  }
80 
81  return false;
82  }
83 
84  init_fields();
85 
86  pre_show();
87 
88  { // Scope the window stack
90  window_stack_handler push_window_stack(this);
91  retval_ = window::show(auto_close_time);
92  }
93 
94  /*
95  * It can happen that when two clicks follow each other fast that the event
96  * handling code in events.cpp generates a DOUBLE_CLICK_EVENT. For some
97  * reason it can happen that this event gets pushed in the queue when the
98  * window is shown, but processed after the window is closed. This causes
99  * the next window to get this pending event.
100  *
101  * This caused a bug where double clicking in the campaign selection dialog
102  * directly selected a difficulty level and started the campaign. In order
103  * to avoid that problem, filter all pending DOUBLE_CLICK_EVENT events after
104  * the window is closed.
105  */
106  SDL_FlushEvent(DOUBLE_CLICK_EVENT);
107 
109 
110  post_show();
111 
112  // post_show may have updated the window retval. Update it here.
114 
115  return retval_ == retval::OK;
116 }
117 
118 template<typename T, typename... Args>
120 {
121  static_assert(std::is_base_of_v<field_base, T>, "Type is not a field type");
122  auto field = std::make_unique<T>(std::forward<Args>(args)...);
123  T* res = field.get();
124  fields_.push_back(std::move(field));
125  return res;
126 }
127 
129  const std::string& id,
130  const bool mandatory,
131  const std::function<bool()>& callback_load_value,
132  const std::function<void(bool)>& callback_save_value,
133  const std::function<void(widget&)>& callback_change,
134  const bool initial_fire)
135 {
136  field_bool* field = new field_bool(id,
137  mandatory,
138  callback_load_value,
139  callback_save_value,
140  callback_change,
141  initial_fire);
142 
143  fields_.emplace_back(field);
144  return field;
145 }
146 
147 field_bool*
148 modal_dialog::register_bool(const std::string& id,
149  const bool mandatory,
150  bool& linked_variable,
151  const std::function<void(widget&)>& callback_change,
152  const bool initial_fire)
153 {
155  = new field_bool(id, mandatory, linked_variable, callback_change, initial_fire);
156 
157  fields_.emplace_back(field);
158  return field;
159 }
160 
162  const std::string& id,
163  const bool mandatory,
164  const std::function<int()>& callback_load_value,
165  const std::function<void(int)>& callback_save_value)
166 {
168  id, mandatory, callback_load_value, callback_save_value);
169 
170  fields_.emplace_back(field);
171  return field;
172 }
173 
175  const bool mandatory,
176  int& linked_variable)
177 {
178  field_integer* field = new field_integer(id, mandatory, linked_variable);
179 
180  fields_.emplace_back(field);
181  return field;
182 }
183 
185  const std::string& id,
186  const bool mandatory,
187  const std::function<std::string()>& callback_load_value,
188  const std::function<void(const std::string&)>& callback_save_value,
189  const bool capture_focus)
190 {
191  field_text* field = new field_text(
192  id, mandatory, callback_load_value, callback_save_value);
193 
194  if(capture_focus) {
195  focus_ = id;
196  }
197 
198  fields_.emplace_back(field);
199  return field;
200 }
201 
203  const bool mandatory,
204  std::string& linked_variable,
205  const bool capture_focus)
206 {
207  field_text* field = new field_text(id, mandatory, linked_variable);
208 
209  if(capture_focus) {
210  focus_ = id;
211  }
212 
213  fields_.emplace_back(field);
214  return field;
215 }
216 
218  const bool mandatory,
219  const std::string& text,
220  const bool use_markup)
221 {
222  field_label* field = new field_label(id, mandatory, text, use_markup);
223 
224  fields_.emplace_back(field);
225  return field;
226 }
227 
229 {
230  /* DO NOTHING */
231 }
232 
234 {
235  /* DO NOTHING */
236 }
237 
239 {
240  for(auto& field : fields_)
241  {
242  field->attach_to_window(*this);
243  field->widget_init();
244  }
245 
246  if(!focus_.empty()) {
247  if(widget* widget = window::find(focus_, false)) {
249  }
250  }
251 }
252 
253 void modal_dialog::finalize_fields(const bool save_fields)
254 {
255  for(auto& field : fields_)
256  {
257  if(save_fields) {
259  }
261  }
262 }
263 
264 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
bool always_save_fields_
Always save the fields upon closing.
void init_fields()
Initializes all fields in the dialog and set the keyboard focus.
field_bool * register_bool(const std::string &id, const bool mandatory, const std::function< bool()> &callback_load_value=nullptr, const std::function< void(bool)> &callback_save_value=nullptr, const std::function< void(widget &)> &callback_change=nullptr, const bool initial_fire=false)
Creates a new boolean field.
virtual void post_show()
Actions to be taken after the window has been shown.
field_text * register_text(const std::string &id, const bool mandatory, const std::function< std::string()> &callback_load_value=nullptr, const std::function< void(const std::string &)> &callback_save_value=nullptr, const bool capture_focus=false)
Creates a new text field.
std::string focus_
Contains the widget that should get the focus when the window is shown.
std::vector< std::unique_ptr< class field_base > > fields_
Contains the automatically managed fields.
virtual const std::string & window_id() const =0
The ID of the window to build.
field_label * register_label(const std::string &id, const bool mandatory, const std::string &text, const bool use_markup=false)
Registers a new styled_widget as a label.
modal_dialog(const std::string &window_id)
virtual void pre_show()
Actions to be taken before showing the window.
bool allow_plugin_skip_
Allow plugins to skip through the dialog? Most dialogs install a plugins context to allow plugins to ...
field_integer * register_integer(const std::string &id, const bool mandatory, const std::function< int()> &callback_load_value=nullptr, const std::function< void(int)> &callback_save_value=nullptr)
Creates a new integer field.
T * register_field(Args &&... args)
Creates a new field of given type with given arguments.
void finalize_fields(const bool save_fields)
When the dialog is closed with the OK status saves all fields.
bool show_even_without_video_
Show the dialog even with –nogui? Some dialogs need to be shown even when –nogui is specified if the ...
bool show(const unsigned auto_close_time=0)
Shows the window.
int retval_
The window's exit code (return value).
void widget_finalize()
Finalizes the widget.
Definition: field.hpp:113
void widget_init()
Initializes the widget.
Definition: field.hpp:96
void detach_from_window()
Detaches the field from a window.
Definition: field.hpp:124
void attach_to_window(window &window)
Attaches the field to a window.
Definition: field.hpp:74
Specialized field class for boolean.
Definition: field.hpp:488
Specialized field class for a styled_widget, used for labels and images.
Definition: field.hpp:568
Specialized field class for text.
Definition: field.hpp:536
Template class to implement the generic field implementation.
Definition: field.hpp:245
Base class for all widgets.
Definition: widget.hpp:55
void set_id(const std::string &id)
Definition: widget.cpp:98
const std::string & id() const
Definition: widget.cpp:110
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
Definition: window.cpp:778
int show(unsigned auto_close_timeout=0)
Shows the window, running an event loop until it should close.
Definition: window.cpp:498
int get_retval()
Definition: window.hpp:401
void play_slice()
Definition: context.cpp:97
void set_callback(const std::string &name, callback_function)
Definition: context.cpp:52
static plugins_manager * get()
Definition: manager.cpp:58
bool any_running()
Definition: manager.cpp:206
#define DOUBLE_CLICK_EVENT
Definition: events.hpp:24
Implements some helper classes to ease adding fields to a dialog and hide the synchronization needed.
window * local_window
static lg::log_domain log_display("display")
#define DBG_DP
@ NORMAL
Definition: cursor.hpp:28
void remove_from_window_stack(window *window)
Removes a entry from the open_window_stack list.
Definition: handler.cpp:1074
field< int, integer_selector > field_integer
Definition: field-fwd.hpp:37
const builder_window::window_resolution & get_window_builder(const std::string &type)
Returns an reference to the requested builder.
std::vector< window * > open_window_stack
Keeps track of any open windows of any type (modal, non-modal, or tooltip) in the order in which they...
Definition: handler.cpp:1072
retval
Default window/dialog return values.
Definition: retval.hpp:30
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
@ NONE
Default, unset return value.
Definition: retval.hpp:32
bool headless()
The game is running headless.
Definition: video.cpp:139