The Battle for Wesnoth  1.15.1+dev
slider.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 
17 #include "gui/widgets/slider.hpp"
18 
19 #include "formatter.hpp"
20 #include "gettext.hpp"
21 #include "gui/core/log.hpp"
23 #include "gui/core/log.hpp"
24 #include "gui/widgets/settings.hpp"
25 #include "gui/widgets/window.hpp"
26 #include "sdl/rect.hpp"
27 #include "sound.hpp"
28 #include "utils/general.hpp"
29 #include "gettext.hpp"
30 #include "wml_exception.hpp"
31 
32 #include "utils/functional.hpp"
33 
34 #if BOOST_VERSION >= 106700
35 #include <boost/integer/common_factor_rt.hpp>
36 #else
37 #include <boost/math/common_factor_rt.hpp>
38 #endif
39 
40 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
41 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
42 
43 namespace gui2
44 {
45 // ------------ WIDGET -----------{
46 
47 REGISTER_WIDGET(slider)
48 
49 slider::slider(const implementation::builder_slider& builder)
50  : slider_base(builder, type())
51  , best_slider_length_(0)
52  , minimum_value_(0)
53  , step_size_(1)
54  , minimum_value_label_()
55  , maximum_value_label_()
56  , value_label_generator_()
57  , current_item_mouse_position_(0, 0)
58 {
59  connect_signal<event::SDL_KEY_DOWN>(std::bind(&slider::signal_handler_sdl_key_down, this, _2, _3, _5));
60 
61  // connect_signal<event::LEFT_BUTTON_DOWN>(
62  // std::bind(&slider::signal_handler_left_button_down, this, _2, _3));
63 
64  connect_signal<event::LEFT_BUTTON_UP>(std::bind(&slider::signal_handler_left_button_up, this, _2, _3));
65 }
66 
68 {
70 
71  // Inherited.
73 
74  if(best_slider_length_ != 0) {
75  // Override length.
76  const auto conf = cast_config_to<slider_definition>();
77  assert(conf);
78 
79  result.x = conf->left_offset + best_slider_length_ + conf->right_offset;
80  }
81 
82  DBG_GUI_L << LOG_HEADER << " best_slider_length " << best_slider_length_ << " result " << result << ".\n";
83  return result;
84 }
85 
86 void slider::set_value(int value)
87 {
89  int old_value = get_value();
90 
91  if(value == old_value) {
92  return;
93  }
94 
96 
97  if(std::abs(get_value() - value) > (step_size_ / 2)) {
98  ERR_GUI_G << "slider::set_value error:"
99  << " old_value=" << old_value
100  << " new_value=" << get_value()
101  << " desired_value=" << value
102  << " minimum_value=" << minimum_value_
103  << " maximum_value=" << get_maximum_value()
104  << " step_size=" << step_size_ << "\n";
105  assert(false);
106  }
107 
108  fire(event::NOTIFY_MODIFIED, *this, nullptr);
109 }
110 
112 {
115  } else if(!minimum_value_label_.empty() && get_value() == get_minimum_value()) {
116  return minimum_value_label_;
117  } else if(!maximum_value_label_.empty() && get_value() == get_maximum_value()) {
118  return maximum_value_label_;
119  }
120 
121  return t_string(formatter() << get_value());
122 }
123 
125 {
127 }
128 
130 {
131  const auto conf = cast_config_to<slider_definition>();
132  assert(conf);
133  return conf->positioner_length;
134 }
135 
136 unsigned slider::offset_before() const
137 {
138  const auto conf = cast_config_to<slider_definition>();
139  assert(conf);
140  return conf->left_offset;
141 }
142 
143 unsigned slider::offset_after() const
144 {
145  const auto conf = cast_config_to<slider_definition>();
146  assert(conf);
147  return conf->right_offset;
148 }
149 
151 {
152  SDL_Rect positioner_rect =
154 
155  // Note we assume the positioner is over the entire height of the widget.
156  return sdl::point_in_rect(coordinate, positioner_rect);
157 }
158 
159 int slider::on_bar(const point& coordinate) const
160 {
161  const unsigned x = static_cast<std::size_t>(coordinate.x);
162  const unsigned y = static_cast<std::size_t>(coordinate.y);
163 
164  // Not on the widget, leave.
165  if(x > get_width() || y > get_height()) {
166  return 0;
167  }
168 
169  // we also assume the bar is over the entire height of the widget.
170  if(x < get_positioner_offset()) {
171  return -1;
172  } else if(x > get_positioner_offset() + get_positioner_length()) {
173  return 1;
174  }
175 
176  return 0;
177 }
178 
180 {
181  // Inherited.
183 
184  for(auto& tmp : get_canvases()) {
185  tmp.set_variable("text", wfl::variant(get_value_label()));
186  }
187 }
188 
189 void slider::handle_key_decrease(bool& handled)
190 {
191  DBG_GUI_E << LOG_HEADER << '\n';
192 
193  handled = true;
194 
196 }
197 
198 void slider::handle_key_increase(bool& handled)
199 {
200  DBG_GUI_E << LOG_HEADER << '\n';
201 
202  handled = true;
203 
205 }
206 
207 void slider::signal_handler_sdl_key_down(const event::ui_event event, bool& handled, const SDL_Keycode key)
208 {
209  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
210 
211  if(key == SDLK_DOWN || key == SDLK_LEFT) {
212  handle_key_decrease(handled);
213  } else if(key == SDLK_UP || key == SDLK_RIGHT) {
214  handle_key_increase(handled);
215  } else {
216  // Do nothing. Ignore other keys.
217  }
218 }
219 
220 #if 0
221 void slider::signal_handler_left_button_down(const event::ui_event event, bool& handled)
222 {
223  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
224 
225  update_current_item_mouse_position();
226 
227  handled = true;
228 }
229 #endif
230 
232 {
233  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
234 
235  get_window()->keyboard_capture(this);
236 
237  handled = true;
238 }
239 
240 static t_string default_value_label_generator(const std::vector<t_string>& value_labels, int item_position, int max)
241 {
242  assert(static_cast<int>(value_labels.size()) == max);
243  assert(item_position < max && item_position >= 0);
244  return value_labels[item_position];
245 }
246 
247 void slider::set_value_labels(const std::vector<t_string>& value_labels)
248 {
249  // Don't use std::ref because we want to store value_labels in the closure.
250  set_value_labels(std::bind(&default_value_label_generator, value_labels, _1, _2));
251 }
252 
253 
254 void slider::set_value_range(int min_value, int max_value)
255 {
256  // Settng both at once instead of having multiple functions set_min(),
257  // set_max() ... fixes an old problem where in cases like
258  // set_min(-10);set_min(-1);
259  // min and max would tmporarily have invalid values where since the starting max value is 0;
260 
261  VALIDATE(min_value <= max_value, "invalid slider data");
262  if (min_value == minimum_value_ && max_value == get_maximum_value()) {
263  return;
264  }
265 
266  int diff = max_value - min_value;
267  int old_value = get_value();
268 
269 #if BOOST_VERSION >= 106700
270  step_size_ = boost::integer::gcd(diff, step_size_);
271 #else
272  step_size_ = boost::math::gcd(diff, step_size_);
273 #endif
274 
275  minimum_value_ = min_value;
276 
278  set_value(old_value);
279 
280  assert(min_value == get_minimum_value());
281  assert(max_value == get_maximum_value());
282 
283 }
284 
285 void slider::set_step_size(int step_size)
286 {
287  const int old_min_value = get_minimum_value();
288  const int old_max_value = get_maximum_value();
289 
290  const int range_diff = get_item_count() - 1;
291  const int old_value = get_value();
292 
293 #if BOOST_VERSION >= 106700
294  step_size_ = boost::integer::gcd(range_diff, step_size);
295 #else
296  step_size_ = boost::math::gcd(range_diff, step_size);
297 #endif
298  slider_set_item_last(range_diff / step_size_);
299  set_value(old_value);
300 
301  assert(old_min_value == get_minimum_value());
302  assert(old_max_value == get_maximum_value());
303 }
304 
305 // }---------- DEFINITION ---------{
306 
309 {
310  DBG_GUI_P << "Parsing slider " << id << '\n';
311 
312  load_resolutions<resolution>(cfg);
313 }
314 
315 /*WIKI
316  * @page = GUIWidgetDefinitionWML
317  * @order = 1_slider
318  *
319  * == Slider ==
320  *
321  * @macro = slider_description
322  *
323  * @begin{parent}{name="gui/"}
324  * @begin{tag}{name="slider_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
325  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
326  * @begin{table}{config}
327  * minimum_positioner_length & unsigned & &
328  * The minimum size the positioner is
329  * allowed to be. The engine needs to know
330  * this in order to calculate the best size
331  * for the positioner. $
332  * maximum_positioner_length & unsigned & 0 &
333  * The maximum size the positioner is
334  * allowed to be. If minimum and maximum are
335  * the same value the positioner is fixed
336  * size. If the maximum is 0 (and the
337  * minimum not) there's no maximum. $
338  * left_offset & unsigned & 0 & The number of pixels at the left side
339  * which can't be used by the positioner. $
340  * right_offset & unsigned & 0 & The number of pixels at the right side
341  * which can't be used by the positioner. $
342  * @end{table}
343  *
344  * The following states exist:
345  * * state_enabled, the slider is enabled.
346  * * state_disabled, the slider is disabled.
347  * * state_pressed, the left mouse button is down on the positioner of the
348  * slider.
349  * * state_focused, the mouse is over the positioner of the slider.
350  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
351  * @end{tag}{name="state_enabled"}
352  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
353  * @end{tag}{name="state_disabled"}
354  * @begin{tag}{name="state_pressed"}{min=0}{max=1}{super="generic/state"}
355  * @end{tag}{name="state_pressed"}
356  * @begin{tag}{name="state_focused"}{min=0}{max=1}{super="generic/state"}
357  * @end{tag}{name="state_focused"}
358  * @end{tag}{name="resolution"}
359  * @end{tag}{name="slider_definition"}
360  * @end{parent}{name="gui/"}
361  */
363  : resolution_definition(cfg)
364  , positioner_length(cfg["minimum_positioner_length"])
365  , left_offset(cfg["left_offset"])
366  , right_offset(cfg["right_offset"])
367 {
368  VALIDATE(positioner_length, missing_mandatory_wml_key("resolution", "minimum_positioner_length"));
369 
370  // Note the order should be the same as the enum state_t is slider.hpp.
371  state.emplace_back(cfg.child("state_enabled"));
372  state.emplace_back(cfg.child("state_disabled"));
373  state.emplace_back(cfg.child("state_pressed"));
374  state.emplace_back(cfg.child("state_focused"));
375 }
376 
377 // }---------- BUILDER -----------{
378 
379 /*WIKI_MACRO
380  * @begin{macro}{slider_description}
381  * A slider is a styled_widget that can select a value by moving a grip on a groove.
382  * @end{macro}
383  */
384 
385 /*WIKI
386  * @page = GUIWidgetInstanceWML
387  * @order = 3_slider
388  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
389  * @begin{tag}{name="slider"}{min="0"}{max="-1"}{super="generic/widget_instance"}
390  * == Slider ==
391  *
392  * @macro = slider_description
393  *
394  * @begin{table}{config}
395  * best_slider_length & unsigned & 0 &
396  * The best length for the sliding part. $
397  * minimum_value & int & 0 & The minimum value the slider can have. $
398  * maximum_value & int & 0 & The maximum value the slider can have. $
399  *
400  * step_size & unsigned & 0 & The number of items the slider's value
401  * increases with one step. $
402  * value & int & 0 & The value of the slider. $
403  *
404  * minimum_value_label & t_string & "" &
405  * If the minimum value is chosen there
406  * might be the need for a special value
407  * (eg off). When this key has a value
408  * that value will be shown if the minimum
409  * is selected. $
410  * maximum_value_label & t_string & "" &
411  * If the maximum value is chosen there
412  * might be the need for a special value
413  * (eg unlimited)). When this key has a
414  * value that value will be shown if the
415  * maximum is selected. $
416  * value_labels & [] & It might be the labels need to be shown
417  * are not a linear number sequence eg
418  * (0.5, 1, 2, 4) in that case for all
419  * items this section can be filled with
420  * the values, which should be the same
421  * number of items as the items in the
422  * slider. NOTE if this option is used,
423  * 'minimum_value_label' and
424  * 'maximum_value_label' are ignored. $
425  * @end{table}
426  * @end{tag}{name="slider"}
427  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
428  */
429 
430 namespace implementation
431 {
432 builder_slider::builder_slider(const config& cfg)
434  , best_slider_length_(cfg["best_slider_length"])
435  , minimum_value_(cfg["minimum_value"])
436  , maximum_value_(cfg["maximum_value"])
437  , step_size_(cfg["step_size"].to_int(1))
438  , value_(cfg["value"])
439  , minimum_value_label_(cfg["minimum_value_label"].t_str())
440  , maximum_value_label_(cfg["maximum_value_label"].t_str())
441  , value_labels_()
442 {
443  const config& labels = cfg.child("value_labels");
444  if(!labels) {
445  return;
446  }
447 
448  for(const auto& label : labels.child_range("value")) {
449  value_labels_.push_back(label["label"]);
450  }
451 }
452 
454 {
455  slider* widget = new slider(*this);
456 
459  widget->set_step_size(step_size_);
460  widget->set_value(value_);
461 
462  widget->finalize_setup();
463 
464  if(!value_labels_.empty()) {
465  VALIDATE(value_labels_.size() == static_cast<std::size_t>(widget->get_item_count()),
466  _("The number of value_labels and values don't match."));
467 
469 
470  } else {
473  }
474 
475  DBG_GUI_G << "Window builder: placed slider '" << id << "' with definition '" << definition << "'.\n";
476 
477  return widget;
478 }
479 
480 } // namespace implementation
481 
482 // }------------ END --------------
483 
484 } // namespace gui2
constexpr const T & clamp(const T &value, const T &min, const T &max)
Definition: general.hpp:31
Define the common log macros for the gui toolkit.
Base class of a resolution, contains the common keys for a resolution.
void keyboard_capture(widget *widget)
Definition: window.cpp:1282
#define DBG_GUI_P
Definition: log.hpp:68
Go one item towards the begin.
Definition: slider_base.hpp:52
#define LOG_HEADER
Definition: slider.cpp:41
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
slider_definition(const config &cfg)
Definition: slider.cpp:307
std::vector< state_definition > state
#define DBG_GUI_L
Definition: log.hpp:57
unsigned offset_after() const override
Inherited from scrollbar_base.
Definition: slider.cpp:143
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
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
unsigned get_positioner_offset() const
Base class for all widgets.
Definition: widget.hpp:47
t_string maximum_value_label_
When the slider shows the maximum value can show a special text.
Definition: slider.hpp:181
unsigned get_slider_position() const
unsigned get_positioner_length() const
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: slider_base.cpp:65
#define ERR_GUI_G
Definition: log.hpp:43
unsigned get_height() const
Definition: widget.cpp:327
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:56
int rounded_division(int a, int b)
Definition: math.hpp:284
Label showing a text.
Definition: label.hpp:32
virtual point calculate_best_size() const override
See widget::calculate_best_size.
std::string sound_slider_adjust
Definition: settings.cpp:42
unsigned get_width() const
Definition: widget.cpp:322
void handle_key_increase(bool &handled)
Definition: slider.cpp:198
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key.
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key)
Signal handlers:
Definition: slider.cpp:207
int x
x coordinate.
Definition: point.hpp:44
Generic file dialog.
Definition: field-fwd.hpp:22
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:96
void set_minimum_value_label(const t_string &minimum_value_label)
Definition: slider.hpp:93
unsigned offset_before() const override
Inherited from scrollbar_base.
Definition: slider.cpp:136
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
std::string definition
Parameters for the styled_widget.
t_string minimum_value_label_
When the slider shows the minimum value can show a special text.
Definition: slider.hpp:175
void set_value_range(int min_value, int max_value)
Definition: slider.cpp:254
virtual int get_maximum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:65
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
This file contains the settings handling of the widget library.
std::ostringstream wrapper.
Definition: formatter.hpp:38
int get_item_count() const
Definition: slider.hpp:71
int positioner_length() const override
Inherited from scrollbar_base.
Definition: slider.cpp:129
int on_bar(const point &coordinate) const override
Inherited from scrollbar_base.
Definition: slider.cpp:159
static t_string default_value_label_generator(const std::vector< t_string > &value_labels, int item_position, int max)
Definition: slider.cpp:240
void set_maximum_value_label(const t_string &maximum_value_label)
Definition: slider.hpp:98
#define log_scope2(domain, description)
Definition: log.hpp:187
std::vector< canvas > & get_canvases()
void set_value_labels(const std::vector< t_string > &value_labels)
Definition: slider.cpp:247
void slider_set_item_last(const unsigned item_last)
virtual void child_callback_positioner_moved() override
Inherited from scrollbar_base.
Definition: slider.cpp:124
void set_slider_position(int item_position)
Note the position isn&#39;t guaranteed to be the wanted position the step size is honored.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
t_string get_value_label() const
Returns the label shown for the current value.
Definition: slider.cpp:111
unsigned best_slider_length_
The best size for the slider part itself, if 0 ignored.
Definition: slider.hpp:127
Go one item towards the end.
Definition: slider_base.hpp:56
#define DBG_GUI_E
Definition: log.hpp:34
int minimum_value_
The minimum value the slider holds.
Definition: slider.hpp:135
window * get_window()
Get the parent window.
Definition: widget.cpp:114
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
Definition: slider.cpp:231
Holds a 2D point.
Definition: point.hpp:23
virtual int get_value() const override
Inherited from integer_selector.
Definition: slider.hpp:53
std::vector< t_string > value_labels_
Definition: slider.hpp:265
virtual void update_canvas() override
Inherited from scrollbar_base.
Definition: slider.cpp:179
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:39
bool empty() const
Definition: tstring.hpp:182
Contains the SDL_Rect helper code.
label_generator value_label_generator_
Function to output custom value labels for the slider.
Definition: slider.hpp:188
#define LOG_SCOPE_HEADER
Definition: slider.cpp:40
A slider.
Definition: slider.hpp:33
int step_size_
Definition: slider.hpp:136
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1019
virtual void set_value(int value) override
Inherited from integer_selector.
Definition: slider.cpp:86
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:130
bool on_positioner(const point &coordinate) const override
Inherited from scrollbar_base.
Definition: slider.cpp:150
Base class for a scroll bar.
Definition: slider_base.hpp:39
void set_best_slider_length(const unsigned length)
Definition: slider.hpp:85
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
void handle_key_decrease(bool &handled)
Handlers for keyboard input.
Definition: slider.cpp:189
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
virtual void update_canvas() override
See styled_widget::update_canvas.
void set_step_size(int step_size)
Definition: slider.cpp:285
int y
y coordinate.
Definition: point.hpp:47
#define DBG_GUI_G
Definition: log.hpp:40
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: slider.cpp:67
resolution(const config &cfg)
Definition: slider.cpp:362
virtual int get_minimum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:59
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