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
6 Modified 2019.05.01 by stujones11, Stuart Jones <stujones111@gmail.com>
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.
13 #include "guiScrollBar.h"
14 #include <IGUIButton.h>
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)
33 bool GUIScrollBar::OnEvent(const SEvent &event)
36 switch (event.EventType) {
37 case EET_KEY_INPUT_EVENT:
38 if (event.KeyInput.PressedDown) {
39 const s32 old_pos = scroll_pos;
41 switch (event.KeyInput.Key) {
44 setPos(scroll_pos - small_step);
48 setPos(scroll_pos + small_step);
54 setPos(scroll_pos - large_step);
60 setPos(scroll_pos + large_step);
65 if (scroll_pos != old_pos) {
67 e.EventType = EET_GUI_EVENT;
68 e.GUIEvent.Caller = this;
69 e.GUIEvent.Element = nullptr;
70 e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
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);
85 e.EventType = EET_GUI_EVENT;
86 e.GUIEvent.Caller = this;
87 e.GUIEvent.Element = nullptr;
88 e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
91 } else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
92 if (event.GUIEvent.Caller == this)
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));
106 e.EventType = EET_GUI_EVENT;
107 e.GUIEvent.Caller = this;
108 e.GUIEvent.Element = nullptr;
109 e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
114 case EMIE_LMOUSE_PRESSED_DOWN: {
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;
122 const s32 new_pos = getPosFromMousePos(p);
123 const s32 old_pos = scroll_pos;
125 // drag in the middle
126 drag_offset = thumb_size / 2;
127 // report the scroll event
128 if (scroll_pos != old_pos && Parent) {
130 e.EventType = EET_GUI_EVENT;
131 e.GUIEvent.Caller = this;
132 e.GUIEvent.Element = nullptr;
133 e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
137 Environment->setFocus(this);
142 case EMIE_LMOUSE_LEFT_UP:
143 case EMIE_MOUSE_MOVED: {
144 if (!event.MouseInput.isLeftPressed())
148 if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
153 if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
157 if (!dragged_by_slider) {
159 dragged_by_slider = slider_rect.isPointInside(p);
160 tray_clicked = !dragged_by_slider;
162 if (!dragged_by_slider) {
163 tray_clicked = false;
164 if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
170 const s32 new_pos = getPosFromMousePos(p);
171 const s32 old_pos = scroll_pos;
175 if (scroll_pos != old_pos && Parent) {
177 e.EventType = EET_GUI_EVENT;
178 e.GUIEvent.Caller = this;
179 e.GUIEvent.Element = nullptr;
180 e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
193 return IGUIElement::OnEvent(event);
196 void GUIScrollBar::draw()
201 IGUISkin *skin = Environment->getSkin();
205 video::SColor icon_color = skin->getColor(
206 isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
207 if (icon_color != current_icon_color)
210 slider_rect = AbsoluteRect;
211 skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), slider_rect,
212 &AbsoluteClippingRect);
214 if (core::isnotzero(range())) {
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;
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;
226 skin->draw3DButtonPaneStandard(this, slider_rect, &AbsoluteClippingRect);
231 void GUIScrollBar::updateAbsolutePosition()
233 IGUIElement::updateAbsolutePosition();
238 s32 GUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
241 s32 offset = dragged_by_slider ? drag_offset : thumb_size / 2;
244 w = RelativeRect.getWidth() - border_size * 2 - thumb_size;
245 p = pos.X - AbsoluteRect.UpperLeftCorner.X - border_size - offset;
247 w = RelativeRect.getHeight() - border_size * 2 - thumb_size;
248 p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - border_size - offset;
250 return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range() + 0.5f) + min_pos : 0;
253 void GUIScrollBar::setPos(const s32 &pos)
259 thumb_min = RelativeRect.getHeight();
260 thumb_area = RelativeRect.getWidth() - border_size * 2;
262 thumb_min = RelativeRect.getWidth();
263 thumb_area = RelativeRect.getHeight() - border_size * 2;
267 thumb_size = s32(thumb_area /
268 (f32(page_size) / f32(thumb_area + border_size * 2)));
270 thumb_size = core::s32_clamp(thumb_size, thumb_min, thumb_area);
271 scroll_pos = core::s32_clamp(pos, min_pos, max_pos);
273 f32 f = core::isnotzero(range()) ? (f32(thumb_area) - f32(thumb_size)) / range()
275 draw_center = s32((f32(scroll_pos - min_pos) * f) + (f32(thumb_size) * 0.5f)) +
279 void GUIScrollBar::setSmallStep(const s32 &step)
281 small_step = step > 0 ? step : 10;
284 void GUIScrollBar::setLargeStep(const s32 &step)
286 large_step = step > 0 ? step : 50;
289 void GUIScrollBar::setMax(const s32 &max)
292 if (min_pos > max_pos)
295 bool enable = core::isnotzero(range());
296 up_button->setEnabled(enable);
297 down_button->setEnabled(enable);
301 void GUIScrollBar::setMin(const s32 &min)
304 if (max_pos < min_pos)
307 bool enable = core::isnotzero(range());
308 up_button->setEnabled(enable);
309 down_button->setEnabled(enable);
313 void GUIScrollBar::setPageSize(const s32 &size)
319 void GUIScrollBar::setArrowsVisible(ArrowVisibility visible)
321 arrow_visibility = visible;
325 s32 GUIScrollBar::getPos() const
330 void GUIScrollBar::refreshControls()
332 IGUISkin *skin = Environment->getSkin();
333 IGUISpriteBank *sprites = nullptr;
334 current_icon_color = video::SColor(255, 255, 255, 255);
337 sprites = skin->getSpriteBank();
339 skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL
340 : EGDC_GRAY_WINDOW_SYMBOL);
343 s32 h = RelativeRect.getHeight();
344 border_size = RelativeRect.getWidth() < h * 4 ? 0 : h;
346 up_button = Environment->addButton(
347 core::rect<s32>(0, 0, h, h), this);
348 up_button->setSubElement(true);
349 up_button->setTabStop(false);
352 up_button->setSpriteBank(sprites);
353 up_button->setSprite(EGBS_BUTTON_UP,
354 s32(skin->getIcon(EGDI_CURSOR_LEFT)),
356 up_button->setSprite(EGBS_BUTTON_DOWN,
357 s32(skin->getIcon(EGDI_CURSOR_LEFT)),
360 up_button->setRelativePosition(core::rect<s32>(0, 0, h, h));
361 up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT,
364 down_button = Environment->addButton(
365 core::rect<s32>(RelativeRect.getWidth() - h, 0,
366 RelativeRect.getWidth(), h),
368 down_button->setSubElement(true);
369 down_button->setTabStop(false);
372 down_button->setSpriteBank(sprites);
373 down_button->setSprite(EGBS_BUTTON_UP,
374 s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
376 down_button->setSprite(EGBS_BUTTON_DOWN,
377 s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
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);
386 s32 w = RelativeRect.getWidth();
387 border_size = RelativeRect.getHeight() < w * 4 ? 0 : w;
389 up_button = Environment->addButton(
390 core::rect<s32>(0, 0, w, w), this);
391 up_button->setSubElement(true);
392 up_button->setTabStop(false);
395 up_button->setSpriteBank(sprites);
396 up_button->setSprite(EGBS_BUTTON_UP,
397 s32(skin->getIcon(EGDI_CURSOR_UP)),
399 up_button->setSprite(EGBS_BUTTON_DOWN,
400 s32(skin->getIcon(EGDI_CURSOR_UP)),
403 up_button->setRelativePosition(core::rect<s32>(0, 0, w, w));
404 up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
405 EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
407 down_button = Environment->addButton(
408 core::rect<s32>(0, RelativeRect.getHeight() - w,
409 w, RelativeRect.getHeight()),
411 down_button->setSubElement(true);
412 down_button->setTabStop(false);
415 down_button->setSpriteBank(sprites);
416 down_button->setSprite(EGBS_BUTTON_UP,
417 s32(skin->getIcon(EGDI_CURSOR_DOWN)),
419 down_button->setSprite(EGBS_BUTTON_DOWN,
420 s32(skin->getIcon(EGDI_CURSOR_DOWN)),
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);
431 if (arrow_visibility == DEFAULT)
432 visible = (border_size != 0);
433 else if (arrow_visibility == HIDE) {
439 border_size = RelativeRect.getHeight();
441 border_size = RelativeRect.getWidth();
444 up_button->setVisible(visible);
445 down_button->setVisible(visible);