The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
slider_base.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 "gui/core/log.hpp"
21#include "gui/widgets/window.hpp" // Needed for invalidate_layout()
22
23#include <functional>
24#include "utils/math.hpp"
25
26#define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
27#define LOG_HEADER LOG_SCOPE_HEADER + ':'
28
29namespace
30{
31int rounded_division(int value, int new_base, int old_base)
32{
33 if(old_base == 0) {
34 return new_base / 2;
35 } else {
36 return ::rounded_division(value * new_base, old_base);
37 }
38}
39} // end anon namespace
40
41namespace gui2
42{
43slider_base::slider_base(const implementation::builder_styled_widget& builder, const std::string& control_type)
44 : styled_widget(builder, control_type)
45 , state_(ENABLED)
46 , item_last_(0)
47 , item_position_(0)
48 , drag_initial_mouse_(0, 0)
49 , drag_initial_offset_(0)
50 , positioner_offset_(0)
51 , positioner_length_(0)
52 , snap_(true)
53{
54 connect_signal<event::MOUSE_ENTER>(
55 std::bind(&slider_base::signal_handler_mouse_enter, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
56 connect_signal<event::MOUSE_MOTION>(
57 std::bind(&slider_base::signal_handler_mouse_motion, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
58 connect_signal<event::MOUSE_LEAVE>(
59 std::bind(&slider_base::signal_handler_mouse_leave, this, std::placeholders::_2, std::placeholders::_3));
60 connect_signal<event::LEFT_BUTTON_DOWN>(
61 std::bind(&slider_base::signal_handler_left_button_down, this, std::placeholders::_2, std::placeholders::_3));
62 connect_signal<event::LEFT_BUTTON_UP>(
63 std::bind(&slider_base::signal_handler_left_button_up, this, std::placeholders::_2, std::placeholders::_3));
64}
65
67{
68 switch(scroll) {
69 case BEGIN:
71 break;
72
73 case ITEM_BACKWARDS:
75 break;
76
79 break;
80
81 case JUMP_BACKWARDS:
83 break;
84
85 case END:
87 break;
88
89 case ITEM_FORWARD:
91 break;
92
95 break;
96
97 case JUMP_FORWARD:
99 break;
100
101 default:
102 assert(false);
103 }
104
105 fire(event::NOTIFY_MODIFIED, *this, nullptr);
106}
107
108void slider_base::place(const point& origin, const point& size)
109{
110 // Inherited.
111 styled_widget::place(origin, size);
112
113 recalculate();
114}
115
116void slider_base::set_active(const bool active)
117{
118 if(get_active() != active) {
119 set_state(active ? ENABLED : DISABLED);
120 }
121}
122
124{
125 return state_ != DISABLED;
126}
127
129{
130 return state_;
131}
132
133void slider_base::set_slider_position(int item_position)
134{
135 // Set the value always execute since we update a part of the state.
136 item_position_ = std::clamp(item_position, 0, item_last_);
137
138 // Determine the pixel offset of the item position.
140
142}
143
145{
146 for(auto& tmp : get_canvases()) {
147 tmp.set_variable("positioner_offset", wfl::variant(positioner_offset_));
148 tmp.set_variable("positioner_length", wfl::variant(positioner_length_));
149 }
150
151 queue_redraw();
152}
153
155{
156 if(state != state_) {
157 state_ = state;
158 queue_redraw();
159 }
160}
161
163{
164 // We can be called before the size has been set up in that case we can't do
165 // the proper recalcultion so stop before we die with an assert.
166 if(!get_length()) {
167 return;
168 }
169
170 assert(available_length() > 0);
171
173
175}
176
178{
179 int max_offset = this->max_offset();
180 new_offset = std::clamp(new_offset, 0, max_offset);
181
182 slider_base::slider_position_t final_offset = {new_offset, max_offset};
183 update_slider_position(final_offset);
184
185 assert(final_offset.max_offset == max_offset);
186 positioner_offset_ = final_offset.offset + offset_before();
187
189}
190
192{
193 int new_position = rounded_division(pos.offset, item_last_, pos.max_offset);
194
195 if(snap_) {
196 pos.offset = rounded_division(new_position, pos.max_offset, item_last_);
197 }
198
199 if(new_position != item_position_) {
200 item_position_ = new_position;
201
203
204 fire(event::NOTIFY_MODIFIED, *this, nullptr);
205 }
206}
207
208void slider_base::signal_handler_mouse_enter(const event::ui_event event, bool& handled, bool& halt)
209{
210 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
211
212 // Send the motion under our event id to make debugging easier.
213 signal_handler_mouse_motion(event, handled, halt, get_mouse_position());
214}
215
217 const event::ui_event event, bool& handled, bool& halt, const point& coordinate)
218{
219 DBG_GUI_E << LOG_HEADER << ' ' << event << " at " << coordinate << ".";
220
221 point mouse = coordinate;
222 mouse.x -= get_x();
223 mouse.y -= get_y();
224
225 switch(state_) {
226 case ENABLED:
227 if(on_positioner(mouse)) {
229 }
230
231 break;
232
233 case PRESSED:
235 break;
236
237 case FOCUSED:
238 if(!on_positioner(mouse)) {
240 }
241
242 break;
243
244 case DISABLED:
245 // Shouldn't be possible, but seems to happen in the lobby
246 // if a resize layout happens during dragging.
247 halt = true;
248 break;
249
250 default:
251 assert(false);
252 }
253
254 handled = true;
255}
256
258{
259 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
260
261 if(state_ == FOCUSED) {
263 }
264
265 handled = true;
266}
267
269{
270 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
271
272 point mouse = get_mouse_position();
273 mouse.x -= get_x();
274 mouse.y -= get_y();
275
276 if(on_positioner(mouse)) {
277 assert(get_window());
278
279 drag_initial_mouse_ = mouse;
281
284 }
285
286 const int bar = on_bar(mouse);
287
288 if(bar == -1) {
290 } else if(bar == 1) {
292 } else {
293 assert(bar == 0);
294 }
295
296 handled = true;
297}
298
300{
301 DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
302
303 point mouse = get_mouse_position();
304 mouse.x -= get_x();
305 mouse.y -= get_y();
306
307 if(state_ != PRESSED) {
308 return;
309 }
310
311 assert(get_window());
312 get_window()->mouse_capture(false);
313
314 if(on_positioner(mouse)) {
316 } else {
318 }
319
320 drag_initial_mouse_ = {0, 0};
322
323 handled = true;
324}
325
327{
328 // These values won't change so set them once.
329 for(auto& tmp : get_canvases()) {
330 tmp.set_variable("offset_before", wfl::variant(offset_before()));
331 tmp.set_variable("offset_after", wfl::variant(offset_after()));
332 }
333}
334
335} // namespace gui2
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
int item_position_
The item the positioner is at, starts at 0.
point drag_initial_mouse_
The position the mouse was when draggin the slider was started.
void set_state(const state_t state)
virtual unsigned offset_after() const =0
The number of pixels we can't use since they're used for borders.
void signal_handler_mouse_enter(const event::ui_event event, bool &handled, bool &halt)
state_t
Possible states of the widget.
void recalculate_positioner()
virtual int get_length_difference(const point &original, const point &current) const =0
Gets the relevant difference in between the two positions.
int drag_initial_offset_
The offset in pixels the slider was when dragging the positioner was started.
virtual void update_canvas() override
See styled_widget::update_canvas.
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
virtual void set_active(const bool active) override
See styled_widget::set_active.
int positioner_offset_
The start offset of the positioner.
void recalculate()
Updates the slider.
virtual bool on_positioner(const point &coordinate) const =0
Is the coordinate on the positioner?
bool snap_
Whether the slider should 'snap' into its supported values or not.
void signal_handler_mouse_motion(const event::ui_event event, bool &handled, bool &halt, const point &coordinate)
int max_offset() const
scroll_mode
scroll 'step size'.
@ ITEM_FORWARD
Go one item towards the end.
@ HALF_JUMP_BACKWARDS
Go half the visible items towards the begin.
@ ITEM_BACKWARDS
Go one item towards the begin.
@ JUMP_BACKWARDS
Go the visible items towards the begin.
@ JUMP_FORWARD
Go the visible items towards the end.
@ BEGIN
Go to begin position.
@ HALF_JUMP_FORWARD
Go half the visible items towards the end.
@ END
Go to the end position.
virtual unsigned get_state() const override
See styled_widget::get_state.
slider_base(const implementation::builder_styled_widget &builder, const std::string &control_type)
int item_last_
one less than the number items the slider 'holds'.
void move_positioner(int offset)
Moves the positioner.
virtual bool get_active() const override
See styled_widget::get_active.
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
state_t state_
Current state of the widget.
virtual unsigned offset_before() const =0
The number of pixels we can't use since they're used for borders.
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
int positioner_length_
The current length of the positioner.
virtual void child_callback_positioner_moved()
Callback for subclasses to get notified about positioner movement.
void update_slider_position(slider_position_t &pos)
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.
int available_length() const
virtual unsigned get_length() const =0
Get the length of the slider.
virtual int on_bar(const point &coordinate) const =0
Is the coordinate on the bar?
virtual int jump_size() const
virtual void place(const point &origin, const point &size) override
See widget::place.
std::vector< canvas > & get_canvases()
virtual void place(const point &origin, const point &size) override
See widget::place.
void queue_redraw()
Indicates that this widget should be redrawn.
Definition widget.cpp:464
int get_x() const
Definition widget.cpp:326
int get_y() const
Definition widget.cpp:331
window * get_window()
Get the parent window.
Definition widget.cpp:117
void mouse_capture(const bool capture=true)
Definition window.cpp:1191
#define LOG_HEADER
Define the common log macros for the gui toolkit.
#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...
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
Generic file dialog.
point get_mouse_position()
Returns the current mouse position.
Definition helper.cpp:167
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
Helper container for the slider's current position.
Holds a 2D point.
Definition point.hpp:25