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
5 #include "CGUIButton.h"
\r
6 #ifdef _IRR_COMPILE_WITH_GUI_
\r
8 #include "IGUISkin.h"
\r
9 #include "IGUIEnvironment.h"
\r
10 #include "IVideoDriver.h"
\r
11 #include "IGUIFont.h"
\r
20 CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent,
\r
21 s32 id, core::rect<s32> rectangle, bool noclip)
\r
22 : IGUIButton(environment, parent, id, rectangle),
\r
23 SpriteBank(0), OverrideFont(0),
\r
24 OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)),
\r
25 ClickTime(0), HoverTime(0), FocusTime(0),
\r
26 ClickShiftState(false), ClickControlState(false),
\r
27 IsPushButton(false), Pressed(false),
\r
28 UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)
\r
31 setDebugName("CGUIButton");
\r
33 setNotClipped(noclip);
\r
35 // This element can be tabbed.
\r
42 CGUIButton::~CGUIButton()
\r
45 OverrideFont->drop();
\r
52 //! Sets if the images should be scaled to fit the button
\r
53 void CGUIButton::setScaleImage(bool scaleImage)
\r
55 ScaleImage = scaleImage;
\r
59 //! Returns whether the button scale the used images
\r
60 bool CGUIButton::isScalingImage() const
\r
66 //! Sets if the button should use the skin to draw its border
\r
67 void CGUIButton::setDrawBorder(bool border)
\r
69 DrawBorder = border;
\r
73 void CGUIButton::setSpriteBank(IGUISpriteBank* sprites)
\r
81 SpriteBank = sprites;
\r
85 void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)
\r
87 ButtonSprites[(u32)state].Index = index;
\r
88 ButtonSprites[(u32)state].Color = color;
\r
89 ButtonSprites[(u32)state].Loop = loop;
\r
90 ButtonSprites[(u32)state].Scale = scale;
\r
93 //! Get the sprite-index for the given state or -1 when no sprite is set
\r
94 s32 CGUIButton::getSpriteIndex(EGUI_BUTTON_STATE state) const
\r
96 return ButtonSprites[(u32)state].Index;
\r
99 //! Get the sprite color for the given state. Color is only used when a sprite is set.
\r
100 video::SColor CGUIButton::getSpriteColor(EGUI_BUTTON_STATE state) const
\r
102 return ButtonSprites[(u32)state].Color;
\r
105 //! Returns if the sprite in the given state does loop
\r
106 bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
\r
108 return ButtonSprites[(u32)state].Loop;
\r
111 //! Returns if the sprite in the given state is scaled
\r
112 bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const
\r
114 return ButtonSprites[(u32)state].Scale;
\r
117 //! called if an event happened.
\r
118 bool CGUIButton::OnEvent(const SEvent& event)
\r
121 return IGUIElement::OnEvent(event);
\r
123 switch(event.EventType)
\r
125 case EET_KEY_INPUT_EVENT:
\r
126 if (event.KeyInput.PressedDown &&
\r
127 (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))
\r
132 setPressed(!Pressed);
\r
136 if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE)
\r
142 if (!event.KeyInput.PressedDown && Pressed &&
\r
143 (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))
\r
151 ClickShiftState = event.KeyInput.Shift;
\r
152 ClickControlState = event.KeyInput.Control;
\r
155 newEvent.EventType = EET_GUI_EVENT;
\r
156 newEvent.GUIEvent.Caller = this;
\r
157 newEvent.GUIEvent.Element = 0;
\r
158 newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
\r
159 Parent->OnEvent(newEvent);
\r
164 case EET_GUI_EVENT:
\r
165 if (event.GUIEvent.Caller == this)
\r
167 if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
\r
171 FocusTime = os::Timer::getTime();
\r
173 else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
\r
175 FocusTime = os::Timer::getTime();
\r
177 else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT)
\r
179 HoverTime = os::Timer::getTime();
\r
183 case EET_MOUSE_INPUT_EVENT:
\r
184 if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
192 if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
194 bool wasPressed = Pressed;
\r
196 if ( !AbsoluteClippingRect.isPointInside( core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y ) ) )
\r
207 setPressed(!Pressed);
\r
210 if ((!IsPushButton && wasPressed && Parent) ||
\r
211 (IsPushButton && wasPressed != Pressed))
\r
213 ClickShiftState = event.MouseInput.Shift;
\r
214 ClickControlState = event.MouseInput.Control;
\r
217 newEvent.EventType = EET_GUI_EVENT;
\r
218 newEvent.GUIEvent.Caller = this;
\r
219 newEvent.GUIEvent.Element = 0;
\r
220 newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
\r
221 Parent->OnEvent(newEvent);
\r
231 return Parent ? Parent->OnEvent(event) : false;
\r
235 //! draws the element and its children
\r
236 void CGUIButton::draw()
\r
241 IGUISkin* skin = Environment->getSkin();
\r
242 video::IVideoDriver* driver = Environment->getVideoDriver();
\r
248 skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect);
\r
252 skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect);
\r
257 const core::position2di buttonCenter(AbsoluteRect.getCenter());
\r
259 EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);
\r
260 if ( ButtonImages[(u32)imageState].Texture )
\r
262 core::position2d<s32> pos(buttonCenter);
\r
263 core::rect<s32> sourceRect(ButtonImages[(u32)imageState].SourceRect);
\r
264 if ( sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0 )
\r
265 sourceRect = core::rect<s32>(core::position2di(0,0), ButtonImages[(u32)imageState].Texture->getOriginalSize());
\r
267 pos.X -= sourceRect.getWidth() / 2;
\r
268 pos.Y -= sourceRect.getHeight() / 2;
\r
272 // Create a pressed-down effect by moving the image when it looks identical to the unpressed state image
\r
273 EGUI_BUTTON_IMAGE_STATE unpressedState = getImageState(false);
\r
274 if ( unpressedState == imageState || ButtonImages[(u32)imageState] == ButtonImages[(u32)unpressedState] )
\r
276 pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X);
\r
277 pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y);
\r
281 driver->draw2DImage(ButtonImages[(u32)imageState].Texture,
\r
282 ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
\r
283 sourceRect, &AbsoluteClippingRect,
\r
284 0, UseAlphaChannel);
\r
289 core::position2di pos(buttonCenter);
\r
292 pos.X += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X);
\r
293 pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y);
\r
298 // pressed / unpressed animation
\r
299 EGUI_BUTTON_STATE state = Pressed ? EGBS_BUTTON_DOWN : EGBS_BUTTON_UP;
\r
300 drawSprite(state, ClickTime, pos);
\r
302 // focused / unfocused animation
\r
303 state = Environment->hasFocus(this) ? EGBS_BUTTON_FOCUSED : EGBS_BUTTON_NOT_FOCUSED;
\r
304 drawSprite(state, FocusTime, pos);
\r
306 // mouse over / off animation
\r
307 state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;
\r
308 drawSprite(state, HoverTime, pos);
\r
313 drawSprite(EGBS_BUTTON_DISABLED, 0, pos);
\r
319 IGUIFont* font = getActiveFont();
\r
321 core::rect<s32> rect = AbsoluteRect;
\r
324 rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X);
\r
325 rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y);
\r
329 font->draw(Text.c_str(), rect,
\r
331 true, true, &AbsoluteClippingRect);
\r
334 IGUIElement::draw();
\r
337 void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center)
\r
339 u32 stateIdx = (u32)state;
\r
341 if (ButtonSprites[stateIdx].Index != -1)
\r
343 if ( ButtonSprites[stateIdx].Scale )
\r
345 const video::SColor colors[] = {ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color};
\r
346 SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect,
\r
347 &AbsoluteClippingRect, colors,
\r
348 os::Timer::getTime()-startTime, ButtonSprites[stateIdx].Loop);
\r
352 SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center,
\r
353 &AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, os::Timer::getTime(),
\r
354 ButtonSprites[stateIdx].Loop, true);
\r
359 EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const
\r
361 // figure state we should have
\r
362 EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED;
\r
363 bool focused = Environment->hasFocus(this);
\r
364 bool mouseOver = static_cast<const IGUIElement*>(Environment->getHovered()) == this; // (static cast for Borland)
\r
369 if ( focused && mouseOver )
\r
370 state = EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER;
\r
371 else if ( focused )
\r
372 state = EGBIS_IMAGE_DOWN_FOCUSED;
\r
373 else if ( mouseOver )
\r
374 state = EGBIS_IMAGE_DOWN_MOUSEOVER;
\r
376 state = EGBIS_IMAGE_DOWN;
\r
380 if ( focused && mouseOver )
\r
381 state = EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER;
\r
382 else if ( focused )
\r
383 state = EGBIS_IMAGE_UP_FOCUSED;
\r
384 else if ( mouseOver )
\r
385 state = EGBIS_IMAGE_UP_MOUSEOVER;
\r
387 state = EGBIS_IMAGE_UP;
\r
391 // find a compatible state that has images
\r
392 while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture )
\r
396 case EGBIS_IMAGE_UP_FOCUSED:
\r
397 state = EGBIS_IMAGE_UP_MOUSEOVER;
\r
399 case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER:
\r
400 state = EGBIS_IMAGE_UP_FOCUSED;
\r
402 case EGBIS_IMAGE_DOWN_MOUSEOVER:
\r
403 state = EGBIS_IMAGE_DOWN;
\r
405 case EGBIS_IMAGE_DOWN_FOCUSED:
\r
406 state = EGBIS_IMAGE_DOWN_MOUSEOVER;
\r
408 case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER:
\r
409 state = EGBIS_IMAGE_DOWN_FOCUSED;
\r
411 case EGBIS_IMAGE_DISABLED:
\r
413 state = EGBIS_IMAGE_DOWN;
\r
415 state = EGBIS_IMAGE_UP;
\r
418 state = EGBIS_IMAGE_UP;
\r
425 //! sets another skin independent font. if this is set to zero, the button uses the font of the skin.
\r
426 void CGUIButton::setOverrideFont(IGUIFont* font)
\r
428 if (OverrideFont == font)
\r
432 OverrideFont->drop();
\r
434 OverrideFont = font;
\r
437 OverrideFont->grab();
\r
440 //! Gets the override font (if any)
\r
441 IGUIFont * CGUIButton::getOverrideFont() const
\r
443 return OverrideFont;
\r
446 //! Get the font which is used right now for drawing
\r
447 IGUIFont* CGUIButton::getActiveFont() const
\r
449 if ( OverrideFont )
\r
450 return OverrideFont;
\r
451 IGUISkin* skin = Environment->getSkin();
\r
453 return skin->getFont(EGDF_BUTTON);
\r
457 //! Sets another color for the text.
\r
458 void CGUIButton::setOverrideColor(video::SColor color)
\r
460 OverrideColor = color;
\r
461 OverrideColorEnabled = true;
\r
464 video::SColor CGUIButton::getOverrideColor() const
\r
466 return OverrideColor;
\r
469 irr::video::SColor CGUIButton::getActiveColor() const
\r
471 if ( OverrideColorEnabled )
\r
472 return OverrideColor;
\r
473 IGUISkin* skin = Environment->getSkin();
\r
475 return OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT);
\r
476 return OverrideColor;
\r
479 void CGUIButton::enableOverrideColor(bool enable)
\r
481 OverrideColorEnabled = enable;
\r
484 bool CGUIButton::isOverrideColorEnabled() const
\r
486 return OverrideColorEnabled;
\r
489 void CGUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image, const core::rect<s32>& sourceRect)
\r
491 if ( state >= EGBIS_COUNT )
\r
497 u32 stateIdx = (u32)state;
\r
498 if ( ButtonImages[stateIdx].Texture )
\r
499 ButtonImages[stateIdx].Texture->drop();
\r
501 ButtonImages[stateIdx].Texture = image;
\r
502 ButtonImages[stateIdx].SourceRect = sourceRect;
\r
505 //! Sets if the button should behave like a push button. Which means it
\r
506 //! can be in two states: Normal or Pressed. With a click on the button,
\r
507 //! the user can change the state of the button.
\r
508 void CGUIButton::setIsPushButton(bool isPushButton)
\r
510 IsPushButton = isPushButton;
\r
514 //! Returns if the button is currently pressed
\r
515 bool CGUIButton::isPressed() const
\r
521 //! Sets the pressed state of the button if this is a pushbutton
\r
522 void CGUIButton::setPressed(bool pressed)
\r
524 if (Pressed != pressed)
\r
526 ClickTime = os::Timer::getTime();
\r
532 //! Returns whether the button is a push button
\r
533 bool CGUIButton::isPushButton() const
\r
535 return IsPushButton;
\r
539 //! Sets if the alpha channel should be used for drawing images on the button (default is false)
\r
540 void CGUIButton::setUseAlphaChannel(bool useAlphaChannel)
\r
542 UseAlphaChannel = useAlphaChannel;
\r
546 //! Returns if the alpha channel should be used for drawing images on the button
\r
547 bool CGUIButton::isAlphaChannelUsed() const
\r
549 return UseAlphaChannel;
\r
553 bool CGUIButton::isDrawingBorder() const
\r
559 //! Writes attributes of the element.
\r
560 void CGUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
\r
562 IGUIButton::serializeAttributes(out,options);
\r
564 out->addBool ("PushButton", IsPushButton );
\r
566 out->addBool("Pressed", Pressed);
\r
568 for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i )
\r
570 if ( ButtonImages[i].Texture )
\r
572 core::stringc name( GUIButtonImageStateNames[i] );
\r
573 out->addTexture(name.c_str(), ButtonImages[i].Texture);
\r
575 out->addRect(name.c_str(), ButtonImages[i].SourceRect);
\r
579 out->addBool ("UseAlphaChannel", UseAlphaChannel);
\r
580 out->addBool ("Border", DrawBorder);
\r
581 out->addBool ("ScaleImage", ScaleImage);
\r
583 for ( u32 i=0; i<(u32)EGBS_COUNT; ++i )
\r
585 if ( ButtonSprites[i].Index >= 0 )
\r
587 core::stringc nameIndex( GUIButtonStateNames[i] );
\r
588 nameIndex += "Index";
\r
589 out->addInt(nameIndex.c_str(), ButtonSprites[i].Index );
\r
591 core::stringc nameColor( GUIButtonStateNames[i] );
\r
592 nameColor += "Color";
\r
593 out->addColor(nameColor.c_str(), ButtonSprites[i].Color );
\r
595 core::stringc nameLoop( GUIButtonStateNames[i] );
\r
596 nameLoop += "Loop";
\r
597 out->addBool(nameLoop.c_str(), ButtonSprites[i].Loop );
\r
599 core::stringc nameScale( GUIButtonStateNames[i] );
\r
600 nameScale += "Scale";
\r
601 out->addBool(nameScale.c_str(), ButtonSprites[i].Scale );
\r
605 // out->addString ("OverrideFont", OverrideFont);
\r
609 //! Reads attributes of the element
\r
610 void CGUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
\r
612 IGUIButton::deserializeAttributes(in,options);
\r
614 IsPushButton = in->getAttributeAsBool("PushButton", IsPushButton);
\r
615 Pressed = IsPushButton ? in->getAttributeAsBool("Pressed", Pressed) : false;
\r
617 for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i )
\r
619 core::stringc nameRect( GUIButtonImageStateNames[i] );
\r
620 nameRect += "Rect";
\r
622 setImage((EGUI_BUTTON_IMAGE_STATE)i,
\r
623 in->getAttributeAsTexture(GUIButtonImageStateNames[i], ButtonImages[i].Texture),
\r
624 in->getAttributeAsRect(nameRect.c_str(), ButtonImages[i].SourceRect) );
\r
627 setDrawBorder(in->getAttributeAsBool("Border", DrawBorder));
\r
628 setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel", UseAlphaChannel));
\r
629 setScaleImage(in->getAttributeAsBool("ScaleImage", ScaleImage));
\r
631 for ( u32 i=0; i<(u32)EGBS_COUNT; ++i )
\r
633 core::stringc nameIndex( GUIButtonStateNames[i] );
\r
634 nameIndex += "Index";
\r
635 ButtonSprites[i].Index = in->getAttributeAsInt(nameIndex.c_str(), ButtonSprites[i].Index );
\r
637 core::stringc nameColor( GUIButtonStateNames[i] );
\r
638 nameColor += "Color";
\r
639 ButtonSprites[i].Color = in->getAttributeAsColor(nameColor.c_str(), ButtonSprites[i].Color );
\r
641 core::stringc nameLoop( GUIButtonStateNames[i] );
\r
642 nameLoop += "Loop";
\r
643 ButtonSprites[i].Loop = in->getAttributeAsBool(nameLoop.c_str(), ButtonSprites[i].Loop );
\r
645 core::stringc nameScale( GUIButtonStateNames[i] );
\r
646 nameScale += "Scale";
\r
647 ButtonSprites[i].Scale = in->getAttributeAsBool(nameScale.c_str(), ButtonSprites[i].Scale );
\r
650 // setOverrideFont(in->getAttributeAsString("OverrideFont"));
\r
652 updateAbsolutePosition();
\r
656 } // end namespace gui
\r
657 } // end namespace irr
\r
659 #endif // _IRR_COMPILE_WITH_GUI_
\r