The Battle for Wesnoth  1.19.8+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  , always_save_fields_(false)
39  , fields_()
40  , focus_()
41  , allow_plugin_skip_(true)
42  , show_even_without_video_(false)
43 {
45 }
46 
48 {
49 }
50 
51 bool modal_dialog::show(const unsigned auto_close_time)
52 {
54  DBG_DP << "modal_dialog::show denied";
55  return false;
56  }
57  if(allow_plugin_skip_) {
58  bool skipped = false;
59 
61  if (pm && pm->any_running())
62  {
63  plugins_context pc("Dialog");
64  pc.set_callback("skip_dialog", [this, &skipped](const config&) { set_retval(retval::OK); skipped = true; }, false);
65  pc.set_callback("quit", [this, &skipped](const config&) { set_retval(retval::CANCEL); skipped = true; }, false);
66  pc.set_callback("select", [this, &skipped](const config& c) { set_retval(c["retval"].to_int()); skipped = true; }, false);
67  pc.set_accessor_string("id", [this](const config&) { return window_id(); });
68  pc.play_slice();
69  }
70 
71  if(skipped) {
72  return false;
73  }
74  }
75 
76  init_fields();
77 
78  pre_show();
79 
80  {
82  window::show(auto_close_time);
83  }
84 
85  /*
86  * It can happen that when two clicks follow each other fast that the event
87  * handling code in events.cpp generates a DOUBLE_CLICK_EVENT. For some
88  * reason it can happen that this event gets pushed in the queue when the
89  * window is shown, but processed after the window is closed. This causes
90  * the next window to get this pending event.
91  *
92  * This caused a bug where double clicking in the campaign selection dialog
93  * directly selected a difficulty level and started the campaign. In order
94  * to avoid that problem, filter all pending DOUBLE_CLICK_EVENT events after
95  * the window is closed.
96  */
97  SDL_FlushEvent(DOUBLE_CLICK_EVENT);
98 
100 
101  post_show();
102 
103  // post_show may update the window retval
104  return get_retval() == retval::OK;
105 }
106 
107 template<typename T, typename... Args>
109 {
110  static_assert(std::is_base_of_v<field_base, T>, "Type is not a field type");
111  auto field = std::make_unique<T>(std::forward<Args>(args)...);
112  T* res = field.get();
113  fields_.push_back(std::move(field));
114  return res;
115 }
116 
118  const std::string& id,
119  const bool mandatory,
120  const std::function<bool()>& callback_load_value,
121  const std::function<void(bool)>& callback_save_value,
122  const std::function<void(widget&)>& callback_change,
123  const bool initial_fire)
124 {
125  field_bool* field = new field_bool(id,
126  mandatory,
127  callback_load_value,
128  callback_save_value,
129  callback_change,
130  initial_fire);
131 
132  fields_.emplace_back(field);
133  return field;
134 }
135 
136 field_bool*
137 modal_dialog::register_bool(const std::string& id,
138  const bool mandatory,
139  bool& linked_variable,
140  const std::function<void(widget&)>& callback_change,
141  const bool initial_fire)
142 {
144  = new field_bool(id, mandatory, linked_variable, callback_change, initial_fire);
145 
146  fields_.emplace_back(field);
147  return field;
148 }
149 
151  const std::string& id,
152  const bool mandatory,
153  const std::function<int()>& callback_load_value,
154  const std::function<void(int)>& callback_save_value)
155 {
157  id, mandatory, callback_load_value, callback_save_value);
158 
159  fields_.emplace_back(field);
160  return field;
161 }
162 
164  const bool mandatory,
165  int& linked_variable)
166 {
167  field_integer* field = new field_integer(id, mandatory, linked_variable);
168 
169  fields_.emplace_back(field);
170  return field;
171 }
172 
174  const std::string& id,
175  const bool mandatory,
176  const std::function<std::string()>& callback_load_value,
177  const std::function<void(const std::string&)>& callback_save_value,
178  const bool capture_focus)
179 {
180  field_text* field = new field_text(
181  id, mandatory, callback_load_value, callback_save_value);
182 
183  if(capture_focus) {
184  focus_ = id;
185  }
186 
187  fields_.emplace_back(field);
188  return field;
189 }
190 
192  const bool mandatory,
193  std::string& linked_variable,
194  const bool capture_focus)
195 {
196  field_text* field = new field_text(id, mandatory, linked_variable);
197 
198  if(capture_focus) {
199  focus_ = id;
200  }
201 
202  fields_.emplace_back(field);
203  return field;
204 }
205 
207  const bool mandatory,
208  const std::string& text,
209  const bool use_markup)
210 {
211  field_label* field = new field_label(id, mandatory, text, use_markup);
212 
213  fields_.emplace_back(field);
214  return field;
215 }
216 
218 {
219  /* DO NOTHING */
220 }
221 
223 {
224  /* DO NOTHING */
225 }
226 
228 {
229  for(auto& field : fields_)
230  {
231  field->attach_to_window(*this);
232  field->widget_init();
233  }
234 
235  if(!focus_.empty()) {
236  if(widget* widget = window::find(focus_, false)) {
238  }
239  }
240 }
241 
242 void modal_dialog::finalize_fields(const bool save_fields)
243 {
244  for(auto& field : fields_)
245  {
246  if(save_fields) {
248  }
250  }
251 }
252 
253 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
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.
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 set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:395
void keyboard_capture(widget *widget)
Definition: window.cpp:1193
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
Definition: window.cpp:769
int show(unsigned auto_close_timeout=0)
Shows the window, running an event loop until it should close.
Definition: window.cpp:489
int get_retval()
Definition: window.hpp:402
void play_slice()
Definition: context.cpp:103
void set_callback(const std::string &name, callback_function)
Definition: context.cpp:53
void set_accessor_string(const std::string &name, const std::function< std::string(config)> &)
Definition: context.cpp:75
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.
static lg::log_domain log_display("display")
#define DBG_DP
@ NORMAL
Definition: cursor.hpp:28
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.
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
@ CANCEL
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
bool headless()
The game is running headless.
Definition: video.cpp:139
mock_char c