]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CGUIScrollBar.cpp
6d6d36754b8401cc1c1e3efba6523b97b18ee009
[irrlicht.git] / source / Irrlicht / CGUIScrollBar.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "CGUIScrollBar.h"\r
6 #ifdef _IRR_COMPILE_WITH_GUI_\r
7 \r
8 #include "IGUISkin.h"\r
9 #include "IGUIEnvironment.h"\r
10 #include "IVideoDriver.h"\r
11 #include "CGUIButton.h"\r
12 #include "IGUIFont.h"\r
13 #include "IGUIFontBitmap.h"\r
14 #include "os.h"\r
15 \r
16 namespace irr\r
17 {\r
18 namespace gui\r
19 {\r
20 \r
21 \r
22 //! constructor\r
23 CGUIScrollBar::CGUIScrollBar(bool horizontal, IGUIEnvironment* environment,\r
24                                 IGUIElement* parent, s32 id,\r
25                                 core::rect<s32> rectangle, bool noclip)\r
26         : IGUIScrollBar(environment, parent, id, rectangle), UpButton(0),\r
27         DownButton(0), Dragging(false), Horizontal(horizontal),\r
28         DraggedBySlider(false), TrayClick(false), Pos(0), DrawPos(0),\r
29         DrawHeight(0), Min(0), Max(100), SmallStep(10), LargeStep(50), DesiredPos(0),\r
30         LastChange(0)\r
31 {\r
32         #ifdef _DEBUG\r
33         setDebugName("CGUIScrollBar");\r
34         #endif\r
35 \r
36         refreshControls();\r
37 \r
38         setNotClipped(noclip);\r
39 \r
40         // this element can be tabbed to\r
41         setTabStop(true);\r
42         setTabOrder(-1);\r
43 \r
44         setPos(0);\r
45 }\r
46 \r
47 \r
48 //! destructor\r
49 CGUIScrollBar::~CGUIScrollBar()\r
50 {\r
51         if (UpButton)\r
52                 UpButton->drop();\r
53 \r
54         if (DownButton)\r
55                 DownButton->drop();\r
56 }\r
57 \r
58 \r
59 //! called if an event happened.\r
60 bool CGUIScrollBar::OnEvent(const SEvent& event)\r
61 {\r
62         if (isEnabled())\r
63         {\r
64 \r
65                 switch(event.EventType)\r
66                 {\r
67                 case EET_KEY_INPUT_EVENT:\r
68                         if (event.KeyInput.PressedDown)\r
69                         {\r
70                                 const s32 oldPos = Pos;\r
71                                 bool absorb = true;\r
72                                 switch (event.KeyInput.Key)\r
73                                 {\r
74                                 case KEY_LEFT:\r
75                                 case KEY_UP:\r
76                                         setPos(Pos-SmallStep);\r
77                                         break;\r
78                                 case KEY_RIGHT:\r
79                                 case KEY_DOWN:\r
80                                         setPos(Pos+SmallStep);\r
81                                         break;\r
82                                 case KEY_HOME:\r
83                                         setPos(Min);\r
84                                         break;\r
85                                 case KEY_PRIOR:\r
86                                         setPos(Pos-LargeStep);\r
87                                         break;\r
88                                 case KEY_END:\r
89                                         setPos(Max);\r
90                                         break;\r
91                                 case KEY_NEXT:\r
92                                         setPos(Pos+LargeStep);\r
93                                         break;\r
94                                 default:\r
95                                         absorb = false;\r
96                                 }\r
97 \r
98                                 if (Pos != oldPos)\r
99                                 {\r
100                                         SEvent newEvent;\r
101                                         newEvent.EventType = EET_GUI_EVENT;\r
102                                         newEvent.GUIEvent.Caller = this;\r
103                                         newEvent.GUIEvent.Element = 0;\r
104                                         newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;\r
105                                         Parent->OnEvent(newEvent);\r
106                                 }\r
107                                 if (absorb)\r
108                                         return true;\r
109                         }\r
110                         break;\r
111                 case EET_GUI_EVENT:\r
112                         if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED)\r
113                         {\r
114                                 if (event.GUIEvent.Caller == UpButton)\r
115                                         setPos(Pos-SmallStep);\r
116                                 else\r
117                                 if (event.GUIEvent.Caller == DownButton)\r
118                                         setPos(Pos+SmallStep);\r
119 \r
120                                 SEvent newEvent;\r
121                                 newEvent.EventType = EET_GUI_EVENT;\r
122                                 newEvent.GUIEvent.Caller = this;\r
123                                 newEvent.GUIEvent.Element = 0;\r
124                                 newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;\r
125                                 Parent->OnEvent(newEvent);\r
126 \r
127                                 return true;\r
128                         }\r
129                         else\r
130                         if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)\r
131                         {\r
132                                 if (event.GUIEvent.Caller == this)\r
133                                         Dragging = false;\r
134                         }\r
135                         break;\r
136                 case EET_MOUSE_INPUT_EVENT:\r
137                 {\r
138                         const core::position2di p(event.MouseInput.X, event.MouseInput.Y);\r
139                         bool isInside = isPointInside ( p );\r
140                         switch(event.MouseInput.Event)\r
141                         {\r
142                         case EMIE_MOUSE_WHEEL:\r
143                                 if (Environment->hasFocus(this))\r
144                                 {\r
145                                         // thanks to a bug report by REAPER\r
146                                         // thanks to tommi by tommi for another bugfix\r
147                                         // everybody needs a little thanking. hallo niko!;-)\r
148                                         setPos( getPos() +\r
149                                                         ( (event.MouseInput.Wheel < 0 ? -1 : 1) * SmallStep * (Horizontal ? 1 : -1 ) )\r
150                                                         );\r
151 \r
152                                         SEvent newEvent;\r
153                                         newEvent.EventType = EET_GUI_EVENT;\r
154                                         newEvent.GUIEvent.Caller = this;\r
155                                         newEvent.GUIEvent.Element = 0;\r
156                                         newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;\r
157                                         Parent->OnEvent(newEvent);\r
158                                         return true;\r
159                                 }\r
160                                 break;\r
161                         case EMIE_LMOUSE_PRESSED_DOWN:\r
162                         {\r
163                                 if (isInside)\r
164                                 {\r
165                                         Dragging = true;\r
166                                         DraggedBySlider = SliderRect.isPointInside(p);\r
167                                         TrayClick = !DraggedBySlider;\r
168                                         DesiredPos = getPosFromMousePos(p);\r
169                                         return true;\r
170                                 }\r
171                                 break;\r
172                         }\r
173                         case EMIE_LMOUSE_LEFT_UP:\r
174                         case EMIE_MOUSE_MOVED:\r
175                         {\r
176                                 if ( !event.MouseInput.isLeftPressed () )\r
177                                         Dragging = false;\r
178 \r
179                                 if ( !Dragging )\r
180                                 {\r
181                                         if ( event.MouseInput.Event == EMIE_MOUSE_MOVED )\r
182                                                 break;\r
183                                         return isInside;\r
184                                 }\r
185 \r
186                                 if ( event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP )\r
187                                         Dragging = false;\r
188 \r
189                                 const s32 newPos = getPosFromMousePos(p);\r
190                                 const s32 oldPos = Pos;\r
191 \r
192                                 if (!DraggedBySlider)\r
193                                 {\r
194                                         if ( isInside )\r
195                                         {\r
196                                                 DraggedBySlider = SliderRect.isPointInside(p);\r
197                                                 TrayClick = !DraggedBySlider;\r
198                                         }\r
199 \r
200                                         if (DraggedBySlider)\r
201                                         {\r
202                                                 setPos(newPos);\r
203                                         }\r
204                                         else\r
205                                         {\r
206                                                 TrayClick = false;\r
207                                                 if (event.MouseInput.Event == EMIE_MOUSE_MOVED)\r
208                                                         return isInside;\r
209                                         }\r
210                                 }\r
211 \r
212                                 if (DraggedBySlider)\r
213                                 {\r
214                                         setPos(newPos);\r
215                                 }\r
216                                 else\r
217                                 {\r
218                                         DesiredPos = newPos;\r
219                                 }\r
220 \r
221                                 if (Pos != oldPos && Parent)\r
222                                 {\r
223                                         SEvent newEvent;\r
224                                         newEvent.EventType = EET_GUI_EVENT;\r
225                                         newEvent.GUIEvent.Caller = this;\r
226                                         newEvent.GUIEvent.Element = 0;\r
227                                         newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;\r
228                                         Parent->OnEvent(newEvent);\r
229                                 }\r
230                                 return isInside;\r
231                         } break;\r
232 \r
233                         default:\r
234                                 break;\r
235                         }\r
236                 } break;\r
237                 default:\r
238                         break;\r
239                 }\r
240         }\r
241 \r
242         return IGUIElement::OnEvent(event);\r
243 }\r
244 \r
245 void CGUIScrollBar::OnPostRender(u32 timeMs)\r
246 {\r
247         if (Dragging && !DraggedBySlider && TrayClick && timeMs > LastChange + 200)\r
248         {\r
249                 LastChange = timeMs;\r
250 \r
251                 const s32 oldPos = Pos;\r
252 \r
253                 if (DesiredPos >= Pos + LargeStep)\r
254                         setPos(Pos + LargeStep);\r
255                 else\r
256                 if (DesiredPos <= Pos - LargeStep)\r
257                         setPos(Pos - LargeStep);\r
258                 else\r
259                 if (DesiredPos >= Pos - LargeStep && DesiredPos <= Pos + LargeStep)\r
260                         setPos(DesiredPos);\r
261 \r
262                 if (Pos != oldPos && Parent)\r
263                 {\r
264                         SEvent newEvent;\r
265                         newEvent.EventType = EET_GUI_EVENT;\r
266                         newEvent.GUIEvent.Caller = this;\r
267                         newEvent.GUIEvent.Element = 0;\r
268                         newEvent.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;\r
269                         Parent->OnEvent(newEvent);\r
270                 }\r
271         }\r
272 \r
273 }\r
274 \r
275 //! draws the element and its children\r
276 void CGUIScrollBar::draw()\r
277 {\r
278         if (!IsVisible)\r
279                 return;\r
280 \r
281         IGUISkin* skin = Environment->getSkin();\r
282         if (!skin)\r
283                 return;\r
284 \r
285 \r
286         video::SColor iconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);\r
287         if ( iconColor != CurrentIconColor )\r
288         {\r
289                 refreshControls();\r
290         }\r
291 \r
292 \r
293         SliderRect = AbsoluteRect;\r
294 \r
295         // draws the background\r
296         skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), SliderRect, &AbsoluteClippingRect);\r
297 \r
298         if ( core::isnotzero ( range() ) )\r
299         {\r
300                 // recalculate slider rectangle\r
301                 if (Horizontal)\r
302                 {\r
303                         SliderRect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X + DrawPos + RelativeRect.getHeight() - DrawHeight/2;\r
304                         SliderRect.LowerRightCorner.X = SliderRect.UpperLeftCorner.X + DrawHeight;\r
305                 }\r
306                 else\r
307                 {\r
308                         SliderRect.UpperLeftCorner.Y = AbsoluteRect.UpperLeftCorner.Y + DrawPos + RelativeRect.getWidth() - DrawHeight/2;\r
309                         SliderRect.LowerRightCorner.Y = SliderRect.UpperLeftCorner.Y + DrawHeight;\r
310                 }\r
311 \r
312                 skin->draw3DButtonPaneStandard(this, SliderRect, &AbsoluteClippingRect);\r
313         }\r
314 \r
315         // draw buttons\r
316         IGUIElement::draw();\r
317 }\r
318 \r
319 \r
320 void CGUIScrollBar::updateAbsolutePosition()\r
321 {\r
322         IGUIElement::updateAbsolutePosition();\r
323         // todo: properly resize\r
324         refreshControls();\r
325         setPos ( Pos );\r
326 }\r
327 \r
328 //!\r
329 s32 CGUIScrollBar::getPosFromMousePos(const core::position2di &pos) const\r
330 {\r
331         f32 w, p;\r
332         if (Horizontal)\r
333         {\r
334                 w = RelativeRect.getWidth() - f32(RelativeRect.getHeight())*3.0f;\r
335                 p = pos.X - AbsoluteRect.UpperLeftCorner.X - RelativeRect.getHeight()*1.5f;\r
336         }\r
337         else\r
338         {\r
339                 w = RelativeRect.getHeight() - f32(RelativeRect.getWidth())*3.0f;\r
340                 p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - RelativeRect.getWidth()*1.5f;\r
341         }\r
342         return (s32) ( p/w * range() ) + Min;\r
343 }\r
344 \r
345 \r
346 //! sets the position of the scrollbar\r
347 void CGUIScrollBar::setPos(s32 pos)\r
348 {\r
349         Pos = core::s32_clamp ( pos, Min, Max );\r
350 \r
351         if ( core::isnotzero ( range() ) )\r
352         {\r
353                 if (Horizontal)\r
354                 {\r
355                         f32 f = (RelativeRect.getWidth() - ((f32)RelativeRect.getHeight()*3.0f)) / range();\r
356                         DrawPos = (s32)( ( ( Pos - Min ) * f) + ((f32)RelativeRect.getHeight() * 0.5f));\r
357                         DrawHeight = RelativeRect.getHeight();\r
358                 }\r
359                 else\r
360                 {\r
361                         f32 f = (RelativeRect.getHeight() - ((f32)RelativeRect.getWidth()*3.0f)) / range();\r
362 \r
363                         DrawPos = (s32)( ( ( Pos - Min ) * f) + ((f32)RelativeRect.getWidth() * 0.5f));\r
364                         DrawHeight = RelativeRect.getWidth();\r
365                 }\r
366         }\r
367 }\r
368 \r
369 \r
370 //! gets the small step value\r
371 s32 CGUIScrollBar::getSmallStep() const\r
372 {\r
373         return SmallStep;\r
374 }\r
375 \r
376 \r
377 //! sets the small step value\r
378 void CGUIScrollBar::setSmallStep(s32 step)\r
379 {\r
380         if (step > 0)\r
381                 SmallStep = step;\r
382         else\r
383                 SmallStep = 10;\r
384 }\r
385 \r
386 \r
387 //! gets the small step value\r
388 s32 CGUIScrollBar::getLargeStep() const\r
389 {\r
390         return LargeStep;\r
391 }\r
392 \r
393 \r
394 //! sets the small step value\r
395 void CGUIScrollBar::setLargeStep(s32 step)\r
396 {\r
397         if (step > 0)\r
398                 LargeStep = step;\r
399         else\r
400                 LargeStep = 50;\r
401 }\r
402 \r
403 \r
404 //! gets the maximum value of the scrollbar.\r
405 s32 CGUIScrollBar::getMax() const\r
406 {\r
407         return Max;\r
408 }\r
409 \r
410 \r
411 //! sets the maximum value of the scrollbar.\r
412 void CGUIScrollBar::setMax(s32 max)\r
413 {\r
414         Max = max;\r
415         if ( Min > Max )\r
416                 Min = Max;\r
417 \r
418         bool enable = core::isnotzero ( range() );\r
419         UpButton->setEnabled(enable);\r
420         DownButton->setEnabled(enable);\r
421         setPos(Pos);\r
422 }\r
423 \r
424 //! gets the minimum value of the scrollbar.\r
425 s32 CGUIScrollBar::getMin() const\r
426 {\r
427         return Min;\r
428 }\r
429 \r
430 \r
431 //! sets the minimum value of the scrollbar.\r
432 void CGUIScrollBar::setMin(s32 min)\r
433 {\r
434         Min = min;\r
435         if ( Max < Min )\r
436                 Max = Min;\r
437 \r
438 \r
439         bool enable = core::isnotzero ( range() );\r
440         UpButton->setEnabled(enable);\r
441         DownButton->setEnabled(enable);\r
442         setPos(Pos);\r
443 }\r
444 \r
445 \r
446 //! gets the current position of the scrollbar\r
447 s32 CGUIScrollBar::getPos() const\r
448 {\r
449         return Pos;\r
450 }\r
451 \r
452 \r
453 //! refreshes the position and text on child buttons\r
454 void CGUIScrollBar::refreshControls()\r
455 {\r
456         CurrentIconColor = video::SColor(255,255,255,255);\r
457 \r
458         IGUISkin* skin = Environment->getSkin();\r
459         IGUISpriteBank* sprites = 0;\r
460 \r
461         if (skin)\r
462         {\r
463                 sprites = skin->getSpriteBank();\r
464                 CurrentIconColor = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);\r
465         }\r
466 \r
467         if (Horizontal)\r
468         {\r
469                 const s32 h = RelativeRect.getHeight();\r
470                 const s32 w = (h < RelativeRect.getWidth() / 2) ? h : RelativeRect.getWidth() / 2;\r
471                 if (!UpButton)\r
472                 {\r
473                         UpButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0,0, w, h), NoClip);\r
474                         UpButton->setSubElement(true);\r
475                         UpButton->setTabStop(false);\r
476                 }\r
477                 if (sprites)\r
478                 {\r
479                         UpButton->setSpriteBank(sprites);\r
480                         UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor);\r
481                         UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor);\r
482                 }\r
483                 UpButton->setRelativePosition(core::rect<s32>(0,0, w, h));\r
484                 UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);\r
485                 if (!DownButton)\r
486                 {\r
487                         DownButton = new CGUIButton(Environment, this, -1, core::rect<s32>(RelativeRect.getWidth()-w, 0, RelativeRect.getWidth(), h), NoClip);\r
488                         DownButton->setSubElement(true);\r
489                         DownButton->setTabStop(false);\r
490                 }\r
491                 if (sprites)\r
492                 {\r
493                         DownButton->setSpriteBank(sprites);\r
494                         DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor);\r
495                         DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor);\r
496                 }\r
497                 DownButton->setRelativePosition(core::rect<s32>(RelativeRect.getWidth()-w, 0, RelativeRect.getWidth(), h));\r
498                 DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);\r
499         }\r
500         else\r
501         {\r
502                 const s32 w = RelativeRect.getWidth();\r
503                 const s32 h = (w < RelativeRect.getHeight() / 2) ? w : RelativeRect.getHeight() / 2;\r
504                 if (!UpButton)\r
505                 {\r
506                         UpButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0,0, w, h), NoClip);\r
507                         UpButton->setSubElement(true);\r
508                         UpButton->setTabStop(false);\r
509                 }\r
510                 if (sprites)\r
511                 {\r
512                         UpButton->setSpriteBank(sprites);\r
513                         UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor);\r
514                         UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor);\r
515                 }\r
516                 UpButton->setRelativePosition(core::rect<s32>(0,0, w, h));\r
517                 UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);\r
518                 if (!DownButton)\r
519                 {\r
520                         DownButton = new CGUIButton(Environment, this, -1, core::rect<s32>(0,RelativeRect.getHeight()-h, w, RelativeRect.getHeight()), NoClip);\r
521                         DownButton->setSubElement(true);\r
522                         DownButton->setTabStop(false);\r
523                 }\r
524                 if (sprites)\r
525                 {\r
526                         DownButton->setSpriteBank(sprites);\r
527                         DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor);\r
528                         DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor);\r
529                 }\r
530                 DownButton->setRelativePosition(core::rect<s32>(0,RelativeRect.getHeight()-h, w, RelativeRect.getHeight()));\r
531                 DownButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);\r
532         }\r
533 }\r
534 \r
535 } // end namespace gui\r
536 } // end namespace irr\r
537 \r
538 #endif // _IRR_COMPILE_WITH_GUI_\r
539 \r