The Battle for Wesnoth  1.19.8+dev
group.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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 
18 #include "gui/core/log.hpp"
21 #include "gui/widgets/widget.hpp"
22 
23 #include <map>
24 #include <vector>
25 
26 namespace gui2
27 {
28 
29 template<class T>
30 class group
31 {
32  using group_map = std::map<T, selectable_item*>;
33  using order_vector = std::vector<styled_widget*>;
34 
35 public:
36  /**
37  * Adds a widget/value pair to the group map. A callback is set that toggles each members'
38  * state to false when clicked. This happens before individual widget handlers fire, ensuring
39  * that the clicked widget will remain the only one selected.
40  */
41  void add_member(selectable_item* w, const T& value)
42  {
43  bool success;
44  std::tie(std::ignore, success) = members_.emplace(value, w);
45 
46  if(!success) {
47  ERR_GUI_G << "Group member with value already exists.";
48  return;
49  }
50 
52  [this](auto&&...) { group_operator(); }, event::dispatcher::front_child);
53 
54  member_order_.push_back(dynamic_cast<styled_widget*>(w));
55  }
56 
57  /**
58  * Removes a member from the group map.
59  */
60  void remove_member(const T& value)
61  {
62  members_.erase(value);
63  }
64 
65  /**
66  * Clears the entire group of members.
67  */
68  void clear()
69  {
70  members_.clear();
71  }
72 
73  /**
74  * Group member getters
75  */
77  {
78  return members_;
79  }
80 
81  const group_map& members() const
82  {
83  return members_;
84  }
85 
86  template<typename W>
87  W& member(const T& value)
88  {
89  return dynamic_cast<W&>(*members_.at(value));
90  }
91 
92  /**
93  * Returns the value paired with the currently actively toggled member of the group.
94  */
96  {
97  for(auto& member : members_) {
98  if(member.second->get_value_bool()) {
99  return member.first;
100  }
101  }
102 
103  return T();
104  }
105 
106  /**
107  * Sets the toggle values for all widgets besides the one associated
108  * with the specified value to false.
109  */
110  void set_member_states(const T& value)
111  {
112  for(auto& member : members_) {
113  member.second->set_value(member.first == value);
114  }
115  }
116 
117  /**
118  * Sets a common callback function for all members.
119  */
120  template<typename Func>
121  void on_modified(const Func& func)
122  {
123  // Ensure this callback is only called on the member being activated
124  const auto callback = [func, this](widget& widget, auto&&...) -> void {
125  if(auto& si = dynamic_cast<selectable_item&>(widget); si.get_value_bool()) {
126  for(auto& [v, w] : members_) {
127  if(&si == w) {
128  func(widget, v);
129  return;
130  }
131  }
132 
133  throw std::runtime_error("Group value change callback invoked for non-member widget");
134  }
135  };
136 
137  for(auto& member : members_) {
138  event::connect_signal_notify_modified(dynamic_cast<widget&>(*member.second), callback);
139  }
140  }
141 
142  /**
143  * Wrapper for enabling or disabling member widgets.
144  * Each member widget will be enabled or disabled based on the result of the specified
145  * predicate, which takes its associated value.
146  *
147  * If a selected widget is to be disabled, it is deselected and the first active member
148  * selected instead. The same happens if no members were previously active at all.
149  */
150  template<typename Func>
151  void set_members_enabled(const Func& predicate)
152  {
153  bool do_reselect = true;
154 
155  for(auto& member : members_) {
156  const bool res = predicate(member.first);
157 
158  selectable_item& w = *member.second;
159  dynamic_cast<styled_widget&>(w).set_active(res);
160 
161  // Only select another member if this was selected
162  if(w.get_value_bool()) {
163  do_reselect = !res;
164 
165  if(do_reselect) {
166  w.set_value_bool(false);
167  }
168  }
169  }
170 
171  if(!do_reselect) {
172  return;
173  }
174 
175  // Look for the first active member to select
176  for(auto& member : member_order_) {
177  if(member->get_active()) {
178  dynamic_cast<selectable_item&>(*member).set_value_bool(true);
179  break;
180  }
181  }
182  }
183 
184 private:
185  /**
186  * Container map for group members, organized by member value, associated widget.
187  */
189 
190  /**
191  * Since iterating over std::map is specified by operator< for it's key values, we can't
192  * guarantee the order would line up with the logical order - ie, that which the widgets
193  * appear in in a specific dialog. Keeping a separate vector here allows iterating over
194  * members in the order which they are added to the group.
195  */
197 
198  /**
199  * The default actions to take when clicking on one of the widgets in the group.
200  */
202  {
203  for(auto& member : members_) {
204  member.second->set_value(false);
205  }
206  }
207 };
208 
209 } // namespace gui2
void connect_signal(const F &func, const queue_position position=back_child)
Adds a callback to the appropriate queue based on event type.
Definition: dispatcher.hpp:351
void group_operator()
The default actions to take when clicking on one of the widgets in the group.
Definition: group.hpp:201
void remove_member(const T &value)
Removes a member from the group map.
Definition: group.hpp:60
void on_modified(const Func &func)
Sets a common callback function for all members.
Definition: group.hpp:121
group_map & members()
Group member getters.
Definition: group.hpp:76
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
Definition: group.hpp:41
void clear()
Clears the entire group of members.
Definition: group.hpp:68
void set_members_enabled(const Func &predicate)
Wrapper for enabling or disabling member widgets.
Definition: group.hpp:151
std::map< T, selectable_item * > group_map
Definition: group.hpp:32
const group_map & members() const
Definition: group.hpp:81
std::vector< styled_widget * > order_vector
Definition: group.hpp:33
void set_member_states(const T &value)
Sets the toggle values for all widgets besides the one associated with the specified value to false.
Definition: group.hpp:110
W & member(const T &value)
Definition: group.hpp:87
T get_active_member_value() const
Returns the value paired with the currently actively toggled member of the group.
Definition: group.hpp:95
order_vector member_order_
Since iterating over std::map is specified by operator< for it's key values, we can't guarantee the o...
Definition: group.hpp:196
group_map members_
Container map for group members, organized by member value, associated widget.
Definition: group.hpp:188
Small abstract helper class.
void set_value_bool(bool value, bool fire_event=false)
Base class for all widgets.
Definition: widget.hpp:55
int w
Define the common log macros for the gui toolkit.
#define ERR_GUI_G
Definition: log.hpp:44
@ LEFT_BUTTON_CLICK
Definition: handler.hpp:122
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.