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