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
master
cutealien 2014-04-02 21:05:39 +00:00
parent 88581a4c42
commit 5b156c1dca
7 changed files with 456 additions and 201 deletions

View File

@ -1,6 +1,7 @@
-------------------------- --------------------------
Changes in 1.9 (not yet released) 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. - Spritebanks can now draw scaled sprites and are a little easier to use.
- Improved i18n key input for X11 (languages like cyrillic work now). - 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) - Fix bug that ListBox would not allow to 'tab' to the next element (thx @ FlavourBoat for reporting)

View File

@ -20,11 +20,16 @@ namespace gui
class IGUIFont; class IGUIFont;
class IGUISpriteBank; 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 enum EGUI_BUTTON_STATE
{ {
//! The button is not pressed //! The button is not pressed.
EGBS_BUTTON_UP=0, EGBS_BUTTON_UP=0,
//! The button is currently pressed down //! The button is currently pressed down.
EGBS_BUTTON_DOWN, EGBS_BUTTON_DOWN,
//! The mouse cursor is over the button //! The mouse cursor is over the button
EGBS_BUTTON_MOUSE_OVER, EGBS_BUTTON_MOUSE_OVER,
@ -34,12 +39,14 @@ namespace gui
EGBS_BUTTON_FOCUSED, EGBS_BUTTON_FOCUSED,
//! The button doesn't have the focus //! The button doesn't have the focus
EGBS_BUTTON_NOT_FOCUSED, 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 //! not used, counts the number of enumerated items
EGBS_COUNT EGBS_COUNT
}; };
//! Names for gui button state icons //! Names for gui button state icons
const c8* const GUIButtonStateNames[] = const c8* const GUIButtonStateNames[EGBS_COUNT+1] =
{ {
"buttonUp", "buttonUp",
"buttonDown", "buttonDown",
@ -47,8 +54,52 @@ namespace gui
"buttonMouseOff", "buttonMouseOff",
"buttonFocused", "buttonFocused",
"buttonNotFocused", "buttonNotFocused",
0, "buttonDisabled",
0, 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. //! GUI Button interface.
@ -77,38 +128,71 @@ namespace gui
font of the active skin otherwise */ font of the active skin otherwise */
virtual IGUIFont* getActiveFont() const = 0; 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<s32>& sourceRect=core::rect<s32>(0,0,0,0)) = 0;
//! Sets an image which should be displayed on the button when it is in normal state. //! 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; virtual void setImage(video::ITexture* image=0) = 0;
//! Sets a background image for the button when it is in normal state. //! Sets a background image for the button when it is in normal state.
/** \param image: Texture containing the image to be displayed /** This is identical to calling setImage(EGBIS_IMAGE_UP, image, sourceRect); and might be deprecated in future revisions.
\param pos: Position in the texture, where the image is located */ \param image: Texture containing the image to be displayed
virtual void setImage(video::ITexture* image, const core::rect<s32>& pos) = 0; \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<s32>& sourceRect) = 0;
//! Sets a background image for the button when it is in pressed state. //! 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. setPressedImage(), this image is also drawn in pressed state.
\param image: Image to be displayed */ \param image: Image to be displayed */
virtual void setPressedImage(video::ITexture* image=0) = 0; virtual void setPressedImage(video::ITexture* image=0) = 0;
//! Sets an image which should be displayed on the button when it is in pressed state. //! 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 /** This is identical to calling setImage(EGBIS_IMAGE_DOWN, image, sourceRect); and might be deprecated in future revisions.
\param pos: Position in the texture, where the image is located */ \param image: Texture containing the image to be displayed
virtual void setPressedImage(video::ITexture* image, const core::rect<s32>& pos) = 0; \param sourceRect: Position in the texture, where the image is located */
virtual void setPressedImage(video::ITexture* image, const core::rect<s32>& sourceRect) = 0;
//! Sets the sprite bank used by the button //! 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; virtual void setSpriteBank(IGUISpriteBank* bank=0) = 0;
//! Sets the animated sprite for a specific button state //! 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 state: State of the button to set the sprite for
\param index: The sprite number from the current sprite bank \param index: The sprite number from the current sprite bank
\param color: The color of the sprite \param color: The color of the sprite
\param loop: True if the animation should loop, false if not \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, 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. //! 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, /** Which means it can be in two states: Normal or Pressed. With a click on the button,

