The Battle for Wesnoth  1.17.17+dev
slider.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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(VALIDATE_WML_CHILD(cfg, "state_enabled", _("Missing required state for slider")));
314  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", _("Missing required state for slider")));
315  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_pressed", _("Missing required state for slider")));
316  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_focused", _("Missing required state for slider")));
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  auto labels = cfg.optional_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
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:389
std::ostringstream wrapper.
Definition: formatter.hpp:40
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:76
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:58
Base class for a scroll bar.
Definition: slider_base.hpp:41
virtual void update_canvas() override
See styled_widget::update_canvas.
@ ITEM_FORWARD
Go one item towards the end.
Definition: slider_base.hpp:57
@ ITEM_BACKWARDS
Go one item towards the begin.
Definition: slider_base.hpp:53
unsigned get_positioner_offset() const
unsigned get_slider_position() const
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
void slider_set_item_last(const unsigned item_last)
void set_slider_position(int item_position)
Note the position isn't guaranteed to be the wanted position the step size is honored.
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: slider_base.cpp:66
unsigned get_positioner_length() const
A slider is a control that can select a value by moving a grip on a groove.
Definition: slider.hpp:60
void set_step_size(int step_size)
Definition: slider.cpp:277
virtual void child_callback_positioner_moved() override
Inherited from scrollbar_base.
Definition: slider.cpp:120
t_string maximum_value_label_
When the slider shows the maximum value can show a special text.
Definition: slider.hpp:207
void handle_key_decrease(bool &handled)
Handlers for keyboard input.
Definition: slider.cpp:186
unsigned best_slider_length_
The best size for the slider part itself, if 0 ignored.
Definition: slider.hpp:153
unsigned offset_before() const override
Inherited from scrollbar_base.
Definition: slider.cpp:132
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key)
Signal handlers:
Definition: slider.cpp:204
int minimum_value_
The minimum value the slider holds.
Definition: slider.hpp:161
virtual int get_minimum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:85
int on_bar(const point &coordinate) const override
Inherited from scrollbar_base.
Definition: slider.cpp:156
void handle_key_increase(bool &handled)
Definition: slider.cpp:195
virtual void set_value(int value) override
Inherited from integer_selector.
Definition: slider.cpp:82
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition: slider.cpp:63
int step_size_
Definition: slider.hpp:162
virtual int get_maximum_value() const override
Inherited from integer_selector.
Definition: slider.hpp:91
void set_value_range(int min_value, int max_value)
Definition: slider.cpp:251
virtual int get_value() const override
Inherited from integer_selector.
Definition: slider.hpp:79
label_generator value_label_generator_
Function to output custom value labels for the slider.
Definition: slider.hpp:214
int get_item_count() const
Definition: slider.hpp:97
t_string get_value_label() const
Returns the label shown for the current value.
Definition: slider.cpp:107
int positioner_length() const override
Inherited from scrollbar_base.
Definition: slider.cpp:125
t_string minimum_value_label_
When the slider shows the minimum value can show a special text.
Definition: slider.hpp:201
bool on_positioner(const point &coordinate) const override
Inherited from scrollbar_base.
Definition: slider.cpp:146
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
Definition: slider.cpp:228
virtual void update_canvas() override
Inherited from scrollbar_base.
Definition: slider.cpp:176
void set_value_labels(const std::vector< t_string > &value_labels)
Definition: slider.cpp:244
unsigned offset_after() const override
Inherited from scrollbar_base.
Definition: slider.cpp:139
std::vector< canvas > & get_canvases()
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Base class for all widgets.
Definition: widget.hpp:54
unsigned get_width() const
Definition: widget.cpp:326
unsigned get_height() const
Definition: widget.cpp:331
window * get_window()
Get the parent window.
Definition: widget.cpp:118
void keyboard_capture(widget *widget)
Definition: window.cpp:1130
bool empty() const
Definition: tstring.hpp:187
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define DBG_GUI_L
Definition: log.hpp:55
#define ERR_GUI_G
Definition: log.hpp:44
#define DBG_GUI_G
Definition: log.hpp:41
#define DBG_GUI_P
Definition: log.hpp:66
#define DBG_GUI_E
Definition: log.hpp:35
This file contains the window object, this object is a top level container which has the event manage...
#define log_scope2(domain, description)
Definition: log.hpp:241
General math utility functions.
int rounded_division(int a, int b)
Definition: math.hpp:186
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
@ NOTIFY_MODIFIED
Definition: handler.hpp:158
std::string sound_slider_adjust
Definition: settings.cpp:51
Generic file dialog.
static t_string default_value_label_generator(const std::vector< t_string > &value_labels, int item_position, int max)
Definition: slider.cpp:237
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:54
Contains the implementation details for lexical_cast and shouldn't be used directly.
void play_UI_sound(const std::string &files)
Definition: sound.cpp:1066
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
Contains the SDL_Rect helper code.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
This file contains the settings handling of the widget library.
#define LOG_HEADER
Definition: slider.cpp:37
#define LOG_SCOPE_HEADER
Definition: slider.cpp:36
virtual std::unique_ptr< widget > build() const override
Definition: slider.cpp:344
std::vector< t_string > value_labels_
Definition: slider.hpp:291
std::string definition
Parameters for the styled_widget.
Base class of a resolution, contains the common keys for a resolution.
std::vector< state_definition > state
resolution(const config &cfg)
Definition: slider.cpp:304
slider_definition(const config &cfg)
Definition: slider.cpp:296
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition: rect.cpp:54
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.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE_WML_CHILD(cfg, key, message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.