The Battle for Wesnoth  1.17.23+dev
menu_button.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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 #include "wml_exception.hpp"
29 #include "gettext.hpp"
30 
31 #include <functional>
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(menu_button)
42 
43 menu_button::menu_button(const implementation::builder_menu_button& builder)
44  : styled_widget(builder, type())
45  , selectable_item()
46  , state_(ENABLED)
47  , values_()
48  , selected_(0)
49  , keep_open_(false)
50 {
51  values_.emplace_back("label", this->get_label());
52 
53  connect_signal<event::MOUSE_ENTER>(
54  std::bind(&menu_button::signal_handler_mouse_enter, this, std::placeholders::_2, std::placeholders::_3));
55 
56  connect_signal<event::MOUSE_LEAVE>(
57  std::bind(&menu_button::signal_handler_mouse_leave, this, std::placeholders::_2, std::placeholders::_3));
58 
59  connect_signal<event::LEFT_BUTTON_DOWN>(
60  std::bind(&menu_button::signal_handler_left_button_down, this, std::placeholders::_2, std::placeholders::_3));
61 
62  connect_signal<event::LEFT_BUTTON_UP>(
63  std::bind(&menu_button::signal_handler_left_button_up, this, std::placeholders::_2, std::placeholders::_3));
64 
65  connect_signal<event::LEFT_BUTTON_CLICK>(
66  std::bind(&menu_button::signal_handler_left_button_click, this, std::placeholders::_2, std::placeholders::_3));
67 
68  connect_signal<event::SDL_WHEEL_UP>(
69  std::bind(&menu_button::signal_handler_sdl_wheel_up, this, std::placeholders::_2, std::placeholders::_3),
71 
72  connect_signal<event::SDL_WHEEL_DOWN>(
73  std::bind(&menu_button::signal_handler_sdl_wheel_down, this, std::placeholders::_2, std::placeholders::_3),
75 }
76 
77 void menu_button::set_active(const bool active)
78 {
79  if(get_active() != active) {
80  set_state(active ? ENABLED : DISABLED);
81  }
82 }
83 
85 {
86  return state_ != DISABLED;
87 }
88 
89 unsigned menu_button::get_state() const
90 {
91  return state_;
92 }
93 
95 {
96  if(state != state_) {
97  state_ = state;
98  queue_redraw();
99  }
100 }
101 
103 {
104  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
105 
107  handled = true;
108 }
109 
111 {
112  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
113 
115  handled = true;
116 }
117 
119 {
120  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
121 
122  window* window = get_window();
123  if(window) {
125  }
126 
128  handled = true;
129 }
130 
132 {
133  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
134 
136  handled = true;
137 }
138 
140 {
141  assert(get_window());
142  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
143 
145 
146  // If a button has a retval do the default handling.
148 
149  if(droplist.show()) {
150  const int selected = droplist.selected_item();
151 
152  // Safety check. If the user clicks a selection in the dropdown and moves their mouse away too
153  // quickly, selected_ could be set to -1. This returns in that case, preventing crashes.
154  if(selected < 0) {
155  return;
156  }
157 
158  set_selected(selected, true);
159  }
160 
161  handled = true;
162 }
163 
165 {
166  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
167 
168  // TODO: should values wrap?
169  if(selected_ > 0) {
170  set_selected(selected_ - 1);
171  }
172 
173  handled = true;
174 }
175 
177 {
178  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
179 
180  // TODO: should values wrap?
181  if(selected_ < values_.size() - 1) {
182  set_selected(selected_ + 1);
183  }
184 
185  handled = true;
186 }
187 
188 void menu_button::set_values(const std::vector<::config>& values, unsigned selected)
189 {
190  assert(selected < values.size());
191  assert(selected_ < values_.size());
192 
193  if(values[selected]["label"] != values_[selected_]["label"]) {
194  queue_redraw();
195  }
196 
197  values_ = values;
199 
200  set_label(values_[selected_]["label"]);
201 }
202 
204 {
205  assert(selected < values_.size());
206  assert(selected_ < values_.size());
207 
208  if(selected != selected_) {
209  queue_redraw();
210  }
211 
213 
214  set_label(values_[selected_]["label"]);
215  if (fire_event) {
216  fire(event::NOTIFY_MODIFIED, *this, nullptr);
217  }
218 }
219 
220 // }---------- DEFINITION ---------{
221 
224 {
225  DBG_GUI_P << "Parsing menu_button " << id;
226 
227  load_resolutions<resolution>(cfg);
228 }
229 
231  : resolution_definition(cfg)
232 {
233  // Note the order should be the same as the enum state_t in menu_button.hpp.
234  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", _("Missing required state for menu button")));
235  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", _("Missing required state for menu button")));
236  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_pressed", _("Missing required state for menu button")));
237  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_focused", _("Missing required state for menu button")));
238 }
239 
240 // }---------- BUILDER -----------{
241 
242 namespace implementation
243 {
244 
245 builder_menu_button::builder_menu_button(const config& cfg)
246  : builder_styled_widget(cfg)
247  , options_()
248 {
249  for(const auto& option : cfg.child_range("option")) {
250  options_.push_back(option);
251  }
252 }
253 
254 std::unique_ptr<widget> builder_menu_button::build() const
255 {
256  auto widget = std::make_unique<menu_button>(*this);
257 
258  if(!options_.empty()) {
259  widget->set_values(options_);
260  }
261 
262  DBG_GUI_G << "Window builder: placed menu_button '" << id
263  << "' with definition '" << definition << "'.";
264 
265  return widget;
266 }
267 
268 } // namespace implementation
269 
270 // }------------ END --------------
271 
272 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
child_itors child_range(config_key_type key)
Definition: config.cpp:277
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:76
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:62
void set_state(const state_t state)
Definition: menu_button.cpp:94
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)
virtual unsigned get_state() const override
See styled_widget::get_state.
Definition: menu_button.cpp:89
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:77
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:84
std::vector<::config > values_
Small abstract helper class.
Base class for all visible items.
virtual void set_label(const t_string &label)
Base class for all widgets.
Definition: widget.hpp:54
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:456
window * get_window()
Get the parent window.
Definition: widget.cpp:118
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:67
void mouse_capture(const bool capture=true)
Definition: window.cpp:1218
static std::string _(const char *str)
Definition: gettext.hpp:93
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:34
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:1066
#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
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)