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