From 5b156c1dca6663fcb7983b660beb927e1bc2defc Mon Sep 17 00:00:00 2001 From: cutealien Date: Wed, 2 Apr 2014 21:05:39 +0000 Subject: [PATCH] Buttons can now now have 7 more image-states, 1 more sprite-state and the sprites are now scaleable. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@4756 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 1 + include/IGUIButton.h | 116 ++++++++-- include/IGUISkin.h | 14 +- source/Irrlicht/CGUIButton.cpp | 393 ++++++++++++++++++++------------- source/Irrlicht/CGUIButton.h | 105 +++++++-- source/Irrlicht/CGUISkin.cpp | 20 +- source/Irrlicht/CGUISkin.h | 8 +- 7 files changed, 456 insertions(+), 201 deletions(-) diff --git a/changes.txt b/changes.txt index be7bc926..3786b464 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,7 @@ -------------------------- Changes in 1.9 (not yet released) +- Buttons can now now have 7 more image-states, 1 more sprite-state and the sprites are now scaleable. - Spritebanks can now draw scaled sprites and are a little easier to use. - Improved i18n key input for X11 (languages like cyrillic work now). - Fix bug that ListBox would not allow to 'tab' to the next element (thx @ FlavourBoat for reporting) diff --git a/include/IGUIButton.h b/include/IGUIButton.h index 1da880e9..d90d8c0d 100644 --- a/include/IGUIButton.h +++ b/include/IGUIButton.h @@ -20,11 +20,16 @@ namespace gui class IGUIFont; class IGUISpriteBank; + //! Current state of buttons used for drawing sprites. + //! Note that up to 3 states can be active at the same time: + //! EGBS_BUTTON_UP or EGBS_BUTTON_DOWN + //! EGBS_BUTTON_MOUSE_OVER or EGBS_BUTTON_MOUSE_OFF + //! EGBS_BUTTON_FOCUSED or EGBS_BUTTON_NOT_FOCUSED enum EGUI_BUTTON_STATE { - //! The button is not pressed + //! The button is not pressed. EGBS_BUTTON_UP=0, - //! The button is currently pressed down + //! The button is currently pressed down. EGBS_BUTTON_DOWN, //! The mouse cursor is over the button EGBS_BUTTON_MOUSE_OVER, @@ -34,12 +39,14 @@ namespace gui EGBS_BUTTON_FOCUSED, //! The button doesn't have the focus EGBS_BUTTON_NOT_FOCUSED, + //! The button is disabled All other states are ignored in that case. + EGBS_BUTTON_DISABLED, //! not used, counts the number of enumerated items EGBS_COUNT }; //! Names for gui button state icons - const c8* const GUIButtonStateNames[] = + const c8* const GUIButtonStateNames[EGBS_COUNT+1] = { "buttonUp", "buttonDown", @@ -47,8 +54,52 @@ namespace gui "buttonMouseOff", "buttonFocused", "buttonNotFocused", - 0, - 0, + "buttonDisabled", + 0 // count + }; + + //! State of buttons used for drawing texture images. + //! Note that only a single state is active at a time + //! Also when no image is defined for a state it will use images from another state + //! and if that state is not set from the replacement for that,etc. + //! So in many cases setting EGBIS_IMAGE_UP and EGBIS_IMAGE_DOWN is sufficient. + enum EGUI_BUTTON_IMAGE_STATE + { + //! When no other states have images they will all use this one. + EGBIS_IMAGE_UP, + //! When not set EGBIS_IMAGE_UP is used. + EGBIS_IMAGE_UP_MOUSEOVER, + //! When not set EGBIS_IMAGE_UP_MOUSEOVER is used. + EGBIS_IMAGE_UP_FOCUSED, + //! When not set EGBIS_IMAGE_UP_FOCUSED is used. + EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, + //! When not set EGBIS_IMAGE_UP is used. + EGBIS_IMAGE_DOWN, + //! When not set EGBIS_IMAGE_DOWN is used. + EGBIS_IMAGE_DOWN_MOUSEOVER, + //! When not set EGBIS_IMAGE_DOWN_MOUSEOVER is used. + EGBIS_IMAGE_DOWN_FOCUSED, + //! When not set EGBIS_IMAGE_DOWN_FOCUSED is used. + EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER, + //! When not set EGBIS_IMAGE_UP or EGBIS_IMAGE_DOWN are used (depending on button state). + EGBIS_IMAGE_DISABLED, + //! not used, counts the number of enumerated items + EGBIS_COUNT + }; + + //! Names for gui button image states + const c8* const GUIButtonImageStateNames[EGBIS_COUNT+1] = + { + "Image", // not "ImageUp" as it otherwise breaks serialization of old files + "ImageUpOver", + "ImageUpFocused", + "ImageUpFocusedOver", + "PressedImage", // not "ImageDown" as it otherwise breaks serialization of old files + "ImageDownOver", + "ImageDownFocused", + "ImageDownFocusedOver", + "ImageDisabled", + 0 // count }; //! GUI Button interface. @@ -77,38 +128,71 @@ namespace gui font of the active skin otherwise */ virtual IGUIFont* getActiveFont() const = 0; + //! Sets an image which should be displayed on the button when it is in the given state. + /** Only one image-state can be active at a time. Images are drawn below sprites. + If a state is without image it will try to use images from other states as described + in ::EGUI_BUTTON_IMAGE_STATE. + Images are a little less flexible than sprites, but easier to use. + \param state: One of ::EGUI_BUTTON_IMAGE_STATE + \param image: Image to be displayed or NULL to remove the image + \param sourceRect: Source rectangle on the image texture. When width or height are 0 then the full texture-size is used (default). */ + virtual void setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image=0, const core::rect& sourceRect=core::rect(0,0,0,0)) = 0; + //! Sets an image which should be displayed on the button when it is in normal state. - /** \param image: Image to be displayed */ + /** This is identical to calling setImage(EGBIS_IMAGE_UP, image); and might be deprecated in future revisions. + \param image: Image to be displayed */ virtual void setImage(video::ITexture* image=0) = 0; //! Sets a background image for the button when it is in normal state. - /** \param image: Texture containing the image to be displayed - \param pos: Position in the texture, where the image is located */ - virtual void setImage(video::ITexture* image, const core::rect& pos) = 0; + /** This is identical to calling setImage(EGBIS_IMAGE_UP, image, sourceRect); and might be deprecated in future revisions. + \param image: Texture containing the image to be displayed + \param sourceRect: Position in the texture, where the image is located. + When width or height are 0 then the full texture-size is used */ + virtual void setImage(video::ITexture* image, const core::rect& sourceRect) = 0; //! Sets a background image for the button when it is in pressed state. - /** If no images is specified for the pressed state via + /** This is identical to calling setImage(EGBIS_IMAGE_DOWN, image); and might be deprecated in future revisions. + If no images is specified for the pressed state via setPressedImage(), this image is also drawn in pressed state. \param image: Image to be displayed */ virtual void setPressedImage(video::ITexture* image=0) = 0; //! Sets an image which should be displayed on the button when it is in pressed state. - /** \param image: Texture containing the image to be displayed - \param pos: Position in the texture, where the image is located */ - virtual void setPressedImage(video::ITexture* image, const core::rect& pos) = 0; + /** This is identical to calling setImage(EGBIS_IMAGE_DOWN, image, sourceRect); and might be deprecated in future revisions. + \param image: Texture containing the image to be displayed + \param sourceRect: Position in the texture, where the image is located */ + virtual void setPressedImage(video::ITexture* image, const core::rect& sourceRect) = 0; + //! Sets the sprite bank used by the button + /** NOTE: The spritebank itself is _not_ serialized so far. The sprites are serialized. + Which means after loading the gui you still have to set the spritebank manually. */ virtual void setSpriteBank(IGUISpriteBank* bank=0) = 0; //! Sets the animated sprite for a specific button state - /** \param index: Number of the sprite within the sprite bank, use -1 for no sprite + /** Several sprites can be drawn at the same time. + Sprites can be animated. + Sprites are drawn above the images. + \param index: Number of the sprite within the sprite bank, use -1 for no sprite \param state: State of the button to set the sprite for \param index: The sprite number from the current sprite bank \param color: The color of the sprite \param loop: True if the animation should loop, false if not - */ + \param scale: True if the sprite should scale to button size, false if not */ virtual void setSprite(EGUI_BUTTON_STATE state, s32 index, - video::SColor color=video::SColor(255,255,255,255), bool loop=false) = 0; + video::SColor color=video::SColor(255,255,255,255), bool loop=false, bool scale=false) = 0; + + //! Get the sprite-index for the given state or -1 when no sprite is set + virtual s32 getSpriteIndex(EGUI_BUTTON_STATE state) const = 0; + + //! Get the sprite color for the given state. Color is only used when a sprite is set. + virtual video::SColor getSpriteColor(EGUI_BUTTON_STATE state) const = 0; + + //! Returns if the sprite in the given state does loop + virtual bool getSpriteLoop(EGUI_BUTTON_STATE state) const = 0; + + //! Returns if the sprite in the given state is scaled + virtual bool getSpriteScale(EGUI_BUTTON_STATE state) const = 0; //! 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, diff --git a/include/IGUISkin.h b/include/IGUISkin.h index d3bf337d..62e0cf53 100644 --- a/include/IGUISkin.h +++ b/include/IGUISkin.h @@ -183,14 +183,18 @@ namespace gui EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT, //! maximal space to reserve for messagebox text-height EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT, - //! pixels to move the button image to the right when a pushbutton is pressed + //! pixels to move an unscaled button image to the right when a button is pressed and the unpressed image looks identical EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X, - //! pixels to move the button image down when a pushbutton is pressed + //! pixels to move an unscaled button image down when a button is pressed and the unpressed image looks identical EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y, - //! pixels to move the button text to the right when a pushbutton is pressed + //! pixels to move the button text to the right when a button is pressed EGDS_BUTTON_PRESSED_TEXT_OFFSET_X, - //! pixels to move the button text down when a pushbutton is pressed + //! pixels to move the button text down when a button is pressed EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y, + //! pixels to move an unscaled button sprite to the right when a button is pressed + EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X, + //! pixels to move an unscaled button sprite down when a button is pressed + EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y, //! this value is not used, it only specifies the amount of default sizes //! available. @@ -222,6 +226,8 @@ namespace gui "ButtonPressedImageOffsetY" "ButtonPressedTextOffsetX", "ButtonPressedTextOffsetY", + "ButtonPressedSpriteOffsetX", + "ButtonPressedSpriteOffsetY", 0 }; diff --git a/source/Irrlicht/CGUIButton.cpp b/source/Irrlicht/CGUIButton.cpp index 187e8ae1..1bcb3fd5 100644 --- a/source/Irrlicht/CGUIButton.cpp +++ b/source/Irrlicht/CGUIButton.cpp @@ -20,7 +20,7 @@ namespace gui 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), + SpriteBank(0), OverrideFont(0), ClickTime(0), HoverTime(0), FocusTime(0), IsPushButton(false), Pressed(false), UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) @@ -30,10 +30,6 @@ CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent, #endif setNotClipped(noclip); - // Initialize the sprites. - for (u32 i=0; idrop(); - if (Image) - Image->drop(); - - if (PressedImage) - PressedImage->drop(); - if (SpriteBank) SpriteBank->drop(); } @@ -91,20 +81,37 @@ void CGUIButton::setSpriteBank(IGUISpriteBank* sprites) } -void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop) +void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale) { - if (SpriteBank) - { - ButtonSprites[(u32)state].Index = index; - ButtonSprites[(u32)state].Color = color; - ButtonSprites[(u32)state].Loop = loop; - } - else - { - ButtonSprites[(u32)state].Index = -1; - } + ButtonSprites[(u32)state].Index = index; + ButtonSprites[(u32)state].Color = color; + ButtonSprites[(u32)state].Loop = loop; + ButtonSprites[(u32)state].Scale = scale; } +//! Get the sprite-index for the given state or -1 when no sprite is set +s32 CGUIButton::getSpriteIndex(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Index; +} + +//! Get the sprite color for the given state. Color is only used when a sprite is set. +video::SColor CGUIButton::getSpriteColor(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Color; +} + +//! Returns if the sprite in the given state does loop +bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Loop; +} + +//! Returns if the sprite in the given state is scaled +bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Scale; +} //! called if an event happened. bool CGUIButton::OnEvent(const SEvent& event) @@ -235,94 +242,77 @@ void CGUIButton::draw() 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) { - if (DrawBorder) - skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect); - - if (Image) + if (!Pressed) { - 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); + skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect); + } + else + { + skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect); } } - else + + + const core::position2di buttonCenter(AbsoluteRect.getCenter()); + + EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed); + if ( ButtonImages[(u32)imageState].Texture ) { - if (DrawBorder) - skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect); + core::position2d pos(buttonCenter); + core::rect sourceRect(ButtonImages[(u32)imageState].SourceRect); + if ( sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0 ) + sourceRect = core::rect(core::position2di(0,0), ButtonImages[(u32)imageState].Texture->getOriginalSize()); - video::ITexture * imgPressed = PressedImage; - core::rect rectPressed(PressedImageRect); - bool movePos = false; // pressed-down effect by moving the image - if (!imgPressed && Image) - { - imgPressed = Image; - rectPressed = ImageRect; - movePos = true; - } - else if (Image == PressedImage && PressedImageRect == ImageRect) - { - movePos = true; - } - if (imgPressed) - { - core::position2d pos = spritePos; - pos.X -= rectPressed.getWidth() / 2; - pos.Y -= rectPressed.getHeight() / 2; + pos.X -= sourceRect.getWidth() / 2; + pos.Y -= sourceRect.getHeight() / 2; - if (movePos) + if ( Pressed ) + { + // Create a pressed-down effect by moving the image when it looks identical to the unpressed state image + EGUI_BUTTON_IMAGE_STATE unpressedState = getImageState(false); + if ( unpressedState == imageState || ButtonImages[(u32)imageState] == ButtonImages[(u32)unpressedState] ) { pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X); pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y); } - driver->draw2DImage(imgPressed, - ScaleImage? AbsoluteRect : - core::recti(pos, rectPressed.getSize()), - rectPressed, &AbsoluteClippingRect, - 0, UseAlphaChannel); } + + driver->draw2DImage(ButtonImages[(u32)imageState].Texture, + ScaleImage? AbsoluteRect : core::rect(pos, sourceRect.getSize()), + sourceRect, &AbsoluteClippingRect, + 0, UseAlphaChannel); } if (SpriteBank) { - // pressed / unpressed animation - u32 state = Pressed ? (u32)EGBS_BUTTON_DOWN : (u32)EGBS_BUTTON_UP; - if (ButtonSprites[state].Index != -1) + core::position2di pos(buttonCenter); + if ( Pressed ) { - SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos, - &AbsoluteClippingRect, ButtonSprites[state].Color, ClickTime, os::Timer::getTime(), - ButtonSprites[state].Loop, true); + IGUISkin* skin = Environment->getSkin(); + pos.X += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X); + pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y); } - // 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); - } + // pressed / unpressed animation + EGUI_BUTTON_STATE state = Pressed ? EGBS_BUTTON_DOWN : EGBS_BUTTON_UP; + drawSprite(state, ClickTime, pos); + + // focused / unfocused animation + state = Environment->hasFocus(this) ? EGBS_BUTTON_FOCUSED : EGBS_BUTTON_NOT_FOCUSED; + drawSprite(state, FocusTime, pos); + + // mouse over / off animation + state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF; + drawSprite(state, HoverTime, pos); + } + else + { + // draw disabled + drawSprite(EGBS_BUTTON_DISABLED, 0, pos); } } @@ -346,6 +336,93 @@ void CGUIButton::draw() IGUIElement::draw(); } +void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center) +{ + u32 stateIdx = (u32)state; + + if (ButtonSprites[stateIdx].Index != -1) + { + if ( ButtonSprites[stateIdx].Scale ) + { + const video::SColor colors[] = {ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color}; + SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect, + &AbsoluteClippingRect, colors, + os::Timer::getTime()-startTime, ButtonSprites[stateIdx].Loop); + } + else + { + SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center, + &AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, os::Timer::getTime(), + ButtonSprites[stateIdx].Loop, true); + } + } +} + +EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const +{ + // figure state we should have + EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED; + bool focused = Environment->hasFocus(this); + bool mouseOver = Environment->getHovered() == this; + if (isEnabled()) + { + if ( pressed ) + { + if ( focused && mouseOver ) + state = EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER; + else if ( focused ) + state = EGBIS_IMAGE_DOWN_FOCUSED; + else if ( mouseOver ) + state = EGBIS_IMAGE_DOWN_MOUSEOVER; + else + state = EGBIS_IMAGE_DOWN; + } + else // !pressed + { + if ( focused && mouseOver ) + state = EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER; + else if ( focused ) + state = EGBIS_IMAGE_UP_FOCUSED; + else if ( mouseOver ) + state = EGBIS_IMAGE_UP_MOUSEOVER; + else + state = EGBIS_IMAGE_UP; + } + } + + // find a compatible state that has images + while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture ) + { + switch ( state ) + { + case EGBIS_IMAGE_UP_FOCUSED: + state = EGBIS_IMAGE_UP_MOUSEOVER; + break; + case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER: + state = EGBIS_IMAGE_UP_FOCUSED; + break; + case EGBIS_IMAGE_DOWN_MOUSEOVER: + state = EGBIS_IMAGE_DOWN; + break; + case EGBIS_IMAGE_DOWN_FOCUSED: + state = EGBIS_IMAGE_DOWN_MOUSEOVER; + break; + case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER: + state = EGBIS_IMAGE_DOWN_FOCUSED; + break; + case EGBIS_IMAGE_DISABLED: + if ( pressed ) + state = EGBIS_IMAGE_DOWN; + else + state = EGBIS_IMAGE_UP; + break; + default: + state = EGBIS_IMAGE_UP; + } + } + + return state; +} //! sets another skin independent font. if this is set to zero, the button uses the font of the skin. void CGUIButton::setOverrideFont(IGUIFont* font) @@ -379,51 +456,22 @@ IGUIFont* CGUIButton::getActiveFont() const return 0; } -//! Sets an image which should be displayed on the button when it is in normal state. -void CGUIButton::setImage(video::ITexture* image) +void CGUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image, const core::rect& sourceRect) { - if (image) - image->grab(); - if (Image) - Image->drop(); + if ( state >= EGBIS_COUNT ) + return; - Image = image; - if (image) - ImageRect = core::rect(core::position2d(0,0), image->getOriginalSize()); -} - - -//! 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) + if ( image ) image->grab(); - if (PressedImage) - PressedImage->drop(); + u32 stateIdx = (u32)state; + if ( ButtonImages[stateIdx].Texture ) + ButtonImages[stateIdx].Texture->drop(); - PressedImage = image; - if (image) - PressedImageRect = core::rect(core::position2d(0,0), image->getOriginalSize()); + ButtonImages[stateIdx].Texture = image; + ButtonImages[stateIdx].SourceRect = sourceRect; } - -//! 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. @@ -489,16 +537,44 @@ void CGUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWri out->addBool ("PushButton", IsPushButton ); if (IsPushButton) - out->addBool("Pressed", Pressed); + out->addBool("Pressed", Pressed); - out->addTexture ("Image", Image); - out->addRect ("ImageRect", ImageRect); - out->addTexture ("PressedImage", PressedImage); - out->addRect ("PressedImageRect", PressedImageRect); + for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i ) + { + if ( ButtonImages[i].Texture ) + { + core::stringc name( GUIButtonImageStateNames[i] ); + out->addTexture(name.c_str(), ButtonImages[i].Texture); + name += "Rect"; + out->addRect(name.c_str(), ButtonImages[i].SourceRect); + } + } - out->addBool ("UseAlphaChannel", isAlphaChannelUsed()); - out->addBool ("Border", isDrawingBorder()); - out->addBool ("ScaleImage", isScalingImage()); + out->addBool ("UseAlphaChannel", UseAlphaChannel); + out->addBool ("Border", DrawBorder); + out->addBool ("ScaleImage", ScaleImage); + + for ( u32 i=0; i<(u32)EGBS_COUNT; ++i ) + { + if ( ButtonSprites[i].Index >= 0 ) + { + core::stringc nameIndex( GUIButtonStateNames[i] ); + nameIndex += "Index"; + out->addInt(nameIndex.c_str(), ButtonSprites[i].Index ); + + core::stringc nameColor( GUIButtonStateNames[i] ); + nameColor += "Color"; + out->addColor(nameColor.c_str(), ButtonSprites[i].Color ); + + core::stringc nameLoop( GUIButtonStateNames[i] ); + nameLoop += "Loop"; + out->addBool(nameLoop.c_str(), ButtonSprites[i].Loop ); + + core::stringc nameScale( GUIButtonStateNames[i] ); + nameScale += "Scale"; + out->addBool(nameScale.c_str(), ButtonSprites[i].Scale ); + } + } // out->addString ("OverrideFont", OverrideFont); } @@ -509,24 +585,41 @@ void CGUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWr { IGUIButton::deserializeAttributes(in,options); - IsPushButton = in->getAttributeAsBool("PushButton"); - Pressed = IsPushButton ? in->getAttributeAsBool("Pressed") : false; + IsPushButton = in->getAttributeAsBool("PushButton", IsPushButton); + Pressed = IsPushButton ? in->getAttributeAsBool("Pressed", Pressed) : false; - core::rect rec = in->getAttributeAsRect("ImageRect"); - if (rec.isValid()) - setImage( in->getAttributeAsTexture("Image"), rec); - else - setImage( in->getAttributeAsTexture("Image") ); + for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i ) + { + core::stringc nameRect( GUIButtonImageStateNames[i] ); + nameRect += "Rect"; - rec = in->getAttributeAsRect("PressedImageRect"); - if (rec.isValid()) - setPressedImage( in->getAttributeAsTexture("PressedImage"), rec); - else - setPressedImage( in->getAttributeAsTexture("PressedImage") ); + setImage((EGUI_BUTTON_IMAGE_STATE)i, + in->getAttributeAsTexture(GUIButtonImageStateNames[i], ButtonImages[i].Texture), + in->getAttributeAsRect(nameRect.c_str(), ButtonImages[i].SourceRect) ); + } - setDrawBorder(in->getAttributeAsBool("Border")); - setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel")); - setScaleImage(in->getAttributeAsBool("ScaleImage")); + setDrawBorder(in->getAttributeAsBool("Border", DrawBorder)); + setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel", UseAlphaChannel)); + setScaleImage(in->getAttributeAsBool("ScaleImage", ScaleImage)); + + for ( u32 i=0; i<(u32)EGBS_COUNT; ++i ) + { + core::stringc nameIndex( GUIButtonStateNames[i] ); + nameIndex += "Index"; + ButtonSprites[i].Index = in->getAttributeAsInt(nameIndex.c_str(), ButtonSprites[i].Index ); + + core::stringc nameColor( GUIButtonStateNames[i] ); + nameColor += "Color"; + ButtonSprites[i].Color = in->getAttributeAsColor(nameColor.c_str(), ButtonSprites[i].Color ); + + core::stringc nameLoop( GUIButtonStateNames[i] ); + nameLoop += "Loop"; + ButtonSprites[i].Loop = in->getAttributeAsBool(nameLoop.c_str(), ButtonSprites[i].Loop ); + + core::stringc nameScale( GUIButtonStateNames[i] ); + nameScale += "Scale"; + ButtonSprites[i].Scale = in->getAttributeAsBool(nameScale.c_str(), ButtonSprites[i].Scale ); + } // setOverrideFont(in->getAttributeAsString("OverrideFont")); diff --git a/source/Irrlicht/CGUIButton.h b/source/Irrlicht/CGUIButton.h index 63ba8e60..42b0de7a 100644 --- a/source/Irrlicht/CGUIButton.h +++ b/source/Irrlicht/CGUIButton.h @@ -10,6 +10,7 @@ #include "IGUIButton.h" #include "IGUISpriteBank.h" +#include "ITexture.h" #include "SColor.h" namespace irr @@ -43,17 +44,32 @@ namespace gui //! Get the font which is used right now for drawing virtual IGUIFont* getActiveFont() const _IRR_OVERRIDE_; - //! Sets an image which should be displayed on the button when it is in normal state. - virtual void setImage(video::ITexture* image=0) _IRR_OVERRIDE_; + //! Sets an image which should be displayed on the button when it is in the given state. + virtual void setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image=0, const core::rect& sourceRect=core::rect(0,0,0,0)) _IRR_OVERRIDE_; //! Sets an image which should be displayed on the button when it is in normal state. - virtual void setImage(video::ITexture* image, const core::rect& pos) _IRR_OVERRIDE_; + virtual void setImage(video::ITexture* image=0) _IRR_OVERRIDE_ + { + setImage(EGBIS_IMAGE_UP, image); + } + + //! Sets an image which should be displayed on the button when it is in normal state. + virtual void setImage(video::ITexture* image, const core::rect& pos) _IRR_OVERRIDE_ + { + setImage(EGBIS_IMAGE_UP, image, pos); + } //! Sets an image which should be displayed on the button when it is in pressed state. - virtual void setPressedImage(video::ITexture* image=0) _IRR_OVERRIDE_; + virtual void setPressedImage(video::ITexture* image=0) _IRR_OVERRIDE_ + { + setImage(EGBIS_IMAGE_DOWN, image); + } //! Sets an image which should be displayed on the button when it is in pressed state. - virtual void setPressedImage(video::ITexture* image, const core::rect& pos) _IRR_OVERRIDE_; + virtual void setPressedImage(video::ITexture* image, const core::rect& pos) _IRR_OVERRIDE_ + { + setImage(EGBIS_IMAGE_DOWN, image, pos); + } //! Sets the sprite bank used by the button virtual void setSpriteBank(IGUISpriteBank* bank=0) _IRR_OVERRIDE_; @@ -65,7 +81,20 @@ namespace gui \param color: The color of the sprite */ virtual void setSprite(EGUI_BUTTON_STATE state, s32 index, - video::SColor color=video::SColor(255,255,255,255), bool loop=false) _IRR_OVERRIDE_; + video::SColor color=video::SColor(255,255,255,255), + bool loop=false, bool scale=false) _IRR_OVERRIDE_; + + //! Get the sprite-index for the given state or -1 when no sprite is set + virtual s32 getSpriteIndex(EGUI_BUTTON_STATE state) const _IRR_OVERRIDE_; + + //! Get the sprite color for the given state. Color is only used when a sprite is set. + virtual video::SColor getSpriteColor(EGUI_BUTTON_STATE state) const _IRR_OVERRIDE_; + + //! Returns if the sprite in the given state does loop + virtual bool getSpriteLoop(EGUI_BUTTON_STATE state) const _IRR_OVERRIDE_; + + //! Returns if the sprite in the given state is scaled + virtual bool getSpriteScale(EGUI_BUTTON_STATE state) const _IRR_OVERRIDE_; //! 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, @@ -105,26 +134,76 @@ namespace gui //! Reads attributes of the element virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) _IRR_OVERRIDE_; + protected: + void drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center); + EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const; + private: struct ButtonSprite { + ButtonSprite() : Index(-1), Loop(false), Scale(false) + { + } + + bool operator==(const ButtonSprite& other) const + { + return Index == other.Index && Color == other.Color && Loop == other.Loop && Scale == other.Scale; + } + s32 Index; video::SColor Color; bool Loop; + bool Scale; }; ButtonSprite ButtonSprites[EGBS_COUNT]; - IGUISpriteBank* SpriteBank; + + struct ButtonImage + { + ButtonImage() : Texture(0), SourceRect(core::rect(0,0,0,0)) + { + } + + ButtonImage(const ButtonImage& other) : Texture(0), SourceRect(core::rect(0,0,0,0)) + { + *this = other; + } + + ~ButtonImage() + { + if ( Texture ) + Texture->drop(); + } + + ButtonImage& operator=(const ButtonImage& other) + { + if ( this == &other ) + return *this; + + if (other.Texture) + other.Texture->grab(); + if ( Texture ) + Texture->drop(); + Texture = other.Texture; + SourceRect = other.SourceRect; + } + + bool operator==(const ButtonImage& other) const + { + return Texture == other.Texture && SourceRect == other.SourceRect; + } + + + video::ITexture* Texture; + core::rect SourceRect; + }; + + ButtonImage ButtonImages[EGBIS_COUNT]; + IGUIFont* OverrideFont; - video::ITexture* Image; - video::ITexture* PressedImage; - - core::rect ImageRect; - core::rect PressedImageRect; - u32 ClickTime, HoverTime, FocusTime; bool IsPushButton; diff --git a/source/Irrlicht/CGUISkin.cpp b/source/Irrlicht/CGUISkin.cpp index b0b2b39c..30573b93 100644 --- a/source/Irrlicht/CGUISkin.cpp +++ b/source/Irrlicht/CGUISkin.cpp @@ -123,6 +123,8 @@ CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y] = 1; Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0; Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y] = 2; + Sizes[EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X] = 0; + Sizes[EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y] = 0; Texts[EGDT_MSG_BOX_OK] = L"OK"; Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel"; @@ -970,9 +972,7 @@ void CGUISkin::draw2DRectangle(IGUIElement* element, } -//! Writes attributes of the object. -//! Implement this to expose the attributes of your scene node animator for -//! scripting languages, editors, debuggers or xml serialization purposes. +//! Writes attributes of the skin void CGUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const { u32 i; @@ -990,25 +990,21 @@ void CGUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWrite } -//! Reads attributes of the object. -//! Implement this to set the attributes of your scene node animator for -//! scripting languages, editors, debuggers or xml deserialization purposes. +//! Reads attributes of the skikn void CGUISkin::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) { - // TODO: This is not nice code for downward compatibility, whenever new values are added and users - // load an old skin the corresponding values will be set to 0. u32 i; for (i=0; igetAttributeAsColor(GUISkinColorNames[i]); + Colors[i] = in->getAttributeAsColor(GUISkinColorNames[i], Colors[i]); for (i=0; igetAttributeAsInt(GUISkinSizeNames[i]); + Sizes[i] = in->getAttributeAsInt(GUISkinSizeNames[i], Sizes[i]); for (i=0; igetAttributeAsStringW(GUISkinTextNames[i]); + Texts[i] = in->getAttributeAsStringW(GUISkinTextNames[i], Texts[i]); for (i=0; igetAttributeAsInt(GUISkinIconNames[i]); + Icons[i] = in->getAttributeAsInt(GUISkinIconNames[i], Icons[i]); } diff --git a/source/Irrlicht/CGUISkin.h b/source/Irrlicht/CGUISkin.h index db17016e..eb58e6be 100644 --- a/source/Irrlicht/CGUISkin.h +++ b/source/Irrlicht/CGUISkin.h @@ -216,14 +216,10 @@ namespace gui //! get the type of this skin virtual EGUI_SKIN_TYPE getType() const _IRR_OVERRIDE_; - //! Writes attributes of the object. - //! Implement this to expose the attributes of your scene node animator for - //! scripting languages, editors, debuggers or xml serialization purposes. + //! Writes attributes of the skin virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const _IRR_OVERRIDE_; - //! Reads attributes of the object. - //! Implement this to set the attributes of your scene node animator for - //! scripting languages, editors, debuggers or xml deserialization purposes. + //! Reads attributes of the skin virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) _IRR_OVERRIDE_; private: