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