The Battle for Wesnoth  1.17.4+dev
multimenu_button.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2022
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 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "gui/core/log.hpp"
25 #include "gui/widgets/settings.hpp"
26 #include "gui/widgets/window.hpp"
27 #include "sound.hpp"
28 
29 #include "formula/string_utils.hpp"
30 #include <functional>
31 #include "gettext.hpp"
32 
33 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
34 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
35 
36 namespace gui2
37 {
38 
39 // ------------ WIDGET -----------{
40 
41 REGISTER_WIDGET(multimenu_button)
42 
43 multimenu_button::multimenu_button(const implementation::builder_multimenu_button& builder)
44  : styled_widget(builder, type())
45  , state_(ENABLED)
46  , max_shown_(1)
47  , values_()
48  , toggle_states_()
49  , droplist_(nullptr)
50 {
51  values_.emplace_back("label", this->get_label());
52 
53  connect_signal<event::MOUSE_ENTER>(
54  std::bind(&multimenu_button::signal_handler_mouse_enter, this, std::placeholders::_2, std::placeholders::_3));
55  connect_signal<event::MOUSE_LEAVE>(
56  std::bind(&multimenu_button::signal_handler_mouse_leave, this, std::placeholders::_2, std::placeholders::_3));
57 
58  connect_signal<event::LEFT_BUTTON_DOWN>(
59  std::bind(&multimenu_button::signal_handler_left_button_down, this, std::placeholders::_2, std::placeholders::_3));
60  connect_signal<event::LEFT_BUTTON_UP>(
61  std::bind(&multimenu_button::signal_handler_left_button_up, this, std::placeholders::_2, std::placeholders::_3));
62  connect_signal<event::LEFT_BUTTON_CLICK>(
63  std::bind(&multimenu_button::signal_handler_left_button_click, this, std::placeholders::_2, std::placeholders::_3));
64 
65  // TODO: might need to position this differently in the queue if it's called after
66  // dialog-specific callbacks.
67  connect_signal<event::NOTIFY_MODIFIED>(
69 }
70 
71 void multimenu_button::set_active(const bool active)
72 {
73  if(get_active() != active) {
74  set_state(active ? ENABLED : DISABLED);
75  }
76 }
77 
79 {
80  return state_ != DISABLED;
81 }
82 
84 {
85  return state_;
86 }
87 
89 {
90  if(state != state_) {
91  state_ = state;
92  set_is_dirty(true);
93  }
94 }
95 
97 {
98  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
99 
101  handled = true;
102 }
103 
105 {
106  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
107 
109  handled = true;
110 }
111 
113 {
114  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
115 
116  window* window = get_window();
117  if(window) {
118  window->mouse_capture();
119  }
120 
122  handled = true;
123 }
124 
126 {
127  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
128 
130  handled = true;
131 }
132 
134 {
135  assert(get_window());
136  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
137 
139 
140  // If a button has a retval do the default handling.
141  dialogs::drop_down_menu droplist(this, values_, -1, true);
142 
143  droplist_ = &droplist;
144  droplist.show();
145  droplist_ = nullptr;
146 
147  /* In order to allow toggle button states to be specified by various dialogs in the values config, we write the state
148  * bools to the values_ config here, but only if a checkbox= key was already provided. The value of the checkbox= key
149  * is handled by the drop_down_menu widget.
150  *
151  * Passing the dynamic_bitset directly to the drop_down_menu ctor would mean bool values would need to be passed to this
152  * class independently of the values config by dialogs that use this widget. However, the bool states are also saved
153  * in a dynamic_bitset class member which can be fetched for other uses if necessary.
154  */
156 
157  handled = true;
158 }
159 
161 {
162  std::vector<t_string> selected;
163  for(std::size_t i = 0; i < toggle_states_.size() && i < values_.size(); i++) {
164  if(!toggle_states_[i]) {
165  continue;
166  }
167 
168  selected.push_back(values_[i]["label"]);
169  }
170 
171  if(selected.size() == values_.size()) {
172  set_label(_("multimenu^All Selected"));
173  } else {
174  if(selected.size() > max_shown_) {
175  const unsigned excess = selected.size() - max_shown_;
176  selected.resize(max_shown_ + 1);
177  // TRANSLATORS: In a drop-down menu that's a list of toggle-boxes, this becomes part
178  // of the text on the button when many of the boxes are selected. The text becomes
179  // "x, y and 1 other", "x, y and 2 others", etc.
180  selected.back() = VNGETTEXT("multimenu^$excess other", "$excess others", excess, {{"excess", std::to_string(excess)}});
181  }
182  set_label(utils::format_conjunct_list(_("multimenu^None Selected"), selected));
183  }
184 }
185 
187 {
188  for(unsigned i = 0; i < values_.size(); i++) {
189  ::config& c = values_[i];
190 
191  c["checkbox"] = toggle_states_[i];
192  }
193 }
194 
196 {
197  toggle_states_.reset();
199  update_label();
200 }
201 
203 {
204  assert(droplist_ != nullptr);
205 
207  update_label();
208 }
209 
210 void multimenu_button::select_option(const unsigned option, const bool selected)
211 {
212  assert(option < values_.size());
213 
214  if(option < toggle_states_.size()) {
215  toggle_states_.resize(option + 1);
216  }
217  toggle_states_[option] = selected;
219  update_label();
220 }
221 
222 void multimenu_button::select_options(boost::dynamic_bitset<> states)
223 {
224  assert(states.size() == values_.size());
225  toggle_states_ = states;
227  update_label();
228 }
229 
230 void multimenu_button::set_values(const std::vector<::config>& values)
231 {
232  set_is_dirty(true);
233 
234  values_ = values;
235  toggle_states_.resize(values_.size(), false);
236  toggle_states_.reset();
237 
238  set_label(_("multimenu^None Selected"));
239 }
240 
241 // }---------- DEFINITION ---------{
242 
245 {
246  DBG_GUI_P << "Parsing multimenu_button " << id << '\n';
247 
248  load_resolutions<resolution>(cfg);
249 }
250 
252  : resolution_definition(cfg)
253 {
254  // Note the order should be the same as the enum state_t in multimenu_button.hpp.
255  state.emplace_back(cfg.child("state_enabled"));
256  state.emplace_back(cfg.child("state_disabled"));
257  state.emplace_back(cfg.child("state_pressed"));
258  state.emplace_back(cfg.child("state_focused"));
259 }
260 
261 // }---------- BUILDER -----------{
262 
263 namespace implementation
264 {
265 
266 builder_multimenu_button::builder_multimenu_button(const config& cfg)
267  : builder_styled_widget(cfg)
268  , max_shown_(cfg["maximum_shown"].to_unsigned(1))
269  , options_()
270 {
271  for(const auto& option : cfg.child_range("option")) {
272  options_.push_back(option);
273  }
274 }
275 
276 std::unique_ptr<widget> builder_multimenu_button::build() const
277 {
278  auto widget = std::make_unique<multimenu_button>(*this);
279 
280  widget->set_max_shown(max_shown_);
281  if(!options_.empty()) {
282  widget->set_values(options_);
283  }
284 
285  DBG_GUI_G << "Window builder: placed multimenu_button '" << id
286  << "' with definition '" << definition << "'.\n";
287 
288  return widget;
289 }
290 
291 } // namespace implementation
292 
293 // }------------ END --------------
294 
295 } // namespace gui2
Define the common log macros for the gui toolkit.
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
Base class of a resolution, contains the common keys for a resolution.
#define DBG_GUI_P
Definition: log.hpp:66
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.
dialogs::drop_down_menu * droplist_
std::vector< state_definition > state
void set_state(const state_t state)
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
void signal_handler_mouse_enter(const event::ui_event event, bool &handled)
This file contains the window object, this object is a top level container which has the event manage...
child_itors child_range(config_key_type key)
Definition: config.cpp:344
Base class for all widgets.
Definition: widget.hpp:49
std::string sound_button_click
Definition: settings.cpp:43
virtual std::unique_ptr< widget > build() const override
#define VNGETTEXT(msgid, msgid_plural, count,...)
A multimenu_button is a styled_widget to choose an element from a list of elements.
static std::string _(const char *str)
Definition: gettext.hpp:93
bool show(const unsigned auto_close_time=0)
Shows the window.
Generic file dialog.
Definition: field-fwd.hpp:23
virtual void set_label(const t_string &label)
virtual unsigned get_state() const override
See styled_widget::get_state.
virtual bool get_active() const override
See styled_widget::get_active.
void signal_handler_left_button_click(const event::ui_event event, bool &handled)
std::string definition
Parameters for the styled_widget.
state_t state_
Current state of the widget.
state_t
Possible states of the widget.
unsigned max_shown_
The maximum number of selected states to list in the label.
This file contains the settings handling of the widget library.
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:467
Used by the menu_button widget.
std::string selected
boost::dynamic_bitset toggle_states_
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
virtual void set_active(const bool active) override
See styled_widget::set_active.
std::size_t i
Definition: function.cpp:967
boost::dynamic_bitset get_toggle_states() const
If a toggle button widget is present, returns the toggled state of each row&#39;s button.
#define DBG_GUI_E
Definition: log.hpp:35
window * get_window()
Get the parent window.
Definition: widget.cpp:118
#define LOG_HEADER
Base class for all visible items.
void mouse_capture(const bool capture=true)
Definition: window.cpp:1247
void select_options(boost::dynamic_bitset<> states)
Set the options selected in the menu.
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1052
multimenu_button_definition(const config &cfg)
void select_option(const unsigned option, const bool selected=true)
Select an option in the menu.
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
void set_values(const std::vector<::config > &values)
Set the available menu options.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
mock_char c
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
#define DBG_GUI_G
Definition: log.hpp:41
std::vector<::config > values_
void reset_toggle_states()
Deselect all the menu options.
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.