Move SMaterial std::hash impl to its header
[minetest.git] / irr / src / CGUIScrollBar.cpp
blobf9ebad09ecb886390b054cc2d7ae417cefbb143e
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
5 #include "CGUIScrollBar.h"
7 #include "IGUISkin.h"
8 #include "IGUIEnvironment.h"
9 #include "IVideoDriver.h"
10 #include "CGUIButton.h"
11 #include "IGUIFont.h"
12 #include "IGUIFontBitmap.h"
13 #include "os.h"
15 namespace irr
17 namespace gui
20 //! constructor
21 CGUIScrollBar::CGUIScrollBar(bool horizontal, IGUIEnvironment *environment,
22 IGUIElement *parent, s32 id,
23 core::rect<s32> rectangle, bool noclip) :
24 IGUIScrollBar(environment, parent, id, rectangle),
25 UpButton(0),
26 DownButton(0), Dragging(false), Horizontal(horizontal),
27 DraggedBySlider(false), TrayClick(false), Pos(0), DrawPos(0),
28 DrawHeight(0), Min(0), Max(100), SmallStep(10), LargeStep(50), DesiredPos(0),
29 LastChange(0)
31 #ifdef _DEBUG
32 setDebugName("CGUIScrollBar");
33 #endif
35 refreshControls();
37 setNotClipped(noclip);
39 // this element can be tabbed to
40 setTabStop(true);
41 setTabOrder(-1);
43 setPos(0);
46 //! destructor
47 CGUIScrollBar::~CGUIScrollBar()
49 if (UpButton)
50 UpButton->drop();
52 if (DownButton)
53 DownButton->drop();
56 //! called if an event happened.
57 bool CGUIScrollBar::OnEvent(const SEvent &event)
59 if (isEnabled()) {
61 switch (event.EventType) {
62 case EET_KEY_INPUT_EVENT:
63 if (event.KeyInput.PressedDown) {
64 const s32 oldPos = Pos;
65 bool absorb = true;
66 switch (event.KeyInput.Key) {
67 case KEY_LEFT:
68 case KEY_UP:
69 setPos(Pos - SmallStep);
70 break;
71 case KEY_RIGHT:
72 case KEY_DOWN:
73 setPos(Pos + SmallStep);
74 break;
75 case KEY_HOME:
76 setPos(Min);
77 break;
78 case KEY_PRIOR:
79 setPos(Pos - LargeStep);
80 break;
81 case KEY_END:
82 setPos(Max);
83 break;
84 case KEY_NEXT:
85 setPos(Pos + LargeStep);
86 break;
87 default:
88 absorb = false;
91 if (Pos != oldPos) {
92 SEvent newEvent;
93 newEvent.EventType = EET_GUI_EVENT;
94 newEvent.GUIEvent.Caller = this;
95 newEvent.GUIEvent.Element = 0;
96 newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
97 Parent->OnEvent(newEvent);
99 if (absorb)
100 return true;
102 break;
103 case EET_GUI_EVENT:
104 if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) {
105 if (event.GUIEvent.Caller == UpButton)
106 setPos(Pos - SmallStep);
107 else if (event.GUIEvent.Caller == DownButton)
108 setPos(Pos + SmallStep);
110 SEvent newEvent;
111 newEvent.EventType = EET_GUI_EVENT;
112 newEvent.GUIEvent.Caller = this;
113 newEvent.GUIEvent.Element = 0;
114 newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
115 Parent->OnEvent(newEvent);
117 return true;
118 } else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
119 if (event.GUIEvent.Caller == this)
120 Dragging = false;
122 break;
123 case EET_MOUSE_INPUT_EVENT: {
124 const core::position2di p(event.MouseInput.X, event.MouseInput.Y);
125 bool isInside = isPointInside(p);
126 switch (event.MouseInput.Event) {
127 case EMIE_MOUSE_WHEEL:
128 if (Environment->hasFocus(this)) {
129 // thanks to a bug report by REAPER
130 // thanks to tommi by tommi for another bugfix
131 // everybody needs a little thanking. hallo niko!;-)
132 setPos(getPos() +
133 ((event.MouseInput.Wheel < 0 ? -1 : 1) * SmallStep * (Horizontal ? 1 : -1)));
135 SEvent newEvent;
136 newEvent.EventType = EET_GUI_EVENT;
137 newEvent.GUIEvent.Caller = this;
138 newEvent.GUIEvent.Element = 0;
139 newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
140 Parent->OnEvent(newEvent);
141 return true;
143 break;
144 case EMIE_LMOUSE_PRESSED_DOWN: {
145 if (isInside) {
146 Dragging = true;
147 DraggedBySlider = SliderRect.isPointInside(p);
148 TrayClick = !DraggedBySlider;
149 DesiredPos = getPosFromMousePos(p);
150 return true;
152 break;
154 case EMIE_LMOUSE_LEFT_UP:
155 case EMIE_MOUSE_MOVED: {
156 if (!event.MouseInput.isLeftPressed())
157 Dragging = false;
159 if (!Dragging) {
160 if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
161 break;
162 return isInside;
165 if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
166 Dragging = false;
168 const s32 newPos = getPosFromMousePos(p);
169 const s32 oldPos = Pos;
171 if (!DraggedBySlider) {
172 if (isInside) {
173 DraggedBySlider = SliderRect.isPointInside(p);
174 TrayClick = !DraggedBySlider;
177 if (DraggedBySlider) {
178 setPos(newPos);
179 } else {
180 TrayClick = false;
181 if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
182 return isInside;
186 if (DraggedBySlider) {
187 setPos(newPos);
188 } else {
189 DesiredPos = newPos;
192 if (Pos != oldPos && Parent) {
193 SEvent newEvent;
194 newEvent.EventType = EET_GUI_EVENT;
195 newEvent.GUIEvent.Caller = this;
196 newEvent.GUIEvent.Element = 0;
197 newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
198 Parent->OnEvent(newEvent);
200 return isInside;
201 } break;
203 default:
204 break;
206 } break;
207 default:
208 break;
212 return IGUIElement::OnEvent(event);
215 void CGUIScrollBar::OnPostRender(u32 timeMs)
217 if (Dragging && !DraggedBySlider && TrayClick && timeMs > LastChange + 200) {
218 LastChange = timeMs;
220 const s32 oldPos = Pos;
222 if (DesiredPos >= Pos + LargeStep)
223 setPos(Pos + LargeStep);
224 else if (DesiredPos <= Pos - LargeStep)
225 setPos(Pos - LargeStep);
226 else if (DesiredPos >= Pos - LargeStep && DesiredPos <= Pos + LargeStep)
227 setPos(DesiredPos);
229 if (Pos != oldPos && Parent) {
230 SEvent newEvent;
231 newEvent.EventType = EET_GUI_EVENT;
232 newEvent.GUIEvent.Caller = this;
233 newEvent.GUIEvent.Element = 0;
234 newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
235 Parent->OnEvent(newEvent);
240 //! draws the element and its children
241 void CGUIScrollBar::draw()
243 if (!IsVisible)
244 return;
246 IGUISkin *skin = Environment->getSkin();
247 if (!skin)
248 return;
250 video::SColor iconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
251 if (iconColor != CurrentIconColor) {
252 refreshControls();
255 SliderRect = AbsoluteRect;
257 // draws the background
258 skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), SliderRect, &AbsoluteClippingRect);
260 if (core::isnotzero(range())) {
261 // recalculate slider rectangle
262 if (Horizontal) {
263 SliderRect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X + DrawPos + RelativeRect.getHeight() - DrawHeight / 2;
264 SliderRect.LowerRightCorner.X = SliderRect.UpperLeftCorner.X + DrawHeight;
265 } else {
266 SliderRect.UpperLeftCorner.Y = AbsoluteRect.UpperLeftCorner.Y + DrawPos + RelativeRect.getWidth() - DrawHeight / 2;
267 SliderRect.LowerRightCorner.Y = SliderRect.UpperLeftCorner.Y + DrawHeight;
270 skin->draw3DButtonPaneStandard(this, SliderRect, &AbsoluteClippingRect);
273 // draw buttons
274 IGUIElement::draw();
277 void CGUIScrollBar::updateAbsolutePosition()
279 IGUIElement::updateAbsolutePosition();
280 // todo: properly resize
281 refreshControls();
282 setPos(Pos);
286 s32 CGUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
288 f32 w, p;
289 if (Horizontal) {
290 w = RelativeRect.getWidth() - f32(RelativeRect.getHeight()) * 3.0f;
291 p = pos.X - AbsoluteRect.UpperLeftCorner.X - RelativeRect.getHeight() * 1.5f;
292 } else {
293 w = RelativeRect.getHeight() - f32(RelativeRect.getWidth()) * 3.0f;
294 p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - RelativeRect.getWidth() * 1.5f;
296 return (s32)(p / w * range()) + Min;
299 //! sets the position of the scrollbar
300 void CGUIScrollBar::setPos(s32 pos)
302 Pos = core::s32_clamp(pos, Min, Max);
304 if (core::isnotzero(range())) {
305 if (Horizontal) {
306 f32 f = (RelativeRect.getWidth() - ((f32)RelativeRect.getHeight() * 3.0f)) / range();
307 DrawPos = (s32)(((Pos - Min) * f) + ((f32)RelativeRect.getHeight() * 0.5f));
308 DrawHeight = RelativeRect.getHeight();
309 } else {
310 f32 f = (RelativeRect.getHeight() - ((f32)RelativeRect.getWidth() * 3.0f)) / range();
312 DrawPos = (s32)(((Pos - Min) * f) + ((f32)RelativeRect.getWidth() * 0.5f));
313 DrawHeight = RelativeRect.getWidth();
318 //! gets the small step value
319 s32 CGUIScrollBar::getSmallStep() const
321 return SmallStep;
324 //! sets the small step value
325 void CGUIScrollBar::setSmallStep(s32 step)
327 if (step > 0)
328 SmallStep = step;
329 else
330 SmallStep = 10;
333 //! gets the small step value
334 s32 CGUIScrollBar::getLargeStep() const
336 return LargeStep;
339 //! sets the small step value
340 void CGUIScrollBar::setLargeStep(s32 step)
342 if (step > 0)
343 LargeStep = step;
344 else
345 LargeStep = 50;
348 //! gets the maximum value of the scrollbar.
349 s32 CGUIScrollBar::getMax() const
351 return Max;
354 //! sets the maximum value of the scrollbar.
355 void CGUIScrollBar::setMax(s32 max)
357 Max = max;
358 if (Min > Max)
359 Min = Max;
361 bool enable = core::isnotzero(range());
362 UpButton->setEnabled(enable);
363 DownButton->setEnabled(enable);
364 setPos(Pos);
367 //! gets the minimum value of the scrollbar.
368 s32 CGUIScrollBar::getMin() const
370 return Min;
373 //! sets the minimum value of the scrollbar.
374 void CGUIScrollBar::setMin(s32 min)
376 Min = min;
377 if (Max < Min)
378 Max = Min;
380 bool enable = core::isnotzero(range());
381 UpButton->setEnabled(enable);
382 DownButton->setEnabled(enable);
383 setPos(Pos);
386 //! gets the current position of the scrollbar
387 s32 CGUIScrollBar::getPos() const
389 return Pos;
392 //! refreshes the position and text on child buttons
393 void CGUIScrollBar::refreshControls()
395 CurrentIconColor = video::SColor(255, 255, 255, 255);
397 IGUISkin *skin = Environment->getSkin();
398 IGUISpriteBank *sprites = 0;
400 if (skin) {
401 sprites = skin->getSpriteBank();
402 CurrentIconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
405 if (Horizontal) {
406 const s32 h = RelativeRect.getHeight();
407 const s32 w = (h < RelativeRect.getWidth() / 2) ? h : RelativeRect.getWidth() / 2;
408 if (!UpButton) {
409 UpButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0, 0, w, h), NoClip);
410 UpButton->setSubElement(true);
411 UpButton->setTabStop(false);
413 if (sprites) {
414 UpButton->setSpriteBank(sprites);
415 UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor);
416 UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor);
418 UpButton->setRelativePosition(core::rect<s32>(0, 0, w, h));
419 UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
420 if (!DownButton) {
421 DownButton = new CGUIButton(Environment, this, -1, core::rect<s32>(RelativeRect.getWidth() - w, 0, RelativeRect.getWidth(), h), NoClip);
422 DownButton->setSubElement(true);
423 DownButton->setTabStop(false);
425 if (sprites) {
426 DownButton->setSpriteBank(sprites);
427 DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor);
428 DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor);
430 DownButton->setRelativePosition(core::rect<s32>(RelativeRect.getWidth() - w, 0, RelativeRect.getWidth(), h));
431 DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
432 } else {
433 const s32 w = RelativeRect.getWidth();
434 const s32 h = (w < RelativeRect.getHeight() / 2) ? w : RelativeRect.getHeight() / 2;
435 if (!UpButton) {
436 UpButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0, 0, w, h), NoClip);
437 UpButton->setSubElement(true);
438 UpButton->setTabStop(false);
440 if (sprites) {
441 UpButton->setSpriteBank(sprites);
442 UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor);
443 UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor);
445 UpButton->setRelativePosition(core::rect<s32>(0, 0, w, h));
446 UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
447 if (!DownButton) {
448 DownButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0, RelativeRect.getHeight() - h, w, RelativeRect.getHeight()), NoClip);
449 DownButton->setSubElement(true);
450 DownButton->setTabStop(false);
452 if (sprites) {
453 DownButton->setSpriteBank(sprites);
454 DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor);
455 DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor);
457 DownButton->setRelativePosition(core::rect<s32>(0, RelativeRect.getHeight() - h, w, RelativeRect.getHeight()));
458 DownButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
462 } // end namespace gui
463 } // end namespace irr