The Battle for Wesnoth  1.19.0-dev
modal_dialog.hpp
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 #pragma once
17 
20 #include "gui/widgets/window.hpp"
21 
22 #include <functional>
23 #include <string>
24 #include <vector>
25 
26 namespace gui2::dialogs
27 {
28 /**
29  * Registers a window.
30  *
31  * This function registers a window. The registration is used to validate
32  * whether the config for the window exists when starting Wesnoth.
33  *
34  * @note Most of the time you want to call @ref REGISTER_DIALOG instead of this
35  * function. It also directly adds the code for the dialog's id function.
36  *
37  * @param id Id of the window, multiple dialogs can use
38  * the same window so the id doesn't need to be
39  * unique.
40  */
41 #define REGISTER_WINDOW(id) \
42  namespace \
43  { \
44  namespace ns_##id \
45  { \
46  struct register_helper \
47  { \
48  register_helper() \
49  { \
50  register_window(#id); \
51  } \
52  }; \
53  \
54  struct register_helper register_helper; \
55  } \
56  }
57 
58 /**
59  * Registers a window for a dialog.
60  *
61  * Call this function to register a window. In the header of the class it adds
62  * the following code:
63  *@code
64  * virtual const std::string& id() const;
65  *@endcode
66  * Then use this macro in the implementation, inside the gui2 namespace.
67  *
68  * @note When the @p id is "foo" and the type tfoo it's easier to use
69  * REGISTER_DIALOG(foo).
70  *
71  * @param type Class type of the window to register.
72  * @param id Id of the window, multiple dialogs can use
73  * the same window so the id doesn't need to be
74  * unique.
75  */
76 #define REGISTER_DIALOG2(type, id) \
77  REGISTER_WINDOW(id) \
78  const std::string& type::window_id() const \
79  { \
80  static const std::string result(#id); \
81  return result; \
82  }
83 
84 /**
85  * Wrapper for REGISTER_DIALOG2.
86  *
87  * "Calls" REGISTER_DIALOG2(window_id, window_id)
88  */
89 #define REGISTER_DIALOG(window_id) REGISTER_DIALOG2(window_id, window_id)
90 
91 /**
92  * Adds a bare-bones static `display` function to a dialog class that immediately
93  * invokes the dialogs's modal_dialog::show function. If more complex behavior
94  * is desired, the function should be defined manually.
95  *
96  * See the modal_dialog documentation (below) for more info.
97  */
98 #define DEFINE_SIMPLE_DISPLAY_WRAPPER(dialog) \
99  template<typename... T> \
100  static void display(T&&... args) \
101  { \
102  dialog(std::forward<T>(args)...).show(); \
103  }
104 
105 /**
106  * Adds a bare-bonesstatic `execute` function to a dialog class that immediately
107  * invokes and return the result of the dialogs's modal_dialog::show function.
108  * If more complex behavior is desired, the function should be defined manually.
109  *
110  * See the modal_dialog documentation (below) for more info.
111  */
112 #define DEFINE_SIMPLE_EXECUTE_WRAPPER(dialog) \
113  template<typename... T> \
114  static bool execute(T&&... args) \
115  { \
116  return dialog(std::forward<T>(args)...).show(); \
117  }
118 
119 /**
120  * Abstract base class for all modal dialogs.
121  *
122  * A dialog shows a certain window instance to the user. The subclasses of this
123  * class will hold the parameters used for a certain window, eg a server
124  * connection dialog will hold the name of the selected server as parameter that
125  * way the caller doesn't need to know about the 'contents' of the window.
126  *
127  * @par Usage
128  *
129  * Simple dialogs that are shown to query user information it is recommended to
130  * add a static member called @p execute. The parameters to the function are:
131  * - references to in + out parameters by reference
132  * - references to the in parameters
133  * - the parameters for @ref modal_dialog::show.
134  *
135  * The 'in + out parameters' are used as initial value and final value when the
136  * OK button is pressed. The 'in parameters' are just extra parameters for
137  * showing.
138  *
139  * When a function only has 'in parameters' it should return a void value and
140  * the function should be called @p display, if it has 'in + out parameters' it
141  * must return a bool value. This value indicates whether or not the OK button
142  * was pressed to close the dialog. See editor_new_map::execute for an
143  * example.
144  */
145 class modal_dialog : public window
146 {
147  /**
148  * Special helper function to get the id of the window.
149  *
150  * This is used in the unit tests, but these implementation details
151  * shouldn't be used in the normal code.
152  */
153  friend std::string get_modal_dialog_id(const modal_dialog& dialog);
154 
155 public:
156  explicit modal_dialog(const std::string& window_id);
157 
158  virtual ~modal_dialog();
159 
160  /**
161  * Shows the window.
162  *
163  * @param auto_close_time The time in ms after which the dialog will
164  * automatically close, if 0 it doesn't close.
165  * @note the timeout is a minimum time and
166  * there's no guarantee about how fast it closes
167  * after the minimum.
168  *
169  * @returns Whether the final retval_ == retval::OK
170  */
171  bool show(const unsigned auto_close_time = 0);
172 
173  /***** ***** ***** setters / getters for members ***** ****** *****/
174 
175  // TODO: this is now completely redundant, as a modal dialog is a window
176  /** Returns a pointer to the dialog's window. Will be null if it hasn't been built yet. */
178  {
179  return this;
180  }
181  const window* get_window() const
182  {
183  return this;
184  }
185 
186  /** Returns the cached window exit code. */
187  int get_retval() const
188  {
189  return retval_;
190  }
191 
192  void set_always_save_fields(const bool always_save_fields)
193  {
194  always_save_fields_ = always_save_fields;
195  }
196 
197  void set_allow_plugin_skip(const bool allow_plugin_skip)
198  {
199  allow_plugin_skip_ = allow_plugin_skip;
200  }
201 
202  void set_show_even_without_video(const bool show_even_without_video)
203  {
204  show_even_without_video_ = show_even_without_video;
205  }
206 
207 protected:
208  /**
209  * Creates a new field of given type with given arguments.
210  *
211  * The field created is owned by modal_dialog, the returned pointer can be used
212  * in the child classes as access to a field.
213  *
214  * @param args Arguments to forward to the field constructor.
215  */
216  template<typename T, typename... Args>
217  T* register_field(Args&&... args);
218 
219  /**
220  * Creates a new boolean field.
221  *
222  * The field created is owned by modal_dialog, the returned pointer can be used
223  * in the child classes as access to a field.
224  *
225  * @param id Id of the widget, same value as in WML.
226  * @param mandatory Is the widget mandatory or mandatory.
227  * @param callback_load_value The callback function to set the initial value
228  * of the widget.
229  * @param callback_save_value The callback function to write the resulting
230  * value of the widget. Saving will only happen
231  * if the widget is enabled and the window closed
232  * with ok.
233  * @param callback_change When the value of the widget changes this
234  * callback is called.
235  * @param initial_fire
236  *
237  * @returns Pointer to the created widget.
238  */
239  field_bool*
240  register_bool(const std::string& id,
241  const bool mandatory,
242  const std::function<bool()> callback_load_value = nullptr,
243  const std::function<void(bool)> callback_save_value = nullptr,
244  const std::function<void(widget&)> callback_change = nullptr,
245  const bool initial_fire = false);
246 
247  /**
248  * Creates a new boolean field.
249  *
250  * The field created is owned by modal_dialog, the returned pointer can be used
251  * in the child classes as access to a field.
252  *
253  * @param id Id of the widget, same value as in WML.
254  * @param mandatory Is the widget mandatory or mandatory.
255  * @param linked_variable The variable the widget is linked to. See
256  * @ref field::field for more information.
257  * @param callback_change When the value of the widget changes this
258  * callback is called.
259  * @param initial_fire
260  *
261  * @returns Pointer to the created widget.
262  */
263  field_bool*
264  register_bool(const std::string& id,
265  const bool mandatory,
266  bool& linked_variable,
267  const std::function<void(widget&)> callback_change = nullptr,
268  const bool initial_fire = false);
269 
270  /**
271  * Creates a new integer field.
272  *
273  * See @ref register_bool for more info.
274  */
276  register_integer(const std::string& id,
277  const bool mandatory,
278  const std::function<int()> callback_load_value = nullptr,
279  const std::function<void(int)> callback_save_value = nullptr);
280 
281  /**
282  * Creates a new integer field.
283  *
284  * See @ref register_bool for more info.
285  */
286  field_integer* register_integer(const std::string& id,
287  const bool mandatory,
288  int& linked_variable);
289  /**
290  * Creates a new text field.
291  *
292  * See @ref register_bool for more info.
293  */
295  const std::string& id,
296  const bool mandatory,
297  const std::function<std::string()> callback_load_value = nullptr,
298  const std::function<void(const std::string&)> callback_save_value = nullptr,
299  const bool capture_focus = false);
300 
301  /**
302  * Creates a new text field.
303  *
304  * See @ref register_bool for more info.
305  */
306  field_text* register_text(const std::string& id,
307  const bool mandatory,
308  std::string& linked_variable,
309  const bool capture_focus = false);
310 
311  /**
312  * Registers a new styled_widget as a label.
313  *
314  * The label is used for a styled_widget to set the 'label' since it calls the
315  * @ref styled_widget::set_label it can also be used for the @ref image since
316  * there this sets the filename. (The @p use_markup makes no sense in an
317  * image but that's a detail.)
318  *
319  * @note In general it's preferred a widget sets its markup flag in WML, but
320  * some generic windows (like messages) may need different versions
321  * depending on where used.
322  *
323  * @param id Id of the widget, same value as in WML.
324  * @param mandatory Is the widget mandatory or optional.
325  * @param text The text for the label.
326  * @param use_markup Whether or not use markup for the label.
327  */
328  field_label* register_label(const std::string& id,
329  const bool mandatory,
330  const std::string& text,
331  const bool use_markup = false);
332 
333  /** Registers a new styled_widget as image. */
334  field_label* register_image(const std::string& id,
335  const bool mandatory,
336  const std::string& filename)
337  {
338  return register_label(id, mandatory, filename);
339  }
340 
341 private:
342  /**
343  * The window's exit code (return value).
344  *
345  * We keep a copy here so it may be accessed even after the dialog is closed and
346  * the window object is destroyed.
347  *
348  * This value is initially set to 0 (retval::NONE) meaning the dialog was not
349  * shown. After @ref show returns, it will hold the most recent retval of the
350  * window object, including any modifications made in @ref post_show.
351  */
352  int retval_;
353 
354  /**
355  * Always save the fields upon closing.
356  *
357  * Normally fields are only saved when the retval::OK button is pressed.
358  * With this flag set is always saves. Be careful with the flag since it
359  * also updates upon canceling, which can be a problem when the field sets
360  * a preference.
361  */
363 
364  /**
365  * Contains the automatically managed fields.
366  *
367  * Since the fields are automatically managed and there are no search
368  * functions defined we don't offer access to the vector. If access is
369  * needed the creator should store a copy of the pointer.
370  */
371  std::vector<std::unique_ptr<class field_base>> fields_;
372 
373  /**
374  * Contains the widget that should get the focus when the window is shown.
375  */
376  std::string focus_;
377 
378  /**
379  * Allow plugins to skip through the dialog?
380  * Most dialogs install a plugins context to allow plugins to accept whatever the dialog is offering
381  * and continue. Some dialogs, especially those that install their own plugins context, may want to
382  * disable this.
383  */
385 
386  /**
387  * Show the dialog even with --nogui?
388  * Some dialogs need to be shown even when --nogui is specified if the game is being driven by a plugin.
389  * Those dialogs allow the plugin to styled_widget them by creating a plugin context in pre_show().
390  */
392 
393  /** The ID of the window to build. Usually set by REGISTER_DIALOG. */
394  virtual const std::string& window_id() const = 0;
395 
396  /**
397  * Actions to be taken before showing the window.
398  *
399  * At this point the registered fields are registered and initialized with
400  * their initial values.
401  *
402  * @param window The window to be shown.
403  */
404  virtual void pre_show(window& window);
405  // TODO: this window parameter is now redundant
406 
407  /**
408  * Actions to be taken after the window has been shown.
409  *
410  * At this point the registered fields already stored their values (if the
411  * OK has been pressed).
412  *
413  * @param window The window which has been shown.
414  */
415  virtual void post_show(window& window);
416  // TODO: this window parameter is now redundant
417 
418  /**
419  * Initializes all fields in the dialog and set the keyboard focus.
420  *
421  * @param window The window which has been shown.
422  */
423  virtual void init_fields();
424  // TODO: Nothing else uses this. Why is it virtual?
425 
426  /**
427  * When the dialog is closed with the OK status saves all fields.
428  *
429  * Saving only happens if a callback handler is installed.
430  *
431  * @param save_fields Does the value in the fields need to be saved?
432  */
433  virtual void finalize_fields(const bool save_fields);
434  // TODO: Nothing else uses this. Why is it virtual?
435 };
436 
437 } // namespace dialogs
Abstract base class for all modal dialogs.
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.
void set_show_even_without_video(const bool show_even_without_video)
virtual void pre_show(window &window)
Actions to be taken before showing the window.
void set_allow_plugin_skip(const bool allow_plugin_skip)
field_label * register_image(const std::string &id, const bool mandatory, const std::string &filename)
Registers a new styled_widget as image.
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)
friend std::string get_modal_dialog_id(const modal_dialog &dialog)
Special helper function to get the id of the window.
Definition: test_gui2.cpp:176
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.
const window * get_window() const
bool allow_plugin_skip_
Allow plugins to skip through the dialog? Most dialogs install a plugins context to allow plugins to ...
void set_always_save_fields(const bool always_save_fields)
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 get_retval() const
Returns the cached window exit code.
int retval_
The window's exit code (return value).
window * get_window()
Returns a pointer to the dialog's window.
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
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:63
dialogs::modal_dialog * dialog()
Inherited from widget.
Definition: window.hpp:272
Contains all forward declarations for field.hpp.
This file contains the window object, this object is a top level container which has the event manage...