The Battle for Wesnoth  1.15.2+dev
toggle_panel.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 
20 #include "gui/widgets/settings.hpp"
21 #include "gui/widgets/window.hpp"
22 #include "gui/core/log.hpp"
24 #include "gettext.hpp"
25 #include "sound.hpp"
26 #include "wml_exception.hpp"
27 
28 #include "utils/functional.hpp"
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(toggle_panel)
39 
40 toggle_panel::toggle_panel(const implementation::builder_toggle_panel& builder)
41  : panel(builder, type())
42  , state_(ENABLED)
43  , state_num_(0)
44  , retval_(retval::NONE)
45 {
46  set_wants_mouse_left_double_click();
47 
48  connect_signal<event::MOUSE_ENTER>(std::bind(
50  connect_signal<event::MOUSE_LEAVE>(std::bind(
52 #if 0
53  connect_signal<event::LEFT_BUTTON_CLICK>(
55  this,
56  _2),
58 #endif
59  connect_signal<event::LEFT_BUTTON_CLICK>(std::bind(
61  connect_signal<event::LEFT_BUTTON_CLICK>(
63  this,
64  _2,
65  _3),
67  connect_signal<event::LEFT_BUTTON_DOUBLE_CLICK>(
69  this,
70  _2,
71  _3));
72  connect_signal<event::LEFT_BUTTON_DOUBLE_CLICK>(
74  this,
75  _2,
76  _3),
78 }
79 
80 unsigned toggle_panel::num_states() const
81 {
82  std::div_t res = std::div(this->config()->state.size(), COUNT);
83  assert(res.rem == 0);
84  assert(res.quot > 0);
85  return res.quot;
86 }
87 
89  const std::map<std::string /* widget id */, string_map>& data)
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 {
164  selected = selected % num_states();
165  if(selected == get_value()) {
166  return;
167  }
169  set_is_dirty(true);
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  set_is_dirty(true);
191 
192  const auto conf = cast_config_to<toggle_panel_definition>();
193  assert(conf);
194 }
195 
197  int x_offset,
198  int y_offset)
199 {
200  // We don't have a fore and background and need to draw depending on
201  // our state, like a styled_widget. So we use the styled_widget's drawing method.
202  styled_widget::impl_draw_background(frame_buffer, x_offset, y_offset);
203 }
204 
206  int x_offset,
207  int y_offset)
208 {
209  // We don't have a fore and background and need to draw depending on
210  // our state, like a styled_widget. So we use the styled_widget's drawing method.
211  styled_widget::impl_draw_foreground(frame_buffer, x_offset, y_offset);
212 }
213 
215  bool& handled)
216 {
217  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
218 
220  handled = true;
221 }
222 
224  bool& handled)
225 {
226  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
227 
229  handled = true;
230 }
231 
232 void
234 {
235  DBG_GUI_E << get_control_type() << "[" << id() << "]: " << event << ".\n";
236 
237  set_value(1, true);
238 
239 #if 0
240  /*
241  * Disabled since it causes issues with gamestate inspector (bug #22095).
242  * It was added in b84f2ebff0b53c7e4194da315c43f62a08494c52 for the lobby,
243  * since that code is still experimental, prefer to fix a real issue caused
244  * by it.
245  *
246  * The issue is that the gui2::listbox::add_row code was changed to
247  * increase the content size. Before the list was shown the list was
248  * cleared. The clear operation did not reduce the size (since the widgets
249  * were not shown yet). The add operation afterwards again reserved the
250  * space causing the size of the listbox to be twice the required space.
251  *
252  * 2014.06.09 -- Mordante
253  */
254 
255  fire(event::NOTIFY_MODIFIED, *this, nullptr);
256 #endif
257 }
258 
260  bool& handled)
261 {
262  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
263 
265 
266  set_value(get_value() + 1, true);
267 
268  handled = true;
269 }
270 
272  const event::ui_event event, bool& handled)
273 {
274  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
275 
276  if(retval_) {
277  window* window = get_window();
278  assert(window);
279 
280  window->set_retval(retval_);
281  }
282 
283  handled = true;
284 }
285 
286 // }---------- DEFINITION ---------{
287 
290 {
291  DBG_GUI_P << "Parsing toggle panel " << id << '\n';
292 
293  load_resolutions<resolution>(cfg);
294 }
295 
296 /*WIKI
297  * @page = GUIWidgetDefinitionWML
298  * @order = 1_toggle_panel
299  *
300  * == Toggle panel ==
301  *
302  * @begin{parent}{name="gui/"}
303  * @begin{tag}{name="oggle_panel_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
304  * The definition of a toggle panel. A toggle panel is like a toggle button, but
305  * instead of being a button it's a panel. This means it can hold multiple child
306  * items.
307  *
308  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super=generic/widget_definition/resolution}
309  * The resolution for a toggle panel also contains the following keys:
310  * @begin{table}{config}
311  * top_border & unsigned & 0 & The size which isn't used for the client
312  * area. $
313  * bottom_border & unsigned & 0 & The size which isn't used for the client
314  * area. $
315  * left_border & unsigned & 0 & The size which isn't used for the client
316  * area. $
317  * right_border & unsigned & 0 & The size which isn't used for the client
318  * area. $
319  * @end{table}
320  *
321  * The following states exist:
322  * * state_enabled, the panel is enabled and not selected.
323  * * state_disabled, the panel is disabled and not selected.
324  * * state_focused, the mouse is over the panel and not selected.
325  *
326  * * state_enabled_selected, the panel is enabled and selected.
327  * * state_disabled_selected, the panel is disabled and selected.
328  * * state_focused_selected, the mouse is over the panel and selected.
329  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
330  * @end{tag}{name="state_enabled"}
331  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
332  * @end{tag}{name="state_disabled"}
333  * @begin{tag}{name="state_focused"}{min=0}{max=1}{super="generic/state"}
334  * @end{tag}{name="state_focused"}
335  * @begin{tag}{name="state_enabled_selected"}{min=0}{max=1}{super="generic/state"}
336  * @end{tag}{name="state_enabled_selected"}
337  * @begin{tag}{name="state_disabled_selected"}{min=0}{max=1}{super="generic/state"}
338  * @end{tag}{name="state_disabled_selected"}
339  * @begin{tag}{name="state_focused_selected"}{min=0}{max=1}{super="generic/state"}
340  * @end{tag}{name="state_focused_selected"}
341  * @end{tag}{name="resolution"}
342  * @end{tag}{name="oggle_panel_definition"}
343  * @end{parent}{name="gui/"}
344  */
346  : resolution_definition(cfg)
347  , top_border(cfg["top_border"])
348  , bottom_border(cfg["bottom_border"])
349  , left_border(cfg["left_border"])
350  , right_border(cfg["right_border"])
351 {
352  // Note the order should be the same as the enum state_t in toggle_panel.hpp.
353  for(const auto& c : cfg.child_range("state"))
354  {
355  state.emplace_back(c.child("enabled"));
356  state.emplace_back(c.child("disabled"));
357  state.emplace_back(c.child("focused"));
358  }
359 }
360 
361 // }---------- BUILDER -----------{
362 
363 /*WIKI
364  * @page = GUIWidgetInstanceWML
365  * @order = 2_toggle_panel
366  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
367  * @begin{tag}{name="toggle_panel"}{min=0}{max=-1}{super="generic/widget_instance"}
368  * == Toggle panel ==
369  *
370  * A toggle panel is an item which can hold other items. The difference between
371  * a grid and a panel is that it's possible to define how a panel looks. A grid
372  * in an invisible container to just hold the items. The toggle panel is a
373  * combination of the panel and a toggle button, it allows a toggle button with
374  * its own grid.
375  *
376  * @begin{table}{config}
377  * grid & grid & & Defines the grid with the widgets to
378  * place on the panel. $
379  * return_value_id & string & "" & The return value id, see
380  * [[GUIToolkitWML#Button]] for more
381  * information. $
382  * return_value & int & 0 & The return value, see
383  * [[GUIToolkitWML#Button]] for more
384  * information. $
385  * @end{table}
386  * @allow{link}{name="gui/window/resolution/grid"}
387  * @end{tag}{name="toggle_panel"}
388  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
389  */
390 
391 namespace implementation
392 {
393 
394 builder_toggle_panel::builder_toggle_panel(const config& cfg)
395  : builder_styled_widget(cfg)
396  , grid(nullptr)
397  , retval_id_(cfg["return_value_id"])
398  , retval_(cfg["return_value"])
399 {
400  const config& c = cfg.child("grid");
401 
402  VALIDATE(c, _("No grid defined."));
403 
404  grid = std::make_shared<builder_grid>(c);
405 }
406 
408 {
409  toggle_panel* widget = new toggle_panel(*this);
410 
411  widget->set_retval(get_retval(retval_id_, retval_, id));
412 
413  DBG_GUI_G << "Window builder: placed toggle panel '" << id
414  << "' with definition '" << definition << "'.\n";
415 
416  widget->init_grid(grid);
417  return widget;
418 }
419 
420 } // namespace implementation
421 
422 // }------------ END --------------
423 
424 } // namespace gui2
Define the common log macros for the gui toolkit.
Base class of a resolution, contains the common keys for a resolution.
#define DBG_GUI_P
Definition: log.hpp:68
Class for a toggle button.
virtual unsigned get_state() const override
See styled_widget::get_state.
virtual void set_value(unsigned selected, bool fire_event=false) override
Inherited from selectable_item.
#define LOG_HEADER
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::vector< state_definition > state
unsigned state_num_
Usually 1 for selected and 0 for not selected, can also have higher values in tristate buttons...
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
virtual bool get_active() const override
See styled_widget::get_active.
void set_child_members(const std::map< std::string, string_map > &data)
Sets the members of the child controls.
Visible container to hold multiple widgets.
Definition: panel.hpp:37
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
virtual point border_space() const override
See container_base::border_space.
virtual void set_members(const string_map &data)
Sets the members of the styled_widget.
const std::string & id() const
Definition: widget.cpp:107
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
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:136
virtual const std::string & get_control_type() const override
Inherited from styled_widget, implemented by REGISTER_WIDGET.
Generic file dialog.
Definition: field-fwd.hpp:22
toggle_panel_definition(const config &cfg)
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:96
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
void signal_handler_pre_left_button_click(const event::ui_event event)
Base container class.
Definition: grid.hpp:30
void signal_handler_mouse_leave(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.
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
This file contains the settings handling of the widget library.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:463
void set_state(const state_t state)
virtual void impl_draw_foreground(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_foreground.
std::string selected
void signal_handler_left_button_click(const event::ui_event event, bool &handled)
virtual unsigned num_states() const override
Inherited from selectable_item.
void init_grid(const std::shared_ptr< builder_grid > &grid_builder)
Initializes and builds the grid.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
virtual void impl_draw_foreground(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_foreground.
resolution_definition_ptr config()
state_t state_
Current state of the widget.
virtual void impl_draw_background(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_background.
int retval_
The return value of the button.
bool fire_event(const ui_event event, std::vector< std::pair< widget *, ui_event >> &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
state_t
Possible states of the widget.
virtual void set_active(const bool active) override
See styled_widget::set_active.
void set_retval(const int retval)
#define DBG_GUI_E
Definition: log.hpp:34
Default, unset return value.
Definition: retval.hpp:31
window * get_window()
Get the parent window.
Definition: widget.cpp:114
virtual void impl_draw_background(surface &frame_buffer, int x_offset, int y_offset) override
See widget::impl_draw_background.
Holds a 2D point.
Definition: point.hpp:23
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:364
Base class for all visible items.
std::string sound_toggle_panel_click
Definition: settings.cpp:41
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1019
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:130
retval
Default window/dialog return values.
Definition: retval.hpp:28
void signal_handler_left_button_double_click(const event::ui_event event, bool &handled)
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
virtual unsigned get_value() const override
Inherited from selectable_item.
mock_char c
virtual SDL_Rect get_client_rect() const override
See container_base::get_client_rect.
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
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
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:371
void signal_handler_mouse_enter(const event::ui_event event, bool &handled)