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
7 #include "IGUISkin.h"
\r
8 #include "IGUIEnvironment.h"
\r
9 #include "IVideoDriver.h"
\r
10 #include "IGUIFont.h"
\r
19 CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent,
\r
20 s32 id, core::rect<s32> rectangle, bool noclip)
\r
21 : IGUIButton(environment, parent, id, rectangle),
\r
22 SpriteBank(0), OverrideFont(0),
\r
23 OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)),
\r
24 ClickTime(0), HoverTime(0), FocusTime(0),
\r
25 ClickShiftState(false), ClickControlState(false),
\r
26 IsPushButton(false), Pressed(false),
\r
27 UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)
\r
30 setDebugName("CGUIButton");
\r
32 setNotClipped(noclip);
\r
34 // This element can be tabbed.
\r
41 CGUIButton::~CGUIButton()
\r
44 OverrideFont->drop();
\r
51 //! Sets if the images should be scaled to fit the button
\r
52 void CGUIButton::setScaleImage(bool scaleImage)
\r
54 ScaleImage = scaleImage;
\r
58 //! Returns whether the button scale the used images
\r
59 bool CGUIButton::isScalingImage() const
\r
65 //! Sets if the button should use the skin to draw its border
\r
66 void CGUIButton::setDrawBorder(bool border)
\r
68 DrawBorder = border;
\r
72 void CGUIButton::setSpriteBank(IGUISpriteBank* sprites)
\r
80 SpriteBank = sprites;
\r
84 void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)
\r
86 ButtonSprites[(u32)state].Index = index;
\r
87 ButtonSprites[(u32)state].Color = color;
\r
88 ButtonSprites[(u32)state].Loop = loop;
\r
89 ButtonSprites[(u32)state].Scale = scale;
\r
92 //! Get the sprite-index for the given state or -1 when no sprite is set
\r
93 s32 CGUIButton::getSpriteIndex(EGUI_BUTTON_STATE state) const
\r
95 return ButtonSprites[(u32)state].Index;
\r
98 //! Get the sprite color for the given state. Color is only used when a sprite is set.
\r
99 video::SColor CGUIButton::getSpriteColor(EGUI_BUTTON_STATE state) const
\r
101 return ButtonSprites[(u32)state].Color;
\r
104 //! Returns if the sprite in the given state does loop
\r
105 bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
\r
107 return ButtonSprites[(u32)state].Loop;
\r
110 //! Returns if the sprite in the given state is scaled
\r
111 bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const
\r
113 return ButtonSprites[(u32)state].Scale;
\r
116 //! called if an event happened.
\r
117 bool CGUIButton::OnEvent(const SEvent& event)
\r
120 return IGUIElement::OnEvent(event);
\r
122 switch(event.EventType)
\r
124 case EET_KEY_INPUT_EVENT:
\r
125 if (event.KeyInput.PressedDown &&
\r
126 (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))
\r
131 setPressed(!Pressed);
\r
135 if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE)
\r
141 if (!event.KeyInput.PressedDown && Pressed &&
\r
142 (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))
\r
150 ClickShiftState = event.KeyInput.Shift;
\r
151 ClickControlState = event.KeyInput.Control;
\r
154 newEvent.EventType = EET_GUI_EVENT;
\r
155 newEvent.GUIEvent.Caller = this;
\r
156 newEvent.GUIEvent.Element = 0;
\r
157 newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
\r
158 Parent->OnEvent(newEvent);
\r
163 case EET_GUI_EVENT:
\r
164 if (event.GUIEvent.Caller == this)
\r
166 if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
\r
170 FocusTime = os::Timer::getTime();
\r
172 else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
\r
174 FocusTime = os::Timer::getTime();
\r
176 else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT)
\r
178 HoverTime = os::Timer::getTime();
\r
182 case EET_MOUSE_INPUT_EVENT:
\r
183 if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
191 if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
193 bool wasPressed = Pressed;
\r
195 if ( !AbsoluteClippingRect.isPointInside( core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y ) ) )
\r
206 setPressed(!Pressed);
\r
209 if ((!IsPushButton && wasPressed && Parent) ||
\r
210 (IsPushButton && wasPressed != Pressed))
\r
212 ClickShiftState = event.MouseInput.Shift;
\r
213 ClickControlState = event.MouseInput.Control;
\r
216 newEvent.EventType = EET_GUI_EVENT;
\r
217 newEvent.GUIEvent.Caller = this;
\r
218 newEvent.GUIEvent.Element = 0;
\r
219 newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED;
\r
220 Parent->OnEvent(newEvent);
\r
230 return Parent ? Parent->OnEvent(event) : false;
\r
234 //! draws the element and its children
\r
235 void CGUIButton::draw()
\r
240 IGUISkin* skin = Environment->getSkin();
\r
241 video::IVideoDriver* driver = Environment->getVideoDriver();
\r
247 skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect);
\r
251 skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect);
\r
256 const core::position2di buttonCenter(AbsoluteRect.getCenter());
\r
258 EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);
\r
259 if ( ButtonImages[(u32)imageState].Texture )
\r
261 core::position2d<s32> pos(buttonCenter);
\r
262 core::rect<s32> sourceRect(ButtonImages[(u32)imageState].SourceRect);
\r
263 if ( sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0 )
\r
264 sourceRect = core::rect<s32>(core::position2di(0,0), ButtonImages[(u32)imageState].Texture->getOriginalSize());
\r
266 pos.X -= sourceRect.getWidth() / 2;
\r
267 pos.Y -= sourceRect.getHeight() / 2;
\r
271 // Create a pressed-down effect by moving the image when it looks identical to the unpressed state image
\r
272 EGUI_BUTTON_IMAGE_STATE unpressedState = getImageState(false);
\r
273 if ( unpressedState == imageState || ButtonImages[(u32)imageState] == ButtonImages[(u32)unpressedState] )
\r
275 pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X);
\r
276 pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y);
\r
280 driver->draw2DImage(ButtonImages[(u32)imageState].Texture,
\r
281 ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
\r
282 sourceRect, &AbsoluteClippingRect,
\r
283 0, UseAlphaChannel);
\r
288 core::position2di pos(buttonCenter);
\r
291 pos.X += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X);
\r
292 pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y);
\r
297 // pressed / unpressed animation
\r
298 EGUI_BUTTON_STATE state = Pressed ? EGBS_BUTTON_DOWN : EGBS_BUTTON_UP;
\r
299 drawSprite(state, ClickTime, pos);
\r
301 // focused / unfocused animation
\r
302 state = Environment->hasFocus(this) ? EGBS_BUTTON_FOCUSED : EGBS_BUTTON_NOT_FOCUSED;
\r
303 drawSprite(state, FocusTime, pos);
\r
305 // mouse over / off animation
\r
306 state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;
\r
307 drawSprite(state, HoverTime, pos);
\r
312 drawSprite(EGBS_BUTTON_DISABLED, 0, pos);
\r
318 IGUIFont* font = getActiveFont();
\r
320 core::rect<s32> rect = AbsoluteRect;
\r
323 rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X);
\r
324 rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y);
\r
328 font->draw(Text.c_str(), rect,
\r
330 true, true, &AbsoluteClippingRect);
\r
333 IGUIElement::draw();
\r
336 void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center)
\r
338 u32 stateIdx = (u32)state;
\r
340 if (ButtonSprites[stateIdx].Index != -1)
\r
342 if ( ButtonSprites[stateIdx].Scale )
\r
344 const video::SColor colors[] = {ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color};
\r
345 SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect,
\r
346 &AbsoluteClippingRect, colors,
\r
347 os::Timer::getTime()-startTime, ButtonSprites[stateIdx].Loop);
\r
351 SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center,
\r
352 &AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, os::Timer::getTime(),
\r
353 ButtonSprites[stateIdx].Loop, true);
\r
358 EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const
\r
360 // figure state we should have
\r
361 EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED;
\r
362 bool focused = Environment->hasFocus(this);
\r
363 bool mouseOver = static_cast<const IGUIElement*>(Environment->getHovered()) == this; // (static cast for Borland)
\r
368 if ( focused && mouseOver )
\r
369 state = EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER;
\r
370 else if ( focused )
\r
371 state = EGBIS_IMAGE_DOWN_FOCUSED;
\r
372 else if ( mouseOver )
\r
373 state = EGBIS_IMAGE_DOWN_MOUSEOVER;
\r
375 state = EGBIS_IMAGE_DOWN;
\r
379 if ( focused && mouseOver )
\r
380 state = EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER;
\r
381 else if ( focused )
\r
382 state = EGBIS_IMAGE_UP_FOCUSED;
\r
383 else if ( mouseOver )
\r
384 state = EGBIS_IMAGE_UP_MOUSEOVER;
\r
386 state = EGBIS_IMAGE_UP;
\r
390 // find a compatible state that has images
\r
391 while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture )
\r
395 case EGBIS_IMAGE_UP_FOCUSED:
\r
396 state = EGBIS_IMAGE_UP_MOUSEOVER;
\r
398 case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER:
\r
399 state = EGBIS_IMAGE_UP_FOCUSED;
\r
401 case EGBIS_IMAGE_DOWN_MOUSEOVER:
\r
402 state = EGBIS_IMAGE_DOWN;
\r
404 case EGBIS_IMAGE_DOWN_FOCUSED:
\r
405 state = EGBIS_IMAGE_DOWN_MOUSEOVER;
\r
407 case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER:
\r
408 state = EGBIS_IMAGE_DOWN_FOCUSED;
\r
410 case EGBIS_IMAGE_DISABLED:
\r
412 state = EGBIS_IMAGE_DOWN;
\r
414 state = EGBIS_IMAGE_UP;
\r
417 state = EGBIS_IMAGE_UP;
\r
424 //! sets another skin independent font. if this is set to zero, the button uses the font of the skin.
\r
425 void CGUIButton::setOverrideFont(IGUIFont* font)
\r
427 if (OverrideFont == font)
\r
431 OverrideFont->drop();
\r
433 OverrideFont = font;
\r
436 OverrideFont->grab();
\r
439 //! Gets the override font (if any)
\r
440 IGUIFont * CGUIButton::getOverrideFont() const
\r
442 return OverrideFont;
\r
445 //! Get the font which is used right now for drawing
\r
446 IGUIFont* CGUIButton::getActiveFont() const
\r
448 if ( OverrideFont )
\r
449 return OverrideFont;
\r
450 IGUISkin* skin = Environment->getSkin();
\r
452 return skin->getFont(EGDF_BUTTON);
\r
456 //! Sets another color for the text.
\r
457 void CGUIButton::setOverrideColor(video::SColor color)
\r
459 OverrideColor = color;
\r
460 OverrideColorEnabled = true;
\r
463 video::SColor CGUIButton::getOverrideColor() const
\r
465 return OverrideColor;
\r
468 irr::video::SColor CGUIButton::getActiveColor() const
\r
470 if ( OverrideColorEnabled )
\r
471 return OverrideColor;
\r
472 IGUISkin* skin = Environment->getSkin();
\r
474 return OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT);
\r
475 return OverrideColor;
\r
478 void CGUIButton::enableOverrideColor(bool enable)
\r
480 OverrideColorEnabled = enable;
\r
483 bool CGUIButton::isOverrideColorEnabled() const
\r
485 return OverrideColorEnabled;
\r
488 void CGUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image, const core::rect<s32>& sourceRect)
\r
490 if ( state >= EGBIS_COUNT )
\r
496 u32 stateIdx = (u32)state;
\r
497 if ( ButtonImages[stateIdx].Texture )
\r
498 ButtonImages[stateIdx].Texture->drop();
\r
500 ButtonImages[stateIdx].Texture = image;
\r
501 ButtonImages[stateIdx].SourceRect = sourceRect;
\r
504 //! Sets if the button should behave like a push button. Which means it
\r
505 //! can be in two states: Normal or Pressed. With a click on the button,
\r
506 //! the user can change the state of the button.
\r
507 void CGUIButton::setIsPushButton(bool isPushButton)
\r
509 IsPushButton = isPushButton;
\r
513 //! Returns if the button is currently pressed
\r
514 bool CGUIButton::isPressed() const
\r
520 //! Sets the pressed state of the button if this is a pushbutton
\r
521 void CGUIButton::setPressed(bool pressed)
\r
523 if (Pressed != pressed)
\r
525 ClickTime = os::Timer::getTime();
\r
531 //! Returns whether the button is a push button
\r
532 bool CGUIButton::isPushButton() const
\r
534 return IsPushButton;
\r
538 //! Sets if the alpha channel should be used for drawing images on the button (default is false)
\r
539 void CGUIButton::setUseAlphaChannel(bool useAlphaChannel)
\r
541 UseAlphaChannel = useAlphaChannel;
\r
545 //! Returns if the alpha channel should be used for drawing images on the button
\r
546 bool CGUIButton::isAlphaChannelUsed() const
\r
548 return UseAlphaChannel;
\r
552 bool CGUIButton::isDrawingBorder() const
\r
558 } // end namespace gui
\r
559 } // end namespace irr
\r