The Battle for Wesnoth  1.17.0-dev
slider_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
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 
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  set_is_dirty(true);
152 }
153 
155 {
156  if(state != state_) {
157  state_ = state;
158  set_is_dirty(true);
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 << ".\n";
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 << ".\n";
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 << ".\n";
260 
261  if(state_ == FOCUSED) {
263  }
264 
265  handled = true;
266 }
267 
269 {
270  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
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 << ".\n";
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
Define the common log macros for the gui toolkit.
int positioner_length_
The current length of the positioner.
Go one item towards the begin.
Definition: slider_base.hpp:53
int get_x() const
Definition: widget.cpp:315
virtual int on_bar(const point &coordinate) const =0
Is the coordinate on the bar?
Go the visible items towards the begin.
Definition: slider_base.hpp:55
virtual unsigned offset_before() const =0
The number of pixels we can&#39;t use since they&#39;re used for borders.
virtual bool on_positioner(const point &coordinate) const =0
Is the coordinate on the positioner?
virtual bool get_active() const override
See styled_widget::get_active.
Go the visible items towards the end.
Definition: slider_base.hpp:59
virtual void place(const point &origin, const point &size) override
See widget::place.
bool snap_
Whether the slider should &#39;snap&#39; into its supported values or not.
int item_last_
one less than the number items the slider &#39;holds&#39;.
virtual void child_callback_positioner_moved()
Callback for subclasses to get notified about positioner movement.
void update_slider_position(slider_position_t &pos)
Helper container for the slider&#39;s current position.
Definition: slider_base.hpp:63
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
void set_state(const state_t state)
This file contains the window object, this object is a top level container which has the event manage...
virtual unsigned get_length() const =0
Get the length of the slider.
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: slider_base.cpp:66
slider_base(const implementation::builder_styled_widget &builder, const std::string &control_type)
Definition: slider_base.cpp:43
state_t state_
Current state of the widget.
int rounded_division(int a, int b)
Definition: math.hpp:305
Go half the visible items towards the begin.
Definition: slider_base.hpp:54
point drag_initial_mouse_
The position the mouse was when draggin the slider was started.
virtual unsigned offset_after() const =0
The number of pixels we can&#39;t use since they&#39;re used for borders.
virtual unsigned get_state() const override
See styled_widget::get_state.
int x
x coordinate.
Definition: point.hpp:45
Generic file dialog.
Definition: field-fwd.hpp:23
void signal_handler_mouse_enter(const event::ui_event event, bool &handled, bool &halt)
Sent by a widget to notify others its contents or state are modified.
Definition: handler.hpp:89
int available_length() const
int drag_initial_offset_
The offset in pixels the slider was when dragging the positioner was started.
virtual void set_active(const bool active) override
See styled_widget::set_active.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
int positioner_offset_
The start offset of the positioner.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:466
void move_positioner(int offset)
Moves the positioner.
Go half the visible items towards the end.
Definition: slider_base.hpp:58
General math utility functions.
virtual int get_length_difference(const point &original, const point &current) const =0
Gets the relevant difference in between the two positions.
std::vector< canvas > & get_canvases()
int get_y() const
Definition: widget.cpp:320
void recalculate()
Updates the slider.
void set_slider_position(int item_position)
Note the position isn&#39;t guaranteed to be the wanted position the step size is honored.
void signal_handler_mouse_motion(const event::ui_event event, bool &handled, bool &halt, const point &coordinate)
virtual int jump_size() const
Go one item towards the end.
Definition: slider_base.hpp:57
int max_offset() const
#define DBG_GUI_E
Definition: log.hpp:35
window * get_window()
Get the parent window.
Definition: widget.cpp:117
Go to begin position.
Definition: slider_base.hpp:52
Holds a 2D point.
Definition: point.hpp:24
Base class for all visible items.
Go to the end position.
Definition: slider_base.hpp:56
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:118
virtual void place(const point &origin, const point &size) override
See widget::place.
int item_position_
The item the positioner is at, starts at 0.
void mouse_capture(const bool capture=true)
Definition: window.cpp:1270
state_t
Possible states of the widget.
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:69
#define LOG_HEADER
Definition: slider_base.cpp:27
void recalculate_positioner()
scroll_mode
scroll &#39;step size&#39;.
Definition: slider_base.hpp:51
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
virtual void update_canvas() override
See styled_widget::update_canvas.
int y
y coordinate.
Definition: point.hpp:48
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
ui_event
The event send to the dispatcher.
Definition: handler.hpp:48
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)