Browse Source

Refactor to centralize GUIButton styling/rendering code (#9090)

Hugues Ross 4 years ago
parent
commit
9284313d17

+ 2 - 0
build/android/jni/Android.mk

@@ -181,6 +181,8 @@ LOCAL_SRC_FILES := \
 		jni/src/gui/guiBackgroundImage.cpp        \
 		jni/src/gui/guiBox.cpp                    \
 		jni/src/gui/guiButton.cpp                 \
+		jni/src/gui/guiButtonImage.cpp            \
+		jni/src/gui/guiButtonItemImage.cpp        \
 		jni/src/gui/guiChatConsole.cpp            \
 		jni/src/gui/guiConfirmRegistration.cpp    \
 		jni/src/gui/guiEditBoxWithScrollbar.cpp   \

+ 10 - 12
doc/lua_api.txt

@@ -2541,20 +2541,20 @@ Some types may inherit styles from parent types.
 * label
 * vertlabel, inherits from field
 * image_button
-* item_image_button, inherits from image_button
+* item_image_button
 * tabheader
 
 
 ### Valid Properties
 
-* button, button_exit
+* button, button_exit, image_button, item_image_button
     * alpha - boolean, whether to draw alpha in bgimg. Default true.
     * bgcolor - color, sets button tint.
     * bgcolor_hovered - color when hovered. Defaults to a lighter bgcolor when not provided.
     * bgcolor_pressed - color when pressed. Defaults to a darker bgcolor when not provided.
-    * bgimg - standard image. Defaults to none.
-    * bgimg_hovered - image when hovered. Defaults to bgimg when not provided.
-    * bgimg_pressed - image when pressed. Defaults to bgimg when not provided.
+    * bgimg - standard background image. Defaults to none.
+    * bgimg_hovered - background image when hovered. Defaults to bgimg when not provided.
+    * bgimg_pressed - background image when pressed. Defaults to bgimg when not provided.
     * border - boolean, draw border. Set to false to hide the bevelled button pane. Default true.
     * noclip - boolean, set to true to allow the element to exceed formspec bounds.
     * textcolor - color, default white.
@@ -2572,13 +2572,11 @@ Some types may inherit styles from parent types.
     * textcolor - color. Default white.
 * label, vertlabel
     * noclip - boolean, set to true to allow the element to exceed formspec bounds.
-* image_button
-    * alpha - boolean, whether to draw alpha in bgimg. Default true.
-    * border - boolean, draw border. Set to false to hide the bevelled button pane. Default false.
-    * noclip - boolean, set to true to allow the element to exceed formspec bounds.
-* item_image_button
-    * border - boolean, draw border. Set to false to hide the bevelled button pane. Default false.
-    * noclip - boolean, set to true to allow the element to exceed formspec bounds.
+* image_button (additional properties)
+    * fgimg - standard image. Defaults to none.
+    * fgimg_hovered - image when hovered. Defaults to fgimg when not provided.
+    * fgimg_pressed - image when pressed. Defaults to fgimg when not provided.
+    * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed
 * tabheader
     * noclip - boolean, set to true to allow the element to exceed formspec bounds.
     * textcolor - color. Default white.

+ 7 - 0
games/minimal/mods/test/formspec.lua

@@ -70,6 +70,13 @@ local style_fs = [[
 	style[one_btn13;border=false]
 	item_image_button[1.25,8.35;1,1;default:sword_steel;one_btn13;NoBor]
 
+	style[one_btn14;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png;fgimg=bubble.png;fgimg_hovered=default_apple.png;fgimg_pressed=heart.png]
+	image_button[0,9.6;1,1;bubble.png;one_btn14;Bg]
+
+	style[one_btn15;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png]
+	item_image_button[1.25,9.6;1,1;default:sword_steel;one_btn15;Bg]
+
+
 
 	container[2.75,0]
 

BIN
games/minimal/mods/test/textures/test_bg.png


BIN
games/minimal/mods/test/textures/test_bg_hovered.png


BIN
games/minimal/mods/test/textures/test_bg_pressed.png


+ 2 - 0
src/gui/CMakeLists.txt

@@ -2,6 +2,8 @@ set(gui_SRCS
 	${CMAKE_CURRENT_SOURCE_DIR}/guiBackgroundImage.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/guiBox.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/guiButton.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/guiButtonImage.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/guiButtonItemImage.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp

+ 34 - 0
src/gui/StyleSpec.h

@@ -17,7 +17,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include "client/tile.h" // ITextureSource
 #include "irrlichttypes_extrabloated.h"
+#include "util/string.h"
 #include <array>
 
 #pragma once
@@ -36,6 +38,9 @@ public:
 		BGIMG,
 		BGIMG_HOVERED,
 		BGIMG_PRESSED,
+		FGIMG,
+		FGIMG_HOVERED,
+		FGIMG_PRESSED,
 		ALPHA,
 		NUM_PROPERTIES,
 		NONE
@@ -66,6 +71,12 @@ public:
 			return BGIMG_HOVERED;
 		} else if (name == "bgimg_pressed") {
 			return BGIMG_PRESSED;
+		} else if (name == "fgimg") {
+			return FGIMG;
+		} else if (name == "fgimg_hovered") {
+			return FGIMG_HOVERED;
+		} else if (name == "fgimg_pressed") {
+			return FGIMG_PRESSED;
 		} else if (name == "alpha") {
 			return ALPHA;
 		} else {
@@ -106,6 +117,29 @@ public:
 		return color;
 	}
 
+	video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc,
+			video::ITexture *def) const
+	{
+		const auto &val = properties[prop];
+		if (val.empty()) {
+			return def;
+		}
+
+		video::ITexture *texture = tsrc->getTexture(val);
+
+		return texture;
+	}
+
+	video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc) const
+	{
+		const auto &val = properties[prop];
+		FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
+
+		video::ITexture *texture = tsrc->getTexture(val);
+
+		return texture;
+	}
+
 	bool getBool(Property prop, bool def) const
 	{
 		const auto &val = properties[prop];

+ 94 - 21
src/gui/guiButton.cpp

@@ -5,11 +5,15 @@
 #include "guiButton.h"
 
 
+#include "client/guiscalingfilter.h"
+#include "client/tile.h"
 #include "IGUISkin.h"
 #include "IGUIEnvironment.h"
 #include "IVideoDriver.h"
 #include "IGUIFont.h"
+#include "irrlicht_changes/static_text.h"
 #include "porting.h"
+#include "StyleSpec.h"
 
 using namespace irr;
 using namespace gui;
@@ -49,6 +53,9 @@ GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent,
 			core::clamp<u32>(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255),
 			core::clamp<u32>(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255));
 	}
