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