The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
multimenu_button.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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 "sound.hpp"
26
28#include <functional>
29#include "gettext.hpp"
30#include "wml_exception.hpp"
31
32#define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
33#define LOG_HEADER LOG_SCOPE_HEADER + ':'
34
35namespace gui2
36{
37
38// ------------ WIDGET -----------{
39
40REGISTER_WIDGET(multimenu_button)
41
42multimenu_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, std::placeholders::_2, std::placeholders::_3));
54 connect_signal<event::MOUSE_LEAVE>(
55 std::bind(&multimenu_button::signal_handler_mouse_leave, this, std::placeholders::_2, std::placeholders::_3));
56
57 connect_signal<event::LEFT_BUTTON_DOWN>(
58 std::bind(&multimenu_button::signal_handler_left_button_down, this, std::placeholders::_2, std::placeholders::_3));
59 connect_signal<event::LEFT_BUTTON_UP>(
60 std::bind(&multimenu_button::signal_handler_left_button_up, this, std::placeholders::_2, std::placeholders::_3));
61 connect_signal<event::LEFT_BUTTON_CLICK>(
62 std::bind(&multimenu_button::signal_handler_left_button_click, this, std::placeholders::_2, std::placeholders::_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
70void 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;
92 }
93}
94
96{
97 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
98
100 handled = true;
101}
102
104{
105 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
106
108 handled = true;
109}
110
112{
113 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
114
116 if(window) {
118 }
119
121 handled = true;
122}
123
125{
126 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
127
129 handled = true;
130}
131
133{
134 assert(get_window());
135 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
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"].t_str());
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 // TRANSLATORS: In a drop-down menu that's a list of toggle-boxes, this becomes part
177 // of the text on the button when many of the boxes are selected. The text becomes
178 // "x, y and 1 other", "x, y and 2 others", etc.
179 selected.back() = VNGETTEXT("multimenu^$excess other", "$excess others", excess, {{"excess", std::to_string(excess)}});
180 }
181 set_label(utils::format_conjunct_list(_("multimenu^None Selected"), selected));
182 }
183}
184
186{
187 for(unsigned i = 0; i < values_.size(); i++) {
188 ::config& c = values_[i];
189
190 c["checkbox"] = toggle_states_[i];
191 }
192}
193
200
208
209void multimenu_button::select_option(const unsigned option, const bool selected)
210{
211 assert(option < values_.size());
212 toggle_states_[option] = selected;
214 update_label();
215}
216
217void multimenu_button::select_options(const boost::dynamic_bitset<>& states)
218{
219 assert(states.size() == values_.size());
220 toggle_states_ = states;
222 update_label();
223}
224
225void multimenu_button::set_values(const std::vector<::config>& values)
226{
227 queue_redraw(); // TODO: draw_manager - does this need a relayout first?
228
229 values_ = values;
230 toggle_states_.resize(values_.size(), false);
231 toggle_states_.reset();
232
233 set_label(_("multimenu^None Selected"));
234}
235
236// }---------- DEFINITION ---------{
237
240{
241 DBG_GUI_P << "Parsing multimenu_button " << id;
242
243 load_resolutions<resolution>(cfg);
244}
245
248{
249 // Note the order should be the same as the enum state_t in multimenu_button.hpp.
250 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("multimenu_button_definition][resolution", "state_enabled")));
251 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("multimenu_button_definition][resolution", "state_disabled")));
252 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_pressed", missing_mandatory_wml_tag("multimenu_button_definition][resolution", "state_pressed")));
253 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_focused", missing_mandatory_wml_tag("multimenu_button_definition][resolution", "state_focused")));
254}
255
256// }---------- BUILDER -----------{
257
258namespace implementation
259{
260
261builder_multimenu_button::builder_multimenu_button(const config& cfg)
263 , max_shown_(cfg["maximum_shown"].to_unsigned(1))
264 , options_()
265{
266 for(const auto& option : cfg.child_range("option")) {
267 options_.push_back(option);
268 }
269}
270
271std::unique_ptr<widget> builder_multimenu_button::build() const
272{
273 auto widget = std::make_unique<multimenu_button>(*this);
274
275 widget->set_max_shown(max_shown_);
276 if(!options_.empty()) {
277 widget->set_values(options_);
278 }
279
280 DBG_GUI_G << "Window builder: placed multimenu_button '" << id
281 << "' with definition '" << definition << "'.";
282
283 return widget;
284}
285
286} // namespace implementation
287
288// }------------ END --------------
289
290} // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
child_itors child_range(std::string_view key)
Definition config.cpp:268
Used by the menu_button widget.
boost::dynamic_bitset get_toggle_states() const
If a toggle button widget is present, returns the toggled state of each row's button.
bool show(const unsigned auto_close_time=0)
Shows the window.
boost::dynamic_bitset toggle_states_
void signal_handler_mouse_enter(const event::ui_event event, bool &handled)
state_t state_
Current state of the widget.
void signal_handler_left_button_click(const event::ui_event event, bool &handled)
void reset_toggle_states()
Deselect all the menu options.
void select_options(const boost::dynamic_bitset<> &states)
Set the options selected in the menu.
void set_values(const std::vector<::config > &values)
Set the available menu options.
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
std::vector<::config > values_
virtual bool get_active() const override
See styled_widget::get_active.
state_t
Possible states of the widget.
virtual void set_active(const bool active) override
See styled_widget::set_active.
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
void select_option(const unsigned option, const bool selected=true)
Select an option in the menu.
dialogs::drop_down_menu * droplist_
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
unsigned max_shown_
The maximum number of selected states to list in the label.
void set_state(const state_t state)
virtual unsigned get_state() const override
See styled_widget::get_state.
virtual void set_label(const t_string &text)
Base class for all widgets.
Definition widget.hpp:55
void queue_redraw()
Indicates that this widget should be redrawn.
Definition widget.cpp:464
window * get_window()
Get the parent window.
Definition widget.cpp:117
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition window.hpp:59
void mouse_capture(const bool capture=true)
Definition window.cpp:1191
#define LOG_HEADER
const config * cfg
#define VNGETTEXT(msgid, msgid_plural, count,...)
static std::string _(const char *str)
Definition gettext.hpp:100
Define the common log macros for the gui toolkit.
#define DBG_GUI_G
Definition log.hpp:41
#define DBG_GUI_P
Definition log.hpp:66
#define DBG_GUI_E
Definition log.hpp:35
This file contains the window object, this object is a top level container which has the event manage...
ui_event
The event sent to the dispatcher.
Definition handler.hpp:115
std::string sound_button_click
Definition settings.cpp:50
Generic file dialog.
Contains the implementation details for lexical_cast and shouldn't be used directly.
void play_UI_sound(const std::string &files)
Definition sound.cpp:1009
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
This file contains the settings handling of the widget library.
virtual std::unique_ptr< widget > build() const override
std::string definition
Parameters for the styled_widget.
std::vector< state_definition > state
std::string missing_mandatory_wml_tag(const std::string &section, const std::string &tag)
Returns a standard message for a missing wml child (tag).
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE_WML_CHILD(cfg, key, message)
#define i
#define c