View File

@ -183,14 +183,18 @@ namespace gui
EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT, EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT,
//! maximal space to reserve for messagebox text-height //! maximal space to reserve for messagebox text-height
EGDS_MESSAGE_BOX_MAX_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, 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, 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, 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, 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 //! this value is not used, it only specifies the amount of default sizes
//! available. //! available.
@ -222,6 +226,8 @@ namespace gui
"ButtonPressedImageOffsetY" "ButtonPressedImageOffsetY"
"ButtonPressedTextOffsetX", "ButtonPressedTextOffsetX",
"ButtonPressedTextOffsetY", "ButtonPressedTextOffsetY",
"ButtonPressedSpriteOffsetX",
"ButtonPressedSpriteOffsetY",
0 0
}; };

View File

@ -20,7 +20,7 @@ namespace gui
CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent, CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent,
s32 id, core::rect<s32> rectangle, bool noclip) s32 id, core::rect<s32> rectangle, bool noclip)
: IGUIButton(environment, parent, id, rectangle), : IGUIButton(environment, parent, id, rectangle),
SpriteBank(0), OverrideFont(0), Image(0), PressedImage(0), SpriteBank(0), OverrideFont(0),
ClickTime(0), HoverTime(0), FocusTime(0), ClickTime(0), HoverTime(0), FocusTime(0),
IsPushButton(false), Pressed(false), IsPushButton(false), Pressed(false),
UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)
@ -30,10 +30,6 @@ CGUIButton::CGUIButton(IGUIEnvironment* environment, IGUIElement* parent,
#endif #endif
setNotClipped(noclip); setNotClipped(noclip);
// Initialize the sprites.
for (u32 i=0; i<EGBS_COUNT; ++i)
ButtonSprites[i].Index = -1;
// This element can be tabbed. // This element can be tabbed.
setTabStop(true); setTabStop(true);
setTabOrder(-1); setTabOrder(-1);
@ -46,12 +42,6 @@ CGUIButton::~CGUIButton()
if (OverrideFont) if (OverrideFont)
OverrideFont->drop(); OverrideFont->drop();
if (Image)
Image->drop();
if (PressedImage)
PressedImage->drop();
if (SpriteBank) if (SpriteBank)
SpriteBank->drop(); 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].Index = index;
ButtonSprites[(u32)state].Color = color; ButtonSprites[(u32)state].Color = color;
ButtonSprites[(u32)state].Loop = loop; ButtonSprites[(u32)state].Loop = loop;
} ButtonSprites[(u32)state].Scale = scale;
else
{
ButtonSprites[(u32)state].Index = -1;
}
} }
//! 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. //! called if an event happened.
bool CGUIButton::OnEvent(const SEvent& event) bool CGUIButton::OnEvent(const SEvent& event)
@ -235,94 +242,77 @@ void CGUIButton::draw()
IGUISkin* skin = Environment->getSkin(); IGUISkin* skin = Environment->getSkin();
video::IVideoDriver* driver = Environment->getVideoDriver(); video::IVideoDriver* driver = Environment->getVideoDriver();
// todo: move sprite up and text down if the pressed state has a sprite if (DrawBorder)
const core::position2di spritePos = AbsoluteRect.getCenter(); {
if (!Pressed) if (!Pressed)
{ {
if (DrawBorder)
skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect); skin->draw3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect);
if (Image)
{
core::position2d<s32> 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);
}
} }
else else
{ {
if (DrawBorder)
skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect); skin->draw3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect);
video::ITexture * imgPressed = PressedImage;
core::rect<s32> 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<s32> pos = spritePos;
pos.X -= rectPressed.getWidth() / 2;
pos.Y -= rectPressed.getHeight() / 2;
if (movePos)
const core::position2di buttonCenter(AbsoluteRect.getCenter());
EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);
if ( ButtonImages[(u32)imageState].Texture )
{
core::position2d<s32> pos(buttonCenter);
core::rect<s32> sourceRect(ButtonImages[(u32)imageState].SourceRect);
if ( sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0 )
sourceRect = core::rect<s32>(core::position2di(0,0), ButtonImages[(u32)imageState].Texture->getOriginalSize());
pos.X -= sourceRect.getWidth() / 2;
pos.Y -= sourceRect.getHeight() / 2;
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.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X);
pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y); 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<s32>(pos, sourceRect.getSize()),
sourceRect, &AbsoluteClippingRect,
0, UseAlphaChannel);
} }
if (SpriteBank) if (SpriteBank)
{ {
// pressed / unpressed animation core::position2di pos(buttonCenter);
u32 state = Pressed ? (u32)EGBS_BUTTON_DOWN : (u32)EGBS_BUTTON_UP; if ( Pressed )
if (ButtonSprites[state].Index != -1)
{ {
SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos, IGUISkin* skin = Environment->getSkin();
&AbsoluteClippingRect, ButtonSprites[state].Color, ClickTime, os::Timer::getTime(), pos.X += skin->getSize(EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X);
ButtonSprites[state].Loop, true); 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()) if (isEnabled())
{ {
state = Environment->getHovered() == this ? (u32)EGBS_BUTTON_MOUSE_OVER : (u32)EGBS_BUTTON_MOUSE_OFF; // pressed / unpressed animation
if (ButtonSprites[state].Index != -1) EGUI_BUTTON_STATE state = Pressed ? EGBS_BUTTON_DOWN : EGBS_BUTTON_UP;
{ drawSprite(state, ClickTime, pos);
SpriteBank->draw2DSprite(ButtonSprites[state].Index, spritePos,
&AbsoluteClippingRect, ButtonSprites[state].Color, HoverTime, os::Timer::getTime(), // focused / unfocused animation
ButtonSprites[state].Loop, true); 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(); 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. //! sets another skin independent font. if this is set to zero, the button uses the font of the skin.
void CGUIButton::setOverrideFont(IGUIFont* font) void CGUIButton::setOverrideFont(IGUIFont* font)
@ -379,51 +456,22 @@ IGUIFont* CGUIButton::getActiveFont() const
return 0; return 0;
} }
//! Sets an image which should be displayed on the button when it is in normal state. void CGUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image, const core::rect<s32>& sourceRect)
void CGUIButton::setImage(video::ITexture* image)
{ {
if (image) if ( state >= EGBIS_COUNT )
image->grab(); return;
if (Image)
Image->drop();
Image = image; if ( image )
if (image)
ImageRect = core::rect<s32>(core::position2d<s32>(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<s32>& 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)
image->grab(); image->grab();
if (PressedImage) u32 stateIdx = (u32)state;
PressedImage->drop(); if ( ButtonImages[stateIdx].Texture )
ButtonImages[stateIdx].Texture->drop();
PressedImage = image; ButtonImages[stateIdx].Texture = image;
if (image) ButtonImages[stateIdx].SourceRect = sourceRect;
PressedImageRect = core::rect<s32>(core::position2d<s32>(0,0), image->getOriginalSize());
} }
//! 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<s32>& pos)
{
setPressedImage(image);
PressedImageRect = pos;
}
//! Sets if the button should behave like a push button. Which means it //! 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, //! can be in two states: Normal or Pressed. With a click on the button,
//! the user can change the state of the button. //! the user can change the state of the button.
@ -491,14 +539,42 @@ void CGUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWri
if (IsPushButton) if (IsPushButton)
out->addBool("Pressed", Pressed); out->addBool("Pressed", Pressed);
out->addTexture ("Image", Image); for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i )
out->addRect ("ImageRect", ImageRect); {
out->addTexture ("PressedImage", PressedImage); if ( ButtonImages[i].Texture )
out->addRect ("PressedImageRect", PressedImageRect); {
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 ("UseAlphaChannel", UseAlphaChannel);
out->addBool ("Border", isDrawingBorder()); out->addBool ("Border", DrawBorder);
out->addBool ("ScaleImage", isScalingImage()); 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); // out->addString ("OverrideFont", OverrideFont);
} }
@ -509,24 +585,41 @@ void CGUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWr
{ {
IGUIButton::deserializeAttributes(in,options); IGUIButton::deserializeAttributes(in,options);
IsPushButton = in->getAttributeAsBool("PushButton"); IsPushButton = in->getAttributeAsBool("PushButton", IsPushButton);
Pressed = IsPushButton ? in->getAttributeAsBool("Pressed") : false; Pressed = IsPushButton ? in->getAttributeAsBool("Pressed", Pressed) : false;
core::rect<s32> rec = in->getAttributeAsRect("ImageRect"); for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i )
if (rec.isValid()) {
setImage( in->getAttributeAsTexture("Image"), rec); core::stringc nameRect( GUIButtonImageStateNames[i] );
else nameRect += "Rect";
setImage( in->getAttributeAsTexture("Image") );
rec = in->getAttributeAsRect("PressedImageRect"); setImage((EGUI_BUTTON_IMAGE_STATE)i,
if (rec.isValid()) in->getAttributeAsTexture(GUIButtonImageStateNames[i], ButtonImages[i].Texture),
setPressedImage( in->getAttributeAsTexture("PressedImage"), rec); in->getAttributeAsRect(nameRect.c_str(), ButtonImages[i].SourceRect) );
else }
setPressedImage( in->getAttributeAsTexture("PressedImage") );
setDrawBorder(in->getAttributeAsBool("Border")); setDrawBorder(in->getAttributeAsBool("Border", DrawBorder));
setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel")); setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel", UseAlphaChannel));
setScaleImage(in->getAttributeAsBool("ScaleImage")); 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")); // setOverrideFont(in->getAttributeAsString("OverrideFont"));

View File

@ -10,6 +10,7 @@
#include "IGUIButton.h" #include "IGUIButton.h"
#include "IGUISpriteBank.h" #include "IGUISpriteBank.h"
#include "ITexture.h"
#include "SColor.h" #include "SColor.h"
namespace irr namespace irr
@ -43,17 +44,32 @@ namespace gui
//! Get the font which is used right now for drawing //! Get the font which is used right now for drawing
virtual IGUIFont* getActiveFont() const _IRR_OVERRIDE_; virtual IGUIFont* getActiveFont() const _IRR_OVERRIDE_;
//! Sets an image which should be displayed on the button when it is in normal state. //! Sets an image which should be displayed on the button when it is in the given state.
virtual void setImage(video::ITexture* image=0) _IRR_OVERRIDE_; virtual void setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image=0, const core::rect<s32>& sourceRect=core::rect<s32>(0,0,0,0)) _IRR_OVERRIDE_;
//! Sets an image which should be displayed on the button when it is in normal state. //! 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<s32>& 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<s32>& 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. //! 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. //! 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<s32>& pos) _IRR_OVERRIDE_; virtual void setPressedImage(video::ITexture* image, const core::rect<s32>& pos) _IRR_OVERRIDE_
{
setImage(EGBIS_IMAGE_DOWN, image, pos);
}
//! Sets the sprite bank used by the button //! Sets the sprite bank used by the button
virtual void setSpriteBank(IGUISpriteBank* bank=0) _IRR_OVERRIDE_; virtual void setSpriteBank(IGUISpriteBank* bank=0) _IRR_OVERRIDE_;
@ -65,7 +81,20 @@ namespace gui
\param color: The color of the sprite \param color: The color of the sprite
*/ */
virtual void setSprite(EGUI_BUTTON_STATE state, s32 index, 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 //! 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, //! 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 //! Reads attributes of the element
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) _IRR_OVERRIDE_; 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: private:
struct ButtonSprite 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; s32 Index;
video::SColor Color; video::SColor Color;
bool Loop; bool Loop;
bool Scale;
}; };
ButtonSprite ButtonSprites[EGBS_COUNT]; ButtonSprite ButtonSprites[EGBS_COUNT];
IGUISpriteBank* SpriteBank; IGUISpriteBank* SpriteBank;
struct ButtonImage
{
ButtonImage() : Texture(0), SourceRect(core::rect<s32>(0,0,0,0))
{
}
ButtonImage(const ButtonImage& other) : Texture(0), SourceRect(core::rect<s32>(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<s32> SourceRect;
};
ButtonImage ButtonImages[EGBIS_COUNT];
IGUIFont* OverrideFont; IGUIFont* OverrideFont;
video::ITexture* Image;
video::ITexture* PressedImage;
core::rect<s32> ImageRect;
core::rect<s32> PressedImageRect;
u32 ClickTime, HoverTime, FocusTime; u32 ClickTime, HoverTime, FocusTime;
bool IsPushButton; bool IsPushButton;

View File

@ -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_IMAGE_OFFSET_Y] = 1;
Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0; Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0;
Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y] = 2; 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_OK] = L"OK";
Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel"; Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel";
@ -970,9 +972,7 @@ void CGUISkin::draw2DRectangle(IGUIElement* element,
} }
//! Writes attributes of the object. //! Writes attributes of the skin
//! Implement this to expose the attributes of your scene node animator for
//! scripting languages, editors, debuggers or xml serialization purposes.
void CGUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const void CGUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{ {
u32 i; u32 i;
@ -990,25 +990,21 @@ void CGUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWrite
} }
//! Reads attributes of the object. //! Reads attributes of the skikn
//! Implement this to set the attributes of your scene node animator for
//! scripting languages, editors, debuggers or xml deserialization purposes.
void CGUISkin::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) 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; u32 i;
for (i=0; i<EGDC_COUNT; ++i) for (i=0; i<EGDC_COUNT; ++i)
Colors[i] = in->getAttributeAsColor(GUISkinColorNames[i]); Colors[i] = in->getAttributeAsColor(GUISkinColorNames[i], Colors[i]);
for (i=0; i<EGDS_COUNT; ++i) for (i=0; i<EGDS_COUNT; ++i)
Sizes[i] = in->getAttributeAsInt(GUISkinSizeNames[i]); Sizes[i] = in->getAttributeAsInt(GUISkinSizeNames[i], Sizes[i]);
for (i=0; i<EGDT_COUNT; ++i) for (i=0; i<EGDT_COUNT; ++i)
Texts[i] = in->getAttributeAsStringW(GUISkinTextNames[i]); Texts[i] = in->getAttributeAsStringW(GUISkinTextNames[i], Texts[i]);
for (i=0; i<EGDI_COUNT; ++i) for (i=0; i<EGDI_COUNT; ++i)
Icons[i] = in->getAttributeAsInt(GUISkinIconNames[i]); Icons[i] = in->getAttributeAsInt(GUISkinIconNames[i], Icons[i]);
} }

View File

@ -216,14 +216,10 @@ namespace gui
//! get the type of this skin //! get the type of this skin
virtual EGUI_SKIN_TYPE getType() const _IRR_OVERRIDE_; virtual EGUI_SKIN_TYPE getType() const _IRR_OVERRIDE_;
//! Writes attributes of the object. //! Writes attributes of the skin
//! Implement this to expose the attributes of your scene node animator for
//! scripting languages, editors, debuggers or xml serialization purposes.
virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const _IRR_OVERRIDE_; virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const _IRR_OVERRIDE_;
//! Reads attributes of the object. //! Reads attributes of the skin
//! Implement this to set the attributes of your scene node animator for
//! scripting languages, editors, debuggers or xml deserialization purposes.
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) _IRR_OVERRIDE_; virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) _IRR_OVERRIDE_;
private: private: