The Battle for Wesnoth  1.15.7+dev
multimenu_button.cpp
Go to the documentation of this file.
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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "gui/core/log.hpp"
24 #include "gui/widgets/settings.hpp"
25 #include "gui/widgets/window.hpp"
26 #include "sound.hpp"
27 
28 #include "formula/string_utils.hpp"
29 #include "utils/functional.hpp"
30 #include "gettext.hpp"
31 
32 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
33 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
34 
35 namespace gui2
36 {
37 
38 // ------------ WIDGET -----------{
39 
40 REGISTER_WIDGET(multimenu_button)
41 
42 multimenu_button::multimenu_button(const implementation::builder_multimenu_button& builder)
43  : styled_widget(builder, type())
44  , state_(ENABLED)
45  , max_shown_(1)
46  , values_()
47  , toggle_states_()
48  , droplist_(nullptr)
49 {
50  values_.emplace_back("label", this->get_label());
51 
52  connect_signal<event::MOUSE_ENTER>(
53  std::bind(&multimenu_button::signal_handler_mouse_enter, this, _2, _3));
54  connect_signal<event::MOUSE_LEAVE>(
55  std::bind(&multimenu_button::signal_handler_mouse_leave, this, _2, _3));
56 
57  connect_signal<event::LEFT_BUTTON_DOWN>(
58  std::bind(&multimenu_button::signal_handler_left_button_down, this, _2, _3));
59  connect_signal<event::LEFT_BUTTON_UP>(
60  std::bind(&multimenu_button::signal_handler_left_button_up, this, _2, _3));
61  connect_signal<event::LEFT_BUTTON_CLICK>(
62  std::bind(&multimenu_button::signal_handler_left_button_click, this, _2, _3));
63 
64  // TODO: might need to position this differently in the queue if it's called after
65  // dialog-specific callbacks.
66  connect_signal<event::NOTIFY_MODIFIED>(
68 }
69 
70 void multimenu_button::set_active(const bool active)
71 {
72  if(get_active() != active) {
73  set_state(active ? ENABLED : DISABLED);
74  }
75 }
76 
78 {
79  return state_ != DISABLED;
80 }
81 
83 {
84  return state_;
85 }
86 
88 {
89  if(state != state_) {
90  state_ = state;
91  set_is_dirty(true);
92  }
93 }
94 
96 {
97  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
98 
100  handled = true;
101 }
102 
104 {
105  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
106 
108  handled = true;
109 }
110 
112 {
113  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
114 
115  window* window = get_window();
116  if(window) {
117  window->mouse_capture();
118  }
119 
121  handled = true;
122 }
123 
125 {
126  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
127 
129  handled = true;
130 }
131 
133 {
134  assert(get_window());
135  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
136 
138 
139  // If a button has a retval do the default handling.
140  dialogs::drop_down_menu droplist(this, values_, -1, true);
141 
142  droplist_ = &droplist;
143  droplist.show();
144  droplist_ = nullptr;
145 
146  /* In order to allow toggle button states to be specified by various dialogs in the values config, we write the state
147  * bools to the values_ config here, but only if a checkbox= key was already provided. The value of the checkbox= key
148  * is handled by the drop_down_menu widget.
149  *
150  * Passing the dynamic_bitset directly to the drop_down_menu ctor would mean bool values would need to be passed to this
151  * class independently of the values config by dialogs that use this widget. However, the bool states are also saved
152  * in a dynamic_bitset class member which can be fetched for other uses if necessary.
153  */
155 
156  handled = true;
157 }
158 
160 {
161  std::vector<t_string> selected;
162  for(std::size_t i = 0; i < toggle_states_.size() && i < values_.size(); i++) {
163  if(!toggle_states_[i]) {
164  continue;
165  }
166 
167  selected.push_back(values_[i]["label"]);
168  }
169 
170  if(selected.size() == values_.size()) {
171  set_label(_("multimenu^All Selected"));
172  } else {
173  if(selected.size() > max_shown_) {
174  const unsigned excess = selected.size() - max_shown_;
175  selected.resize(max_shown_ + 1);
176  selected.back() = VNGETTEXT("multimenu^$excess other", "$excess others", excess, {{"excess", std::to_string(excess)}});
177  }
178  set_label(utils::format_conjunct_list(_("multimenu^None Selected"), selected));
179  }
180 }
181 
183 {
184  for(unsigned i = 0; i < values_.size(); i++) {
185  ::config& c = values_[i];
186 
187  c["checkbox"] = toggle_states_[i];
188  }
189 }
190 
192 {
193  toggle_states_.reset();
195  update_label();
196 }
197 
199 {
200  assert(droplist_ != nullptr);
201 
203  update_label();
204 }
205 
206 void multimenu_button::select_option(const unsigned option, const bool selected)
207 {
208  assert(option < values_.size());
209 
210  if(option < toggle_states_.size()) {
211  toggle_states_.resize(option + 1);
212  }
213  toggle_states_[option] = selected;
215  update_label();
216 }
217 
218 void multimenu_button::select_options(boost::dynamic_bitset<> states)
219 {
220  assert(states.size() == values_.size());
221  toggle_states_ = states;
223  update_label();
224 }
225 
226 void multimenu_button::set_values(const std::vector<::config>& values)
227 {
228  set_is_dirty(true);
229 
230  values_ = values;
231  toggle_states_.resize(values_.size(), false);
232  toggle_states_.reset();
233 
234  set_label(_("multimenu^None Selected"));
235 }
236 
237 // }---------- DEFINITION ---------{
238 
241 {
242  DBG_GUI_P << "Parsing multimenu_button " << id << '\n';
243 
244  load_resolutions<resolution>(cfg);
245 }
246 
247 /*WIKI
248  * @page = GUIWidgetDefinitionWML
249  * @order = 1_multimenu_button
250  *
251  * == multimenu_button ==
252  *
253  * @macro = multimenu_button_description
254  *
255  * The following states exist:
256  * * state_enabled, the multimenu_button is enabled.
257  * * state_disabled, the multimenu_button is disabled.
258  * * state_pressed, the left mouse multimenu_button is down.
259  * * state_focused, the mouse is over the multimenu_button.
260  * @begin{parent}{name="gui/"}
261  * @begin{tag}{name="multimenu_button_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
262  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
263  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
264  * @end{tag}{name="state_enabled"}
265  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
266  * @end{tag}{name="state_disabled"}
267  * @begin{tag}{name="state_pressed"}{min=0}{max=1}{super="generic/state"}
268  * @end{tag}{name="state_pressed"}
269  * @begin{tag}{name="state_focused"}{min=0}{max=1}{super="generic/state"}
270  * @end{tag}{name="state_focused"}
271  * @end{tag}{name="resolution"}
272  * @end{tag}{name="multimenu_button_definition"}
273  * @end{parent}{name="gui/"}
274  */
276  : resolution_definition(cfg)
277 {
278  // Note the order should be the same as the enum state_t in multimenu_button.hpp.
279  state.emplace_back(cfg.child("state_enabled"));
280  state.emplace_back(cfg.child("state_disabled"));
281  state.emplace_back(cfg.child("state_pressed"));
282  state.emplace_back(cfg.child("state_focused"));
283 }
284 
285 // }---------- BUILDER -----------{
286 
287 /*WIKI_MACRO
288  * @begin{macro}{multimenu_button_description}
289  *
290  * A multimenu_button is a styled_widget to choose an element from a list of elements.
291  * @end{macro}
292  */
293 
294 /*WIKI
295  * @page = GUIWidgetInstanceWML
296  * @order = 2_multimenu_button
297  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
298  * @begin{tag}{name="multimenu_button"}{min=0}{max=-1}{super="generic/widget_instance"}
299  * == multimenu_button ==
300  *
301  * @macro = multimenu_button_description
302  *
303  * Instance of a multimenu_button. When a multimenu_button has a return value it sets the
304  * return value for the window. Normally this closes the window and returns
305  * this value to the caller. The return value can either be defined by the
306  * user or determined from the id of the multimenu_button. The return value has a
307  * higher precedence as the one defined by the id. (Of course it's weird to
308  * give a multimenu_button an id and then override its return value.)
309  *
310  * When the multimenu_button doesn't have a standard id, but you still want to use the
311  * return value of that id, use return_value_id instead. This has a higher
312  * precedence as return_value.
313  *
314  * List with the multimenu_button specific variables:
315  * @begin{table}{config}
316  * return_value_id & string & "" & The return value id. $
317  * return_value & int & 0 & The return value. $
318  * maximum_shown & int & -1 & The maximum number of currently selected values to list on the button. $
319  *
320  * @end{table}
321  * @end{tag}{name="multimenu_button"}
322  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
323  */
324 
325 namespace implementation
326 {
327 
328 builder_multimenu_button::builder_multimenu_button(const config& cfg)
329  : builder_styled_widget(cfg)
330  , max_shown_(cfg["maximum_shown"].to_unsigned(1))
331  , options_()
332 {
333  for(const auto& option : cfg.child_range("option")) {
334  options_.push_back(option);
335  }
336 }
337 
339 {
341 
342  widget->set_max_shown(max_shown_);
343  if(!options_.empty()) {
344  widget->set_values(options_);
345  }
346 
347  DBG_GUI_G << "Window builder: placed multimenu_button '" << id
348  << "' with definition '" << definition << "'.\n";
349 
350  return widget;
351 }
352 
353 } // namespace implementation
354 
355 // }------------ END --------------
356 
357 } // 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:65
void set_max_shown(const unsigned max)
Sets the maximum number of selected elements shown on the label.
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:420
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)
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:362
Base class for all widgets.
Definition: widget.hpp:49
std::string sound_button_click
Definition: settings.cpp:42
#define VNGETTEXT(msgid, msgid_plural, count,...)
Simple push button.
bool show(const unsigned auto_close_time=0)
Shows the window.
Generic file dialog.
Definition: field-fwd.hpp:22
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)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:100
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:465
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:933
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:34
window * get_window()
Get the parent window.
Definition: widget.cpp:116
#define LOG_HEADER
Base class for all visible items.
void mouse_capture(const bool capture=true)
Definition: window.cpp:1276
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:1051
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:68
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:62
#define DBG_GUI_G
Definition: log.hpp:40
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.
ui_event
The event send to the dispatcher.
Definition: handler.hpp:47