#include "guiButton.h"\r
\r
\r
+#include "client/guiscalingfilter.h"\r
+#include "client/tile.h"\r
#include "IGUISkin.h"\r
#include "IGUIEnvironment.h"\r
#include "IVideoDriver.h"\r
#include "IGUIFont.h"\r
+#include "irrlicht_changes/static_text.h"\r
#include "porting.h"\r
+#include "StyleSpec.h"\r
+#include "util/numeric.h"\r
\r
using namespace irr;\r
using namespace gui;\r
\r
+// Multiply with a color to get the default corresponding hovered color\r
+#define COLOR_HOVERED_MOD 1.25f\r
+\r
+// Multiply with a color to get the default corresponding pressed color\r
+#define COLOR_PRESSED_MOD 0.85f\r
+\r
//! constructor\r
GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent,\r
- s32 id, core::rect<s32> rectangle, bool noclip)\r
+ s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc,\r
+ bool noclip)\r
: IGUIButton(environment, parent, id, rectangle),\r
SpriteBank(0), OverrideFont(0),\r
OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)),\r
ClickTime(0), HoverTime(0), FocusTime(0),\r
ClickShiftState(false), ClickControlState(false),\r
IsPushButton(false), Pressed(false),\r
- UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)\r
+ UseAlphaChannel(false), DrawBorder(true), ScaleImage(false), TSrc(tsrc)\r
{\r
setNotClipped(noclip);\r
\r
for (size_t i = 0; i < 4; i++) {\r
Colors[i] = Environment->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);\r
}\r
+ StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id);\r
+ StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);\r
// END PATCH\r
}\r
\r
case EET_MOUSE_INPUT_EVENT:\r
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
{\r
- if (!IsPushButton)\r
+ // Sometimes formspec elements can receive mouse events when the\r
+ // mouse is outside of the formspec. Thus, we test the position here.\r
+ if ( !IsPushButton && AbsoluteClippingRect.isPointInside(\r
+ core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y ))) {\r
setPressed(true);\r
+ }\r
\r
return true;\r
}\r
return;\r
\r
// PATCH\r
+ // Track hovered state, if it has changed then we need to update the style.\r
+ bool hovered = isHovered();\r
+ if (hovered != WasHovered) {\r
+ WasHovered = hovered;\r
+ setFromState();\r
+ }\r
+\r
GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());\r
video::IVideoDriver* driver = Environment->getVideoDriver();\r
// END PATCH\r
if (!Pressed)\r
{\r
// PATCH\r
- skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect, Colors);\r
+ skin->drawColored3DButtonPaneStandard(this, AbsoluteRect,\r
+ &AbsoluteClippingRect, Colors);\r
// END PATCH\r
}\r
else\r
{\r
// PATCH\r
- skin->drawColored3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect, Colors);\r
+ skin->drawColored3DButtonPanePressed(this, AbsoluteRect,\r
+ &AbsoluteClippingRect, Colors);\r
// END PATCH\r
}\r
}\r
\r
const core::position2di buttonCenter(AbsoluteRect.getCenter());\r
- EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);\r
+ // PATCH\r
+ // The image changes based on the state, so we use the default every time.\r
+ EGUI_BUTTON_IMAGE_STATE imageState = EGBIS_IMAGE_UP;\r
+ // END PATCH\r
if ( ButtonImages[(u32)imageState].Texture )\r
{\r
core::position2d<s32> pos(buttonCenter);\r
}\r
}\r
\r
- driver->draw2DImage(ButtonImages[(u32)imageState].Texture,\r
- ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),\r
- sourceRect, &AbsoluteClippingRect,\r
- 0, UseAlphaChannel);\r
+ // PATCH\r
+ video::ITexture* texture = ButtonImages[(u32)imageState].Texture;\r
+ if (BgMiddle.getArea() == 0) {\r
+ driver->draw2DImage(texture,\r
+ ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),\r
+ sourceRect, &AbsoluteClippingRect,\r
+ 0, UseAlphaChannel);\r
+ } else {\r
+ core::rect<s32> middle = BgMiddle;\r
+ // `-x` is interpreted as `w - x`\r
+ if (middle.LowerRightCorner.X < 0)\r
+ middle.LowerRightCorner.X += texture->getOriginalSize().Width;\r
+ if (middle.LowerRightCorner.Y < 0)\r
+ middle.LowerRightCorner.Y += texture->getOriginalSize().Height;\r
+ draw2DImage9Slice(driver, texture,\r
+ ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),\r
+ middle, &AbsoluteClippingRect);\r
+ }\r
+ // END PATCH\r
}\r
\r
if (SpriteBank)\r
drawSprite(state, FocusTime, pos);\r
\r
// mouse over / off animation\r
- state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;\r
+ state = isHovered() ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;\r
drawSprite(state, HoverTime, pos);\r
}\r
else\r
}\r
}\r
\r
- if (Text.size())\r
- {\r
- IGUIFont* font = getActiveFont();\r
-\r
- core::rect<s32> rect = AbsoluteRect;\r
- if (Pressed)\r
- {\r
- rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X);\r
- rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y);\r
- }\r
-\r
- if (font)\r
- font->draw(Text.c_str(), rect,\r
- OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),\r
- true, true, &AbsoluteClippingRect);\r
- }\r
-\r
IGUIElement::draw();\r
}\r
\r
}\r
\r
EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed) const\r
+{\r
+ // PATCH\r
+ return getImageState(pressed, ButtonImages);\r
+ // END PATCH\r
+}\r
+\r
+EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed, const ButtonImage* images) const\r
{\r
// figure state we should have\r
EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED;\r
bool focused = Environment->hasFocus((IGUIElement*)this);\r
- bool mouseOver = static_cast<const IGUIElement*>(Environment->getHovered()) == this; // (static cast for Borland)\r
+ bool mouseOver = isHovered();\r
if (isEnabled())\r
{\r
if ( pressed )\r
}\r
\r
// find a compatible state that has images\r
- while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture )\r
+ while ( state != EGBIS_IMAGE_UP && !images[(u32)state].Texture )\r
{\r
+ // PATCH\r
switch ( state )\r
{\r
case EGBIS_IMAGE_UP_FOCUSED:\r
- state = EGBIS_IMAGE_UP_MOUSEOVER;\r
+ state = EGBIS_IMAGE_UP;\r
break;\r
case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER:\r
state = EGBIS_IMAGE_UP_FOCUSED;\r
state = EGBIS_IMAGE_DOWN;\r
break;\r
case EGBIS_IMAGE_DOWN_FOCUSED:\r
- state = EGBIS_IMAGE_DOWN_MOUSEOVER;\r
+ state = EGBIS_IMAGE_DOWN;\r
break;\r
case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER:\r
state = EGBIS_IMAGE_DOWN_FOCUSED;\r
default:\r
state = EGBIS_IMAGE_UP;\r
}\r
+ // END PATCH\r
}\r
\r
return state;\r
\r
if (OverrideFont)\r
OverrideFont->grab();\r
+\r
+ StaticText->setOverrideFont(font);\r
}\r
\r
//! Gets the override font (if any)\r
{\r
OverrideColor = color;\r
OverrideColorEnabled = true;\r
+\r
+ StaticText->setOverrideColor(color);\r
}\r
\r
video::SColor GUIButton::getOverrideColor() const\r
ButtonImages[stateIdx].SourceRect = sourceRect;\r
}\r
\r
+// PATCH\r
+void GUIButton::setImage(video::ITexture* image)\r
+{\r
+ setImage(gui::EGBIS_IMAGE_UP, image);\r
+}\r
+\r
+void GUIButton::setImage(video::ITexture* image, const core::rect<s32>& pos)\r
+{\r
+ setImage(gui::EGBIS_IMAGE_UP, image, pos);\r
+}\r
+\r
+void GUIButton::setPressedImage(video::ITexture* image)\r
+{\r
+ setImage(gui::EGBIS_IMAGE_DOWN, image);\r
+}\r
+\r
+void GUIButton::setPressedImage(video::ITexture* image, const core::rect<s32>& pos)\r
+{\r
+ setImage(gui::EGBIS_IMAGE_DOWN, image, pos);\r
+}\r
+\r
+//! Sets the text displayed by the button\r
+void GUIButton::setText(const wchar_t* text)\r
+{\r
+ StaticText->setText(text);\r
+\r
+ IGUIButton::setText(text);\r
+}\r
+// END PATCH\r
+\r
//! Sets if the button should behave like a push button. Which means it\r
//! can be in two states: Normal or Pressed. With a click on the button,\r
//! the user can change the state of the button.\r
return Pressed;\r
}\r
\r
+// PATCH\r
+//! Returns if this element (or one of its direct children) is hovered\r
+bool GUIButton::isHovered() const\r
+{\r
+ IGUIElement *hovered = Environment->getHovered();\r
+ return hovered == this || (hovered != nullptr && hovered->getParent() == this);\r
+}\r
+// END PATCH\r
\r
//! Sets the pressed state of the button if this is a pushbutton\r
void GUIButton::setPressed(bool pressed)\r
{\r
ClickTime = porting::getTimeMs();\r
Pressed = pressed;\r
+ setFromState();\r
}\r
}\r
\r
}\r
\r
// PATCH\r
-GUIButton* GUIButton::addButton(IGUIEnvironment *environment, const core::rect<s32>& rectangle,\r
- IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext)\r
+GUIButton* GUIButton::addButton(IGUIEnvironment *environment,\r
+ const core::rect<s32>& rectangle, ISimpleTextureSource *tsrc,\r
+ IGUIElement* parent, s32 id, const wchar_t* text,\r
+ const wchar_t *tooltiptext)\r
{\r
- GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle);\r
+ GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc);\r
if (text)\r
button->setText(text);\r
\r
Colors[i] = base.getInterpolated(color, d);\r
}\r
}\r
+\r
+//! Set element properties from a StyleSpec corresponding to the button state\r
+void GUIButton::setFromState()\r
+{\r
+ StyleSpec::State state = StyleSpec::STATE_DEFAULT;\r
+\r
+ if (isPressed())\r
+ state = static_cast<StyleSpec::State>(state | StyleSpec::STATE_PRESSED);\r
+\r
+ if (isHovered())\r
+ state = static_cast<StyleSpec::State>(state | StyleSpec::STATE_HOVERED);\r
+\r
+ setFromStyle(StyleSpec::getStyleFromStatePropagation(Styles, state));\r
+}\r
+\r
+//! Set element properties from a StyleSpec\r
+void GUIButton::setFromStyle(const StyleSpec& style)\r
+{\r
+ bool hovered = (style.getState() & StyleSpec::STATE_HOVERED) != 0;\r
+ bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0;\r
+\r
+ if (style.isNotDefault(StyleSpec::BGCOLOR)) {\r
+\r
+ setColor(style.getColor(StyleSpec::BGCOLOR));\r
+\r
+ // If we have a propagated hover/press color, we need to automatically\r
+ // lighten/darken it\r
+ if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) {\r
+ for (size_t i = 0; i < 4; i++) {\r
+ if (pressed) {\r
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);\r
+ } else if (hovered) {\r
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);\r
+ }\r
+ }\r
+ }\r
+\r
+ } else {\r
+ for (size_t i = 0; i < 4; i++) {\r
+ video::SColor base =\r
+ Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);\r
+ if (pressed) {\r
+ Colors[i] = multiplyColorValue(base, COLOR_PRESSED_MOD);\r
+ } else if (hovered) {\r
+ Colors[i] = multiplyColorValue(base, COLOR_HOVERED_MOD);\r
+ } else {\r
+ Colors[i] = base;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {\r
+ setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));\r
+ } else {\r
+ setOverrideColor(video::SColor(255,255,255,255));\r
+ OverrideColorEnabled = false;\r
+ }\r
+ setNotClipped(style.getBool(StyleSpec::NOCLIP, false));\r
+ setDrawBorder(style.getBool(StyleSpec::BORDER, true));\r
+ setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));\r
+ setOverrideFont(style.getFont());\r
+\r
+ if (style.isNotDefault(StyleSpec::BGIMG)) {\r
+ video::ITexture *texture = style.getTexture(StyleSpec::BGIMG,\r
+ getTextureSource());\r
+ setImage(guiScalingImageButton(\r
+ Environment->getVideoDriver(), texture,\r
+ AbsoluteRect.getWidth(), AbsoluteRect.getHeight()));\r
+ setScaleImage(true);\r
+ } else {\r
+ setImage(nullptr);\r
+ }\r
+\r
+ BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle);\r
+\r
+ // Child padding and offset\r
+ Padding = style.getRect(StyleSpec::PADDING, core::rect<s32>());\r
+ Padding = core::rect<s32>(\r
+ Padding.UpperLeftCorner + BgMiddle.UpperLeftCorner,\r
+ Padding.LowerRightCorner + BgMiddle.LowerRightCorner);\r
+\r
+ GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());\r
+ core::vector2d<s32> defaultPressOffset(\r
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),\r
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));\r
+ ContentOffset = style.getVector2i(StyleSpec::CONTENT_OFFSET, isPressed()\r
+ ? defaultPressOffset\r
+ : core::vector2d<s32>(0));\r
+\r
+ core::rect<s32> childBounds(\r
+ Padding.UpperLeftCorner.X + ContentOffset.X,\r
+ Padding.UpperLeftCorner.Y + ContentOffset.Y,\r
+ AbsoluteRect.getWidth() + Padding.LowerRightCorner.X + ContentOffset.X,\r
+ AbsoluteRect.getHeight() + Padding.LowerRightCorner.Y + ContentOffset.Y);\r
+\r
+ for (IGUIElement *child : getChildren()) {\r
+ child->setRelativePosition(childBounds);\r
+ }\r
+}\r
+\r
+//! Set the styles used for each state\r
+void GUIButton::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES>& styles)\r
+{\r
+ Styles = styles;\r
+ setFromState();\r
+}\r
// END PATCH\r