The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
slider.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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
19
20#include "formatter.hpp"
21#include "gettext.hpp"
22#include "gui/core/log.hpp"
26#include "sdl/rect.hpp"
27#include "sound.hpp"
28#include "gettext.hpp"
29#include "utils/math.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
38namespace gui2
39{
40// ------------ WIDGET -----------{
41
42REGISTER_WIDGET(slider)
43
44slider::slider(const implementation::builder_slider& builder)
45 : slider_base(builder, type())
46 , best_slider_length_(builder.best_slider_length)
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 << ".";
78 return result;
79}
80
82{
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_;
100 assert(false);
101 }
102
103 fire(event::NOTIFY_MODIFIED, *this, nullptr);
104}
105
118
123
125{
126 const auto conf = cast_config_to<slider_definition>();
127 assert(conf);
128 return conf->positioner_length;
129}
130
131unsigned slider::offset_before() const
132{
133 const auto conf = cast_config_to<slider_definition>();
134 assert(conf);
135 return conf->left_offset;
136}
137
138unsigned slider::offset_after() const
139{
140 const auto conf = cast_config_to<slider_definition>();
141 assert(conf);
142 return conf->right_offset;
143}
144
145bool slider::on_positioner(const point& coordinate) const
146{
147 rect positioner_rect(
149 );
150
151 // Note we assume the positioner is over the entire height of the widget.
152 return positioner_rect.contains(coordinate);
153}
154
155int slider::on_bar(const point& coordinate) const
156{
157 const unsigned x = static_cast<std::size_t>(coordinate.x);
158 const unsigned y = static_cast<std::size_t>(coordinate.y);
159
160 // Not on the widget, leave.
161 if(x > get_width() || y > get_height()) {
162 return 0;
163 }
164
165 // we also assume the bar is over the entire height of the widget.
166 if(x < get_positioner_offset()) {
167 return -1;
168 } else if(x > get_positioner_offset() + get_positioner_length()) {
169 return 1;
170 }
171
172 return 0;
173}
174
176{
177 // Inherited.
179
180 for(auto& tmp : get_canvases()) {
181 tmp.set_variable("text", wfl::variant(get_value_label()));
182 }
183}
184
186{
188
189 handled = true;
190
192}
193
195{
197
198 handled = true;
199
201}
202
203void slider::signal_handler_sdl_key_down(const event::ui_event event, bool& handled, const SDL_Keycode key)
204{
205 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
206
207 if(key == SDLK_DOWN || key == SDLK_LEFT) {
208 handle_key_decrease(handled);
209 } else if(key == SDLK_UP || key == SDLK_RIGHT) {
210 handle_key_increase(handled);
211 } else {
212 // Do nothing. Ignore other keys.
213 }
214}
215
216#if 0
217void slider::signal_handler_left_button_down(const event::ui_event event, bool& handled)
218{
219 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
220
221 update_current_item_mouse_position();
222
223 handled = true;
224}
225#endif
226
228{
229 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
230
232
233 handled = true;
234}
235
236static t_string default_value_label_generator(const std::vector<t_string>& value_labels, int item_position, int max)
237{
238 assert(static_cast<int>(value_labels.size()) == max);
239 assert(item_position < max && item_position >= 0);
240 return value_labels[item_position];
241}
242
243void slider::set_value_labels(const std::vector<t_string>& value_labels)
244{
245 // Don't use std::ref because we want to store value_labels in the closure.
246 set_value_labels(std::bind(&default_value_label_generator, value_labels, std::placeholders::_1, std::placeholders::_2));
247}
248
249void 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 // set_value won't fire this since the actual slider value hasn't changed
271 fire(event::NOTIFY_MODIFIED, *this, nullptr);
272
273 assert(min_value == get_minimum_value());
274 assert(max_value == get_maximum_value());
275}
276
277void 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
306 , positioner_length(cfg["minimum_positioner_length"].to_unsigned())
307 , left_offset(cfg["left_offset"].to_unsigned())
308 , right_offset(cfg["right_offset"].to_unsigned())
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_mandatory_wml_tag("slider_definition][resolution", "state_enabled")));
314 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("slider_definition][resolution", "state_disabled")));
315 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_pressed", missing_mandatory_wml_tag("slider_definition][resolution", "state_pressed")));
316 state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_focused", missing_mandatory_wml_tag("slider_definition][resolution", "state_focused")));
317}
318
319// }---------- BUILDER -----------{
320
321namespace implementation
322{
323builder_slider::builder_slider(const config& cfg)
325 , best_slider_length(cfg["best_slider_length"].to_unsigned())
326 , minimum_value_(cfg["minimum_value"].to_int())
327 , maximum_value_(cfg["maximum_value"].to_int())
328 , step_size_(cfg["step_size"].to_int(1))
329 , value_(cfg["value"].to_int())
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"].t_str());
341 }
342}
343
344std::unique_ptr<widget> builder_slider::build() const
345{
346 auto widget = std::make_unique<slider>(*this);
347
348 widget->set_value_range(minimum_value_, maximum_value_);
349 widget->set_step_size(step_size_);
350 widget->set_value(value_);
351
352 widget->finalize_setup();
353
354 if(!value_labels_.empty()) {
355 VALIDATE(value_labels_.size() == static_cast<std::size_t>(widget->get_item_count()),
356 _("The number of value_labels and values don’t match."));
357
358 widget->set_value_labels(value_labels_);
359
360 } else {
361 widget->set_minimum_value_label(minimum_value_label_);
362 widget->set_maximum_value_label(maximum_value_label_);
363 }
364
365 DBG_GUI_G << "Window builder: placed slider '" << id << "' with definition '" << definition << "'.";
366
367 return widget;
368}
369
370} // namespace implementation
371
372// }------------ END --------------
373
374} // namespace gui2
std::unique_ptr< PangoAttribute, void(*)(PangoAttribute *)> value_
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
std::ostringstream wrapper.
Definition formatter.hpp:40
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Base class for a scroll bar.
virtual void update_canvas() override
See styled_widget::update_canvas.
@ ITEM_FORWARD
Go one item towards the end.
@ ITEM_BACKWARDS
Go one item towards the begin.
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.
unsigned get_positioner_length() const
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:119
t_string maximum_value_label_
When the slider shows the maximum value can show a special text.
Definition slider.hpp:195
void handle_key_decrease(bool &handled)
Handlers for keyboard input.
Definition slider.cpp:185
unsigned best_slider_length_
The best size for the slider part itself, if 0 ignored.
Definition slider.hpp:141
unsigned offset_before() const override
Inherited from scrollbar_base.
Definition slider.cpp:131
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key)
Signal handlers:
Definition slider.cpp:203
int minimum_value_
The minimum value the slider holds.
Definition slider.hpp:149
virtual int get_minimum_value() const override
Inherited from integer_selector.
Definition slider.hpp:58
int on_bar(const point &coordinate) const override
Inherited from scrollbar_base.
Definition slider.cpp:155
void handle_key_increase(bool &handled)
Definition slider.cpp:194
virtual void set_value(int value) override
Inherited from integer_selector.
Definition slider.cpp:81
virtual point calculate_best_size() const override
See widget::calculate_best_size.
Definition slider.cpp:62
virtual int get_maximum_value() const override
Inherited from integer_selector.
Definition slider.hpp:64
void set_value_range(int min_value, int max_value)
Definition slider.cpp:249
virtual int get_value() const override
Inherited from integer_selector.
Definition slider.hpp:52
label_generator value_label_generator_
Function to output custom value labels for the slider.
Definition slider.hpp:202
int get_item_count() const
Definition slider.hpp:70
t_string get_value_label() const
Returns the label shown for the current value.
Definition slider.cpp:106
int positioner_length() const override
Inherited from scrollbar_base.
Definition slider.cpp:124
t_string minimum_value_label_
When the slider shows the minimum value can show a special text.
Definition slider.hpp:189
bool on_positioner(const point &coordinate) const override
Inherited from scrollbar_base.
Definition slider.cpp:145
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
Definition slider.cpp:227
virtual void update_canvas() override
Inherited from scrollbar_base.
Definition slider.cpp:175
void set_value_labels(const std::vector< t_string > &value_labels)
Definition slider.cpp:243
unsigned offset_after() const override
Inherited from scrollbar_base.
Definition slider.cpp:138
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:55
unsigned get_width() const
Definition widget.cpp:336
unsigned get_height() const
Definition widget.cpp:341
window * get_window()
Get the parent window.
Definition widget.cpp:117
void keyboard_capture(widget *widget)
Definition window.cpp:1197
bool empty() const
Definition tstring.hpp:200
#define LOG_HEADER
const config * cfg
static std::string _(const char *str)
Definition gettext.hpp:100
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
#define LOG_SCOPE_HEADER
Definition button.cpp:34
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:276
General math utility functions.
int rounded_division(int a, int b)
Definition math.hpp:184
ui_event
The event sent to the dispatcher.
Definition handler.hpp:115
std::string sound_slider_adjust
Definition settings.cpp:53
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:236
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:1009
Contains the SDL_Rect helper code.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
This file contains the settings handling of the widget library.
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
virtual std::unique_ptr< widget > build() const override
Definition slider.cpp:344
std::vector< t_string > value_labels_
Definition slider.hpp:280
std::string definition
Parameters for the styled_widget.
std::vector< state_definition > state
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:49
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
Definition rect.cpp:54
std::string missing_mandatory_wml_tag(const std::string &section, const std::string &tag)
Returns a standard message for a missing wml child (tag).
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 (attribute).
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.