The Battle for Wesnoth  1.19.0-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 {
47 }
48 
50 {
51 }
52 
53 namespace {
54  struct window_stack_handler {
55  window_stack_handler(window* win) : local_window(win) {
57  }
58  ~window_stack_handler() {
60  }
61  window* local_window;
62  };
63 }
64 
65 bool modal_dialog::show(const unsigned auto_close_time)
66 {
68  DBG_DP << "modal_dialog::show denied";
69  if(!allow_plugin_skip_) {
70  return false;
71  }
72 
74  if (pm && pm->any_running())
75  {
76  plugins_context pc("Dialog");
77  pc.set_callback("skip_dialog", [this](config) { retval_ = retval::OK; }, false);
78  pc.set_callback("quit", [](config) {}, false);
79  pc.play_slice();
80  }
81 
82  return false;
83  }
84 
85  init_fields();
86 
87  pre_show(*this);
88 
89  { // Scope the window stack
91  window_stack_handler push_window_stack(this);
92  retval_ = window::show(auto_close_time);
93  }
94 
95  /*
96  * It can happen that when two clicks follow each other fast that the event
97  * handling code in events.cpp generates a DOUBLE_CLICK_EVENT. For some
98  * reason it can happen that this event gets pushed in the queue when the
99  * window is shown, but processed after the window is closed. This causes
100  * the next window to get this pending event.
101  *
102  * This caused a bug where double clicking in the campaign selection dialog
103  * directly selected a difficulty level and started the campaign. In order
104  * to avoid that problem, filter all pending DOUBLE_CLICK_EVENT events after
105  * the window is closed.
106  */
107  SDL_FlushEvent(DOUBLE_CLICK_EVENT);
108 
110 
111  post_show(*this);
112 
113  // post_show may have updated the window retval. Update it here.
115 
116  return retval_ == retval::OK;
117 }
118 
119 template<typename T, typename... Args>
121 {
122  static_assert(std::is_base_of_v<field_base, T>, "Type is not a field type");
123  auto field = std::make_unique<T>(std::forward<Args>(args)...);
124  T* res = field.get();
125  fields_.push_back(std::move(field));
126  return res;
127 }
128 
130  const std::string& id,
131  const bool mandatory,
132  const std::function<bool()> callback_load_value,
133  const std::function<void(bool)> callback_save_value,
134  const std::function<void(widget&)> callback_change,
135  const bool initial_fire)
136 {
137  field_bool* field = new field_bool(id,
138  mandatory,
139  callback_load_value,
140  callback_save_value,
141  callback_change,
142  initial_fire);
143 
144  fields_.emplace_back(field);
145  return field;
146 }
147 
148 field_bool*
149 modal_dialog::register_bool(const std::string& id,
150  const bool mandatory,
151  bool& linked_variable,
152  const std::function<void(widget&)> callback_change,
153  const bool initial_fire)
154 {
156  = new field_bool(id, mandatory, linked_variable, callback_change, initial_fire);
157 
158  fields_.emplace_back(field);
159  return field;
160 }
161 
163  const std::string& id,
164  const bool mandatory,
165  const std::function<int()> callback_load_value,
166  const std::function<void(const int)> callback_save_value)
167 {
169  id, mandatory, callback_load_value, callback_save_value);
170 
171  fields_.emplace_back(field);
172  return field;
173 }
174 
176  const bool mandatory,
177  int& linked_variable)
178 {
179  field_integer* field = new field_integer(id, mandatory, linked_variable);
180 
181  fields_.emplace_back(field);
182  return field;
183 }
184 
186  const std::string& id,
187  const bool mandatory,
188  const std::function<std::string()> callback_load_value,
189  const std::function<void(const std::string&)> callback_save_value,
190  const bool capture_focus)
191 {
192  field_text* field = new field_text(
193  id, mandatory, callback_load_value, callback_save_value);
194 
195  if(capture_focus) {
196  focus_ = id;
197  }
198 
199  fields_.emplace_back(field);
200  return field;
201 }
202 
204  const bool mandatory,
205  std::string& linked_variable,
206  const bool capture_focus)
207 {
208  field_text* field = new field_text(id, mandatory, linked_variable);
209 
210  if(capture_focus) {
211  focus_ = id;
212  }
213 
214  fields_.emplace_back(field);
215  return field;
216 }
217 
219  const bool mandatory,
220  const std::string& text,
221  const bool use_markup)
222 {
223  field_label* field = new field_label(id, mandatory, text, use_markup);
224 
225  fields_.emplace_back(field);
226  return field;
227 }
228 
230 {
231  /* DO NOTHING */
232 }
233 
235 {
236  /* DO NOTHING */
237 }
238 
240 {
241  for(auto& field : fields_)
242  {
243  field->attach_to_window(*this);
244  field->widget_init();
245  }
246 
247  if(!focus_.empty()) {
248  if(widget* widget = window::find(focus_, false)) {
250  }
251  }
252 }
253 
254 void modal_dialog::finalize_fields(const bool save_fields)
255 {
256  for(auto& field : fields_)
257  {
258  if(save_fields) {
260  }
262  }
263 }
264 
265 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
bool always_save_fields_
Always save the fields upon closing.
virtual void init_fields()
Initializes all fields in the dialog and set the keyboard focus.
virtual void post_show(window &window)
Actions to be taken after the window has been shown.
virtual void pre_show(window &window)
Actions to be taken before showing the window.
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)
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.
bool allow_plugin_skip_
Allow plugins to skip through the dialog? Most dialogs install a plugins context to allow plugins to ...
T * register_field(Args &&... args)
Creates a new field of given type with given arguments.
virtual 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 ...
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.
bool show(const unsigned auto_close_time=0)
Shows the window.
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.
int retval_
The window's exit code (return value).
void widget_finalize()
Finalizes the widget.
Definition: field.hpp:114
void widget_init()
Initializes the widget.
Definition: field.hpp:97
void detach_from_window()
Detaches the field from a window.
Definition: field.hpp:125
void attach_to_window(window &window)
Attaches the field to a window.
Definition: field.hpp:75
Specialized field class for boolean.
Definition: field.hpp:489
Specialized field class for a styled_widget, used for labels and images.
Definition: field.hpp:569
Specialized field class for text.
Definition: field.hpp:537
Template class to implement the generic field implementation.
Definition: field.hpp:246
Base class for all widgets.
Definition: widget.hpp:53
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:63
void keyboard_capture(widget *widget)
Definition: window.cpp:1215
void finish_build(const builder_window::window_resolution &)
Definition: window.cpp:417
int show(unsigned auto_close_timeout=0)
Shows the window, running an event loop until it should close.
Definition: window.cpp:510
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: window.cpp:784
int get_retval()
Definition: window.hpp:406
void play_slice()
Definition: context.cpp:96
void set_callback(const std::string &name, callback_function)
Definition: context.cpp:51
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:1075
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:1073
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:141