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