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