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