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 /**
16 * @file
17 * Implements some helper classes to ease adding fields to a dialog and hide
18 * the synchronization needed. Since some templates are used all is stored in
19 * the header.
20 *
21 */
22
23 #pragma once
24
25 #include "gui/auxiliary/find_widget.hpp"
26 #include "gui/auxiliary/field-fwd.hpp"
27 #include "gui/widgets/styled_widget.hpp"
28 #include "gui/widgets/selectable_item.hpp"
29 #include "gui/widgets/text_box.hpp"
30 #include "gui/widgets/window.hpp"
31
32 namespace gui2
33 {
34
35 /**
36 * Abstract base class for the fields.
37 *
38 * @note In this context a widget is a @ref gui2::styled_widget and not a @ref
39 * gui2::widget. This name widget is a generic name and fits, however some
40 * functions used are first declared in a styled_widget.
41 */
42 class field_base
43 {
44 public:
45 /**
46 * Constructor.
47 *
48 * @param id The id of the widget to connect to the window.
49 * A widget can only be connected once.
50 * @param mandatory Is the widget mandatory
51 */
field_base(const std::string & id,const bool mandatory)52 field_base(const std::string& id, const bool mandatory)
53 : id_(id), mandatory_(mandatory), widget_(nullptr)
54 {
55 }
56
~field_base()57 virtual ~field_base()
58 {
59 }
60
61 /**
62 * Attaches the field to a window.
63 *
64 * When attached the widget which we're a wrapper around is stored linked
65 * in here.
66 *
67 * @warning After attaching the window must remain a valid. Before the
68 * window is destroyed the @ref detach_from_window function must be called.
69 *
70 * @todo Most functions that have a window parameter only use it to get the
71 * widget. Evaluate and remove the window parameter where applicable.
72 *
73 * @pre widget_ == nullptr
74 *
75 * @param window The window to be attached to.
76 */
attach_to_window(window & window)77 void attach_to_window(window& window)
78 {
79 assert(!widget_);
80 widget_ = find_widget<styled_widget>(&window, id(), false, mandatory_);
81 }
82
83 /**
84 * Initializes the widget.
85 *
86 * This routine is called before the dialog is shown and the pre_show() is
87 * called. So the user can override the values set. This routine does the
88 * following:
89 * - If no widget available exit gives feedback it the widget must exist.
90 * - If a getter is defined we use to set value_ and the widget.
91 * - If no setter is defined we use the widget value to set value_.
92 *
93 * The function calls two functions
94 * - init_generic which is to be used in the template subclass.
95 * - init_specialized which is to be used in subclasses of the template
96 * class. This way they can override this function without to use their
97 * signature to inherit.
98 *
99 * @param window The window containing the widget.
100 */
widget_init(window & window)101 void widget_init(window& window)
102 {
103 init_generic(window);
104 init_specialized(window);
105 }
106
107 /**
108 * Finalizes the widget.
109 *
110 * This routine is called after the dialog is closed with OK. It's called
111 * before post_show(). This routine does the following:
112 * - if no active widget available exit.
113 * - if a setter is defined the widget value is saved in the setter.
114 * - The widget value is saved in value_.
115 *
116 * Like widget_init it calls two functions with the same purpose.
117 *
118 * @param window The window containing the widget.
119 */
widget_finalize(window & window)120 void widget_finalize(window& window)
121 {
122 finalize_generic(window);
123 finalize_specialized(window);
124 }
125
126 /**
127 * Detaches the field from a window.
128 *
129 * @pre widget_ != nullptr || !mandatory_
130 */
detach_from_window()131 void detach_from_window()
132 {
133 assert(!mandatory_ || widget_);
134 widget_ = nullptr;
135 }
136
137 /**
138 * Saves a widget.
139 *
140 * It can be a window must be recreated, in that case the state needs to be
141 * saved and restored. This routine does the following:
142 * - if no widget available exit (doesn't look at the active state).
143 * - The widget value is saved in value_.
144 *
145 * @param window The window containing the widget.
146 */
147 virtual void widget_save(window& window) = 0;
148
149 /**
150 * Restores a widget.
151 *
152 * See widget_save for more info.
153 *
154 * @param window The window containing the widget.
155 */
156 virtual void widget_restore(window& window) = 0;
157
158 /**
159 * Enables a widget.
160 *
161 * @param window The window containing the widget.
162 * @param enable If true enables the widget, disables
163 * otherwise.
164 * @param sync If the state is changed do we need to
165 * synchronize. Upon disabling, write the value
166 * of the widget in the variable value_. Upon
167 * enabling write the value of value_ in the
168 * widget.
169 */
widget_set_enabled(window & window,const bool enable,const bool sync)170 void widget_set_enabled(window& window, const bool enable, const bool sync)
171 {
172 styled_widget* widget = dynamic_cast<styled_widget*>(window.find(id(), false));
173
174 if(!widget) {
175 return;
176 }
177
178 const bool widget_state = widget->get_active();
179 if(widget_state == enable) {
180 return;
181 }
182
183 if(sync) {
184 if(enable) {
185 widget_restore(window);
186 } else {
187 widget_save(window);
188 }
189 }
190
191 widget->set_active(enable);
192 }
193
194 /***** ***** ***** setters / getters for members ***** ****** *****/
195
id() const196 const std::string& id() const
197 {
198 return id_;
199 }
200
is_mandatory() const201 bool is_mandatory() const
202 {
203 return mandatory_;
204 }
205
get_widget()206 styled_widget* get_widget()
207 {
208 return widget_;
209 }
210
get_widget() const211 const styled_widget* get_widget() const
212 {
213 return widget_;
214 }
215
216 private:
217 /** The id field of the widget, should be unique in a window. */
218 const std::string id_;
219
220 /** Is the widget optional or mandatory in this window. */
221 const bool mandatory_;
222
223 /** The widget attached to the field. */
224 styled_widget* widget_;
225
226 /** See widget_init. */
227 virtual void init_generic(window& window) = 0;
228
229 /** See widget_init. */
init_specialized(window &)230 virtual void init_specialized(window& /*window*/)
231 {
232 }
233
234 /** See widget_finalize. */
235 virtual void finalize_generic(window& window) = 0;
236
237 /** See widget_finalize. */
finalize_specialized(window &)238 virtual void finalize_specialized(window& /*window*/)
239 {
240 }
241 };
242
243 /**
244 * Template class to implement the generic field implementation.
245 *
246 * @tparam T The type of the item to show in the widget.
247 * @tparam W The type of widget to show, this is not a
248 * widget class but a behavior class.
249 * @tparam CT The type tp be used in the
250 * callback_save_value callback. Normally this
251 * is const T but for example with strings it
252 * can be const T&. Note the const needs to be
253 * in the template otherwise compilation on
254 * GCC-4.3 fails (not sure whether compiler bug
255 * or not).
256 */
257 template <class T, class W, class CT>
258 class field : public field_base
259 {
260 public:
261 /**
262 * Constructor.
263 *
264 * @param id The id of the widget to connect to the window.
265 * A widget can only be connected once.
266 * @param mandatory Is the widget mandatory?
267 * @param callback_load_value A callback function which is called when the
268 * window is shown. This callback returns the
269 * initial value of the field.
270 * @param callback_save_value A callback function which is called when the
271 * window closed with the OK button. The
272 * callback is executed with the new value of
273 * the field. It's meant to set the value of
274 * some variable in the engine after the window
275 * is closed with OK.
276 */
field(const std::string & id,const bool mandatory,const std::function<T ()> & callback_load_value,const std::function<void (CT)> & callback_save_value)277 field(const std::string& id,
278 const bool mandatory,
279 const std::function<T()>& callback_load_value,
280 const std::function<void(CT)>& callback_save_value)
281 : field_base(id, mandatory)
282 , value_(T())
283 , link_(value_)
284 , callback_load_value_(callback_load_value)
285 , callback_save_value_(callback_save_value)
286 {
287 static_assert(!std::is_same<styled_widget, W>::value, "Second template argument cannot be styled_widget");
288 }
289
290 /**
291 * Constructor.
292 *
293 * @param id The id of the widget to connect to the window.
294 * A widget can only be connected once.
295 * @param mandatory Is the widget mandatory?
296 * @param linked_variable The variable which is linked to the field.
297 * * Upon loading its value is used as initial
298 * value of the widget.
299 * * Upon closing:
300 * * with OK its value is set to the value of
301 * the widget.
302 * * else, its value is undefined.
303 */
field(const std::string & id,const bool mandatory,T & linked_variable)304 field(const std::string& id, const bool mandatory, T& linked_variable)
305 : field_base(id, mandatory)
306 , value_(T())
307 , link_(linked_variable)
308 , callback_load_value_(nullptr)
309 , callback_save_value_(nullptr)
310 {
311 static_assert(!std::is_same<styled_widget, W>::value, "Second template argument cannot be styled_widget");
312 }
313
314 /**
315 * Constructor.
316 *
317 * This version is used for read only variables.
318 *
319 * @note The difference between this constructor and the one above is the
320 * sending of the third parameter as const ref instead of a non-const ref.
321 * So it feels a bit tricky. Since this constructor is only used for a
322 * the @ref styled_widget class and the other constructors not the issue is
323 * solved by using static asserts to test whether the proper constructor
324 * is used.
325 *
326 * @param mandatory Is the widget mandatory?
327 * @param id The id of the widget to connect to the window.
328 * A widget can only be connected once.
329 * @param value The value of the widget.
330 */
field(const std::string & id,const bool mandatory,const T & value)331 field(const std::string& id, const bool mandatory, const T& value)
332 : field_base(id, mandatory)
333 , value_(value)
334 , link_(value_)
335 , callback_load_value_(nullptr)
336 , callback_save_value_(nullptr)
337 {
338 static_assert(std::is_same<styled_widget, W>::value, "Second template argument must be styled_widget");
339 }
340
341 /** Inherited from field_base. */
widget_restore(window & window)342 void widget_restore(window& window)
343 {
344 validate_widget(window);
345
346 restore(window);
347 }
348
349 /**
350 * Sets the value of the field.
351 *
352 * This sets the value in both the internal cache value and in the widget
353 * itself.
354 *
355 * @param window The window containing the widget.
356 * @param value The new value.
357 */
set_widget_value(window & window,CT value)358 void set_widget_value(window& window, CT value)
359 {
360 value_ = value;
361 restore(window);
362 }
363
364 /**
365 * Sets the value of the field.
366 *
367 * This sets the internal cache value but not the widget value, this can
368 * be used to initialize the field.
369 *
370 * @param value The new value.
371 */
set_cache_value(CT value)372 void set_cache_value(CT value)
373 {
374 value_ = value;
375 }
376
377 /** Inherited from field_base. */
widget_save(window & window)378 void widget_save(window& window)
379 {
380 save(window, false);
381 }
382
383 /**
384 * Gets the value of the field.
385 *
386 * This function gets the value of the widget and stores that in the
387 * internal cache, then that value is returned.
388 *
389 * @deprecated Use references to a variable instead.
390 *
391 * @param window The window containing the widget.
392 *
393 * @returns The current value of the widget.
394 */
get_widget_value(window & window)395 T get_widget_value(window& window)
396 {
397 save(window, false);
398 return value_;
399 }
400
401 private:
402 /**
403 * The value_ of the widget, this value is also available once the widget
404 * is destroyed.
405 */
406 T value_;
407
408 /**
409 * The variable linked to the field.
410 *
411 * When set determines the initial value and the final value is stored here
412 * in the finalizer.
413 */
414 T& link_;
415
416 /**
417 * The callback function to load the value.
418 *
419 * This is used to load the initial value of the widget, if defined.
420 */
421 std::function<T()> callback_load_value_;
422
423 /** Inherited from field_base. */
init_generic(window & window)424 void init_generic(window& window)
425 {
426 validate_widget(window);
427
428 if(callback_load_value_) {
429 value_ = callback_load_value_();
430 } else {
431 value_ = link_;
432 }
433
434 restore(window);
435 }
436
437 /** Inherited from field_base. */
finalize_generic(window & window)438 void finalize_generic(window& window)
439 {
440 save(window, true);
441
442 if(callback_save_value_) {
443 callback_save_value_(value_);
444 } else {
445 link_ = value_;
446 }
447 }
448
449 /**
450 * The callback function to save the value.
451 *
452 * Once the dialog has been successful this function is used to store the
453 * result of this widget.
454 */
455 std::function<void(CT)> callback_save_value_;
456
457 /**
458 * Test whether the widget exists if the widget is mandatory.
459 *
460 * @param window The window containing the widget.
461 */
validate_widget(window & window)462 void validate_widget(window& window)
463 {
464 if(!is_mandatory()) {
465 return;
466 }
467 find_widget<const W>(&window, id(), false);
468 }
469
470 /**
471 * Stores the value in the widget in the interval value_.
472 *
473 * @param window The window containing the widget.
474 * @param must_be_active If true only active widgets will store their
475 *value.
476 */
477 void save(window& window, const bool must_be_active);
478
479 /**
480 * Stores the internal value_ in the widget.
481 *
482 * @param window The window containing the widget.
483 */
484 void restore(window& window);
485 };
486
487 template <class T, class W, class CT>
save(window & window,const bool must_be_active)488 void field<T, W, CT>::save(window& window, const bool must_be_active)
489 {
490 const W* widget
491 = find_widget<const W>(&window, id(), must_be_active, false);
492
493 if(widget) {
494 value_ = widget->get_value();
495 }
496 }
497
498 template <>
save(window & window,const bool must_be_active)499 inline void field<bool, selectable_item>::save(
500 window& window, const bool must_be_active)
501 {
502 const selectable_item* selectable
503 = find_widget<const selectable_item>(&window, id(), must_be_active, false);
504
505 if(selectable) {
506 value_ = selectable->get_value_bool();
507 }
508 }
509
510 template <>
save(window & window,const bool must_be_active)511 inline void field<std::string, styled_widget, const std::string&>::save(
512 window& window, const bool must_be_active)
513 {
514 const styled_widget* control
515 = find_widget<const styled_widget>(&window, id(), must_be_active, false);
516
517 if(control) {
518 value_ = control->get_label();
519 }
520 }
521
522 template <class T, class W, class CT>
restore(window & window)523 void field<T, W, CT>::restore(window& window)
524 {
525 W* widget = find_widget<W>(&window, id(), false, false);
526
527 if(widget) {
528 widget->set_value(value_);
529 }
530 }
531
532 template <>
533 inline void
restore(window & window)534 field<std::string, styled_widget, const std::string&>::restore(window& window)
535 {
536 styled_widget* control = find_widget<styled_widget>(&window, id(), false, false);
537
538 if(control) {
539 control->set_label(value_);
540 }
541 }
542
543 /** Specialized field class for boolean. */
544 class field_bool : public field<bool, selectable_item>
545 {
546 public:
field_bool(const std::string & id,const bool mandatory,const std::function<bool ()> & callback_load_value,const std::function<void (const bool)> & callback_save_value,const std::function<void (widget &)> & callback_change,const bool initial_fire)547 field_bool(const std::string& id,
548 const bool mandatory,
549 const std::function<bool()>& callback_load_value,
550 const std::function<void(const bool)>& callback_save_value,
551 const std::function<void(widget&)>& callback_change,
552 const bool initial_fire)
553 : field<bool, gui2::selectable_item>(
554 id, mandatory, callback_load_value, callback_save_value)
555 , callback_change_(callback_change)
556 , initial_fire_(initial_fire)
557 {
558 }
559
field_bool(const std::string & id,const bool mandatory,bool & linked_variable,const std::function<void (widget &)> & callback_change,const bool initial_fire)560 field_bool(const std::string& id,
561 const bool mandatory,
562 bool& linked_variable,
563 const std::function<void(widget&)>& callback_change,
564 const bool initial_fire)
565 : field<bool, gui2::selectable_item>(id, mandatory, linked_variable)
566 , callback_change_(callback_change)
567 , initial_fire_(initial_fire)
568 {
569 }
570
571 private:
572 /** Overridden from field_base. */
init_specialized(window & window)573 void init_specialized(window& window)
574 {
575 if(callback_change_) {
576 if(widget* widget = window.find(id(), false)) {
577 if(initial_fire_) {
578 callback_change_(*widget);
579 }
580
581 connect_signal_notify_modified(*widget, std::bind(callback_change_, _1));
582 }
583 }
584 }
585
586 std::function<void(widget&)> callback_change_;
587
588 const bool initial_fire_;
589 };
590
591 /** Specialized field class for text. */
592 class field_text : public field<std::string, text_box_base, const std::string&>
593 {
594 public:
field_text(const std::string & id,const bool mandatory,const std::function<std::string ()> & callback_load_value,const std::function<void (const std::string &)> & callback_save_value)595 field_text(const std::string& id,
596 const bool mandatory,
597 const std::function<std::string()>& callback_load_value,
598 const std::function<void(const std::string&)>&
599 callback_save_value)
600 : field<std::string, text_box_base, const std::string&>(
601 id, mandatory, callback_load_value, callback_save_value)
602 {
603 }
604
field_text(const std::string & id,const bool mandatory,std::string & linked_variable)605 field_text(const std::string& id,
606 const bool mandatory,
607 std::string& linked_variable)
608 : field<std::string, text_box_base, const std::string&>(
609 id, mandatory, linked_variable)
610 {
611 }
612
613 private:
614 /** Overridden from field_base. */
finalize_specialized(window & window)615 void finalize_specialized(window& window)
616 {
617 text_box* widget = dynamic_cast<text_box*>(window.find(id(), false));
618
619 if(widget) {
620 widget->save_to_history();
621 }
622 }
623 };
624
625 /** Specialized field class for a styled_widget, used for labels and images. */
626 class field_label : public field<std::string, styled_widget, const std::string&>
627 {
628 public:
field_label(const std::string & id,const bool mandatory,const std::string & text,const bool use_markup)629 field_label(const std::string& id,
630 const bool mandatory,
631 const std::string& text,
632 const bool use_markup)
633 : field<std::string, styled_widget, const std::string&>(id, mandatory, text)
634 , use_markup_(use_markup)
635 {
636 }
637
638 private:
639 /** Whether or not the label uses markup. */
640 bool use_markup_;
641
642 /** Overridden from field_base. */
init_specialized(window & window)643 void init_specialized(window& window)
644 {
645 find_widget<styled_widget>(&window, id(), false).set_use_markup(use_markup_);
646 }
647 };
648
649 } // namespace gui2
650