]> git.lizzy.rs Git - minetest.git/blob - src/gui/guiScrollBar.cpp
Add crosshair support for Android (#7865)
[minetest.git] / src / gui / guiScrollBar.cpp
1 /*
2 Copyright (C) 2002-2013 Nikolaus Gebhardt
3 This file is part of the "Irrlicht Engine".
4 For conditions of distribution and use, see copyright notice in irrlicht.h
5
6 Modified 2019.05.01 by stujones11, Stuart Jones <stujones111@gmail.com>
7
8 This is a heavily modified copy of the Irrlicht CGUIScrollBar class
9 which includes automatic scaling of the thumb slider and hiding of
10 the arrow buttons where there is insufficient space.
11 */
12
13 #include "guiScrollBar.h"
14 #include <IGUIButton.h>
15 #include <IGUISkin.h>
16
17 GUIScrollBar::GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
18                 core::rect<s32> rectangle, bool horizontal, bool auto_scale) :
19                 IGUIElement(EGUIET_ELEMENT, environment, parent, id, rectangle),
20                 up_button(nullptr), down_button(nullptr), is_dragging(false),
21                 is_horizontal(horizontal), is_auto_scaling(auto_scale),
22                 dragged_by_slider(false), tray_clicked(false), scroll_pos(0),
23                 draw_center(0), thumb_size(0), min_pos(0), max_pos(100), small_step(10),
24                 large_step(50), drag_offset(0), page_size(100), border_size(0)
25 {
26         refreshControls();
27         setNotClipped(false);
28         setTabStop(true);
29         setTabOrder(-1);
30         setPos(0);
31 }
32
33 bool GUIScrollBar::OnEvent(const SEvent &event)
34 {
35         if (isEnabled()) {
36                 switch (event.EventType) {
37                 case EET_KEY_INPUT_EVENT:
38                         if (event.KeyInput.PressedDown) {
39                                 const s32 old_pos = scroll_pos;
40                                 bool absorb = true;
41                                 switch (event.KeyInput.Key) {
42                                 case KEY_LEFT:
43                                 case KEY_UP:
44                                         setPos(scroll_pos - small_step);
45                                         break;
46                                 case KEY_RIGHT:
47                                 case KEY_DOWN:
48                                         setPos(scroll_pos + small_step);
49                                         break;
50                                 case KEY_HOME:
51                                         setPos(min_pos);
52                                         break;
53                                 case KEY_PRIOR:
54                                         setPos(scroll_pos - large_step);
55                                         break;
56                                 case KEY_END:
57                                         setPos(max_pos);
58                                         break;
59                                 case KEY_NEXT:
60                                         setPos(scroll_pos + large_step);
61                                         break;
62                                 default:
63                                         absorb = false;
64                                 }
65                                 if (scroll_pos != old_pos) {
66                                         SEvent e;
67                                         e.EventType = EET_GUI_EVENT;
68                                         e.GUIEvent.Caller = this;
69                                         e.GUIEvent.Element = nullptr;
70                                         e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
71                                         Parent->OnEvent(e);
72                                 }
73                                 if (absorb)
74                                         return true;
75                         }
76                         break;
77                 case EET_GUI_EVENT:
78                         if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) {
79                                 if (event.GUIEvent.Caller == up_button)
80                                         setPos(scroll_pos - small_step);
81                                 else if (event.GUIEvent.Caller == down_button)
82                                         setPos(scroll_pos + small_step);
83
84                                 SEvent e;
85                                 e.EventType = EET_GUI_EVENT;
86                                 e.GUIEvent.Caller = this;
87                                 e.GUIEvent.Element = nullptr;
88                                 e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
89                                 Parent->OnEvent(e);
90                                 return true;
91                         } else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
92                                 if (event.GUIEvent.Caller == this)
93                                         is_dragging = false;
94                         break;
95                 case EET_MOUSE_INPUT_EVENT: {
96                         const core::position2di p(event.MouseInput.X, event.MouseInput.Y);
97                         bool is_inside = isPointInside(p);
98                         switch (event.MouseInput.Event) {
99                         case EMIE_MOUSE_WHEEL:
100                                 if (Environment->hasFocus(this)) {
101                                         s8 d = event.MouseInput.Wheel < 0 ? -1 : 1;
102                                         s8 h = is_horizontal ? 1 : -1;
103                                         setPos(getPos() + (d * small_step * h));
104
105                                         SEvent e;
106                                         e.EventType = EET_GUI_EVENT;
107                                         e.GUIEvent.Caller = this;
108                                         e.GUIEvent.Element = nullptr;
109                                         e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
110                                         Parent->OnEvent(e);
111                                         return true;
112                                 }
113                                 break;
114                         case EMIE_LMOUSE_PRESSED_DOWN: {
115                                 if (is_inside) {
116                                         is_dragging = true;
117                                         dragged_by_slider = slider_rect.isPointInside(p);
118                                         core::vector2di corner = slider_rect.UpperLeftCorner;
119                                         drag_offset = is_horizontal ? p.X - corner.X : p.Y - corner.Y;
120                                         tray_clicked = !dragged_by_slider;
121                                         if (tray_clicked) {
122                                                 const s32 new_pos = getPosFromMousePos(p);
123                                                 const s32 old_pos = scroll_pos;
124                                                 setPos(new_pos);
125                                                 // drag in the middle
126                                                 drag_offset = thumb_size / 2;
127                                                 // report the scroll event
128                                                 if (scroll_pos != old_pos && Parent) {
129                                                         SEvent e;
130                                                         e.EventType = EET_GUI_EVENT;
131                                                         e.GUIEvent.Caller = this;
132                                                         e.GUIEvent.Element = nullptr;
133                                                         e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
134                                                         Parent->OnEvent(e);
135                                                 }
136                                         }
137                                         Environment->setFocus(this);
138                                         return true;
139                                 }
140                                 break;
141                         }
142                         case EMIE_LMOUSE_LEFT_UP:
143                         case EMIE_MOUSE_MOVED: {
144                                 if (!event.MouseInput.isLeftPressed())
145                                         is_dragging = false;
146
147                                 if (!is_dragging) {
148                                         if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
149                                                 break;
150                                         return is_inside;
151                                 }
152
153                                 if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
154                                         is_dragging = false;
155
156                                 // clang-format off
157                                 if (!dragged_by_slider) {
158                                         if (is_inside) {
159                                                 dragged_by_slider = slider_rect.isPointInside(p);
160                                                 tray_clicked = !dragged_by_slider;
161                                         }
162                                         if (!dragged_by_slider) {
163                                                 tray_clicked = false;
164                                                 if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
165                                                         return is_inside;
166                                         }
167                                 }
168                                 // clang-format on
169
170                                 const s32 new_pos = getPosFromMousePos(p);
171                                 const s32 old_pos = scroll_pos;
172
173                                 setPos(new_pos);
174
175                                 if (scroll_pos != old_pos && Parent) {
176                                         SEvent e;
177                                         e.EventType = EET_GUI_EVENT;
178                                         e.GUIEvent.Caller = this;
179                                         e.GUIEvent.Element = nullptr;
180                                         e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
181                                         Parent->OnEvent(e);
182                                 }
183                                 return is_inside;
184                         }
185                         default:
186                                 break;
187                         }
188                 } break;
189                 default:
190                         break;
191                 }
192         }
193         return IGUIElement::OnEvent(event);
194 }
195
196 void GUIScrollBar::draw()
197 {
198         if (!IsVisible)
199                 return;
200
201         IGUISkin *skin = Environment->getSkin();
202         if (!skin)
203                 return;
204
205         video::SColor icon_color = skin->getColor(
206                         isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
207         if (icon_color != current_icon_color)
208                 refreshControls();
209
210         slider_rect = AbsoluteRect;
211         skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), slider_rect,
212                         &AbsoluteClippingRect);
213
214         if (core::isnotzero(range())) {
215                 if (is_horizontal) {
216                         slider_rect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X +
217                                                         draw_center - thumb_size / 2;
218                         slider_rect.LowerRightCorner.X =
219                                         slider_rect.UpperLeftCorner.X + thumb_size;
220                 } else {
221                         slider_rect.UpperLeftCorner.Y = AbsoluteRect.UpperLeftCorner.Y +
222                                                         draw_center - thumb_size / 2;
223                         slider_rect.LowerRightCorner.Y =
224                                         slider_rect.UpperLeftCorner.Y + thumb_size;
225                 }
226                 skin->draw3DButtonPaneStandard(this, slider_rect, &AbsoluteClippingRect);
227         }
228         IGUIElement::draw();
229 }
230
231 void GUIScrollBar::updateAbsolutePosition()
232 {
233         IGUIElement::updateAbsolutePosition();
234         refreshControls();
235         setPos(scroll_pos);
236 }
237
238 s32 GUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
239 {
240         s32 w, p;
241         s32 offset = dragged_by_slider ? drag_offset : thumb_size / 2;
242
243         if (is_horizontal) {
244                 w = RelativeRect.getWidth() - border_size * 2 - thumb_size;
245                 p = pos.X - AbsoluteRect.UpperLeftCorner.X - border_size - offset;
246         } else {
247                 w = RelativeRect.getHeight() - border_size * 2 - thumb_size;
248                 p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - border_size - offset;
249         }
250         return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range() + 0.5f) + min_pos : 0;
251 }
252
253 void GUIScrollBar::setPos(const s32 &pos)
254 {
255         s32 thumb_area = 0;
256         s32 thumb_min = 0;
257
258         if (is_horizontal) {
259                 thumb_min = RelativeRect.getHeight();
260                 thumb_area = RelativeRect.getWidth() - border_size * 2;
261         } else {
262                 thumb_min = RelativeRect.getWidth();
263                 thumb_area = RelativeRect.getHeight() - border_size * 2;
264         }
265
266         if (is_auto_scaling)
267                 thumb_size = s32(thumb_area /
268                                  (f32(page_size) / f32(thumb_area + border_size * 2)));
269
270         thumb_size = core::s32_clamp(thumb_size, thumb_min, thumb_area);
271         scroll_pos = core::s32_clamp(pos, min_pos, max_pos);
272
273         f32 f = core::isnotzero(range()) ? (f32(thumb_area) - f32(thumb_size)) / range()
274                                          : 1.0f;
275         draw_center = s32((f32(scroll_pos - min_pos) * f) + (f32(thumb_size) * 0.5f)) +
276                 border_size;
277 }
278
279 void GUIScrollBar::setSmallStep(const s32 &step)
280 {
281         small_step = step > 0 ? step : 10;
282 }
283
284 void GUIScrollBar::setLargeStep(const s32 &step)
285 {
286         large_step = step > 0 ? step : 50;
287 }
288
289 void GUIScrollBar::setMax(const s32 &max)
290 {
291         max_pos = max;
292         if (min_pos > max_pos)
293                 min_pos = max_pos;
294
295         bool enable = core::isnotzero(range());
296         up_button->setEnabled(enable);
297         down_button->setEnabled(enable);
298         setPos(scroll_pos);
299 }
300
301 void GUIScrollBar::setMin(const s32 &min)
302 {
303         min_pos = min;
304         if (max_pos < min_pos)
305                 max_pos = min_pos;
306
307         bool enable = core::isnotzero(range());
308         up_button->setEnabled(enable);
309         down_button->setEnabled(enable);
310         setPos(scroll_pos);
311 }
312
313 void GUIScrollBar::setPageSize(const s32 &size)
314 {
315         page_size = size;
316         setPos(scroll_pos);
317 }
318
319 void GUIScrollBar::setArrowsVisible(ArrowVisibility visible)
320 {
321         arrow_visibility = visible;
322         refreshControls();
323 }
324
325 s32 GUIScrollBar::getPos() const
326 {
327         return scroll_pos;
328 }
329
330 void GUIScrollBar::refreshControls()
331 {
332         IGUISkin *skin = Environment->getSkin();
333         IGUISpriteBank *sprites = nullptr;
334         current_icon_color = video::SColor(255, 255, 255, 255);
335
336         if (skin) {
337                 sprites = skin->getSpriteBank();
338                 current_icon_color =
339                                 skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL
340                                                            : EGDC_GRAY_WINDOW_SYMBOL);
341         }
342         if (is_horizontal) {
343                 s32 h = RelativeRect.getHeight();
344                 border_size = RelativeRect.getWidth() < h * 4 ? 0 : h;
345                 if (!up_button) {
346                         up_button = Environment->addButton(
347                                         core::rect<s32>(0, 0, h, h), this);
348                         up_button->setSubElement(true);
349                         up_button->setTabStop(false);
350                 }
351                 if (sprites) {
352                         up_button->setSpriteBank(sprites);
353                         up_button->setSprite(EGBS_BUTTON_UP,
354                                         s32(skin->getIcon(EGDI_CURSOR_LEFT)),
355                                         current_icon_color);
356                         up_button->setSprite(EGBS_BUTTON_DOWN,
357                                         s32(skin->getIcon(EGDI_CURSOR_LEFT)),
358                                         current_icon_color);
359                 }
360                 up_button->setRelativePosition(core::rect<s32>(0, 0, h, h));
361                 up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT,
362                                 EGUIA_LOWERRIGHT);
363                 if (!down_button) {
364                         down_button = Environment->addButton(
365                                         core::rect<s32>(RelativeRect.getWidth() - h, 0,
366                                                         RelativeRect.getWidth(), h),
367                                         this);
368                         down_button->setSubElement(true);
369                         down_button->setTabStop(false);
370                 }
371                 if (sprites) {
372                         down_button->setSpriteBank(sprites);
373                         down_button->setSprite(EGBS_BUTTON_UP,
374                                         s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
375                                         current_icon_color);
376                         down_button->setSprite(EGBS_BUTTON_DOWN,
377                                         s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
378                                         current_icon_color);
379                 }
380                 down_button->setRelativePosition(
381                                 core::rect<s32>(RelativeRect.getWidth() - h, 0,
382                                                 RelativeRect.getWidth(), h));
383                 down_button->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT,
384                                 EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
385         } else {
386                 s32 w = RelativeRect.getWidth();
387                 border_size = RelativeRect.getHeight() < w * 4 ? 0 : w;
388                 if (!up_button) {
389                         up_button = Environment->addButton(
390                                         core::rect<s32>(0, 0, w, w), this);
391                         up_button->setSubElement(true);
392                         up_button->setTabStop(false);
393                 }
394                 if (sprites) {
395                         up_button->setSpriteBank(sprites);
396                         up_button->setSprite(EGBS_BUTTON_UP,
397                                         s32(skin->getIcon(EGDI_CURSOR_UP)),
398                                         current_icon_color);
399                         up_button->setSprite(EGBS_BUTTON_DOWN,
400                                         s32(skin->getIcon(EGDI_CURSOR_UP)),
401                                         current_icon_color);
402                 }
403                 up_button->setRelativePosition(core::rect<s32>(0, 0, w, w));
404                 up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
405                                 EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
406                 if (!down_button) {
407                         down_button = Environment->addButton(
408                                         core::rect<s32>(0, RelativeRect.getHeight() - w,
409                                                         w, RelativeRect.getHeight()),
410                                         this);
411                         down_button->setSubElement(true);
412                         down_button->setTabStop(false);
413                 }
414                 if (sprites) {
415                         down_button->setSpriteBank(sprites);
416                         down_button->setSprite(EGBS_BUTTON_UP,
417                                         s32(skin->getIcon(EGDI_CURSOR_DOWN)),
418                                         current_icon_color);
419                         down_button->setSprite(EGBS_BUTTON_DOWN,
420                                         s32(skin->getIcon(EGDI_CURSOR_DOWN)),
421                                         current_icon_color);
422                 }
423                 down_button->setRelativePosition(
424                                 core::rect<s32>(0, RelativeRect.getHeight() - w, w,
425                                                 RelativeRect.getHeight()));
426                 down_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
427                                 EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
428         }
429
430         bool visible;
431         if (arrow_visibility == DEFAULT)
432                 visible = (border_size != 0);
433         else if (arrow_visibility == HIDE) {
434                 visible = false;
435                 border_size = 0;
436         } else {
437                 visible = true;
438                 if (is_horizontal)
439                         border_size = RelativeRect.getHeight();
440                 else
441                         border_size = RelativeRect.getWidth();
442         }
443
444         up_button->setVisible(visible);
445         down_button->setVisible(visible);
446 }