+	
+	StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id);
+	StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
 	// END PATCH
 }
 
@@ -262,7 +269,7 @@ void GUIButton::draw()
 		{
 			// PATCH
 			skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect,
-				Environment->getHovered() == this ? HoveredColors : Colors);
+				isHovered() ? HoveredColors : Colors);
 			// END PATCH
 		}
 		else
@@ -318,7 +325,7 @@ void GUIButton::draw()
 			drawSprite(state, FocusTime, pos);
 
 			// mouse over / off animation
-			state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;
+			state = isHovered() ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;
 			drawSprite(state, HoverTime, pos);
 		}
 		else
@@ -328,23 +335,6 @@ void GUIButton::draw()
 		}
 	}
 
-	if (Text.size())
-	{
-		IGUIFont* font = getActiveFont();
-
-		core::rect<s32> rect = AbsoluteRect;
-		if (Pressed)
-		{
-			rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X);
-			rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y);
-		}
-
-		if (font)
-			font->draw(Text.c_str(), rect,
-				OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
-				true, true, &AbsoluteClippingRect);
-	}
-
 	IGUIElement::draw();
 }
 
@@ -371,11 +361,18 @@ void GUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::p
 }
 
 EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed) const
+{
+	// PATCH
+	return getImageState(pressed, ButtonImages);
+	// END PATCH
+}
+
+EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed, const ButtonImage* images) const
 {
 	// figure state we should have
 	EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED;
 	bool focused = Environment->hasFocus((IGUIElement*)this);
-	bool mouseOver = static_cast<const IGUIElement*>(Environment->getHovered()) == this;	// (static cast for Borland)
+	bool mouseOver = isHovered();
 	if (isEnabled())
 	{
 		if ( pressed )
@@ -403,7 +400,7 @@ EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed) const
 	}
 
 	// find a compatible state that has images
