The Battle for Wesnoth  1.19.0+dev
slider_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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 
29 namespace
30 {
31 int 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 
41 namespace gui2
42 {
43 slider_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 
66 void slider_base::scroll(const scroll_mode scroll)
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 
93  case HALF_JUMP_FORWARD:
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 
108 void slider_base::place(const point& origin, const point& size)
109 {
110  // Inherited.
111  styled_widget::place(origin, size);
112 
113  recalculate();
114 }
115 
116 void 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 
128 unsigned slider_base::get_state() const
129 {
130  return state_;
131 }
132 
133 void 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 
141  update_canvas();
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 
177 void slider_base::move_positioner(int new_offset)
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 
188  update_canvas();
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 
208 void 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.
Definition: dispatcher.cpp:74
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'.
Definition: slider_base.hpp:49
@ ITEM_FORWARD
Go one item towards the end.
Definition: slider_base.hpp:55
@ HALF_JUMP_BACKWARDS
Go half the visible items towards the begin.
Definition: slider_base.hpp:52
@ ITEM_BACKWARDS
Go one item towards the begin.
Definition: slider_base.hpp:51
@ JUMP_BACKWARDS
Go the visible items towards the begin.
Definition: slider_base.hpp:53
@ JUMP_FORWARD
Go the visible items towards the end.
Definition: slider_base.hpp:57
@ BEGIN
Go to begin position.
Definition: slider_base.hpp:50
@ HALF_JUMP_FORWARD
Go half the visible items towards the end.
Definition: slider_base.hpp:56
@ END
Go to the end position.
Definition: slider_base.hpp:54
virtual unsigned get_state() const override
See styled_widget::get_state.
slider_base(const implementation::builder_styled_widget &builder, const std::string &control_type)
Definition: slider_base.cpp:43
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.
Definition: slider_base.cpp:66
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:454
int get_x() const
Definition: widget.cpp:316
int get_y() const
Definition: widget.cpp:321
window * get_window()
Get the parent window.
Definition: widget.cpp:117
void mouse_capture(const bool capture=true)
Definition: window.cpp:1215
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:186
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
@ NOTIFY_MODIFIED
Definition: handler.hpp:158
Generic file dialog.
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:112
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
#define LOG_HEADER
Definition: slider_base.cpp:27
Helper container for the slider's current position.
Definition: slider_base.hpp:62
Holds a 2D point.
Definition: point.hpp:25