The Battle for Wesnoth  1.19.0-dev
menu_button.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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"
23 #include "gui/widgets/settings.hpp"
24 #include "gui/widgets/window.hpp"
25 #include "sound.hpp"
26 #include "wml_exception.hpp"
27 
28 #include <functional>
29 
30 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
31 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
32 
33 namespace gui2
34 {
35 
36 // ------------ WIDGET -----------{
37 
38 REGISTER_WIDGET(menu_button)
39 
40 menu_button::menu_button(const implementation::builder_menu_button& builder)
41  : styled_widget(builder, type())
42  , selectable_item()
43  , state_(ENABLED)
44  , values_()
45  , selected_(0)
46  , keep_open_(false)
47 {
48  values_.emplace_back("label", this->get_label());
49 
50  connect_signal<event::MOUSE_ENTER>(
51  std::bind(&menu_button::signal_handler_mouse_enter, this, std::placeholders::_2, std::placeholders::_3));
52 
53  connect_signal<event::MOUSE_LEAVE>(
54  std::bind(&menu_button::signal_handler_mouse_leave, this, std::placeholders::_2, std::placeholders::_3));
55 
56  connect_signal<event::LEFT_BUTTON_DOWN>(
57  std::bind(&menu_button::signal_handler_left_button_down, this, std::placeholders::_2, std::placeholders::_3));
58 
59  connect_signal<event::LEFT_BUTTON_UP>(
60  std::bind(&menu_button::signal_handler_left_button_up, this, std::placeholders::_2, std::placeholders::_3));
61 
62  connect_signal<event::LEFT_BUTTON_CLICK>(
63  std::bind(&menu_button::signal_handler_left_button_click, this, std::placeholders::_2, std::placeholders::_3));
64 
65  connect_signal<event::SDL_WHEEL_UP>(
66  std::bind(&menu_button::signal_handler_sdl_wheel_up, this, std::placeholders::_2, std::placeholders::_3),
68 
69  connect_signal<event::SDL_WHEEL_DOWN>(
70  std::bind(&menu_button::signal_handler_sdl_wheel_down, this, std::placeholders::_2, std::placeholders::_3),
72 }
73 
74 void menu_button::set_active(const bool active)
75 {
76  if(get_active() != active) {
77  set_state(active ? ENABLED : DISABLED);
78  }
79 }
80 
82 {
83  return state_ != DISABLED;
84 }
85 
86 unsigned menu_button::get_state() const
87 {
88  return state_;
89 }
90 
92 {
93  if(state != state_) {
94  state_ = state;
95  queue_redraw();
96  }
97 }
98 
100 {
101  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
102 
104  handled = true;
105 }
106 
108 {
109  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
110 
112  handled = true;
113 }
114 
116 {
117  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
118 
119  window* window = get_window();
120  if(window) {
122  }
123 
125  handled = true;
126 }
127 
129 {
130  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
131 
133  handled = true;
134 }
135 
137 {
138  assert(get_window());
139  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
140 
142 
143  // If a button has a retval do the default handling.
145 
146  if(droplist.show()) {
147  const int selected = droplist.selected_item();
148 
149  // Safety check. If the user clicks a selection in the dropdown and moves their mouse away too
150  // quickly, selected_ could be set to -1. This returns in that case, preventing crashes.
151  if(selected < 0) {
152  return;
153  }
154 
155  set_selected(selected, true);
156  }
157 
158  handled = true;
159 }
160 
162 {
163  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
164 
165  // TODO: should values wrap?
166  if(selected_ > 0) {
167  set_selected(selected_ - 1);
168  }
169 
170  handled = true;
171 }
172 
174 {
175  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
176 
177  // TODO: should values wrap?
178  if(selected_ < values_.size() - 1) {
179  set_selected(selected_ + 1);
180  }
181 
182  handled = true;
183 }
184 
185 void menu_button::set_values(const std::vector<::config>& values, unsigned selected)
186 {
187  assert(selected < values.size());
188  assert(selected_ < values_.size());
189 
190  if(values[selected]["label"] != values_[selected_]["label"]) {
191  queue_redraw();
192  }
193 
194  values_ = values;
196 
197  set_label(values_[selected_]["label"]);
198 }
199 
201 {
202  assert(selected < values_.size());
203  assert(selected_ < values_.size());
204 
205  if(selected != selected_) {
206  queue_redraw();
207  }
208 
210 
211  set_label(values_[selected_]["label"]);
212  if (fire_event) {
213  fire(event::NOTIFY_MODIFIED, *this, nullptr);
214  }
215 }
216 
217 // }---------- DEFINITION ---------{
218 
221 {
222  DBG_GUI_P << "Parsing menu_button " << id;
223 
224  load_resolutions<resolution>(cfg);
225 }
226 
228  : resolution_definition(cfg)
229 {
230  // Note the order should be the same as the enum state_t in menu_button.hpp.
231  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("menu_button_definition][resolution", "state_enabled")));
232  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("menu_button_definition][resolution", "state_disabled")));
233  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_pressed", missing_mandatory_wml_tag("menu_button_definition][resolution", "state_pressed")));
234  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_focused", missing_mandatory_wml_tag("menu_button_definition][resolution", "state_focused")));
235 }
236 
237 // }---------- BUILDER -----------{
238 
239 namespace implementation
240 {
241 
242 builder_menu_button::builder_menu_button(const config& cfg)
243  : builder_styled_widget(cfg)
244  , options_()
245 {
246  for(const auto& option : cfg.child_range("option")) {
247  options_.push_back(option);
248  }
249 }
250 
251 std::unique_ptr<widget> builder_menu_button::build() const
252 {
253  auto widget = std::make_unique<menu_button>(*this);
254 
255  if(!options_.empty()) {
256  widget->set_values(options_);
257  }
258 
259  DBG_GUI_G << "Window builder: placed menu_button '" << id
260  << "' with definition '" << definition << "'.";
261 
262  return widget;
263 }
264 
265 } // namespace implementation
266 
267 // }------------ END --------------
268 
269 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
child_itors child_range(config_key_type key)
Definition: config.cpp:273
Used by the menu_button widget.
bool show(const unsigned auto_close_time=0)
Shows the window.
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:74
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:59
void set_state(const state_t state)
Definition: menu_button.cpp:91
void signal_handler_left_button_click(const event::ui_event event, bool &handled)
state_t state_
Current state of the widget.
void set_selected(unsigned selected, bool fire_event=true)
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
void set_values(const std::vector<::config > &values, unsigned selected=0)
void signal_handler_mouse_enter(const event::ui_event event, bool &handled)
Definition: menu_button.cpp:99
virtual unsigned get_state() const override
See styled_widget::get_state.
Definition: menu_button.cpp:86
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:74
void signal_handler_sdl_wheel_up(const event::ui_event event, bool &handled)
void signal_handler_sdl_wheel_down(const event::ui_event event, bool &handled)
state_t
Possible states of the widget.
virtual bool get_active() const override
See styled_widget::get_active.
Definition: menu_button.cpp:81
std::vector<::config > values_
Small abstract helper class.
Base class for all visible items.
virtual void set_label(const t_string &text)
Base class for all widgets.
Definition: widget.hpp:53
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:455
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:63
void mouse_capture(const bool capture=true)
Definition: window.cpp:1215
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...
#define LOG_HEADER
Definition: menu_button.cpp:31
std::string selected
bool fire_event(const ui_event event, const std::vector< std::pair< widget *, ui_event >> &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
@ NOTIFY_MODIFIED
Definition: handler.hpp:158
std::string sound_button_click
Definition: settings.cpp:48
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:1064
#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.
menu_button_definition(const config &cfg)
Base class of a resolution, contains the common keys for a resolution.
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)