The Battle for Wesnoth  1.19.7+dev
toggle_panel.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 
21 #include "gui/widgets/settings.hpp"
22 #include "gui/widgets/window.hpp"
23 #include "gui/core/log.hpp"
25 #include "gettext.hpp"
26 #include "sound.hpp"
27 #include "wml_exception.hpp"
28 
29 #include <functional>
30 
31 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
32 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
33 
34 namespace gui2
35 {
36 
37 // ------------ WIDGET -----------{
38 
39 REGISTER_WIDGET(toggle_panel)
40 
41 toggle_panel::toggle_panel(const implementation::builder_toggle_panel& builder)
42  : panel(builder, type())
43  , state_(ENABLED)
44  , state_num_(0)
45  , retval_(retval::NONE)
46 {
47  set_wants_mouse_left_double_click();
48 
49  connect_signal<event::MOUSE_ENTER>(std::bind(
50  &toggle_panel::signal_handler_mouse_enter, this, std::placeholders::_2, std::placeholders::_3));
51  connect_signal<event::MOUSE_LEAVE>(std::bind(
52  &toggle_panel::signal_handler_mouse_leave, this, std::placeholders::_2, std::placeholders::_3));
53 #if 0
54  connect_signal<event::LEFT_BUTTON_CLICK>(
56  this,
57  std::placeholders::_2),
59 #endif
60  connect_signal<event::LEFT_BUTTON_CLICK>(std::bind(
61  &toggle_panel::signal_handler_left_button_click, this, std::placeholders::_2, std::placeholders::_3));
62  connect_signal<event::LEFT_BUTTON_CLICK>(
64  this,
65  std::placeholders::_2,
66  std::placeholders::_3),
68  connect_signal<event::LEFT_BUTTON_DOUBLE_CLICK>(
70  this,
71  std::placeholders::_2,
72  std::placeholders::_3));
73  connect_signal<event::LEFT_BUTTON_DOUBLE_CLICK>(
75  this,
76  std::placeholders::_2,
77  std::placeholders::_3),
79 }
80 
81 unsigned toggle_panel::num_states() const
82 {
83  std::div_t res = std::div(this->get_config()->state.size(), COUNT);
84  assert(res.rem == 0);
85  assert(res.quot > 0);
86  return res.quot;
87 }
88 
90 {
91  for(const auto & item : data)
92  {
93  styled_widget* control = dynamic_cast<styled_widget*>(find(item.first, false));
94  if(control) {
95  control->set_members(item.second);
96  }
97  }
98 }
100  const bool must_be_active)
101 {
102  /**
103  * @todo since there is no mouse event nesting (or event nesting at all)
104  * we need to capture all events. This means items on the panel will
105  * never receive an event, which gives problems with for example the
106  * intended button on the addon panel. So we need to chain mouse events
107  * as well and also add a handled flag for them.
108  */
109 
110  widget* result = container_base::find_at(coordinate, must_be_active);
111  return result ? result : styled_widget::find_at(coordinate, must_be_active);
112 }
113 
115  const bool must_be_active) const
116 {
117  const widget* result = container_base::find_at(coordinate, must_be_active);
118  return result ? result : styled_widget::find_at(coordinate, must_be_active);
119 }
120 
121 void toggle_panel::set_active(const bool active)
122 {
123  if(active) {
125  } else {
127  }
128 }
129 
131 {
132  return state_ != DISABLED;
133 }
134 
135 unsigned toggle_panel::get_state() const
136 {
137  return state_ + COUNT * state_num_;
138 }
139 
141 {
142  const auto conf = cast_config_to<toggle_panel_definition>();
143  assert(conf);
144 
145  SDL_Rect result = get_rectangle();
146  result.x += conf->left_border;
147  result.y += conf->top_border;
148  result.w -= conf->left_border + conf->right_border;
149  result.h -= conf->top_border + conf->bottom_border;
150 
151  return result;
152 }
153 
155 {
156  const auto conf = cast_config_to<toggle_panel_definition>();
157  assert(conf);
158 
159  return point(conf->left_border + conf->right_border, conf->top_border + conf->bottom_border);
160 }
161 
163 {
165  if(selected == get_value()) {
166  return;
167  }
169  queue_redraw();
170 
171  // Check for get_window() is here to prevent the callback from
172  // being called when the initial value is set.
173  if(get_window() && fire_event) {
174  fire(event::NOTIFY_MODIFIED, *this, nullptr);
175  }
176 }
177 
179 {
180  retval_ = retval;
181 }
182 
184 {
185  if(state == state_) {
186  return;
187  }
188 
189  state_ = state;
190  queue_redraw();
191 
192  const auto conf = cast_config_to<toggle_panel_definition>();
193  assert(conf);
194 }
195 
197 {
198  // We don't have a fore and background and need to draw depending on
199  // our state, like a styled_widget. So we use the styled_widget's drawing method.
201 }
202 
204 {
205  // We don't have a fore and background and need to draw depending on
206  // our state, like a styled_widget. So we use the styled_widget's drawing method.
208 }
209 
211  bool& handled)
212 {
213  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
214 
216  handled = true;
217 }
218 
220  bool& handled)
221 {
222  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
223 
225  handled = true;
226 }
227 
228 void
230 {
231  DBG_GUI_E << get_control_type() << "[" << id() << "]: " << event << ".";
232 
233  set_value(1, true);
234 
235 #if 0
236  /*
237  * Disabled since it causes issues with gamestate inspector (bug #22095).
238  * It was added in b84f2ebff0b53c7e4194da315c43f62a08494c52 for the lobby,
239  * since that code is still experimental, prefer to fix a real issue caused
240  * by it.
241  *
242  * The issue is that the gui2::listbox::add_row code was changed to
243  * increase the content size. Before the list was shown the list was
244  * cleared. The clear operation did not reduce the size (since the widgets
245  * were not shown yet). The add operation afterwards again reserved the
246  * space causing the size of the listbox to be twice the required space.
247  *
248  * 2014.06.09 -- Mordante
249  */
250 
251  fire(event::NOTIFY_MODIFIED, *this, nullptr);
252 #endif
253 }
254 
256  bool& handled)
257 {
258  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
259 
261 
262  set_value(get_value() + 1, true);
263 
264  handled = true;
265 }
266 
268  const event::ui_event event, bool& handled)
269 {
270  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
271 
272  if(retval_) {
273  window* window = get_window();
274  assert(window);
275 
277  }
278 
279  handled = true;
280 }
281 
282 // }---------- DEFINITION ---------{
283 
286 {
287  DBG_GUI_P << "Parsing toggle panel " << id;
288 
289  load_resolutions<resolution>(cfg);
290 }
291 
293  : resolution_definition(cfg)
294  , top_border(cfg["top_border"].to_unsigned())
295  , bottom_border(cfg["bottom_border"].to_unsigned())
296  , left_border(cfg["left_border"].to_unsigned())
297  , right_border(cfg["right_border"].to_unsigned())
298 {
299  // Note the order should be the same as the enum state_t in toggle_panel.hpp.
300  for(const auto& c : cfg.child_range("state"))
301  {
302  state.emplace_back(VALIDATE_WML_CHILD(c, "enabled", missing_mandatory_wml_tag("toggle_panel_definition][resolution][state", "enabled")));
303  state.emplace_back(VALIDATE_WML_CHILD(c, "disabled", missing_mandatory_wml_tag("toggle_panel_definition][resolution][state", "disabled")));
304  state.emplace_back(VALIDATE_WML_CHILD(c, "focused", missing_mandatory_wml_tag("toggle_panel_definition][resolution][state", "focused")));
305  }
306 }
307 
308 // }---------- BUILDER -----------{
309 
310 namespace implementation
311 {
312 
313 builder_toggle_panel::builder_toggle_panel(const config& cfg)
314  : builder_styled_widget(cfg)
315  , grid(nullptr)
316  , retval_id_(cfg["return_value_id"])
317  , retval_(cfg["return_value"].to_int())
318 {
319  auto c = cfg.optional_child("grid");
320 
321  VALIDATE(c, _("No grid defined."));
322 
323  grid = std::make_shared<builder_grid>(*c);
324 }
325 
326 std::unique_ptr<widget> builder_toggle_panel::build() const
327 {
328  auto widget = std::make_unique<toggle_panel>(*this);
329 
330  widget->set_retval(get_retval(retval_id_, retval_, id));
331 
332  DBG_GUI_G << "Window builder: placed toggle panel '" << id
333  << "' with definition '" << definition << "'.";
334 
335  widget->init_grid(*grid);
336  return widget;
337 }
338 
339 } // namespace implementation
340 
341 // }------------ END --------------
342 
343 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
child_itors child_range(config_key_type key)
Definition: config.cpp:272
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:74
Base container class.
Definition: grid.hpp:32
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
virtual void set_members(const widget_item &data)
Sets the members of the styled_widget.
virtual bool impl_draw_background() override
See widget::impl_draw_background.
virtual bool impl_draw_foreground() override
See widget::impl_draw_foreground.
resolution_definition_ptr get_config()
void signal_handler_left_button_double_click(const event::ui_event event, bool &handled)
unsigned state_num_
Usually 1 for selected and 0 for not selected, can also have higher values in tristate buttons.
virtual bool impl_draw_foreground() override
See widget::impl_draw_foreground.
virtual bool impl_draw_background() override
See widget::impl_draw_background.
void signal_handler_mouse_enter(const event::ui_event event, bool &handled)
void set_state(const state_t state)
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
void set_child_members(const widget_data &data)
Sets the members of the child controls.
virtual unsigned get_state() const override
See styled_widget::get_state.
virtual void set_active(const bool active) override
See styled_widget::set_active.
int retval_
The return value of the button.
void set_retval(const int retval)
virtual unsigned get_value() const override
Inherited from selectable_item.
void signal_handler_left_button_click(const event::ui_event event, bool &handled)
virtual point border_space() const override
See container_base::border_space.
virtual bool get_active() const override
See styled_widget::get_active.
virtual void set_value(unsigned selected, bool fire_event=false) override
Inherited from selectable_item.
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
state_t state_
Current state of the widget.
virtual unsigned num_states() const override
Inherited from selectable_item.
void signal_handler_pre_left_button_click(const event::ui_event event)
state_t
Possible states of the widget.
virtual const std::string & get_control_type() const override
Inherited from styled_widget, implemented by REGISTER_WIDGET.
virtual SDL_Rect get_client_rect() const override
See container_base::get_client_rect.
Base class for all widgets.
Definition: widget.hpp:55
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:464
const std::string & id() const
Definition: widget.cpp:110
window * get_window()
Get the parent window.
Definition: widget.cpp:117
rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:321
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:394
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...
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
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
int get_retval(const std::string &retval_id, const int retval, const std::string &id)
Returns the return value for a widget.
Definition: helper.cpp:137
std::string sound_toggle_panel_click
Definition: settings.cpp:42
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
retval
Default window/dialog return values.
Definition: retval.hpp:30
@ NONE
Default, unset return value.
Definition: retval.hpp:32
Contains the implementation details for lexical_cast and shouldn't be used directly.
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1078
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::string_view data
Definition: picture.cpp:178
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
This file contains the settings handling of the widget library.
std::string definition
Parameters for the styled_widget.
virtual std::unique_ptr< widget > build() const override
std::vector< state_definition > state
toggle_panel_definition(const config &cfg)
Holds a 2D point.
Definition: point.hpp:25
mock_char c
#define LOG_HEADER
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 VALIDATE(cond, message)
The macro to use for the validation of WML.