The Battle for Wesnoth  1.15.1+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>(std::bind(
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>(std::bind(
63 }
64 
65 void multimenu_button::set_active(const bool active)
66 {
67  if(get_active() != active) {
68  set_state(active ? ENABLED : DISABLED);
69  }
70 }
71 
73 {
74  return state_ != DISABLED;
75 }
76 
78 {
79  return state_;
80 }
81 
83 {
84  if(state != state_) {
85  state_ = state;
86  set_is_dirty(true);
87  }
88 }
89 
91 {
92  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
93 
95  handled = true;
96 }
97 
99 {
100  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
101 
103  handled = true;
104 }
105 
107 {
108  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
109 
110  window* window = get_window();
111  if(window) {
112  window->mouse_capture();
113  }
114 
116  handled = true;
117 }
118 
120 {
121  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
122 
124  handled = true;
125 }
126 
128 {
129  assert(get_window());
130  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
131 
133 
134  // If a button has a retval do the default handling.
135  dialogs::drop_down_menu droplist(this->get_rectangle(), this->values_, -1, this->get_use_markup(), true,
136  std::bind(&multimenu_button::toggle_state_changed, this));
137 
138  droplist_ = &droplist;
139  droplist.show();
140  droplist_ = nullptr;
141 
142  /* In order to allow toggle button states to be specified by various dialogs in the values config, we write the state
143  * bools to the values_ config here, but only if a checkbox= key was already provided. The value of the checkbox= key
144  * is handled by the drop_down_menu widget.
145  *
146  * Passing the dynamic_bitset directly to the drop_down_menu ctor would mean bool values would need to be passed to this
147  * class independently of the values config by dialogs that use this widget. However, the bool states are also saved
148  * in a dynamic_bitset class member which can be fetched for other uses if necessary.
149  */
151 
152  handled = true;
153 }
154 
156 {
157  std::vector<t_string> selected;
158  for(std::size_t i = 0; i < toggle_states_.size() && i < values_.size(); i++) {
159  if(!toggle_states_[i]) {
160  continue;
161  }
162 
163  selected.push_back(values_[i]["label"]);
164  }
165 
166  if(selected.size() == values_.size()) {
167  set_label(_("multimenu^All Selected"));
168  } else {
169  if(selected.size() > max_shown_) {
170  const unsigned excess = selected.size() - max_shown_;
171  selected.resize(max_shown_ + 1);
172  selected.back() = VNGETTEXT("multimenu^$excess other", "$excess others", excess, {{"excess", std::to_string(excess)}});
173  }
174  set_label(utils::format_conjunct_list(_("multimenu^None Selected"), selected));
175  }
176 }
177 
179 {
180  for(unsigned i = 0; i < values_.size(); i++) {
181  ::config& c = values_[i];
182 
183  c["checkbox"] = toggle_states_[i];
184  }
185 }
186 
188 {
189  toggle_states_.reset();
191  update_label();
192 }
193 
195 {
196  assert(droplist_ != nullptr);
197 
199  fire(event::NOTIFY_MODIFIED, *this, nullptr);
200  update_label();
201 }
202 
203 void multimenu_button::select_option(const unsigned option, const bool selected)
204 {
205  assert(option < values_.size());
206 
207  if(option < toggle_states_.size()) {
208  toggle_states_.resize(option + 1);
209  }
210  toggle_states_[option] = selected;
212  update_label();
213 }
214 
215 void multimenu_button::select_options(boost::dynamic_bitset<> states)
216 {
217  assert(states.size() == values_.size());
218  toggle_states_ = states;
220  update_label();
221 }
222 
223 void multimenu_button::set_values(const std::vector<::config>& values)
224 {
225  set_is_dirty(true);
226 
227  values_ = values;
228  toggle_states_.resize(values_.size(), false);
229  toggle_states_.reset();
230 
231  set_label(_("multimenu^None Selected"));
232 }
233 
234 // }---------- DEFINITION ---------{
235 
238 {
239  DBG_GUI_P << "Parsing multimenu_button " << id << '\n';
240 
241  load_resolutions<resolution>(cfg);
242 }
243 
244 /*WIKI
245  * @page = GUIWidgetDefinitionWML
246  * @order = 1_multimenu_button
247  *
248  * == multimenu_button ==
249  *
250  * @macro = multimenu_button_description
251  *
252  * The following states exist:
253  * * state_enabled, the multimenu_button is enabled.
254  * * state_disabled, the multimenu_button is disabled.
255  * * state_pressed, the left mouse multimenu_button is down.
256  * * state_focused, the mouse is over the multimenu_button.
257  * @begin{parent}{name="gui/"}
258  * @begin{tag}{name="multimenu_button_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
259  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
260  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
261  * @end{tag}{name="state_enabled"}
262  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
263  * @end{tag}{name="state_disabled"}
264  * @begin{tag}{name="state_pressed"}{min=0}{max=1}{super="generic/state"}
265  * @end{tag}{name="state_pressed"}
266  * @begin{tag}{name="state_focused"}{min=0}{max=1}{super="generic/state"}
267  * @end{tag}{name="state_focused"}
268  * @end{tag}{name="resolution"}
269  * @end{tag}{name="multimenu_button_definition"}
270  * @end{parent}{name="gui/"}
271  */
273  : resolution_definition(cfg)
274 {
275  // Note the order should be the same as the enum state_t in multimenu_button.hpp.
276  state.emplace_back(cfg.child("state_enabled"));
277  state.emplace_back(cfg.child("state_disabled"));
278  state.emplace_back(cfg.child("state_pressed"));
279  state.emplace_back(cfg.child("state_focused"));
280 }
281 
282 // }---------- BUILDER -----------{
283 
284 /*WIKI_MACRO
285  * @begin{macro}{multimenu_button_description}
286  *
287  * A multimenu_button is a styled_widget to choose an element from a list of elements.
288  * @end{macro}
289  */
290 
291 /*WIKI
292  * @page = GUIWidgetInstanceWML
293  * @order = 2_multimenu_button
294  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
295  * @begin{tag}{name="multimenu_button"}{min=0}{max=-1}{super="generic/widget_instance"}
296  * == multimenu_button ==
297  *
298  * @macro = multimenu_button_description
299  *
300  * Instance of a multimenu_button. When a multimenu_button has a return value it sets the
301  * return value for the window. Normally this closes the window and returns
302  * this value to the caller. The return value can either be defined by the
303  * user or determined from the id of the multimenu_button. The return value has a
304  * higher precedence as the one defined by the id. (Of course it's weird to
305  * give a multimenu_button an id and then override its return value.)
306  *
307  * When the multimenu_button doesn't have a standard id, but you still want to use the
308  * return value of that id, use return_value_id instead. This has a higher
309  * precedence as return_value.
310  *
311  * List with the multimenu_button specific variables:
312  * @begin{table}{config}
313  * return_value_id & string & "" & The return value id. $
314  * return_value & int & 0 & The return value. $
315  * maximum_shown & int & -1 & The maximum number of currently selected values to list on the button. $
316  *
317  * @end{table}
318  * @end{tag}{name="multimenu_button"}
319  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
320  */
321 
322 namespace implementation
323 {
324 
325 builder_multimenu_button::builder_multimenu_button(const config& cfg)
326  : builder_styled_widget(cfg)
327  , max_shown_(cfg["maximum_shown"].to_unsigned(1))
328  , options_()
329 {
330  for(const auto& option : cfg.child_range("option")) {
331  options_.push_back(option);
332  }
333 }
334 
336 {
338 
339  widget->set_max_shown(max_shown_);
340  if(!options_.empty()) {
341  widget->set_values(options_);
342  }
343 
344  DBG_GUI_G << "Window builder: placed multimenu_button '" << id
345  << "' with definition '" << definition << "'.\n";
346 
347  return widget;
348 }
349 
350 } // namespace implementation
351 
352 // }------------ END --------------
353 
354 } // 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:68
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)
bool get_use_markup() const
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
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:307
Base class for all widgets.
Definition: widget.hpp:47
std::string sound_button_click
Definition: settings.cpp:39
#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
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:96
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:91
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:463
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:114
#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:1019
multimenu_button_definition(const config &cfg)
void select_option(const unsigned option, const bool selected=true)
Select an option in the menu.
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:130
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:92
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:63
#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:55