// Copyright (C) 2002-2012 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CGUIButton.h" #ifdef _IRR_COMPILE_WITH_GUI_ #include "IGUISkin.h" #include "IGUIEnvironment.h" #include "IVideoDriver.h" #include "IGUIFont.h" #include "os.h" namespace irr { namespace gui { //! constructor CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect rectangle, bool noclip) : IGUIButton(environment, parent, id, rectangle), SpriteBank(0), OverrideFont(0), Image(0), PressedImage(0), ClickTime(0), HoverTime(0), FocusTime(0), IsPushButton(false), Pressed(false), UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) { #ifdef _DEBUG setDebugName("CGUIButton"); #endif setNotClipped(noclip); // Initialize the sprites. for (u32 i=0; idrop(); if (Image) Image->drop(); if (PressedImage) PressedImage->drop(); if (SpriteBank) SpriteBank->drop(); } //! Sets if the images should be scaled to fit the button void CGUIButton::setScaleImage(bool scaleImage) { ScaleImage = scaleImage; } //! Returns whether the button scale the used images bool CGUIButton::isScalingImage() const { _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return ScaleImage; } //! Sets if the button should use the skin to draw its border void CGUIButton::setDrawBorder(bool border) { DrawBorder = border; } void CGUIButton::setSpriteBank(IGUISpriteBank* sprites) { if (sprites) sprites->grab(); if (SpriteBank) SpriteBank->drop(); SpriteBank = sprites; } void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop) { if (SpriteBank) { ButtonSprites[(u32)state].Index = index; ButtonSprites[(u32)state].Color = color; ButtonSprites[(u32)state].Loop = loop; } else { ButtonSprites[(u32)state].Index = -1; } } //! called if an event happened. bool CGUIButton::OnEvent(const SEvent& event) { if (!isEnabled()) return IGUIElement::OnEvent(event); switch(event.EventType) { case EET_KEY_INPUT_EVENT: if (event.KeyInput.PressedDown && (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) { if (!IsPushButton) setPressed(true); else setPressed(!Pressed); return true; } if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE) { setPressed(false); return true; } else if (!event.KeyInput.PressedDown && Pressed && (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) { if (!IsPushButton) setPressed(false); if (Parent) { SEvent newEvent; newEvent.EventType = EET_GUI_EVENT; newEvent.GUIEvent.Caller = this; newEvent.GUIEvent.Element = 0; newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED; Parent->OnEvent(newEvent); } return true; } break; case EET_GUI_EVENT: if (event.GUIEvent.Caller == this) { if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) { if (!IsPushButton) setPressed(false); FocusTime = os::Timer::getTime(); } else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED) { FocusTime = os::Timer::getTime(); } else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT) { HoverTime = os::Timer::getTime(); } } break; case EET_MOUSE_INPUT_EVENT: if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { if (Environment->hasFocus(this) && !AbsoluteClippingRect.isPointInside(core::position2d(event.MouseInput.X, event.MouseInput.Y))) { Environment->removeFocus(this); return false; } if (!IsPushButton) setPressed(true); Environment->setFocus(this); return true; } else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { bool wasPressed = Pressed; if ( !AbsoluteClippingRect.isPointInside( core::position2d(event.MouseInput.X, event.MouseInput.Y ) ) ) { if (!IsPushButton) setPressed(false); return true; } if (!IsPushButton) setPressed(false); else { setPressed(!Pressed); } if ((!IsPushButton && wasPressed && Parent) || (IsPushButton && wasPressed != Pressed)) { SEvent newEvent; newEvent.EventType = EET_GUI_EVENT; newEvent.GUIEvent.Caller = this; newEvent.GUIEvent.Element = 0; newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED; Parent->OnEvent(newEvent); } return true; } break; default: break; } return Parent ? Parent->OnEvent(event) : false; } //! draws the element and its children void CGUIButton::draw() { if (!IsVisible) return; IGUISkin* skin = Environment->getSkin(); video::IVideoDriver* driver = Environment->getVideoDriver(); // todo: move sprite up and text down if the pressed state has a sprite const core::position2di spritePos = AbsoluteRect.getCenter(); if (!Pressed) { if (DrawBorder) skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect); if (Image) { core::position2d pos = spritePos; pos.X -= ImageRect.getWidth() / 2; pos.Y -= ImageRect.getHeight() / 2; driver->draw2DImage(Image, ScaleImage? AbsoluteRect : core::recti(pos, ImageRect.getSize()), ImageRect, &AbsoluteClippingRect, 0, UseAlphaChannel); } } else { if (DrawBorder) skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect); if (PressedImage) { core::position2d pos = spritePos; pos.X -= PressedImageRect.getWidth() / 2; pos.Y -= PressedImageRect.getHeight() / 2; if (Image == PressedImage && PressedImageRect == ImageRect) { pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X); pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y); } driver->draw2DImage(PressedImage, ScaleImage? AbsoluteRect : core::recti(pos, PressedImageRect.getSize()), PressedImageRect, &AbsoluteClippingRect, 0, UseAlphaChannel); } } if (SpriteBank) { // pressed / unpressed animation u32 state = Pressed ? (u32)EGBS_BUTTON_DOWN : (u32)EGBS_BUTTON_UP; if (ButtonSprites[state].Index != -1) { SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos, &AbsoluteClippingRect, ButtonSprites[state].Color, ClickTime, os::Timer::getTime(), ButtonSprites[state].Loop, true); } // focused / unfocused animation state = Environment->hasFocus(this) ? (u32)EGBS_BUTTON_FOCUSED : (u32)EGBS_BUTTON_NOT_FOCUSED; if (ButtonSprites[state].Index != -1) { SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos, &AbsoluteClippingRect, ButtonSprites[state].Color, FocusTime, os::Timer::getTime(), ButtonSprites[state].Loop, true); } // mouse over / off animation if (isEnabled()) { state = Environment->getHovered() == this ? (u32)EGBS_BUTTON_MOUSE_OVER : (u32)EGBS_BUTTON_MOUSE_OFF; if (ButtonSprites[state].Index != -1) { SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos, &AbsoluteClippingRect, ButtonSprites[state].Color, HoverTime, os::Timer::getTime(), ButtonSprites[state].Loop, true); } } } if (Text.size()) { IGUIFont* font = getActiveFont(); core::rect rect = AbsoluteRect; if (Pressed) { rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X); rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y); } if (font) font->draw(Text.c_str(), rect, skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), true, true, &AbsoluteClippingRect); } IGUIElement::draw(); } //! sets another skin independent font. if this is set to zero, the button uses the font of the skin. void CGUIButton::setOverrideFont(IGUIFont* font) { if (OverrideFont == font) return; if (OverrideFont) OverrideFont->drop(); OverrideFont = font; if (OverrideFont) OverrideFont->grab(); } //! Gets the override font (if any) IGUIFont * CGUIButton::getOverrideFont() const { return OverrideFont; } //! Get the font which is used right now for drawing IGUIFont* CGUIButton::getActiveFont() const { if ( OverrideFont ) return OverrideFont; IGUISkin* skin = Environment->getSkin(); if (skin) return skin->getFont(EGDF_BUTTON); return 0; } //! Sets an image which should be displayed on the button when it is in normal state. void CGUIButton::setImage(video::ITexture* image) { if (image) image->grab(); if (Image) Image->drop(); Image = image; if (image) ImageRect = core::rect(core::position2d(0,0), image->getOriginalSize()); if (!PressedImage) setPressedImage(Image); } //! Sets the image which should be displayed on the button when it is in its normal state. void CGUIButton::setImage(video::ITexture* image, const core::rect& pos) { setImage(image); ImageRect = pos; } //! Sets an image which should be displayed on the button when it is in pressed state. void CGUIButton::setPressedImage(video::ITexture* image) { if (image) image->grab(); if (PressedImage) PressedImage->drop(); PressedImage = image; if (image) PressedImageRect = core::rect(core::position2d(0,0), image->getOriginalSize()); } //! Sets the image which should be displayed on the button when it is in its pressed state. void CGUIButton::setPressedImage(video::ITexture* image, const core::rect& pos) { setPressedImage(image); PressedImageRect = pos; } //! Sets if the button should behave like a push button. Which means it //! can be in two states: Normal or Pressed. With a click on the button, //! the user can change the state of the button. void CGUIButton::setIsPushButton(bool isPushButton) { IsPushButton = isPushButton; } //! Returns if the button is currently pressed bool CGUIButton::isPressed() const { _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return Pressed; } //! Sets the pressed state of the button if this is a pushbutton void CGUIButton::setPressed(bool pressed) { if (Pressed != pressed) { ClickTime = os::Timer::getTime(); Pressed = pressed; } } //! Returns whether the button is a push button bool CGUIButton::isPushButton() const { _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return IsPushButton; } //! Sets if the alpha channel should be used for drawing images on the button (default is false) void CGUIButton::setUseAlphaChannel(bool useAlphaChannel) { UseAlphaChannel = useAlphaChannel; } //! Returns if the alpha channel should be used for drawing images on the button bool CGUIButton::isAlphaChannelUsed() const { _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return UseAlphaChannel; } bool CGUIButton::isDrawingBorder() const { _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; return DrawBorder; } //! Writes attributes of the element. void CGUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const { IGUIButton::serializeAttributes(out,options); out->addBool ("PushButton", IsPushButton ); if (IsPushButton) out->addBool("Pressed", Pressed); out->addTexture ("Image", Image); out->addRect ("ImageRect", ImageRect); out->addTexture ("PressedImage", PressedImage); out->addRect ("PressedImageRect", PressedImageRect); out->addBool ("UseAlphaChannel", isAlphaChannelUsed()); out->addBool ("Border", isDrawingBorder()); out->addBool ("ScaleImage", isScalingImage()); // out->addString ("OverrideFont", OverrideFont); } //! Reads attributes of the element void CGUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) { IGUIButton::deserializeAttributes(in,options); IsPushButton = in->getAttributeAsBool("PushButton"); Pressed = IsPushButton ? in->getAttributeAsBool("Pressed") : false; core::rect rec = in->getAttributeAsRect("ImageRect"); if (rec.isValid()) setImage( in->getAttributeAsTexture("Image"), rec); else setImage( in->getAttributeAsTexture("Image") ); rec = in->getAttributeAsRect("PressedImageRect"); if (rec.isValid()) setPressedImage( in->getAttributeAsTexture("PressedImage"), rec); else setPressedImage( in->getAttributeAsTexture("PressedImage") ); setDrawBorder(in->getAttributeAsBool("Border")); setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel")); setScaleImage(in->getAttributeAsBool("ScaleImage")); // setOverrideFont(in->getAttributeAsString("OverrideFont")); updateAbsolutePosition(); } } // end namespace gui } // end namespace irr #endif // _IRR_COMPILE_WITH_GUI_