-	while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture )
+	while ( state != EGBIS_IMAGE_UP && !images[(u32)state].Texture )
 	{
 		// PATCH
 		switch ( state )
@@ -451,6 +448,8 @@ void GUIButton::setOverrideFont(IGUIFont* font)
 
 	if (OverrideFont)
 		OverrideFont->grab();
+
+	StaticText->setOverrideFont(font);
 }
 
 //! Gets the override font (if any)
@@ -475,6 +474,8 @@ void GUIButton::setOverrideColor(video::SColor color)
 {
 	OverrideColor = color;
 	OverrideColorEnabled = true;
+
+	StaticText->setOverrideColor(color);
 }
 
 video::SColor GUIButton::getOverrideColor() const
@@ -540,6 +541,14 @@ void GUIButton::setHoveredImage(video::ITexture* image, const core::rect<s32>& p
 	setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos);
 	setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos);
 }
+
+//! Sets the text displayed by the button
+void GUIButton::setText(const wchar_t* text)
+{
+	StaticText->setText(text);
+
+	IGUIButton::setText(text);
+}
 // END PATCH
 
 //! Sets if the button should behave like a push button. Which means it
@@ -557,6 +566,14 @@ bool GUIButton::isPressed() const
 	return Pressed;
 }
 
+// PATCH
+//! Returns if this element (or one of its direct children) is hovered
+bool GUIButton::isHovered() const
+{
+	IGUIElement *hovered = Environment->getHovered();
+	return  hovered == this || (hovered != nullptr && hovered->getParent() == this);
+}
+// END PATCH
 
 //! Sets the pressed state of the button if this is a pushbutton
 void GUIButton::setPressed(bool pressed)
@@ -565,6 +582,24 @@ void GUIButton::setPressed(bool pressed)
 	{
 		ClickTime = porting::getTimeMs();
 		Pressed = pressed;
+
+		GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());
+
+		for(IGUIElement *child : getChildren())
+		{
+			core::rect<s32> originalRect = child->getRelativePosition();
+			if (Pressed) {
+				child->setRelativePosition(originalRect +
+						core::dimension2d<s32>(
+							skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+							skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)));
+			} else {
+				child->setRelativePosition(originalRect -
+						core::dimension2d<s32>(
+							skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+							skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)));
+			}
+		}
 	}
 }
 
@@ -722,4 +757,42 @@ void GUIButton::setPressedColor(video::SColor color)
 		PressedColors[i] = base.getInterpolated(color, d);
 	}
 }
