The Battle for Wesnoth  1.15.1+dev
slider_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "gui/core/log.hpp"
20 #include "gui/widgets/window.hpp" // Needed for invalidate_layout()
21 
22 #include "utils/functional.hpp"
23 #include "utils/math.hpp"
24 
25 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
26 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
27 
28 namespace
29 {
30 int rounded_division(int value, int new_base, int old_base)
31 {
32  if(old_base == 0) {
33  return new_base / 2;
34  } else {
35  return ::rounded_division(value * new_base, old_base);
36  }
37 }
38 } // end anon namespace
39 
40 namespace gui2
41 {
42 slider_base::slider_base(const implementation::builder_styled_widget& builder, const std::string& control_type)
43  : styled_widget(builder, control_type)
44  , state_(ENABLED)
45  , item_last_(0)
46  , item_position_(0)
47  , drag_initial_mouse_(0, 0)
48  , drag_initial_offset_(0)
49  , positioner_offset_(0)
50  , positioner_length_(0)
51  , snap_(true)
52 {
53  connect_signal<event::MOUSE_ENTER>(
54  std::bind(&slider_base::signal_handler_mouse_enter, this, _2, _3, _4));
55  connect_signal<event::MOUSE_MOTION>(
56  std::bind(&slider_base::signal_handler_mouse_motion, this, _2, _3, _4, _5));
57  connect_signal<event::MOUSE_LEAVE>(
58  std::bind(&slider_base::signal_handler_mouse_leave, this, _2, _3));
59  connect_signal<event::LEFT_BUTTON_DOWN>(
60  std::bind(&slider_base::signal_handler_left_button_down, this, _2, _3));
61  connect_signal<event::LEFT_BUTTON_UP>(
62  std::bind(&slider_base::signal_handler_left_button_up, this, _2, _3));
63 }
64 
66 {
67  switch(scroll) {
68  case BEGIN:
70  break;
71 
72  case ITEM_BACKWARDS:
74  break;
75 
78  break;
79 
80  case JUMP_BACKWARDS:
82  break;
83 
84  case END:
86  break;
87 
88  case ITEM_FORWARD:
90  break;
91 
92  case HALF_JUMP_FORWARD:
94  break;
95 
96  case JUMP_FORWARD:
98  break;
99 
100  default:
101  assert(false);
102  }
103 
104  fire(event::NOTIFY_MODIFIED, *this, nullptr);
105 }
106 
107 void slider_base::place(const point& origin, const point& size)
108 {
109  // Inherited.
110  styled_widget::place(origin, size);
111 
112  recalculate();
113 }
114 
115 void slider_base::set_active(const bool active)
116 {
117  if(get_active() != active) {
118  set_state(active ? ENABLED : DISABLED);
119  }
120 }
121 
123 {
124  return state_ != DISABLED;
125 }
126 
127 unsigned slider_base::get_state() const
128 {
129  return state_;
130 }
131 
132 void slider_base::set_slider_position(int item_position)
133 {
134  // Set the value always execute since we update a part of the state.
135  item_position_ = utils::clamp(item_position, 0, item_last_);
136 
137  // Determine the pixel offset of the item position.
139 
140  update_canvas();
141 }
142 
144 {
145  for(auto& tmp : get_canvases()) {
146  tmp.set_variable("positioner_offset", wfl::variant(positioner_offset_));
147  tmp.set_variable("positioner_length", wfl::variant(positioner_length_));
148  }
149 
150  set_is_dirty(true);
151 }
152 
154 {
155  if(state != state_) {
156  state_ = state;
157  set_is_dirty(true);
158  }
159 }
160 
162 {
163  // We can be called before the size has been set up in that case we can't do
164  // the proper recalcultion so stop before we die with an assert.
165  if(!get_length()) {
166  return;
167  }
168 
169  assert(available_length() > 0);
170 
172 
174 }
175 
176 void slider_base::move_positioner(int new_offset)
177 {
178  int max_offset = this->max_offset();
179  new_offset = utils::clamp(new_offset, 0, max_offset);
180 
181  slider_base::slider_position_t final_offset = {new_offset, max_offset};
182  update_slider_position(final_offset);
183 
184  assert(final_offset.max_offset == max_offset);
185  positioner_offset_ = final_offset.offset + offset_before();
186 
187  update_canvas();
188 }
189 
191 {
192  int new_position = rounded_division(pos.offset, item_last_, pos.max_offset);
193 
194  if(snap_) {
195  pos.offset = rounded_division(new_position, pos.max_offset, item_last_);
196  }
197 
198  if(new_position != item_position_) {
199  item_position_ = new_position;
200 
202 
203  fire(event::NOTIFY_MODIFIED, *this, nullptr);
204  }
205 }
206 
207 void slider_base::signal_handler_mouse_enter(const event::ui_event event, bool& handled, bool& halt)
208 {
209  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
210 
211  // Send the motion under our event id to make debugging easier.
212  signal_handler_mouse_motion(event, handled, halt, get_mouse_position());
213 }
214 
216  const event::ui_event event, bool& handled, bool& halt, const point& coordinate)
217 {
218  DBG_GUI_E << LOG_HEADER << ' ' << event << " at " << coordinate << ".\n";
219 
220  point mouse = coordinate;
221  mouse.x -= get_x();
222  mouse.y -= get_y();
223 
224  switch(state_) {
225  case ENABLED:
226  if(on_positioner(mouse)) {
228  }
229 
230  break;
231 
232  case PRESSED:
234  break;
235 
236  case FOCUSED:
237  if(!on_positioner(mouse)) {
239  }
240 
241  break;
242 
243  case DISABLED:
244  // Shouldn't be possible, but seems to happen in the lobby
245  // if a resize layout happens during dragging.
246  halt = true;
247  break;
248 
249  default:
250  assert(false);
251  }
252 
253  handled = true;
254 }
255 
257 {
258  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
259 
260  if(state_ == FOCUSED) {
262  }
263 
264  handled = true;
265 }
266 
268 {
269  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
270 
271  point mouse = get_mouse_position();
272  mouse.x -= get_x();
273  mouse.y -= get_y();
274 
275  if(on_positioner(mouse)) {
276  assert(get_window());
277 
278  drag_initial_mouse_ = mouse;
280 
283  }
284 
285  const int bar = on_bar(mouse);
286 
287  if(bar == -1) {
289  } else if(bar == 1) {
291  } else {
292  assert(bar == 0);
293  }
294 
295  handled = true;
296 }
297 
299 {
300  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
301 
302  point mouse = get_mouse_position();
303  mouse.x -= get_x();
304  mouse.y -= get_y();
305 
306  if(state_ != PRESSED) {
307  return;
308  }
309 
310  assert(get_window());
311  get_window()->mouse_capture(false);
312 
313  if(on_positioner(mouse)) {
315  } else {
317  }
318 
319  drag_initial_mouse_ = {0, 0};
321 
322  handled = true;
323 }
324 
326 {
327  // These values won't change so set them once.
328  for(auto& tmp : get_canvases()) {
329  tmp.set_variable("offset_before", wfl::variant(offset_before()));
330  tmp.set_variable("offset_after", wfl::variant(offset_after()));
331  }
332 }
333 
334 } // namespace gui2
constexpr const T & clamp(const T &value, const T &min, const T &max)
Definition: general.hpp:31
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:52
int get_x() const
Definition: widget.cpp:312
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:54
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:58
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:62
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:65
slider_base(const implementation::builder_styled_widget &builder, const std::string &control_type)
Definition: slider_base.cpp:42
state_t state_
Current state of the widget.
int rounded_division(int a, int b)
Definition: math.hpp:284
Go half the visible items towards the begin.
Definition: slider_base.hpp:53
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:44
Generic file dialog.
Definition: field-fwd.hpp:22
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:96
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:86
int positioner_offset_
The start offset of the positioner.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:463
void move_positioner(int offset)
Moves the positioner.
Go half the visible items towards the end.
Definition: slider_base.hpp:57
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:317
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:56
int max_offset() const
#define DBG_GUI_E
Definition: log.hpp:34
window * get_window()
Get the parent window.
Definition: widget.cpp:114
Go to begin position.
Definition: slider_base.hpp:51
Holds a 2D point.
Definition: point.hpp:23
Base class for all visible items.
Go to the end position.
Definition: slider_base.hpp:55
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:116
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:1276
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:130
#define LOG_HEADER
Definition: slider_base.cpp:26
void recalculate_positioner()
scroll_mode
scroll &#39;step size&#39;.
Definition: slider_base.hpp:50
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:47
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
ui_event
The event send to the dispatcher.
Definition: handler.hpp:55
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)