The Battle for Wesnoth  1.17.0-dev
slider.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
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 
18 #include "gui/widgets/slider.hpp"
19 
20 #include "formatter.hpp"
21 #include "gettext.hpp"
22 #include "gui/core/log.hpp"
24 #include "gui/core/log.hpp"
25 #include "gui/widgets/settings.hpp"
26 #include "gui/widgets/window.hpp"
27 #include "sdl/rect.hpp"
28 #include "sound.hpp"
29 #include "gettext.hpp"
30 #include "wml_exception.hpp"
31 
32 #include <functional>
33 #include <numeric>
34 
35 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
36 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
37 
38 namespace gui2
39 {
40 // ------------ WIDGET -----------{
41 
42 REGISTER_WIDGET(slider)
43 
44 slider::slider(const implementation::builder_slider& builder)
45  : slider_base(builder, type())
46  , best_slider_length_(0)
47  , minimum_value_(0)
48  , step_size_(1)
49  , minimum_value_label_()
50  , maximum_value_label_()
51  , value_label_generator_()
52  , current_item_mouse_position_(0, 0)
53 {
54  connect_signal<event::SDL_KEY_DOWN>(std::bind(&slider::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5));
55 
56  // connect_signal<event::LEFT_BUTTON_DOWN>(
57  // std::bind(&slider::signal_handler_left_button_down, this, std::placeholders::_2, std::placeholders::_3));
58 
59  connect_signal<event::LEFT_BUTTON_UP>(std::bind(&slider::signal_handler_left_button_up, this, std::placeholders::_2, std::placeholders::_3));
60 }
61 
63 {
65 
66  // Inherited.
68 
69  if(best_slider_length_ != 0) {
70  // Override length.
71  const auto conf = cast_config_to<slider_definition>();
72  assert(conf);
73 
74  result.x = conf->left_offset + best_slider_length_ + conf->right_offset;
75  }
76 
77  DBG_GUI_L << LOG_HEADER << " best_slider_length " << best_slider_length_ << " result " << result << ".\n";
78  return result;
79 }
80 
81 void slider::set_value(int value)
82 {
83  value = std::clamp(value, minimum_value_, get_maximum_value());
84  int old_value = get_value();
85 
86  if(value == old_value) {
87  return;
88  }
89 
91 
92  if(std::abs(get_value() - value) > (step_size_ / 2)) {
93  ERR_GUI_G << "slider::set_value error:"
94  << " old_value=" << old_value
95  << " new_value=" << get_value()
96  << " desired_value=" << value
97  << " minimum_value=" << minimum_value_
98  << " maximum_value=" << get_maximum_value()
99  << " step_size=" << step_size_ << "\n";
100  assert(false);
101  }
102 
103  fire(event::NOTIFY_MODIFIED, *this, nullptr);
104 }
105 
107 {
110  } else if(!minimum_value_label_.empty() && get_value() == get_minimum_value()) {
111  return minimum_value_label_;
112  } else if(!maximum_value_label_.empty() && get_value() == get_maximum_value()) {
113  return maximum_value_label_;
114  }
115 
116  return t_string(formatter() << get_value());
117 }
118 
120 {
122 }
123 
125 {
126  const auto conf = cast_config_to<slider_definition>();
127  assert(conf);
128  return conf->positioner_length;
129 }
130 
131 unsigned slider::offset_before() const
132 {
133  const auto conf = cast_config_to<slider_definition>();
134  assert(conf);
135  return conf->left_offset;
136 }
137 
138 unsigned slider::offset_after() const
139 {
140  const auto conf = cast_config_to<slider_definition>();
141  assert(conf);
142  return conf->right_offset;
143 }
144 
146 {
147  SDL_Rect positioner_rect =
149 
150  // Note we assume the positioner is over the entire height of the widget.
151  return sdl::point_in_rect(coordinate, positioner_rect);
152 }
153 
154 int slider::on_bar(const point& coordinate) const
155 {
156  const unsigned x = static_cast<std::size_t>(coordinate.x);
157  const unsigned y = static_cast<std::size_t>(coordinate.y);
158 
159  // Not on the widget, leave.
160  if(x > get_width() || y > get_height()) {
161  return 0;
162  }
163 
164  // we also assume the bar is over the entire height of the widget.
165  if(x < get_positioner_offset()) {
166  return -1;
167  } else if(x > get_positioner_offset() + get_positioner_length()) {
168  return 1;
169  }
170 
171  return 0;
172 }
173 
175 {
176  // Inherited.
178 
179  for(auto& tmp : get_canvases()) {
180  tmp.set_variable("text", wfl::variant(get_value_label()));
181  }
182 }
183 
184 void slider::handle_key_decrease(bool& handled)
185 {
186  DBG_GUI_E << LOG_HEADER << '\n';
187 
188  handled = true;
189 
191 }
192 
193 void slider::handle_key_increase(bool& handled)
194 {
195  DBG_GUI_E << LOG_HEADER << '\n';
196 
197  handled = true;
198 
200 }
201 
202 void slider::signal_handler_sdl_key_down(const event::ui_event event, bool& handled, const SDL_Keycode key)
203 {
204  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
205 
206  if(key == SDLK_DOWN || key == SDLK_LEFT) {
207  handle_key_decrease(handled);
208  } else if(key == SDLK_UP || key == SDLK_RIGHT) {
209  handle_key_increase(handled);
210  } else {
211  // Do nothing. Ignore other keys.
212  }
213 }
214 
215 #if 0
216 void slider::signal_handler_left_button_down(const event::ui_event event, bool& handled)
217 {
218  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
219 
220  update_current_item_mouse_position();
221 
222  handled = true;
223 }
224 #endif
225 
227 {
228  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
229 
230  get_window()->keyboard_capture(this);
231 
232  handled = true;
233 }
234 
235 static t_string default_value_label_generator(const std::vector<t_string>& value_labels, int item_position, int max)
236 {
237  assert(static_cast<int>(value_labels.size()) == max);
238  assert(item_position < max && item_position >= 0);
239  return value_labels[item_position];
240 }
241 
242 void slider::set_value_labels(const std::vector<t_string>& value_labels)
243 {
244  // Don't use std::ref because we want to store value_labels in the closure.
245  set_value_labels(std::bind(&default_value_label_generator, value_labels, std::placeholders::_1, std::placeholders::_2));
246 }
247 
248 
249 void slider::set_value_range(int min_value, int max_value)
250 {
251  // Settng both at once instead of having multiple functions set_min(),
252  // set_max() ... fixes an old problem where in cases like
253  // set_min(-10);set_min(-1);
254  // min and max would tmporarily have invalid values where since the starting max value is 0;
255 
256  VALIDATE(min_value <= max_value, "invalid slider data");
257  if (min_value == minimum_value_ && max_value == get_maximum_value()) {
258  return;
259  }
260 
261  int diff = max_value - min_value;
262  int old_value = get_value();
263 
264  step_size_ = std::gcd(diff, step_size_);
265  minimum_value_ = min_value;
266 
268  set_value(old_value);
269 
270  assert(min_value == get_minimum_value());
271  assert(max_value == get_maximum_value());
272 
273 }
274 
275 void slider::set_step_size(int step_size)
276 {
277  const int old_min_value = get_minimum_value();
278  const int old_max_value = get_maximum_value();
279 
280  const int range_diff = get_item_count() - 1;
281  const int old_value = get_value();
282 
283  step_size_ = std::gcd(range_diff, step_size);
284 
285  slider_set_item_last(range_diff / step_size_);
286  set_value(old_value);
287 
288  assert(old_min_value == get_minimum_value());
289  assert(old_max_value == get_maximum_value());
290 }
291 
292 // }---------- DEFINITION ---------{
293 
296 {
297  DBG_GUI_P << "Parsing slider " << id << '\n';
298 
299  load_resolutions<resolution>(cfg);
300 }
301 
303  : resolution_definition(cfg)
304  , positioner_length(cfg["minimum_positioner_length"])
305  , left_offset(cfg["left_offset"])
306  , right_offset(cfg["right_offset"])
307 {
308  VALIDATE(positioner_length, missing_mandatory_wml_key("resolution", "minimum_positioner_length"));
309 
310  // Note the order should be the same as the enum state_t is slider.hpp.
311  state.emplace_back(cfg.child("state_enabled"));
312  state.emplace_back(cfg.child("state_disabled"));
313  state.emplace_back(cfg.child("state_pressed"));
314  state.emplace_back(cfg.child("state_focused"));
315 }
316 
317 // }---------- BUILDER -----------{
318 
319 namespace implementation
320 {
321 builder_slider::builder_slider(const config& cfg)
323  , best_slider_length_(cfg["best_slider_length"])
324  , minimum_value_(cfg["minimum_value"])
325  , maximum_value_(cfg["maximum_value"])
326  , step_size_(cfg["step_size"].to_int(1))
327  , value_(cfg["value"])
328  , minimum_value_label_(cfg["minimum_value_label"].t_str())
329  , maximum_value_label_(cfg["maximum_value_label"].t_str())
330  , value_labels_()
331 {
332  const config& labels = cfg.child("value_labels");
333  if(!labels) {
334  return;
335  }
336 
337  for(const auto& label : labels.child_range("value")) {
338  value_labels_.push_back(label["label"]);
339  }
340 }
341 
343 {
344  slider* widget = new slider(*this);
345 
348  widget->set_step_size(step_size_);
349  widget->set_value(value_);
350 
351  widget->finalize_setup();
352 
353  if(!value_labels_.empty()) {
354  VALIDATE(value_labels_.size() == static_cast<std::size_t>(widget->get_item_count()),
355  _("The number of value_labels and values don't match."));
356 
358 
359  } else {
362  }
363 
364  DBG_GUI_G << "Window builder: placed slider '" << id << "' with definition '" << definition << "'.\n";
365 
366  return widget;
367 }
368 
369 } // namespace implementation
370 
371 // }------------ END --------------
372 
373 } // namespace gui2
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:1276
#define DBG_GUI_P
Definition: log.hpp:66
Go one item towards the begin.
Definition: slider_base.hpp:53
#define LOG_HEADER
Definition: slider.cpp:36
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:402
slider_definition(const config &cfg)
Definition: slider.cpp:294
std::vector< state_definition > state
#define DBG_GUI_L
Definition: log.hpp:55
unsigned offset_after() const override
Inherited from scrollbar_base.
Definition: slider.cpp:138
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:344
unsigned get_positioner_offset() const
Base class for all widgets.
Definition: widget.hpp:49
t_string maximum_value_label_
When the slider shows the maximum value can show a special text.
Definition: slider.hpp:207
unsigned get_slider_position() const
unsigned get_positioner_length() const
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: slider_base.cpp:66
#define ERR_GUI_G
Definition: log.hpp:44
unsigned get_height() const
Definition: widget.cpp:330
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:54
int rounded_division(int a, int b)
Definition: math.hpp:305
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
static std::string _(const char *str)
Definition: gettext.hpp:93
virtual point calculate_best_size() const override
See widget::calculate_best_size.
std::string sound_slider_adjust
Definition: settings.cpp:46
unsigned get_width() const
Definition: widget.cpp:325
void handle_key_increase(bool &handled)
Definition: slider.cpp:193
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:202
int x
x coordinate.
Definition: point.hpp:45
Generic file dialog.
Definition: field-fwd.hpp:23
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:89
void set_minimum_value_label(const t_string &minimum_value_label)
Definition: slider.hpp:119
unsigned offset_before() const override
Inherited from scrollbar_base.
Definition: slider.cpp:131
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:201
void set_value_range(int min_value, int max_value)
Definition: slider.cpp:249
virtual int get_maximum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:91
#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:39
int get_item_count() const
Definition: slider.hpp:97
int positioner_length() const override
Inherited from scrollbar_base.
Definition: slider.cpp:124
int on_bar(const point &coordinate) const override
Inherited from scrollbar_base.
Definition: slider.cpp:154
static t_string default_value_label_generator(const std::vector< t_string > &value_labels, int item_position, int max)
Definition: slider.cpp:235
void set_maximum_value_label(const t_string &maximum_value_label)
Definition: slider.hpp:124
#define log_scope2(domain, description)
Definition: log.hpp:219
std::vector< canvas > & get_canvases()
void set_value_labels(const std::vector< t_string > &value_labels)
Definition: slider.cpp:242
void slider_set_item_last(const unsigned item_last)
virtual void child_callback_positioner_moved() override
Inherited from scrollbar_base.
Definition: slider.cpp:119
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:23
t_string get_value_label() const
Returns the label shown for the current value.
Definition: slider.cpp:106
unsigned best_slider_length_
The best size for the slider part itself, if 0 ignored.
Definition: slider.hpp:153
Go one item towards the end.
Definition: slider_base.hpp:57
#define DBG_GUI_E
Definition: log.hpp:35
int minimum_value_
The minimum value the slider holds.
Definition: slider.hpp:161
window * get_window()
Get the parent window.
Definition: widget.cpp:117
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
Definition: slider.cpp:226
Holds a 2D point.
Definition: point.hpp:24
virtual int get_value() const override
Inherited from integer_selector.
Definition: slider.hpp:79
std::vector< t_string > value_labels_
Definition: slider.hpp:291
virtual void update_canvas() override
Inherited from scrollbar_base.
Definition: slider.cpp:174
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:40
bool empty() const
Definition: tstring.hpp:187
Contains the SDL_Rect helper code.
label_generator value_label_generator_
Function to output custom value labels for the slider.
Definition: slider.hpp:214
#define LOG_SCOPE_HEADER
Definition: slider.cpp:35
A slider is a control that can select a value by moving a grip on a groove.
Definition: slider.hpp:59
int step_size_
Definition: slider.hpp:162
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1052
virtual void set_value(int value) override
Inherited from integer_selector.
Definition: slider.cpp:81
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:69
bool on_positioner(const point &coordinate) const override
Inherited from scrollbar_base.
Definition: slider.cpp:145
virtual widget * build() const override
Definition: slider.cpp:342
Base class for a scroll bar.
Definition: slider_base.hpp:40
void set_best_slider_length(const unsigned length)
Definition: slider.hpp:111
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:184
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
virtual void update_canvas() override
See styled_widget::update_canvas.
void set_step_size(int step_size)
Definition: slider.cpp:275
int y
y coordinate.
Definition: point.hpp:48
#define DBG_GUI_G
Definition: log.hpp:41
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: slider.cpp:62
resolution(const config &cfg)
Definition: slider.cpp:302
virtual int get_minimum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:85
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:48