+
+//! Set element properties from a StyleSpec
+void GUIButton::setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc)
+{
+	if (style.isNotDefault(StyleSpec::BGCOLOR)) {
+		setColor(style.getColor(StyleSpec::BGCOLOR));
+	}
+	if (style.isNotDefault(StyleSpec::BGCOLOR_HOVERED)) {
+		setHoveredColor(style.getColor(StyleSpec::BGCOLOR_HOVERED));
+	}
+	if (style.isNotDefault(StyleSpec::BGCOLOR_PRESSED)) {
+		setPressedColor(style.getColor(StyleSpec::BGCOLOR_PRESSED));
+	}
+
+	if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
+		setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
+	}
+	setNotClipped(style.getBool(StyleSpec::NOCLIP, isNotClipped()));
+	setDrawBorder(style.getBool(StyleSpec::BORDER, DrawBorder));
+	setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
+
+	if (style.isNotDefault(StyleSpec::BGIMG)) {
+		video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, tsrc);
+		video::ITexture *hovered_texture = style.getTexture(StyleSpec::BGIMG_HOVERED, tsrc, texture);
+		video::ITexture *pressed_texture = style.getTexture(StyleSpec::BGIMG_PRESSED, tsrc, texture);
+
+		const core::position2di buttonCenter(AbsoluteRect.getCenter());
+		core::position2d<s32> geom(buttonCenter);
+
+		setImage(guiScalingImageButton(
+					Environment->getVideoDriver(), texture, geom.X, geom.Y));
+		setHoveredImage(guiScalingImageButton(
+					Environment->getVideoDriver(), hovered_texture, geom.X, geom.Y));
+		setPressedImage(guiScalingImageButton(
+					Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
+		setScaleImage(true);
+	}
+}
 // END PATCH

+ 42 - 22
src/gui/guiButton.h

@@ -6,6 +6,8 @@
 
 #include "IrrCompileConfig.h"
 
+#include <IGUIStaticText.h>
+#include "irrlicht_changes/static_text.h"
 #include "IGUIButton.h"
 #include "IGUISpriteBank.h"
 #include "ITexture.h"
@@ -64,6 +66,9 @@ using namespace irr;
 
 #endif
 
+class ISimpleTextureSource;
+class StyleSpec;
+
 class GUIButton : public gui::IGUIButton
 {
 public:
@@ -122,6 +127,9 @@ public:
 
 	//! Sets an image which should be displayed on the button when it is in hovered state.
 	virtual void setHoveredImage(video::ITexture* image=nullptr);
+
+	//! Sets the text displayed by the button
+	virtual void setText(const wchar_t* text) override;
 	// END PATCH
 
 	//! Sets an image which should be displayed on the button when it is in hovered state.
@@ -172,6 +180,11 @@ public:
 	//! Returns if the button is currently pressed
 	virtual bool isPressed() const override;
 
+	// PATCH
+	//! Returns if this element (or one of its direct children) is hovered
+	bool isHovered() const;
+	// END PATCH
+
 	//! Sets if the button should use the skin to draw its border
 	virtual void setDrawBorder(bool border=true) override;
 
@@ -214,6 +227,9 @@ public:
 	// PATCH
 	void setHoveredColor(video::SColor color);
 	void setPressedColor(video::SColor color);
+
+	//! Set element properties from a StyleSpec
+	virtual void setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc);
 	// END PATCH
 
 
@@ -225,28 +241,6 @@ protected:
 	void drawSprite(gui::EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center);
 	gui::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[gui::EGBS_COUNT];
-	gui::IGUISpriteBank* SpriteBank;
-
 	struct ButtonImage
 	{
 		ButtonImage() : Texture(0), SourceRect(core::rect<s32>(0,0,0,0))
@@ -288,6 +282,30 @@ private:
 		core::rect<s32> SourceRect;
 	};
 
+	gui::EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed, const ButtonImage* images) 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[gui::EGBS_COUNT];
+	gui::IGUISpriteBank* SpriteBank;
+
 	ButtonImage ButtonImages[gui::EGBIS_COUNT];
 
 	gui::IGUIFont* OverrideFont;
@@ -310,5 +328,7 @@ private:
 	// PATCH
 	video::SColor HoveredColors[4];
 	video::SColor PressedColors[4];
+
+	gui::IGUIStaticText *StaticText;
 	// END PATCH
 };

+ 149 - 0
src/gui/guiButtonImage.cpp

@@ -0,0 +1,149 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiButtonImage.h"
+
+#include "client/guiscalingfilter.h"
+#include "debug.h"
+#include "IGUIEnvironment.h"
+#include "IGUIImage.h"
+#include "IVideoDriver.h"
+#include "StyleSpec.h"
+
+using namespace irr;
+using namespace gui;
+
+GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment,
+		gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle, bool noclip)
+	: GUIButton (environment, parent, id, rectangle, noclip)
+{
+	m_image = Environment->addImage(
+			core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), this);
+	m_image->setScaleImage(isScalingImage());
+	sendToBack(m_image);
+}
+
+bool GUIButtonImage::OnEvent(const SEvent& event)
+{
+	bool result = GUIButton::OnEvent(event);
+
+	EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images);
+	video::ITexture *texture = m_foreground_images[(u32)imageState].Texture;
+	if (texture != nullptr)
+		m_image->setImage(texture);
+
+	return result;
+}
+
+void GUIButtonImage::setForegroundImage(EGUI_BUTTON_IMAGE_STATE state,
+		video::ITexture *image, const core::rect<s32> &sourceRect)
+{
+	if (state >= EGBIS_COUNT)
+		return;
+
+	if (image)
+		image->grab();
+
+	u32 stateIdx = (u32)state;
+	if (m_foreground_images[stateIdx].Texture)
+		m_foreground_images[stateIdx].Texture->drop();
+
+	m_foreground_images[stateIdx].Texture = image;
+	m_foreground_images[stateIdx].SourceRect = sourceRect;
+
+	EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images);
+	if (imageState == stateIdx)
+		m_image->setImage(image);
+}
+
+void GUIButtonImage::setForegroundImage(video::ITexture *image)
+{
+	setForegroundImage(gui::EGBIS_IMAGE_UP, image);
+}
+
+void GUIButtonImage::setForegroundImage(video::ITexture *image, const core::rect<s32> &pos)
+{
+	setForegroundImage(gui::EGBIS_IMAGE_UP, image, pos);
+}
+
+void GUIButtonImage::setPressedForegroundImage(video::ITexture *image)
+{
+	setForegroundImage(gui::EGBIS_IMAGE_DOWN, image);
+}
+
+void GUIButtonImage::setPressedForegroundImage(video::ITexture *image, const core::rect<s32> &pos)
+{
+	setForegroundImage(gui::EGBIS_IMAGE_DOWN, image, pos);
+}
+
+void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image)
+{
+	setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image);
+	setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image);
+}
+
+void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image, const core::rect<s32> &pos)
+{
+	setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos);
+	setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos);
+}
+
+void GUIButtonImage::setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc)
+{
+	GUIButton::setFromStyle(style, tsrc);
+
+	video::IVideoDriver *driver = Environment->getVideoDriver();
+
+	if (style.isNotDefault(StyleSpec::FGIMG)) {
+		video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, tsrc);
+		video::ITexture *hovered_texture = style.getTexture(StyleSpec::FGIMG_HOVERED, tsrc, texture);
+		video::ITexture *pressed_texture = style.getTexture(StyleSpec::FGIMG_PRESSED, tsrc, texture);
+
+		const core::position2di buttonCenter(AbsoluteRect.getCenter());
+		core::position2d<s32> geom(buttonCenter);
+
+		setForegroundImage(guiScalingImageButton(driver, texture, geom.X, geom.Y));
+		setHoveredForegroundImage(guiScalingImageButton(driver, hovered_texture, geom.X, geom.Y));
+		setPressedForegroundImage(guiScalingImageButton(driver, pressed_texture, geom.X, geom.Y));
+		setScaleImage(true);
+	}
+}
+
+void GUIButtonImage::setScaleImage(bool scaleImage)
+{
+	GUIButton::setScaleImage(scaleImage);
+	m_image->setScaleImage(scaleImage);
+}
+
+GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment,
+		const core::rect<s32> &rectangle, IGUIElement *parent, s32 id,
+		const wchar_t *text, const wchar_t *tooltiptext)
+{
+	GUIButtonImage *button = new GUIButtonImage(environment,
+			parent ? parent : environment->getRootGUIElement(), id, rectangle);
+
+	if (text)
+		button->setText(text);
+
+	if (tooltiptext)
+		button->setToolTipText(tooltiptext);
+
+	button->drop();
+	return button;
+}

+ 59 - 0
src/gui/guiButtonImage.h

@@ -0,0 +1,59 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiButton.h"
+#include "IGUIButton.h"
+
+using namespace irr;
+
+class GUIButtonImage : public GUIButton
+{
+public:
+	//! constructor
+	GUIButtonImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent,
+			s32 id, core::rect<s32> rectangle, bool noclip = false);
+
+	virtual bool OnEvent(const SEvent& event) override;
+
+	void setForegroundImage(gui::EGUI_BUTTON_IMAGE_STATE state,
+			video::ITexture *image = nullptr,
+			const core::rect<s32> &sourceRect = core::rect<s32>(0, 0, 0, 0));
+
+	void setForegroundImage(video::ITexture *image = nullptr);
+	void setForegroundImage(video::ITexture *image, const core::rect<s32> &pos);
+
+	void setPressedForegroundImage(video::ITexture *image = nullptr);
+	void setPressedForegroundImage(video::ITexture *image, const core::rect<s32> &pos);
+
+	void setHoveredForegroundImage(video::ITexture *image = nullptr);
+	void setHoveredForegroundImage(video::ITexture *image, const core::rect<s32> &pos);
+
+	virtual void setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) override;
+
+	virtual void setScaleImage(bool scaleImage=true) override;
+
+	//! Do not drop returned handle
+	static GUIButtonImage *addButton(gui::IGUIEnvironment *environment,
+			const core::rect<s32> &rectangle, IGUIElement *parent, s32 id,
+			const wchar_t *text, const wchar_t *tooltiptext = L"");
+
+private:
+	ButtonImage m_foreground_images[gui::EGBIS_COUNT];
+	gui::IGUIImage *m_image;
+};

+ 57 - 0
src/gui/guiButtonItemImage.cpp

@@ -0,0 +1,57 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiButtonItemImage.h"
+
+#include "client/client.h"
+#include "client/hud.h" // drawItemStack
+#include "guiItemImage.h"
+#include "IGUIEnvironment.h"
+#include "itemdef.h"
+
+using namespace irr;
+using namespace gui;
+
+GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent,
+		s32 id, core::rect<s32> rectangle, std::string item, Client *client, bool noclip)
+		: GUIButton (environment, parent, id, rectangle, noclip)
+{
+	m_image = new GUIItemImage(environment, this, id,
+			core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()),
+			item, getActiveFont(), client);
+	sendToBack(m_image);
+
+	m_item_name = item;
+	m_client = client;
+}
+
+GUIButtonItemImage *GUIButtonItemImage::addButton(IGUIEnvironment *environment,
+		const core::rect<s32> &rectangle, IGUIElement *parent, s32 id,
+		const wchar_t *text, std::string item, Client *client)
+{
+	GUIButtonItemImage *button = new GUIButtonItemImage(environment,
+			parent ? parent : environment->getRootGUIElement(),
+			id, rectangle, item, client);
+
+	if (text)
+		button->setText(text);
+
+	button->drop();
+	return button;
+}

+ 45 - 0
src/gui/guiButtonItemImage.h

@@ -0,0 +1,45 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiButton.h"
+#include "IGUIButton.h"
+
+using namespace irr;
+
+class Client;
+class GUIItemImage;
+
+class GUIButtonItemImage : public GUIButton
+{
+public:
+	//! constructor
+	GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent,
+			s32 id, core::rect<s32> rectangle, std::string item,
+			Client *client, bool noclip = false);
+
+	//! Do not drop returned handle
+	static GUIButtonItemImage *addButton(gui::IGUIEnvironment *environment,
+			const core::rect<s32> &rectangle, IGUIElement *parent, s32 id,
+			const wchar_t *text, std::string item, Client *client);
+
+private:
+	std::string m_item_name;
+	Client *m_client;
+	GUIItemImage *m_image;
+};

+ 16 - 77
src/gui/guiFormSpecMenu.cpp

@@ -58,6 +58,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiBackgroundImage.h"
 #include "guiBox.h"
 #include "guiButton.h"
+#include "guiButtonImage.h"
+#include "guiButtonItemImage.h"
 #include "guiEditBoxWithScrollbar.h"
 #include "guiItemImage.h"
 #include "guiScrollBar.h"
@@ -879,49 +881,7 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
 		GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
 
 		auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
-		if (style.isNotDefault(StyleSpec::BGCOLOR)) {
-			e->setColor(style.getColor(StyleSpec::BGCOLOR));
-		}
-		if (style.isNotDefault(StyleSpec::BGCOLOR_HOVERED)) {
-			e->setHoveredColor(style.getColor(StyleSpec::BGCOLOR_HOVERED));
-		}
-		if (style.isNotDefault(StyleSpec::BGCOLOR_PRESSED)) {
-			e->setPressedColor(style.getColor(StyleSpec::BGCOLOR_PRESSED));
-		}
-
-		if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
-			e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
-		}
-		e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
-		e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
-
-		if (style.isNotDefault(StyleSpec::BGIMG)) {
-			std::string image_name = style.get(StyleSpec::BGIMG, "");
-			std::string hovered_image_name = style.get(StyleSpec::BGIMG_HOVERED, "");
-			std::string pressed_image_name = style.get(StyleSpec::BGIMG_PRESSED, "");
-
-			video::ITexture *texture = 0;
-			video::ITexture *hovered_texture = 0;
-			video::ITexture *pressed_texture = 0;
-			texture = m_tsrc->getTexture(image_name);
-			if (!hovered_image_name.empty())
-				hovered_texture = m_tsrc->getTexture(hovered_image_name);
-			else
-				hovered_texture = texture;
-			if (!pressed_image_name.empty())
-				pressed_texture = m_tsrc->getTexture(pressed_image_name);
-			else
-				pressed_texture = texture;
-
-			e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
-			e->setImage(guiScalingImageButton(
-					Environment->getVideoDriver(), texture, geom.X, geom.Y));
-			e->setHoveredImage(guiScalingImageButton(
-					Environment->getVideoDriver(), hovered_texture, geom.X, geom.Y));
-			e->setPressedImage(guiScalingImageButton(
-					Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
-			e->setScaleImage(true);
-		}
+		e->setFromStyle(style, m_tsrc);
 
 		if (spec.fname == data->focused_fieldname) {
 			Environment->setFocus(e);
@@ -1876,33 +1836,31 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
 			spec.is_exit = true;
 
 		video::ITexture *texture = 0;
-		video::ITexture *pressed_texture = 0;
 		texture = m_tsrc->getTexture(image_name);
-		if (!pressed_image_name.empty())
-			pressed_texture = m_tsrc->getTexture(pressed_image_name);
-		else
-			pressed_texture = texture;
 
-		GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
+		GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
 
 		if (spec.fname == data->focused_fieldname) {
 			Environment->setFocus(e);
 		}
 
 		auto style = getStyleForElement("image_button", spec.fname);
+		e->setFromStyle(style, m_tsrc);
 
-		e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
-		e->setImage(guiScalingImageButton(
+		// We explicitly handle these arguments *after* the style properties in
+		// order to override them if they are provided
+		e->setForegroundImage(guiScalingImageButton(
 			Environment->getVideoDriver(), texture, geom.X, geom.Y));
-		e->setPressedImage(guiScalingImageButton(
-			Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
+		if (!pressed_image_name.empty()) {
+			video::ITexture *pressed_texture = m_tsrc->getTexture(pressed_image_name);
+			e->setPressedForegroundImage(guiScalingImageButton(
+						Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
+		}
 		e->setScaleImage(true);
+
 		if (parts.size() >= 7) {
 			e->setNotClipped(noclip);
 			e->setDrawBorder(drawborder);
-		} else {
-			e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
-			e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
 		}
 
 		m_fields.push_back(spec);
@@ -2083,11 +2041,10 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
 			2
 		);
 
-		gui::IGUIButton *e_btn = GUIButton::addButton(Environment, rect, this, spec_btn.fid, L"");
+		GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, rect, this, spec_btn.fid, spec_btn.flabel.c_str(), item_name, m_client);
 
 		auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
-		e_btn->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
-		e_btn->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+		e_btn->setFromStyle(style, m_tsrc);
 
 		if (spec_btn.fname == data->focused_fieldname) {
 			Environment->setFocus(e_btn);
@@ -2097,24 +2054,6 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
 		rect += data->basepos-padding;
 		spec_btn.rect = rect;
 		m_fields.push_back(spec_btn);
-
-		// the spec for the item-image
-		FieldSpec spec_img(
-			name,
-			L"",
-			L"",
-			258 + m_fields.size(),
-			2
-		);
-
-		GUIItemImage *e_img = new GUIItemImage(Environment, e_btn, spec_img.fid,
-				core::rect<s32>(0, 0, geom.X, geom.Y), item_name, m_font, m_client);
-
-		e_img->setText(utf8_to_wide(label).c_str());
-
-		e_img->drop();
-
-		m_fields.push_back(spec_img);
 		return;
 	}
 	errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;

+ 0 - 12
src/gui/guiItemImage.cpp

@@ -43,18 +43,6 @@ void GUIItemImage::draw()
 	item.deSerialize(m_item_name, idef);
 	// Viewport rectangle on screen
 	core::rect<s32> rect = core::rect<s32>(AbsoluteRect);
-	if (Parent->getType() == gui::EGUIET_BUTTON &&
-			((irr::gui::IGUIButton *)Parent)->isPressed()) {
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
-		rect += core::dimension2d<s32>(0.05 * (float)rect.getWidth(),
-				0.05 * (float)rect.getHeight());
-#else
-		gui::IGUISkin *skin = Environment->getSkin();
-		rect += core::dimension2d<s32>(
-				skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
-				skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
-#endif
-	}
 	drawItemStack(Environment->getVideoDriver(), m_font, item, rect,
 			&AbsoluteClippingRect, m_client, IT_ROT_NONE);
 	video::SColor color(255, 255, 255, 255);

+ 1 - 1
src/irrlicht_changes/static_text.cpp

@@ -112,7 +112,7 @@ void StaticText::draw()
 #if USE_FREETYPE
 				if (font->getType() == irr::gui::EGFT_CUSTOM) {
 					irr::gui::CGUITTFont *tmp = static_cast<irr::gui::CGUITTFont*>(font);
-					tmp->draw(cText, frameRect,
+					tmp->draw(Text, frameRect,
 						OverrideColorEnabled ? OverrideColor :
 							skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT),
 						HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER,

+ 4 - 0
util/travis/clang-format-whitelist.txt

@@ -161,6 +161,10 @@ src/gui/guiBox.cpp
 src/gui/guiBox.h
 src/gui/guiButton.cpp
 src/gui/guiButton.h
+src/gui/guiButtonImage.cpp
+src/gui/guiButtonImage.h
+src/gui/guiButtonItemImage.cpp
+src/gui/guiButtonItemImage.h
 src/gui/guiChatConsole.cpp
 src/gui/guiChatConsole.h
 src/gui/guiConfirmRegistration.cpp