The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
scrollbar.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by Mark de Wever <[email protected]>
3  Part of the Battle for Wesnoth Project http://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 
24 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
25 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
26 
27 namespace gui2
28 {
29 
31  : tcontrol(COUNT)
32  , state_(ENABLED)
33  , item_count_(0)
34  , item_position_(0)
35  , visible_items_(1)
36  , step_size_(1)
37  , pixels_per_step_(0.0)
38  , mouse_(0, 0)
39  , positioner_offset_(0)
40  , positioner_length_(0)
41 {
42  connect_signal<event::MOUSE_ENTER>(std::bind(
43  &tscrollbar_::signal_handler_mouse_enter, this, _2, _3, _4));
44  connect_signal<event::MOUSE_MOTION>(std::bind(
45  &tscrollbar_::signal_handler_mouse_motion, this, _2, _3, _4, _5));
46  connect_signal<event::MOUSE_LEAVE>(std::bind(
48  connect_signal<event::LEFT_BUTTON_DOWN>(std::bind(
50  connect_signal<event::LEFT_BUTTON_UP>(std::bind(
52 }
53 
54 void tscrollbar_::scroll(const tscroll scroll)
55 {
56  switch(scroll) {
57  case BEGIN:
59  break;
60 
61  case ITEM_BACKWARDS:
62  if(item_position_) {
64  }
65  break;
66 
70  : 0);
71  break;
72 
73  case JUMP_BACKWARDS:
76  : 0);
77  break;
78 
79  case END:
81  break;
82 
83  case ITEM_FORWARD:
85  break;
86 
87  case HALF_JUMP_FORWARD:
89  break;
90 
91  case JUMP_FORWARD:
93  break;
94 
95  default:
96  assert(false);
97  }
98 
99  fire(event::NOTIFY_MODIFIED, *this, nullptr);
100 }
101 
102 void tscrollbar_::place(const tpoint& origin, const tpoint& size)
103 {
104  // Inherited.
105  tcontrol::place(origin, size);
106 
107  recalculate();
108 }
109 
110 void tscrollbar_::set_active(const bool active)
111 {
112  if(get_active() != active) {
113  set_state(active ? ENABLED : DISABLED);
114  }
115 }
116 
118 {
119  return state_ != DISABLED;
120 }
121 
122 unsigned tscrollbar_::get_state() const
123 {
124  return state_;
125 }
126 
127 void tscrollbar_::set_item_position(const unsigned item_position)
128 {
129  // Set the value always execute since we update a part of the state.
130  item_position_ = item_position > item_count_ - visible_items_
132  : item_position;
133 
135 
136  if(all_items_visible()) {
137  item_position_ = 0;
138  }
139 
140  // Determine the pixel offset of the item position.
142  = static_cast<unsigned>(item_position_ * pixels_per_step_);
143 
144  update_canvas();
145 
147 }
148 
150 {
151 
152  for(auto & tmp : canvas())
153  {
154  tmp.set_variable("positioner_offset", variant(positioner_offset_));
155  tmp.set_variable("positioner_length", variant(positioner_length_));
156  }
157  set_is_dirty(true);
158 }
159 
161 {
162  if(state != state_) {
163  state_ = state;
164  set_is_dirty(true);
165  }
166 }
167 
169 {
170  // We can be called before the size has been set up in that case we can't do
171  // the proper recalcultion so stop before we die with an assert.
172  if(!get_length()) {
173  return;
174  }
175 
176  // Get the available size for the slider to move.
177  const int available_length = get_length() - offset_before()
178  - offset_after();
179 
180  assert(available_length > 0);
181 
182  // All visible.
183  if(item_count_ <= visible_items_) {
185  positioner_length_ = available_length;
187  item_position_ = 0;
188  update_canvas();
189  return;
190  }
191 
192  /**
193  * @todo In the MP lobby it can happen that a listbox has first zero items,
194  * then gets filled and since there are no visible items the second assert
195  * after this block will be triggered. Use this ugly hack to avoid that
196  * case. (This hack also added the gui/widgets/window.hpp include.)
197  */
198  if(!visible_items_) {
199  twindow* window = get_window();
200  assert(window);
201  window->invalidate_layout();
203  << " Can't recalculate size, force a window layout phase.\n";
204  return;
205  }
206 
207  assert(step_size_);
208  assert(visible_items_);
209 
210  const unsigned steps = (item_count_ - visible_items_ - step_size_)
211  / step_size_;
212 
213  positioner_length_ = available_length * visible_items_ / item_count_;
215 
216  // Make sure we can also show the last step, so add one more step.
217  pixels_per_step_ = (available_length - positioner_length_)
218  / static_cast<float>(steps + 1);
219 
221 #if 0
222  std::cerr << "Scrollbar recalculate overview:\n"
223  << "item_count_ " << item_count_
224  << " visible_items_ " << visible_items_
225  << " step_size_ " << step_size_
226  << " steps " << steps
227  << "\n"
228  << "minimum_positioner_length() " << minimum_positioner_length()
229  << " maximum_positioner_length() " << maximum_positioner_length()
230  << "\n"
231  << " positioner_length_ " << positioner_length_
232  << " positioner_offset_ " << positioner_offset_
233  << "\n"
234  << "available_length " << available_length
235  << " pixels_per_step_ " << pixels_per_step_
236  << ".\n\n";
237 #endif
238 }
239 
241 {
242  const unsigned minimum = minimum_positioner_length();
243  const unsigned maximum = maximum_positioner_length();
244 
245  if(minimum == maximum) {
246  positioner_length_ = maximum;
247  } else if(maximum != 0 && positioner_length_ > maximum) {
248  positioner_length_ = maximum;
249  } else if(positioner_length_ < minimum) {
250  positioner_length_ = minimum;
251  }
252 }
253 
254 void tscrollbar_::move_positioner(const int distance)
255 {
256  if(distance < 0 && -distance > static_cast<int>(positioner_offset_)) {
257  positioner_offset_ = 0;
258  } else {
259  positioner_offset_ += distance;
260  }
261 
262  const unsigned length = get_length() - offset_before() - offset_after()
264 
265  if(positioner_offset_ > length) {
267  }
268 
269  unsigned position
270  = static_cast<unsigned>(positioner_offset_ / pixels_per_step_);
271 
272  // Note due to floating point rounding the position might be outside the
273  // available positions so set it back.
274  if(position > item_count_ - visible_items_) {
275  position = item_count_ - visible_items_;
276  }
277 
278  if(position != item_position_) {
279  item_position_ = position;
280 
282 
283  fire(event::NOTIFY_MODIFIED, *this, nullptr);
284 
285  // positioner_moved_notifier_.notify();
286  }
287 #if 0
288  std::cerr << "Scrollbar move overview:\n"
289  << "item_count_ " << item_count_
290  << " visible_items_ " << visible_items_
291  << " step_size_ " << step_size_
292  << "\n"
293  << "minimum_positioner_length() " << minimum_positioner_length()
294  << " maximum_positioner_length() " << maximum_positioner_length()
295  << "\n"
296  << " positioner_length_ " << positioner_length_
297  << " positioner_offset_ " << positioner_offset_
298  << "\n"
299  << " pixels_per_step_ " << pixels_per_step_
300  << " item_position_ " << item_position_
301  << ".\n\n";
302 #endif
303  update_canvas();
304 }
305 
307 {
308  // These values won't change so set them here.
309  for(auto & tmp : canvas())
310  {
311  tmp.set_variable("offset_before", variant(offset_before()));
312  tmp.set_variable("offset_after", variant(offset_after()));
313  }
314 }
315 
317  bool& handled,
318  bool& halt)
319 {
320  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
321 
322  // Send the motion under our event id to make debugging easier.
323  signal_handler_mouse_motion(event, handled, halt, get_mouse_position());
324 }
325 
327  bool& handled,
328  bool& halt,
329  const tpoint& coordinate)
330 {
331  DBG_GUI_E << LOG_HEADER << ' ' << event << " at " << coordinate << ".\n";
332 
333  tpoint mouse = coordinate;
334  mouse.x -= get_x();
335  mouse.y -= get_y();
336 
337  switch(state_) {
338  case ENABLED:
339  if(on_positioner(mouse)) {
341  }
342 
343  break;
344 
345  case PRESSED: {
346  if(in_orthogonal_range(mouse)) {
347  const int distance = get_length_difference(mouse_, mouse);
348  mouse_ = mouse;
349  move_positioner(distance);
350  }
351 
352  } break;
353 
354  case FOCUSED:
355  if(!on_positioner(mouse)) {
357  }
358  break;
359 
360  case DISABLED:
361  // Shouldn't be possible, but seems to happen in the lobby
362  // if a resize layout happens during dragging.
363  halt = true;
364  break;
365 
366  default:
367  assert(false);
368  }
369  handled = true;
370 }
371 
373  bool& handled)
374 {
375  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
376 
377  if(state_ == FOCUSED) {
379  }
380  handled = true;
381 }
382 
383 
385  bool& handled)
386 {
387  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
388 
389  tpoint mouse = get_mouse_position();
390  mouse.x -= get_x();
391  mouse.y -= get_y();
392 
393  if(on_positioner(mouse)) {
394  assert(get_window());
395  mouse_ = mouse;
398  }
399 
400  const int bar = on_bar(mouse);
401 
402  if(bar == -1) {
404  fire(event::NOTIFY_MODIFIED, *this, nullptr);
405  // positioner_moved_notifier_.notify();
406  } else if(bar == 1) {
408  fire(event::NOTIFY_MODIFIED, *this, nullptr);
409  // positioner_moved_notifier_.notify();
410  } else {
411  assert(bar == 0);
412  }
413 
414  handled = true;
415 }
416 
418  bool& handled)
419 {
420  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
421 
422  tpoint mouse = get_mouse_position();
423  mouse.x -= get_x();
424  mouse.y -= get_y();
425 
426  if(state_ != PRESSED) {
427  return;
428  }
429 
430  assert(get_window());
431  get_window()->mouse_capture(false);
432 
433  if(on_positioner(mouse)) {
435  } else {
437  }
438 
439  handled = true;
440 }
441 
442 } // namespace gui2
Define the common log macros for the gui toolkit.
void signal_handler_left_button_down(const event::tevent event, bool &handled)
Definition: scrollbar.cpp:384
unsigned positioner_offset_
The start offset of the positioner.
Definition: scrollbar.hpp:251
unsigned positioner_length_
The current length of the positioner.
Definition: scrollbar.hpp:254
void signal_handler_mouse_motion(const event::tevent event, bool &handled, bool &halt, const tpoint &coordinate)
Definition: scrollbar.cpp:326
Go the visibile items towards the begin.
Definition: scrollbar.hpp:60
virtual void child_callback_positioner_moved()
Callback for subclasses to get notified about positioner movement.
Definition: scrollbar.hpp:181
This file contains the window object, this object is a top level container which has the event manage...
#define ERR_GUI_G
Definition: log.hpp:44
virtual unsigned get_state() const override
See tcontrol::get_state.
Definition: scrollbar.cpp:122
virtual bool get_active() const override
See tcontrol::get_active.
Definition: scrollbar.cpp:117
Go to the end position.
Definition: scrollbar.hpp:61
void load_config_extra()
Inherited from tcontrol.
Definition: scrollbar.cpp:306
void signal_handler_left_button_up(const event::tevent event, bool &handled)
Definition: scrollbar.cpp:417
int get_y() const
Definition: widget.cpp:289
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: scrollbar.cpp:102
Go one item towards the begin.
Definition: scrollbar.hpp:57
virtual int get_length_difference(const tpoint &original, const tpoint &current) const =0
Gets the relevant difference in between the two positions.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:435
bool all_items_visible() const
Are all items visible?
Definition: scrollbar.hpp:93
virtual void set_active(const bool active) override
See tcontrol::set_active.
Definition: scrollbar.cpp:110
virtual bool on_positioner(const tpoint &coordinate) const =0
Is the coordinate on the positioner?
virtual unsigned get_length() const =0
Get the length of the scrollbar.
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
GLuint GLsizei GLsizei * length
Definition: glew.h:1793
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
virtual unsigned offset_before() const =0
The number of pixels we can't use since they're used for borders.
bool fire(const tevent event, twidget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:144
void recalculate()
Updates the scrollbar.
Definition: scrollbar.cpp:168
int y
y coordinate.
Definition: point.hpp:34
void move_positioner(const int distance)
Moves the positioner.
Definition: scrollbar.cpp:254
virtual unsigned offset_after() const =0
The number of pixels we can't use since they're used for borders.
void set_state(const tstate state)
Definition: scrollbar.cpp:160
tpoint get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:149
Send by a widget to notify others its contents or state are modified.
Definition: handler.hpp:133
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: control.cpp:250
int get_x() const
Definition: widget.cpp:284
tevent
The event send to the dispatcher.
Definition: handler.hpp:54
#define LOG_HEADER
Definition: scrollbar.cpp:25
Go half the visible items towards the begin.
Definition: scrollbar.hpp:58
unsigned step_size_
Number of items moved when scrolling.
Definition: scrollbar.hpp:228
int x
x coordinate.
Definition: point.hpp:31
#define DBG_GUI_E
Definition: log.hpp:35
virtual bool in_orthogonal_range(const tpoint &coordinate) const =0
Is the coordinate in the bar's orthogonal range?
unsigned item_position_
The item the positioner is at, starts at 0.
Definition: scrollbar.hpp:212
unsigned item_count_
The number of items the scrollbar 'holds'.
Definition: scrollbar.hpp:209
virtual void update_canvas() override
See tcontrol::update_canvas.
Definition: scrollbar.cpp:149
void signal_handler_mouse_leave(const event::tevent event, bool &handled)
Definition: scrollbar.cpp:372
Holds a 2D point.
Definition: point.hpp:24
Go to begin position.
Definition: scrollbar.hpp:56
std::vector< tcanvas > & canvas()
Definition: control.hpp:282
virtual int on_bar(const tpoint &coordinate) const =0
Is the coordinate on the bar?
void signal_handler_mouse_enter(const event::tevent event, bool &handled, bool &halt)
Definition: scrollbar.cpp:316
Base class for all visible items.
Definition: control.hpp:34
virtual unsigned maximum_positioner_length() const =0
The maximum length of the positioner.
GLsizeiptr size
Definition: glew.h:1649
tpoint mouse_
The position the mouse was at the last movement.
Definition: scrollbar.hpp:244
void scroll(const tscroll scroll)
Sets the item position.
Definition: scrollbar.cpp:54
unsigned visible_items_
The number of items which can be shown at the same time.
Definition: scrollbar.hpp:219
cl_event event
Definition: glew.h:3070
void mouse_capture(const bool capture=true)
Definition: window.cpp:1388
tstate state_
Current state of the widget.
Definition: scrollbar.hpp:206
void set_item_position(const unsigned item_position)
Note the position isn't guaranteed to be the wanted position the step size is honored.
Definition: scrollbar.cpp:127
tstate
Possible states of the widget.
Definition: scrollbar.hpp:191
Go one item towards the end.
Definition: scrollbar.hpp:62
twindow * get_window()
Get the parent window.
Definition: widget.cpp:116
void recalculate_positioner()
Updates the positioner.
Definition: scrollbar.cpp:240
virtual unsigned minimum_positioner_length() const =0
The minimum length of the positioner.
Go half the visible items towards the end.
Definition: scrollbar.hpp:63
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:941
float pixels_per_step_
Number of pixels per step.
Definition: scrollbar.hpp:237
tscroll
scroll 'step size'.
Definition: scrollbar.hpp:55