The Battle for Wesnoth  1.17.21+dev
scrollbar.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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 #pragma once
17 
18 #include "gui/core/notifier.hpp"
20 #include "sdl/rect.hpp"
21 #include <functional>
22 
23 namespace gui2
24 {
25 
26 /**
27  * Base class for a scroll bar.
28  *
29  * class will be subclassed for the horizontal and vertical scroll bar.
30  * It might be subclassed for a slider class.
31  *
32  * To make this class generic we talk a lot about offset and length and use
33  * pure virtual functions. The classes implementing us can use the heights or
34  * widths, whichever is applicable.
35  *
36  * The NOTIFY_MODIFIED event is send when the position of scrollbar is changed.
37  *
38  * Common signal handlers:
39  * - connect_signal_notify_modified
40  */
42 {
43  /** @todo Abstract the code so this friend is no longer needed. */
44  friend class slider;
45 
46 public:
47  scrollbar_base(const implementation::builder_styled_widget& builder, const std::string& control_type);
48 
49  /**
50  * scroll 'step size'.
51  *
52  * When scrolling we always scroll a 'fixed' amount, these are the
53  * parameters for these amounts.
54  */
55  enum scroll_mode {
56  BEGIN, /**< Go to begin position. */
57  ITEM_BACKWARDS, /**< Go one item towards the begin. */
58  HALF_JUMP_BACKWARDS, /**< Go half the visible items towards the begin.
59  */
60  JUMP_BACKWARDS, /**< Go the visible items towards the begin. */
61  END, /**< Go to the end position. */
62  ITEM_FORWARD, /**< Go one item towards the end. */
63  HALF_JUMP_FORWARD, /**< Go half the visible items towards the end. */
65  }; /**< Go the visible items towards the end. */
66 
67  /**
68  * Sets the item position.
69  *
70  * We scroll a predefined step.
71  *
72  * @param scroll 'step size' to scroll.
73  */
74  void scroll(const scroll_mode scroll);
75 
76  void scroll_by(const int pixels);
77 
78  /** Is the positioner at the beginning of the scrollbar? */
79  bool at_begin() const
80  {
81  return item_position_ == 0;
82  }
83 
84  /**
85  * Is the positioner at the and of the scrollbar?
86  *
87  * Note both begin and end might be true at the same time.
88  */
89  bool at_end() const
90  {
92  }
93 
94  /** Are all items visible? */
95  bool all_items_visible() const
96  {
97  return visible_items_ >= item_count_;
98  }
99 
100  /***** ***** ***** ***** layout functions ***** ***** ***** *****/
101 
102  /** See @ref widget::place. */
103  virtual void place(const point& origin, const point& size) override;
104 
105  /***** ***** ***** ***** Inherited ***** ***** ***** *****/
106 
107  /** See @ref styled_widget::set_active. */
108  virtual void set_active(const bool active) override;
109 
110  /** See @ref styled_widget::get_active. */
111  virtual bool get_active() const override;
112 
113  /** See @ref styled_widget::get_state. */
114  virtual unsigned get_state() const override;
115 
116  /**
117  * Possible states of the widget.
118  *
119  * Note the order of the states must be the same as defined in settings.hpp.
120  */
121  enum state_t {
126  };
127 
128  /***** ***** ***** setters / getters for members ***** ****** *****/
129 
130  void set_item_count(const unsigned item_count)
131  {
132  item_count_ = item_count;
133  recalculate();
134  }
135  unsigned get_item_count() const
136  {
137  return item_count_;
138  }
139 
140  /**
141  * Note the position isn't guaranteed to be the wanted position
142  * the step size is honored. The value will be rouded down.
143  */
144  void set_item_position(const unsigned item_position);
145  unsigned get_item_position() const
146  {
147  return item_position_;
148  }
149 
150  unsigned get_visible_items() const
151  {
152  return visible_items_;
153  }
154 
155  void set_visible_items(const unsigned visible_items)
156  {
157  visible_items_ = visible_items;
158  recalculate();
159  }
160 
161  unsigned get_step_size() const
162  {
163  return step_size_;
164  }
165 
166  void set_step_size(const unsigned step_size)
167  {
168  // Step size can't be 0!
169  if(step_size == 0) {
170  throw std::invalid_argument("GUI2: scrollbar step size cannot be 0");
171  }
172 
173  step_size_ = step_size;
174  recalculate();
175  }
176 
177  float get_pixels_per_step() const
178  {
179  return pixels_per_step_;
180  }
181 
182 protected:
183  void finalize_setup();
184 
185  unsigned get_positioner_offset() const
186  {
187  return positioner_offset_;
188  }
189 
190  unsigned get_positioner_length() const
191  {
192  return positioner_length_;
193  }
194 
196  {
197  return mouse_;
198  }
199 
200  /**
201  * See @ref styled_widget::update_canvas.
202  *
203  * After a recalculation the canvasses also need to be updated.
204  */
205  virtual void update_canvas() override;
206 
207  /**
208  * Callback for subclasses to get notified about positioner movement.
209  *
210  * @todo This is a kind of hack due to the fact there's no simple
211  * callback slot mechanism. See whether we can implement a generic way to
212  * attach callback which would remove quite some extra code.
213  */
215  {
216  }
217 
218 private:
219  void set_state(const state_t state);
220  /**
221  * Current state of the widget.
222  *
223  * The state of the widget determines what to render and how the widget
224  * reacts to certain 'events'.
225  */
227 
228  /** The number of items the scrollbar 'holds'. */
229  unsigned item_count_;
230 
231  /** The item the positioner is at, starts at 0. */
232  unsigned item_position_;
233 
234  /**
235  * The number of items which can be shown at the same time.
236  *
237  * As long as all items are visible we don't need to scroll.
238  */
239  unsigned visible_items_;
240 
241  /**
242  * Number of items moved when scrolling.
243  *
244  * The step size is the minimum number of items we scroll through when we
245  * move. Normally this value is 1, we can move per item. But for example
246  * sliders want for example to move per 5 items.
247  */
248  unsigned step_size_;
249 
250  /**
251  * Number of pixels per step.
252  *
253  * The number of pixels the positioner needs to move to go to the next step.
254  * Note if there is too little space it can happen 1 pixel does more than 1
255  * step.
256  */
258 
259  /**
260  * The position the mouse was at the last movement.
261  *
262  * This is used during dragging the positioner.
263  */
265 
266  /**
267  * The start offset of the positioner.
268  *
269  * This takes the offset before in consideration.
270  */
272 
273  /** The current length of the positioner. */
275 
276  /***** ***** ***** ***** Pure virtual functions ***** ***** ***** *****/
277 
278  /** Get the length of the scrollbar. */
279  virtual unsigned get_length() const = 0;
280 
281  /** The minimum length of the positioner. */
282  virtual unsigned minimum_positioner_length() const = 0;
283 
284  /** The maximum length of the positioner. */
285  virtual unsigned maximum_positioner_length() const = 0;
286 
287  /**
288  * The number of pixels we can't use since they're used for borders.
289  *
290  * These are the pixels before the widget (left side if horizontal,
291  * top side if vertical).
292  */
293  virtual unsigned offset_before() const = 0;
294 
295  /**
296  * The number of pixels we can't use since they're used for borders.
297  *
298  * These are the pixels after the widget (right side if horizontal,
299  * bottom side if vertical).
300  */
301  virtual unsigned offset_after() const = 0;
302 
303  /**
304  * Is the coordinate on the positioner?
305  *
306  * @param coordinate Coordinate to test whether it's on the
307  * positioner.
308  *
309  * @returns Whether the location on the positioner is.
310  */
311  virtual bool on_positioner(const point& coordinate) const = 0;
312 
313  /**
314  * Is the coordinate on the bar?
315  *
316  * @param coordinate Coordinate to test whether it's on the
317  * bar.
318  *
319  * @returns Whether the location on the bar is.
320  * @retval -1 Coordinate is on the bar before positioner.
321  * @retval 0 Coordinate is not on the bar.
322  * @retval 1 Coordinate is on the bar after the positioner.
323  */
324  virtual int on_bar(const point& coordinate) const = 0;
325 
326  /**
327  * Is the coordinate in the bar's orthogonal range?
328  *
329  * @param coordinate Coordinate to test whether it's in-range.
330  *
331  * @returns Whether the location is in the bar's.
332  * orthogonal range.
333  */
334  virtual bool in_orthogonal_range(const point& coordinate) const = 0;
335 
336  /**
337  * Gets the relevant difference in between the two positions.
338  *
339  * This function is used to determine how much the positioner needs to be
340  * moved.
341  */
342  virtual int get_length_difference(const point& original,
343  const point& current) const = 0;
344 
345  /***** ***** ***** ***** Private functions ***** ***** ***** *****/
346 
347  /**
348  * Updates the scrollbar.
349  *
350  * Needs to be called when something changes eg number of items
351  * or available size. It can only be called once we have a size
352  * otherwise we can't calculate a thing.
353  */
354  void recalculate();
355 
356  /**
357  * Updates the positioner.
358  *
359  * This is a helper for recalculate().
360  */
361  void recalculate_positioner();
362 
363  /**
364  * Moves the positioner.
365  *
366  * @param distance The distance moved, negative to begin, positive
367  * to end.
368  */
369  virtual void move_positioner(const int distance);
370 
371  /***** ***** ***** signal handlers ***** ****** *****/
372 
374  bool& handled,
375  bool& halt);
376 
378  bool& handled,
379  bool& halt,
380  const point& coordinate);
381 
382  void signal_handler_mouse_leave(const event::ui_event event, bool& handled);
383 
385  bool& handled);
386 
388  bool& handled);
389 };
390 
391 } // namespace gui2
Base class for a scroll bar.
Definition: scrollbar.hpp:42
bool at_begin() const
Is the positioner at the beginning of the scrollbar?
Definition: scrollbar.hpp:79
virtual unsigned minimum_positioner_length() const =0
The minimum length of the positioner.
virtual bool get_active() const override
See styled_widget::get_active.
Definition: scrollbar.cpp:142
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:152
void set_step_size(const unsigned step_size)
Definition: scrollbar.hpp:166
point get_mouse_position_last_move() const
Definition: scrollbar.hpp:195
void signal_handler_mouse_leave(const event::ui_event event, bool &handled)
Definition: scrollbar.cpp:391
void signal_handler_left_button_up(const event::ui_event event, bool &handled)
Definition: scrollbar.cpp:434
virtual void update_canvas() override
See styled_widget::update_canvas.
Definition: scrollbar.cpp:179
unsigned get_positioner_offset() const
Definition: scrollbar.hpp:185
unsigned positioner_offset_
The start offset of the positioner.
Definition: scrollbar.hpp:271
unsigned get_item_position() const
Definition: scrollbar.hpp:145
float pixels_per_step_
Number of pixels per step.
Definition: scrollbar.hpp:257
virtual void move_positioner(const int distance)
Moves the positioner.
Definition: scrollbar.cpp:283
virtual bool in_orthogonal_range(const point &coordinate) const =0
Is the coordinate in the bar's orthogonal range?
void signal_handler_left_button_down(const event::ui_event event, bool &handled)
Definition: scrollbar.cpp:403
bool at_end() const
Is the positioner at the and of the scrollbar?
Definition: scrollbar.hpp:89
bool all_items_visible() const
Are all items visible?
Definition: scrollbar.hpp:95
virtual void place(const point &origin, const point &size) override
See widget::place.
Definition: scrollbar.cpp:127
scrollbar_base(const implementation::builder_styled_widget &builder, const std::string &control_type)
Definition: scrollbar.cpp:31
unsigned get_step_size() const
Definition: scrollbar.hpp:161
virtual void child_callback_positioner_moved()
Callback for subclasses to get notified about positioner movement.
Definition: scrollbar.hpp:214
float get_pixels_per_step() const
Definition: scrollbar.hpp:177
virtual unsigned maximum_positioner_length() const =0
The maximum length of the positioner.
void set_item_count(const unsigned item_count)
Definition: scrollbar.hpp:130
state_t
Possible states of the widget.
Definition: scrollbar.hpp:121
virtual int on_bar(const point &coordinate) const =0
Is the coordinate on the bar?
void set_state(const state_t state)
Definition: scrollbar.cpp:189
unsigned positioner_length_
The current length of the positioner.
Definition: scrollbar.hpp:274
unsigned item_count_
The number of items the scrollbar 'holds'.
Definition: scrollbar.hpp:229
unsigned get_item_count() const
Definition: scrollbar.hpp:135
void recalculate()
Updates the scrollbar.
Definition: scrollbar.cpp:197
unsigned visible_items_
The number of items which can be shown at the same time.
Definition: scrollbar.hpp:239
virtual unsigned offset_after() const =0
The number of pixels we can't use since they're used for borders.
virtual int get_length_difference(const point &original, const point &current) const =0
Gets the relevant difference in between the two positions.
virtual unsigned get_state() const override
See styled_widget::get_state.
Definition: scrollbar.cpp:147
void set_visible_items(const unsigned visible_items)
Definition: scrollbar.hpp:155
point mouse_
The position the mouse was at the last movement.
Definition: scrollbar.hpp:264
void scroll(const scroll_mode scroll)
Sets the item position.
Definition: scrollbar.cpp:71
unsigned get_positioner_length() const
Definition: scrollbar.hpp:190
state_t state_
Current state of the widget.
Definition: scrollbar.hpp:226
virtual unsigned get_length() const =0
Get the length of the scrollbar.
scroll_mode
scroll 'step size'.
Definition: scrollbar.hpp:55
@ ITEM_FORWARD
Go one item towards the end.
Definition: scrollbar.hpp:62
@ ITEM_BACKWARDS
Go one item towards the begin.
Definition: scrollbar.hpp:57
@ END
Go to the end position.
Definition: scrollbar.hpp:61
@ HALF_JUMP_FORWARD
Go half the visible items towards the end.
Definition: scrollbar.hpp:63
@ BEGIN
Go to begin position.
Definition: scrollbar.hpp:56
@ JUMP_BACKWARDS
Go the visible items towards the begin.
Definition: scrollbar.hpp:60
@ HALF_JUMP_BACKWARDS
Go half the visible items towards the begin.
Definition: scrollbar.hpp:58
void recalculate_positioner()
Updates the positioner.
Definition: scrollbar.cpp:269
void signal_handler_mouse_enter(const event::ui_event event, bool &handled, bool &halt)
Definition: scrollbar.cpp:335
virtual unsigned offset_before() const =0
The number of pixels we can't use since they're used for borders.
unsigned get_visible_items() const
Definition: scrollbar.hpp:150
unsigned item_position_
The item the positioner is at, starts at 0.
Definition: scrollbar.hpp:232
void scroll_by(const int pixels)
Definition: scrollbar.cpp:66
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: scrollbar.cpp:135
void signal_handler_mouse_motion(const event::ui_event event, bool &handled, bool &halt, const point &coordinate)
Definition: scrollbar.cpp:345
unsigned step_size_
Number of items moved when scrolling.
Definition: scrollbar.hpp:248
virtual bool on_positioner(const point &coordinate) const =0
Is the coordinate on the positioner?
A slider is a control that can select a value by moving a grip on a groove.
Definition: slider.hpp:60
Base class for all visible items.
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
Generic file dialog.
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:87
Contains the SDL_Rect helper code.
Holds a 2D point.
Definition: point.hpp:25