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