]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CGUIButton.cpp
704fd3d2c52b878d592ee311edd53cd5b5052f8d
[irrlicht.git] / source / Irrlicht / CGUIButton.cpp
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
4 \r
5 #include "CGUIButton.h"\r
6 #ifdef _IRR_COMPILE_WITH_GUI_\r
7 \r
8 #include "IGUISkin.h"\r
9 #include "IGUIEnvironment.h"\r
10 #include "IVideoDriver.h"\r
11 #include "IGUIFont.h"\r
12 #include "os.h"\r
13 \r
14 namespace irr\r
15 {\r
16 namespace gui\r
17 {\r
18 \r
19 //! constructor\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
29 {\r
30         #ifdef _DEBUG\r
31         setDebugName("CGUIButton");\r
32         #endif\r
33         setNotClipped(noclip);\r
34 \r
35         // This element can be tabbed.\r
36         setTabStop(true);\r
37         setTabOrder(-1);\r
38 }\r
39 \r
40 \r
41 //! destructor\r
42 CGUIButton::~CGUIButton()\r
43 {\r
44         if (OverrideFont)\r
45                 OverrideFont->drop();\r
46 \r
47         if (SpriteBank)\r
48                 SpriteBank->drop();\r
49 }\r
50 \r
51 \r
52 //! Sets if the images should be scaled to fit the button\r
53 void CGUIButton::setScaleImage(bool scaleImage)\r
54 {\r
55         ScaleImage = scaleImage;\r
56 }\r
57 \r
58 \r
59 //! Returns whether the button scale the used images\r
60 bool CGUIButton::isScalingImage() const\r
61 {\r
62         return ScaleImage;\r
63 }\r
64 \r
65 \r
66 //! Sets if the button should use the skin to draw its border\r
67 void CGUIButton::setDrawBorder(bool border)\r
68 {\r
69         DrawBorder = border;\r
70 }\r
71 \r
72 \r
73 void CGUIButton::setSpriteBank(IGUISpriteBank* sprites)\r
74 {\r
75         if (sprites)\r
76                 sprites->grab();\r
77 \r
78         if (SpriteBank)\r
79                 SpriteBank->drop();\r
80 \r
81         SpriteBank = sprites;\r
82 }\r
83 \r
84 \r
85 void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)\r
86 {\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
91 }\r
92 \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
95 {\r
96         return ButtonSprites[(u32)state].Index;\r
97 }\r
98 \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
101 {\r
102         return ButtonSprites[(u32)state].Color;\r
103 }\r
104 \r
105 //! Returns if the sprite in the given state does loop\r
106 bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const\r
107 {\r
108         return ButtonSprites[(u32)state].Loop;\r
109 }\r
110 \r
111 //! Returns if the sprite in the given state is scaled\r
112 bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const\r
113 {\r
114         return ButtonSprites[(u32)state].Scale;\r
115 }\r
116 \r
117 //! called if an event happened.\r
118 bool CGUIButton::OnEvent(const SEvent& event)\r
119 {\r
120         if (!isEnabled())\r
121                 return IGUIElement::OnEvent(event);\r
122 \r
123         switch(event.EventType)\r
124         {\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
128                 {\r
129                         if (!IsPushButton)\r
130                                 setPressed(true);\r
131                         else\r
132                                 setPressed(!Pressed);\r
133 \r
134                         return true;\r
135                 }\r
136                 if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE)\r
137                 {\r
138                         setPressed(false);\r
139                         return true;\r
140                 }\r
141                 else\r
142                 if (!event.KeyInput.PressedDown && Pressed &&\r
143                         (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE))\r
144                 {\r
145 \r
146                         if (!IsPushButton)\r
147                                 setPressed(false);\r
148 \r
149                         if (Parent)\r
150                         {\r
151                                 ClickShiftState = event.KeyInput.Shift;\r
152                                 ClickControlState = event.KeyInput.Control;\r
153 \r
154                                 SEvent newEvent;\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
160                         }\r
161                         return true;\r
162                 }\r
163                 break;\r
164         case EET_GUI_EVENT:\r
165                 if (event.GUIEvent.Caller == this)\r
166                 {\r
167                         if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)\r
168                         {\r
169                                 if (!IsPushButton)\r
170                                         setPressed(false);\r
171                                 FocusTime = os::Timer::getTime();\r
172                         }\r
173                         else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)\r
174                         {\r
175                                 FocusTime = os::Timer::getTime();\r
176                         }\r
177                         else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT)\r
178                         {\r
179                                 HoverTime = os::Timer::getTime();\r
180                         }\r
181                 }\r
182                 break;\r
183         case EET_MOUSE_INPUT_EVENT:\r
184                 if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
185                 {\r
186                         if (!IsPushButton)\r
187                                 setPressed(true);\r
188 \r
189                         return true;\r
190                 }\r
191                 else\r
192                 if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
193                 {\r
194                         bool wasPressed = Pressed;\r
195 \r
196                         if ( !AbsoluteClippingRect.isPointInside( core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y ) ) )\r
197                         {\r
198                                 if (!IsPushButton)\r
199                                         setPressed(false);\r
200                                 return true;\r
201                         }\r
202 \r
203                         if (!IsPushButton)\r
204                                 setPressed(false);\r
205                         else\r
206                         {\r
207                                 setPressed(!Pressed);\r
208                         }\r
209 \r
210                         if ((!IsPushButton && wasPressed && Parent) ||\r
211                                 (IsPushButton && wasPressed != Pressed))\r
212                         {\r
213                                 ClickShiftState = event.MouseInput.Shift;\r
214                                 ClickControlState = event.MouseInput.Control;\r
215 \r
216                                 SEvent newEvent;\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
222                         }\r
223 \r
224                         return true;\r
225                 }\r
226                 break;\r
227         default:\r
228                 break;\r
229         }\r
230 \r
231         return Parent ? Parent->OnEvent(event) : false;\r
232 }\r
233 \r
234 \r
235 //! draws the element and its children\r
236 void CGUIButton::draw()\r
237 {\r
238         if (!IsVisible)\r
239                 return;\r
240 \r
241         IGUISkin* skin = Environment->getSkin();\r
242         video::IVideoDriver* driver = Environment->getVideoDriver();\r
243 \r
244         if (DrawBorder)\r
245         {\r
246                 if (!Pressed)\r
247                 {\r
248                         skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect);\r
249                 }\r
250                 else\r
251                 {\r
252                         skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect);\r
253                 }\r
254         }\r
255 \r
256 \r
257         const core::position2di buttonCenter(AbsoluteRect.getCenter());\r
258 \r
259         EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);\r
260         if ( ButtonImages[(u32)imageState].Texture )\r
261         {\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
266 \r
267                 pos.X -= sourceRect.getWidth() / 2;\r
268                 pos.Y -= sourceRect.getHeight() / 2;\r
269 \r
270                 if ( Pressed )\r
271                 {\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
275                         {\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
278                         }\r
279                 }\r
280 \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
285         }\r
286 \r
287         if (SpriteBank)\r
288         {\r
289                 core::position2di pos(buttonCenter);\r
290                 if ( Pressed )\r
291                 {\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
294                 }\r
295 \r
296                 if (isEnabled())\r
297                 {\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
301 \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
305 \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
309                 }\r
310                 else\r
311                 {\r
312                         // draw disabled\r
313                         drawSprite(EGBS_BUTTON_DISABLED, 0, pos);\r
314                 }\r
315         }\r
316 \r
317         if (Text.size())\r
318         {\r
319                 IGUIFont* font = getActiveFont();\r
320 \r
321                 core::rect<s32> rect = AbsoluteRect;\r
322                 if (Pressed)\r
323                 {\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
326                 }\r
327 \r
328                 if (font)\r
329                         font->draw(Text.c_str(), rect,\r
330                                 getActiveColor(),\r
331                                 true, true, &AbsoluteClippingRect);\r
332         }\r
333 \r
334         IGUIElement::draw();\r
335 }\r
336 \r
337 void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center)\r
338 {\r
339         u32 stateIdx = (u32)state;\r
340 \r
341         if (ButtonSprites[stateIdx].Index != -1)\r
342         {\r
343                 if ( ButtonSprites[stateIdx].Scale )\r
344                 {\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
349                 }\r
350                 else\r
351                 {\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
355                 }\r
356         }\r
357 }\r
358 \r
359 EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const\r
360 {\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
365         if (isEnabled())\r
366         {\r
367                 if ( pressed )\r
368                 {\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
375                         else\r
376                                 state = EGBIS_IMAGE_DOWN;\r
377                 }\r
378                 else // !pressed\r
379                 {\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
386                         else\r
387                                 state = EGBIS_IMAGE_UP;\r
388                 }\r
389         }\r
390 \r
391         // find a compatible state that has images\r
392         while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture )\r
393         {\r
394                 switch ( state )\r
395                 {\r
396                         case EGBIS_IMAGE_UP_FOCUSED:\r
397                                 state = EGBIS_IMAGE_UP_MOUSEOVER;\r
398                                 break;\r
399                         case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER:\r
400                                 state = EGBIS_IMAGE_UP_FOCUSED;\r
401                                 break;\r
402                         case EGBIS_IMAGE_DOWN_MOUSEOVER:\r
403                                 state = EGBIS_IMAGE_DOWN;\r
404                                 break;\r
405                         case EGBIS_IMAGE_DOWN_FOCUSED:\r
406                                 state = EGBIS_IMAGE_DOWN_MOUSEOVER;\r
407                                 break;\r
408                         case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER:\r
409                                 state = EGBIS_IMAGE_DOWN_FOCUSED;\r
410                                 break;\r
411                         case EGBIS_IMAGE_DISABLED:\r
412                                 if ( pressed )\r
413                                         state = EGBIS_IMAGE_DOWN;\r
414                                 else\r
415                                         state = EGBIS_IMAGE_UP;\r
416                                 break;\r
417                         default:\r
418                                 state = EGBIS_IMAGE_UP;\r
419                 }\r
420         }\r
421 \r
422         return state;\r
423 }\r
424 \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
427 {\r
428         if (OverrideFont == font)\r
429                 return;\r
430 \r
431         if (OverrideFont)\r
432                 OverrideFont->drop();\r
433 \r
434         OverrideFont = font;\r
435 \r
436         if (OverrideFont)\r
437                 OverrideFont->grab();\r
438 }\r
439 \r
440 //! Gets the override font (if any)\r
441 IGUIFont * CGUIButton::getOverrideFont() const\r
442 {\r
443         return OverrideFont;\r
444 }\r
445 \r
446 //! Get the font which is used right now for drawing\r
447 IGUIFont* CGUIButton::getActiveFont() const\r
448 {\r
449         if ( OverrideFont )\r
450                 return OverrideFont;\r
451         IGUISkin* skin = Environment->getSkin();\r
452         if (skin)\r
453                 return skin->getFont(EGDF_BUTTON);\r
454         return 0;\r
455 }\r
456 \r
457 //! Sets another color for the text.\r
458 void CGUIButton::setOverrideColor(video::SColor color)\r
459 {\r
460         OverrideColor = color;\r
461         OverrideColorEnabled = true;\r
462 }\r
463 \r
464 video::SColor CGUIButton::getOverrideColor() const\r
465 {\r
466         return OverrideColor;\r
467 }\r
468 \r
469 irr::video::SColor CGUIButton::getActiveColor() const\r
470 {\r
471         if ( OverrideColorEnabled )\r
472                 return OverrideColor;\r
473         IGUISkin* skin = Environment->getSkin();\r
474         if (skin)\r
475                 return OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT);\r
476         return OverrideColor;\r
477 }\r
478 \r
479 void CGUIButton::enableOverrideColor(bool enable)\r
480 {\r
481         OverrideColorEnabled = enable;\r
482 }\r
483 \r
484 bool CGUIButton::isOverrideColorEnabled() const\r
485 {\r
486         return OverrideColorEnabled;\r
487 }\r
488 \r
489 void CGUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image, const core::rect<s32>& sourceRect)\r
490 {\r
491         if ( state >= EGBIS_COUNT )\r
492                 return;\r
493 \r
494         if ( image )\r
495                 image->grab();\r
496 \r
497         u32 stateIdx = (u32)state;\r
498         if ( ButtonImages[stateIdx].Texture )\r
499                 ButtonImages[stateIdx].Texture->drop();\r
500 \r
501         ButtonImages[stateIdx].Texture = image;\r
502         ButtonImages[stateIdx].SourceRect = sourceRect;\r
503 }\r
504 \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
509 {\r
510         IsPushButton = isPushButton;\r
511 }\r
512 \r
513 \r
514 //! Returns if the button is currently pressed\r
515 bool CGUIButton::isPressed() const\r
516 {\r
517         return Pressed;\r
518 }\r
519 \r
520 \r
521 //! Sets the pressed state of the button if this is a pushbutton\r
522 void CGUIButton::setPressed(bool pressed)\r
523 {\r
524         if (Pressed != pressed)\r
525         {\r
526                 ClickTime = os::Timer::getTime();\r
527                 Pressed = pressed;\r
528         }\r
529 }\r
530 \r
531 \r
532 //! Returns whether the button is a push button\r
533 bool CGUIButton::isPushButton() const\r
534 {\r
535         return IsPushButton;\r
536 }\r
537 \r
538 \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
541 {\r
542         UseAlphaChannel = useAlphaChannel;\r
543 }\r
544 \r
545 \r
546 //! Returns if the alpha channel should be used for drawing images on the button\r
547 bool CGUIButton::isAlphaChannelUsed() const\r
548 {\r
549         return UseAlphaChannel;\r
550 }\r
551 \r
552 \r
553 bool CGUIButton::isDrawingBorder() const\r
554 {\r
555         return DrawBorder;\r
556 }\r
557 \r
558 \r
559 } // end namespace gui\r
560 } // end namespace irr\r
561 \r
562 #endif // _IRR_COMPILE_WITH_GUI_\r
563 \r