1 /* 2 Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl> 3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/ 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY. 11 12 See the COPYING file for more details. 13 */ 14 15 #pragma once 16 17 #include "gui/auxiliary/field-fwd.hpp" 18 #include "gui/core/static_registry.hpp" 19 #include "utils/functional.hpp" 20 21 #include <string> 22 #include <vector> 23 24 namespace gui2 25 { 26 namespace 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 * // Inherited from modal_dialog, implemented by REGISTER_DIALOG. 65 * virtual const std::string& id() const; 66 *@endcode 67 * Then use this macro in the implementation, inside the gui2 namespace. 68 * 69 * @note When the @p id is "foo" and the type tfoo it's easier to use 70 * REGISTER_DIALOG(foo). 71 * 72 * @param type Class type of the window to register. 73 * @param id Id of the window, multiple dialogs can use 74 * the same window so the id doesn't need to be 75 * unique. 76 */ 77 #define REGISTER_DIALOG2(type, id) \ 78 REGISTER_WINDOW(id) 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 @ref modal_dialog::show function. If more complex behavior 94 * is desired, the function should be defined manually. 95 * 96 * See the @ref 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 @ref modal_dialog::show function. 108 * If more complex behavior is desired, the function should be defined manually. 109 * 110 * See the @ref 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 @ref editor_new_map::execute for an 143 * example. 144 */ 145 class modal_dialog 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 unit_test_mark_as_tested(const modal_dialog& dialog); 154 155 public: 156 modal_dialog(); 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 174 /***** ***** ***** setters / getters for members ***** ****** *****/ 175 176 /** Returns a pointer to the dialog's window. Will be null if it hasn't been built yet. */ get_window() const177 window* get_window() const 178 { 179 return window_.get(); 180 } 181 get_retval() const182 int get_retval() const 183 { 184 return retval_; 185 } 186 set_always_save_fields(const bool always_save_fields)187 void set_always_save_fields(const bool always_save_fields) 188 { 189 always_save_fields_ = always_save_fields; 190 } 191 set_restore(const bool restore)192 void set_restore(const bool restore) 193 { 194 restore_ = restore; 195 } 196 set_allow_plugin_skip(const bool allow_plugin_skip)197 void set_allow_plugin_skip(const bool allow_plugin_skip) 198 { 199 allow_plugin_skip_ = allow_plugin_skip; 200 } 201 set_show_even_without_video(const bool show_even_without_video)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 boolean field. 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 id Id of the widget, same value as in WML. 215 * @param mandatory Is the widget mandatory or mandatory. 216 * @param callback_load_value The callback function to set the initial value 217 * of the widget. 218 * @param callback_save_value The callback function to write the resulting 219 * value of the widget. Saving will only happen 220 * if the widget is enabled and the window closed 221 * with ok. 222 * @param callback_change When the value of the widget changes this 223 * callback is called. 224 * 225 * @returns Pointer to the created widget. 226 */ 227 field_bool* 228 register_bool(const std::string& id, 229 const bool mandatory, 230 const std::function<bool()> callback_load_value = nullptr, 231 const std::function<void(bool)> callback_save_value = nullptr, 232 const std::function<void(widget&)> callback_change = nullptr, 233 const bool initial_fire = false); 234 235 /** 236 * Creates a new boolean field. 237 * 238 * The field created is owned by modal_dialog, the returned pointer can be used 239 * in the child classes as access to a field. 240 * 241 * @param id Id of the widget, same value as in WML. 242 * @param mandatory Is the widget mandatory or mandatory. 243 * @param linked_variable The variable the widget is linked to. See 244 * @ref field::field for more information. 245 * @param callback_change When the value of the widget changes this 246 * callback is called. 247 * 248 * @returns Pointer to the created widget. 249 */ 250 field_bool* 251 register_bool(const std::string& id, 252 const bool mandatory, 253 bool& linked_variable, 254 const std::function<void(widget&)> callback_change = nullptr, 255 const bool initial_fire = false); 256 257 /** 258 * Creates a new integer field. 259 * 260 * See @ref register_bool for more info. 261 */ 262 field_integer* 263 register_integer(const std::string& id, 264 const bool mandatory, 265 const std::function<int()> callback_load_value = nullptr, 266 const std::function<void(int)> callback_save_value = nullptr); 267 268 /** 269 * Creates a new integer field. 270 * 271 * See @ref register_bool for more info. 272 */ 273 field_integer* register_integer(const std::string& id, 274 const bool mandatory, 275 int& linked_variable); 276 /** 277 * Creates a new text field. 278 * 279 * See @ref register_bool for more info. 280 */ 281 field_text* register_text( 282 const std::string& id, 283 const bool mandatory, 284 const std::function<std::string()> callback_load_value = nullptr, 285 const std::function<void(const std::string&)> callback_save_value = nullptr, 286 const bool capture_focus = false); 287 288 /** 289 * Creates a new text field. 290 * 291 * See @ref register_bool for more info. 292 */ 293 field_text* register_text(const std::string& id, 294 const bool mandatory, 295 std::string& linked_variable, 296 const bool capture_focus = false); 297 298 /** 299 * Registers a new styled_widget as a label. 300 * 301 * The label is used for a styled_widget to set the 'label' since it calls the 302 * @ref styled_widget::set_label it can also be used for the @ref image since 303 * there this sets the filename. (The @p use_markup makes no sense in an 304 * image but that's a detail.) 305 * 306 * @note In general it's preferred a widget sets its markup flag in WML, but 307 * some generic windows (like messages) may need different versions 308 * depending on where used. 309 * 310 * @param id Id of the widget, same value as in WML. 311 * @param mandatory Is the widget mandatory or optional. 312 * @param text The text for the label. 313 * @param use_markup Whether or not use markup for the label. 314 */ 315 field_label* register_label(const std::string& id, 316 const bool mandatory, 317 const std::string& text, 318 const bool use_markup = false); 319 320 /** Registers a new styled_widget as image. */ register_image(const std::string & id,const bool mandatory,const std::string & filename)321 field_label* register_image(const std::string& id, 322 const bool mandatory, 323 const std::string& filename) 324 { 325 return register_label(id, mandatory, filename); 326 } 327 328 protected: 329 /** The window object build for this dialog. */ 330 std::unique_ptr<window> window_; 331 332 private: 333 /** Returns the window exit status, 0 means not shown. */ 334 int retval_; 335 336 /** 337 * Always save the fields upon closing. 338 * 339 * Normally fields are only saved when the retval::OK button is pressed. 340 * With this flag set is always saves. Be careful with the flag since it 341 * also updates upon canceling, which can be a problem when the field sets 342 * a preference. 343 */ 344 bool always_save_fields_; 345 346 /** 347 * Contains the automatically managed fields. 348 * 349 * Since the fields are automatically managed and there are no search 350 * functions defined we don't offer access to the vector. If access is 351 * needed the creator should store a copy of the pointer. 352 */ 353 std::vector<std::unique_ptr<class field_base>> fields_; 354 355 /** 356 * Contains the widget that should get the focus when the window is shown. 357 */ 358 std::string focus_; 359 360 /** 361 * Restore the screen after showing? 362 * 363 * Most windows should restore the display after showing so this value 364 * defaults to true. Toplevel windows (like the titlescreen don't want this 365 * behavior so they can change it in pre_show(). 366 */ 367 bool restore_; 368 369 /** 370 * Allow plugins to skip through the dialog? 371 * Most dialogs install a plugins context to allow plugins to accept whatever the dialog is offering 372 * and continue. Some dialogs, especially those that install their own plugins context, may want to 373 * disable this. 374 */ 375 bool allow_plugin_skip_; 376 377 /** 378 * Show the dialog even with --nogui? 379 * Some dialogs need to be shown even when --nogui is specified if the game is being driven by a plugin. 380 * Those dialogs allow the plugin to styled_widget them by creating a plugin context in pre_show(). 381 */ 382 bool show_even_without_video_; 383 384 /** The id of the window to build. */ 385 virtual const std::string& window_id() const = 0; 386 387 /** 388 * Builds the window. 389 * 390 * Every dialog shows it's own kind of window, this function should return 391 * the window to show. 392 * 393 * @returns The window to show. 394 */ 395 window* build_window() const; 396 397 /** 398 * Actions to be taken directly after the window is build. 399 * 400 * At this point the registered fields are not yet registered. 401 * 402 * @param window The window just created. 403 */ 404 virtual void post_build(window& window); 405 406 /** 407 * Actions to be taken before showing the window. 408 * 409 * At this point the registered fields are registered and initialized with 410 * their initial values. 411 * 412 * @param window The window to be shown. 413 */ 414 virtual void pre_show(window& window); 415 416 /** 417 * Actions to be taken after the window has been shown. 418 * 419 * At this point the registered fields already stored their values (if the 420 * OK has been pressed). 421 * 422 * @param window The window which has been shown. 423 */ 424 virtual void post_show(window& window); 425 426 /** 427 * Initializes all fields in the dialog and set the keyboard focus. 428 * 429 * @param window The window which has been shown. 430 */ 431 virtual void init_fields(window& window); 432 433 /** 434 * When the dialog is closed with the OK status saves all fields. 435 * 436 * Saving only happens if a callback handler is installed. 437 * 438 * @param window The window which has been shown. 439 * @param save_fields Does the value in the fields need to be saved? 440 */ 441 virtual void finalize_fields(window& window, const bool save_fields); 442 }; 443 444 } // namespace dialogs 445 } // namespace gui2 446