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 "CGUIButton.h"
6 #ifdef _IRR_COMPILE_WITH_GUI_
7 
8 #include "IGUISkin.h"
9 #include "IGUIEnvironment.h"
10 #include "IVideoDriver.h"
11 #include "IGUIFont.h"
12 #include "os.h"
13 
14 namespace irr
15 {
16 namespace gui
17 {
18 
19 //! constructor
CGUIButton(IGUIEnvironment * environment,IGUIElement * parent,s32 id,core::rect<s32> rectangle,bool noclip)20 CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent,
21 			s32 id, core::rect<s32> rectangle, bool noclip)
22 : IGUIButton(environment, parent, id, rectangle),
23 	SpriteBank(0), OverrideFont(0), Image(0), PressedImage(0),
24 	ClickTime(0), HoverTime(0), FocusTime(0),
25 	IsPushButton(false), Pressed(false),
26 	UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)
27 {
28 	#ifdef _DEBUG
29 	setDebugName("CGUIButton");
30 	#endif
31 	setNotClipped(noclip);
32 
33 	// Initialize the sprites.
34 	for (u32 i=0; i<EGBS_COUNT; ++i)
35 		ButtonSprites[i].Index = -1;
36 
37 	// This element can be tabbed.
38 	setTabStop(true);
39 	setTabOrder(-1);
40 }
41 
42 
43 //! destructor
~CGUIButton()44 CGUIButton::~CGUIButton()
45 {
46 	if (OverrideFont)
47 		OverrideFont->drop();
48 
49 	if (Image)
50 		Image->drop();
51 
52 	if (PressedImage)
53 		PressedImage->drop();
54 
55 	if (SpriteBank)
56 		SpriteBank->drop();
57 }
58 
59 
60 //! Sets if the images should be scaled to fit the button
setScaleImage(bool scaleImage)61 void CGUIButton::setScaleImage(bool scaleImage)
62 {
63 	ScaleImage = scaleImage;
64 }
65 
66 
67 //! Returns whether the button scale the used images
isScalingImage() const68 bool CGUIButton::isScalingImage() const
69 {
70 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
71 	return ScaleImage;
72 }
73 
74 
75 //! Sets if the button should use the skin to draw its border
setDrawBorder(bool border)76 void CGUIButton::setDrawBorder(bool border)
77 {
78 	DrawBorder = border;
79 }
80 
81 
setSpriteBank(IGUISpriteBank * sprites)82 void CGUIButton::setSpriteBank(IGUISpriteBank* sprites)
83 {
84 	if (sprites)
85 		sprites->grab();
86 
87 	if (SpriteBank)
88 		SpriteBank->drop();
89 
90 	SpriteBank = sprites;
91 }
92 
93 
setSprite(EGUI_BUTTON_STATE state,s32 index,video::SColor color,bool loop)94 void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop)
95 {
96 	if (SpriteBank)
97 	{
98 		ButtonSprites[(u32)state].Index	= index;
99 		ButtonSprites[(u32)state].Color	= color;
100 		ButtonSprites[(u32)state].Loop	= loop;
101 	}
102 	else
103 	{
104 		ButtonSprites[(u32)state].Index = -1;
105 	}
106 }
107 
108 
109 //! called if an event happened.
OnEvent(const SEvent & event)110 bool CGUIButton::OnEvent(const SEvent& event)
111 {
112 	if (!isEnabled())
113 		return IGUIElement::OnEvent(event);
114 
115 	switch(event.EventType)
116 	{
117 	case EET_KEY_INPUT_EVENT:
118 		if (event.KeyInput.PressedDown &&
119 			(event.KeyInput.Key == IRR_KEY_RETURN ||
120 			event.KeyInput.Key == IRR_KEY_SPACE))
121 		{
122 			if (!IsPushButton)
123 				setPressed(true);
124 			else
125 				setPressed(!Pressed);
126 
127 			return true;
128 		}
129 		if (Pressed && !IsPushButton && event.KeyInput.PressedDown &&
130 			event.KeyInput.Key == IRR_KEY_ESCAPE)
131 		{
132 			setPressed(false);
133 			return true;
134 		}
135 		else
136 		if (!event.KeyInput.PressedDown && Pressed &&
137 			(event.KeyInput.Key == IRR_KEY_RETURN ||
138 			event.KeyInput.Key == IRR_KEY_SPACE))
139 		{
140 
141 			if (!IsPushButton)
142 				setPressed(false);
143 
144 			if (Parent)
145 			{
146 				SEvent newEvent;
147 				newEvent.EventType = EET_GUI_EVENT;
148 				newEvent.GUIEvent.Caller = this;
149 				newEvent.GUIEvent.Element = 0;
150 				newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
151 				Parent->OnEvent(newEvent);
152 			}
153 			return true;
154 		}
155 		break;
156 	case EET_GUI_EVENT:
157 		if (event.GUIEvent.Caller == this)
158 		{
159 			if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
160 			{
161 				if (!IsPushButton)
162 					setPressed(false);
163 				FocusTime = os::Timer::getTime();
164 			}
165 			else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
166 			{
167 				FocusTime = os::Timer::getTime();
168 			}
169 			else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT)
170 			{
171 				HoverTime = os::Timer::getTime();
172 			}
173 		}
174 		break;
175 	case EET_MOUSE_INPUT_EVENT:
176 		if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
177 		{
178 			if (Environment->hasFocus(this) &&
179 				!AbsoluteClippingRect.isPointInside(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
180 			{
181 					Environment->removeFocus(this);
182 					return false;
183 			}
184 
185 			if (!IsPushButton)
186 				setPressed(true);
187 
188 			Environment->setFocus(this);
189 			return true;
190 		}
191 		else
192 		if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
193 		{
194 			bool wasPressed = Pressed;
195 
196 			if ( !AbsoluteClippingRect.isPointInside( core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y ) ) )
197 			{
198 				if (!IsPushButton)
199 					setPressed(false);
200 				return true;
201 			}
202 
203 			if (!IsPushButton)
204 				setPressed(false);
205 			else
206 			{
207 				setPressed(!Pressed);
208 			}
209 
210 			if ((!IsPushButton && wasPressed && Parent) ||
211 				(IsPushButton && wasPressed != Pressed))
212 			{
213 				SEvent newEvent;
214 				newEvent.EventType = EET_GUI_EVENT;
215 				newEvent.GUIEvent.Caller = this;
216 				newEvent.GUIEvent.Element = 0;
217 				newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
218 				Parent->OnEvent(newEvent);
219 			}
220 
221 			return true;
222 		}
223 		break;
224 	default:
225 		break;
226 	}
227 
228 	return Parent ? Parent->OnEvent(event) : false;
229 }
230 
231 
232 //! draws the element and its children
draw()233 void CGUIButton::draw()
234 {
235 	if (!IsVisible)
236 		return;
237 
238 	IGUISkin* skin = Environment->getSkin();
239 
240 	// todo:	move sprite up and text down if the pressed state has a sprite
241 	const core::position2di spritePos = AbsoluteRect.getCenter();
242 
243 	if (!Pressed)
244 	{
245 		if (DrawBorder)
246 			skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect);
247 
248 		if (Image)
249 		{
250 			core::position2d<s32> pos = spritePos;
251 			pos.X -= ImageRect.getWidth() / 2;
252 			pos.Y -= ImageRect.getHeight() / 2;
253 
254             skin->draw2DImage(Image,
255                 ScaleImage ? AbsoluteRect :
256                 core::recti(pos, ImageRect.getSize()),
257                 ImageRect, &AbsoluteClippingRect,
258                 0, UseAlphaChannel);
259 			//video::IVideoDriver* driver = Environment->getVideoDriver();
260 			//driver->draw2DImage(Image,
261 			//		ScaleImage? AbsoluteRect :
262 			//			core::recti(pos, ImageRect.getSize()),
263 			//		ImageRect, &AbsoluteClippingRect,
264 			//		0, UseAlphaChannel);
265 		}
266 	}
267 	else
268 	{
269 		if (DrawBorder)
270 			skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect);
271 
272 		if (PressedImage)
273 		{
274 			core::position2d<s32> pos = spritePos;
275 			pos.X -= PressedImageRect.getWidth() / 2;
276 			pos.Y -= PressedImageRect.getHeight() / 2;
277 
278 			if (Image == PressedImage && PressedImageRect == ImageRect)
279 			{
280 				pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X);
281 				pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y);
282 			}
283 			skin->draw2DImage(PressedImage,
284 					ScaleImage? AbsoluteRect :
285 						core::recti(pos, PressedImageRect.getSize()),
286 					PressedImageRect, &AbsoluteClippingRect,
287 					0, UseAlphaChannel);
288 		}
289 	}
290 
291 	if (false) //SpriteBank)
292 	{
293 		// pressed / unpressed animation
294 		u32 state = Pressed ? (u32)EGBS_BUTTON_DOWN : (u32)EGBS_BUTTON_UP;
295 		if (ButtonSprites[state].Index != -1)
296 		{
297 			SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos,
298 			 	&AbsoluteClippingRect, ButtonSprites[state].Color, ClickTime, os::Timer::getTime(),
299 				ButtonSprites[state].Loop, true);
300 		}
301 
302 		// focused / unfocused animation
303 		state = Environment->hasFocus(this) ? (u32)EGBS_BUTTON_FOCUSED : (u32)EGBS_BUTTON_NOT_FOCUSED;
304 		if (ButtonSprites[state].Index != -1)
305 		{
306 			SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos,
307 			 	&AbsoluteClippingRect, ButtonSprites[state].Color, FocusTime, os::Timer::getTime(),
308 				ButtonSprites[state].Loop, true);
309 		}
310 
311 		// mouse over / off animation
312 		if (isEnabled())
313 		{
314 			state = Environment->getHovered() == this ? (u32)EGBS_BUTTON_MOUSE_OVER : (u32)EGBS_BUTTON_MOUSE_OFF;
315 			if (ButtonSprites[state].Index != -1)
316 			{
317 				SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos,
318 				 	&AbsoluteClippingRect, ButtonSprites[state].Color, HoverTime, os::Timer::getTime(),
319 					ButtonSprites[state].Loop, true);
320 			}
321 		}
322 	}
323 
324 	if (Text.size())
325 	{
326 		IGUIFont* font = getActiveFont();
327 
328 		core::rect<s32> rect = AbsoluteRect;
329 		if (Pressed)
330 		{
331 			rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X);
332 			rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y);
333 		}
334 
335 		if (font)
336 			font->draw(Text.c_str(), rect,
337 				skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
338 				true, true, &AbsoluteClippingRect);
339 	}
340 
341 	IGUIElement::draw();
342 }
343 
344 
345 //! sets another skin independent font. if this is set to zero, the button uses the font of the skin.
setOverrideFont(IGUIFont * font)346 void CGUIButton::setOverrideFont(IGUIFont* font)
347 {
348 	if (OverrideFont == font)
349 		return;
350 
351 	if (OverrideFont)
352 		OverrideFont->drop();
353 
354 	OverrideFont = font;
355 
356 	if (OverrideFont)
357 		OverrideFont->grab();
358 }
359 
360 //! Gets the override font (if any)
getOverrideFont() const361 IGUIFont * CGUIButton::getOverrideFont() const
362 {
363 	return OverrideFont;
364 }
365 
366 //! Get the font which is used right now for drawing
getActiveFont() const367 IGUIFont* CGUIButton::getActiveFont() const
368 {
369 	if ( OverrideFont )
370 		return OverrideFont;
371 	IGUISkin* skin = Environment->getSkin();
372 	if (skin)
373 		return skin->getFont(EGDF_BUTTON);
374 	return 0;
375 }
376 
377 //! Sets an image which should be displayed on the button when it is in normal state.
setImage(video::ITexture * image)378 void CGUIButton::setImage(video::ITexture* image)
379 {
380 	if (image)
381 		image->grab();
382 	if (Image)
383 		Image->drop();
384 
385 	Image = image;
386 	if (image)
387 		ImageRect = core::rect<s32>(core::position2d<s32>(0,0), image->getSize());
388 
389 	if (!PressedImage)
390 		setPressedImage(Image);
391 }
392 
393 
394 //! Sets the image which should be displayed on the button when it is in its normal state.
setImage(video::ITexture * image,const core::rect<s32> & pos)395 void CGUIButton::setImage(video::ITexture* image, const core::rect<s32>& pos)
396 {
397 	setImage(image);
398 	ImageRect = pos;
399 }
400 
401 
402 //! Sets an image which should be displayed on the button when it is in pressed state.
setPressedImage(video::ITexture * image)403 void CGUIButton::setPressedImage(video::ITexture* image)
404 {
405 	if (image)
406 		image->grab();
407 
408 	if (PressedImage)
409 		PressedImage->drop();
410 
411 	PressedImage = image;
412 	if (image)
413 		PressedImageRect = core::rect<s32>(core::position2d<s32>(0,0), image->getSize());
414 }
415 
416 
417 //! Sets the image which should be displayed on the button when it is in its pressed state.
setPressedImage(video::ITexture * image,const core::rect<s32> & pos)418 void CGUIButton::setPressedImage(video::ITexture* image, const core::rect<s32>& pos)
419 {
420 	setPressedImage(image);
421 	PressedImageRect = pos;
422 }
423 
424 
425 //! Sets if the button should behave like a push button. Which means it
426 //! can be in two states: Normal or Pressed. With a click on the button,
427 //! the user can change the state of the button.
setIsPushButton(bool isPushButton)428 void CGUIButton::setIsPushButton(bool isPushButton)
429 {
430 	IsPushButton = isPushButton;
431 }
432 
433 
434 //! Returns if the button is currently pressed
isPressed() const435 bool CGUIButton::isPressed() const
436 {
437 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
438 	return Pressed;
439 }
440 
441 
442 //! Sets the pressed state of the button if this is a pushbutton
setPressed(bool pressed)443 void CGUIButton::setPressed(bool pressed)
444 {
445 	if (Pressed != pressed)
446 	{
447 		ClickTime = os::Timer::getTime();
448 		Pressed = pressed;
449 	}
450 }
451 
452 
453 //! Returns whether the button is a push button
isPushButton() const454 bool CGUIButton::isPushButton() const
455 {
456 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
457 	return IsPushButton;
458 }
459 
460 
461 //! Sets if the alpha channel should be used for drawing images on the button (default is false)
setUseAlphaChannel(bool useAlphaChannel)462 void CGUIButton::setUseAlphaChannel(bool useAlphaChannel)
463 {
464 	UseAlphaChannel = useAlphaChannel;
465 }
466 
467 
468 //! Returns if the alpha channel should be used for drawing images on the button
isAlphaChannelUsed() const469 bool CGUIButton::isAlphaChannelUsed() const
470 {
471 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
472 	return UseAlphaChannel;
473 }
474 
475 
isDrawingBorder() const476 bool CGUIButton::isDrawingBorder() const
477 {
478 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
479 	return DrawBorder;
480 }
481 
482 
483 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options=0) const484 void CGUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
485 {
486 	IGUIButton::serializeAttributes(out,options);
487 
488 	out->addBool	("PushButton",		IsPushButton );
489 	if (IsPushButton)
490 		out->addBool("Pressed",		Pressed);
491 
492 	out->addTexture ("Image",		Image);
493 	out->addRect	("ImageRect",		ImageRect);
494 	out->addTexture	("PressedImage",	PressedImage);
495 	out->addRect	("PressedImageRect",	PressedImageRect);
496 
497 	out->addBool	("UseAlphaChannel",	isAlphaChannelUsed());
498 	out->addBool	("Border",		isDrawingBorder());
499 	out->addBool	("ScaleImage",		isScalingImage());
500 
501 	//   out->addString  ("OverrideFont",	OverrideFont);
502 }
503 
504 
505 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options=0)506 void CGUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
507 {
508 	IGUIButton::deserializeAttributes(in,options);
509 
510 	IsPushButton	= in->getAttributeAsBool("PushButton");
511 	Pressed		= IsPushButton ? in->getAttributeAsBool("Pressed") : false;
512 
513 	core::rect<s32> rec = in->getAttributeAsRect("ImageRect");
514 	if (rec.isValid())
515 		setImage( in->getAttributeAsTexture("Image"), rec);
516 	else
517 		setImage( in->getAttributeAsTexture("Image") );
518 
519 	rec = in->getAttributeAsRect("PressedImageRect");
520 	if (rec.isValid())
521 		setPressedImage( in->getAttributeAsTexture("PressedImage"), rec);
522 	else
523 		setPressedImage( in->getAttributeAsTexture("PressedImage") );
524 
525 	setDrawBorder(in->getAttributeAsBool("Border"));
526 	setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel"));
527 	setScaleImage(in->getAttributeAsBool("ScaleImage"));
528 
529 	//   setOverrideFont(in->getAttributeAsString("OverrideFont"));
530 
531 	updateAbsolutePosition();
532 }
533 
534 
535 } // end namespace gui
536 } // end namespace irr
537 
538 #endif // _IRR_COMPILE_WITH_GUI_
539 
540