The Battle for Wesnoth  1.19.0+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 <functional>
24 #include <map>
25 #include <vector>
26 
27 namespace gui2
28 {
29 
30 template<class T>
31 class group
32 {
33  using group_map = std::map<T, selectable_item*>;
34  using order_vector = std::vector<styled_widget*>;
35 
36 public:
37  /**
38  * Adds a widget/value pair to the group map. A callback is set that toggles each members'
39  * state to false when clicked. This happens before individual widget handlers fire, ensuring
40  * that the clicked widget will remain the only one selected.
41  */
42  void add_member(selectable_item* w, const T& value)
43  {
44  bool success;
45  std::tie(std::ignore, success) = members_.emplace(value, w);
46 
47  if(!success) {
48  ERR_GUI_G << "Group member with value already exists.";
49  return;
50  }
51 
54 
55  member_order_.push_back(dynamic_cast<styled_widget*>(w));
56  }
57 
58  /**
59  * Removes a member from the group map.
60  */
61  void remove_member(const T& value)
62  {
63  members_.erase(value);
64  }
65 
66  /**
67  * Clears the entire group of members.
68  */
69  void clear()
70  {
71  members_.clear();
72  }
73 
74  /**
75  * Group member getters
76  */
78  {
79  return members_;
80  }
81 
82  const group_map& members() const
83  {
84  return members_;
85  }
86 
87  template<typename W>
88  W& member(const T& value)
89  {
90  return dynamic_cast<W&>(*members_.at(value));
91  }
92 
93  /**
94  * Returns the value paired with the currently actively toggled member of the group.
95  */
97  {
98  for(auto& member : members_) {
99  if(member.second->get_value_bool()) {
100  return member.first;
101  }
102  }
103 
104  return T();
105  }
106 
107  /**
108  * Sets the toggle values for all widgets besides the one associated
109  * with the specified value to false.
110  */
111  void set_member_states(const T& value)
112  {
113  for(auto& member : members_) {
114  member.second->set_value(member.first == value);
115  }
116  }
117 
118  /**
119  * Sets a common callback function for all members.
120  */
121  void set_callback_on_value_change(std::function<void(widget&, const T)> func)
122  {
123  // Ensure this callback is only called on the member being activated
124  const auto callback = [func, this](widget& widget) -> 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), std::bind(callback, std::placeholders::_1));
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  void set_members_enabled(std::function<bool(const T&)> predicate)
151  {
152  bool do_reselect = true;
153 
154  for(auto& member : members_) {
155  const bool res = predicate(member.first);
156 
157  selectable_item& w = *member.second;
158  dynamic_cast<styled_widget&>(w).set_active(res);
159 
160  // Only select another member if this was selected
161  if(w.get_value_bool()) {
162  do_reselect = !res;
163 
164  if(do_reselect) {
165  w.set_value_bool(false);
166  }
167  }
168  }
169 
170  if(!do_reselect) {
171  return;
172  }
173 
174  // Look for the first active member to select
175  for(auto& member : member_order_) {
176  if(member->get_active()) {
177  dynamic_cast<selectable_item&>(*member).set_value_bool(true);
178  break;
179  }
180  }
181  }
182 
183 private:
184  /**
185  * Container map for group members, organized by member value, associated widget.
186  */
188 
189  /**
190  * Since iterating over std::map is specified by operator< for it's key values, we can't
191  * guarantee the order would line up with the logical order - ie, that which the widgets
192  * appear in in a specific dialog. Keeping a separate vector here allows iterating over
193  * members in the order which they are added to the group.
194  */
196 
197  /**
198  * The default actions to take when clicking on one of the widgets in the group.
199  */
201  {
202  for(auto& member : members_) {
203  member.second->set_value(false);
204  }
205  }
206 };
207 
208 } // 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:200
void remove_member(const T &value)
Removes a member from the group map.
Definition: group.hpp:61
group_map & members()
Group member getters.
Definition: group.hpp:77
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
Definition: group.hpp:42
void clear()
Clears the entire group of members.
Definition: group.hpp:69
std::map< T, selectable_item * > group_map
Definition: group.hpp:33
const group_map & members() const
Definition: group.hpp:82
std::vector< styled_widget * > order_vector
Definition: group.hpp:34
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:111
W & member(const T &value)
Definition: group.hpp:88
void set_callback_on_value_change(std::function< void(widget &, const T)> func)
Sets a common callback function for all members.
Definition: group.hpp:121
void set_members_enabled(std::function< bool(const T &)> predicate)
Wrapper for enabling or disabling member widgets.
Definition: group.hpp:150
T get_active_member_value()
Returns the value paired with the currently actively toggled member of the group.
Definition: group.hpp:96
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:195
group_map members_
Container map for group members, organized by member value, associated widget.
Definition: group.hpp:187
Small abstract helper class.
void set_value_bool(bool value, bool fire_event=false)
Base class for all widgets.
Definition: widget.hpp:53
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.