Selaa lähdekoodia

Added a base GUIButtonBase class for button, toggle and list box
Menus in MenuBar now open on hover if a menu is already open
Listbox button and menu bar button now remain active while drop down is open
Add support for button "on" state

Marko Pintera 12 vuotta sitten
vanhempi
sitoutus
c64f7a2727

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -234,6 +234,7 @@
     <ClInclude Include="Include\BsEngineGUI.h" />
     <ClInclude Include="Include\BsGUIArea.h" />
     <ClInclude Include="Include\BsGUIButton.h" />
+    <ClInclude Include="Include\BsGUIButtonBase.h" />
     <ClInclude Include="Include\BsGUICommandEvent.h" />
     <ClInclude Include="Include\BsGUIContent.h" />
     <ClInclude Include="Include\BsGUIContextMenu.h" />
@@ -287,6 +288,7 @@
     <ClInclude Include="Include\BsD3D9BuiltinMaterialFactory.h" />
     <ClInclude Include="Include\BsGLBuiltinMaterialFactory.h" />
     <ClInclude Include="Include\BsUpdateCallback.h" />
+    <ClCompile Include="Source\BsGUIButtonBase.cpp" />
     <ClCompile Include="Source\BsGUIContextMenu.cpp" />
   </ItemGroup>
   <ItemGroup>

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -219,6 +219,9 @@
     <ClInclude Include="Include\BsGUIDropDownBoxManager.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIButtonBase.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -383,5 +386,8 @@
     <ClCompile Include="Source\BsGUIContextMenu.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIButtonBase.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 2 - 55
BansheeEngine/Include/BsGUIButton.h

@@ -1,7 +1,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
-#include "BsGUIElement.h"
+#include "BsGUIButtonBase.h"
 #include "BsImageSprite.h"
 #include "BsTextSprite.h"
 #include "BsGUIContent.h"
@@ -9,7 +9,7 @@
 
 namespace BansheeEngine
 {
-	class BS_EXPORT GUIButton : public GUIElement
+	class BS_EXPORT GUIButton : public GUIButtonBase
 	{
 	public:
 		static const CM::String& getGUITypeName();
@@ -19,61 +19,8 @@ namespace BansheeEngine
 
 		static GUIButton* create(GUIWidget& parent, const GUIContent& content, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const GUIContent& content, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
-	
-		void setContent(const GUIContent& content);
 
-		boost::signal<void()> onClick;
-		boost::signal<void()> onHover;
-		boost::signal<void()> onOut;
-	protected:
-		~GUIButton();
-
-		/**
-		 * @copydoc GUIElement::getNumRenderElements()
-		 */
-		virtual CM::UINT32 getNumRenderElements() const;
-
-		/**
-		 * @copydoc GUIElement::getMaterial()
-		 */
-		virtual const CM::HMaterial& getMaterial(CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::getNumQuads()
-		 */
-		virtual CM::UINT32 getNumQuads(CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::fillBuffer()
-		 */
-		virtual void fillBuffer(CM::UINT8* vertices, CM::UINT8* uv, CM::UINT32* indices, CM::UINT32 startingQuad, 
-			CM::UINT32 maxNumQuads, CM::UINT32 vertexStride, CM::UINT32 indexStride, CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::updateRenderElementsInternal()
-		 */
-		virtual void updateRenderElementsInternal();
-
-		/**
-		 * @copydoc GUIElement::updateBounds()
-		 */
-		virtual void updateClippedBounds();
-
-		virtual CM::Int2 _getOptimalSize() const;
-
-		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 	private:
-		ImageSprite* mImageSprite;
-		ImageSprite* mContentImageSprite;
-		TextSprite* mTextSprite;
-
-		IMAGE_SPRITE_DESC mImageDesc;
-		GUIContent mContent;
-
 		GUIButton(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions);
-
-		virtual bool mouseEvent(const GUIMouseEvent& ev);
-
-		TEXT_SPRITE_DESC getTextDesc() const;
 	};
 }

+ 87 - 0
BansheeEngine/Include/BsGUIButtonBase.h

@@ -0,0 +1,87 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIElement.h"
+#include "BsImageSprite.h"
+#include "BsTextSprite.h"
+#include "BsGUIContent.h"
+#include "boost/signal.hpp"
+
+namespace BansheeEngine
+{
+	enum class GUIButtonState
+	{
+		Normal = 0x01,
+		Hover = 0x02,
+		Active = 0x04,
+		Focused = 0x08,
+		NormalOn = 0x11,
+		HoverOn = 0x12,
+		ActiveOn = 0x14,
+		FocusedOn = 0x18
+	};
+
+	class BS_EXPORT GUIButtonBase : public GUIElement
+	{
+	public:
+		void setContent(const GUIContent& content);
+
+		void _setOn(bool on);
+		bool _isOn() const;
+
+		boost::signal<void()> onClick;
+		boost::signal<void()> onHover;
+		boost::signal<void()> onOut;
+	protected:
+		GUIButtonBase(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions);
+		virtual ~GUIButtonBase();
+
+		/**
+		 * @copydoc GUIElement::getNumRenderElements()
+		 */
+		virtual CM::UINT32 getNumRenderElements() const;
+
+		/**
+		 * @copydoc GUIElement::getMaterial()
+		 */
+		virtual const CM::HMaterial& getMaterial(CM::UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::getNumQuads()
+		 */
+		virtual CM::UINT32 getNumQuads(CM::UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::fillBuffer()
+		 */
+		virtual void fillBuffer(CM::UINT8* vertices, CM::UINT8* uv, CM::UINT32* indices, CM::UINT32 startingQuad, 
+			CM::UINT32 maxNumQuads, CM::UINT32 vertexStride, CM::UINT32 indexStride, CM::UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::updateRenderElementsInternal()
+		 */
+		virtual void updateRenderElementsInternal();
+
+		/**
+		 * @copydoc GUIElement::updateBounds()
+		 */
+		virtual void updateClippedBounds();
+
+		virtual bool mouseEvent(const GUIMouseEvent& ev);
+
+		virtual CM::Int2 _getOptimalSize() const;
+		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
+
+		TEXT_SPRITE_DESC getTextDesc() const;
+
+		void setState(GUIButtonState state);
+	private:
+		ImageSprite* mImageSprite;
+		ImageSprite* mContentImageSprite;
+		TextSprite* mTextSprite;
+		GUIButtonState mActiveState;
+
+		IMAGE_SPRITE_DESC mImageDesc;
+		GUIContent mContent;
+	};
+}

+ 3 - 43
BansheeEngine/Include/BsGUIListBox.h

@@ -1,73 +1,33 @@
 #pragma once
 
 #include "BsPrerequisites.h"
-#include "BsGUIElement.h"
+#include "BsGUIButtonBase.h"
 #include "BsImageSprite.h"
 #include "BsTextSprite.h"
 #include "boost/signal.hpp"
 
 namespace BansheeEngine
 {
-	class BS_EXPORT GUIListBox : public GUIElement
+	class BS_EXPORT GUIListBox : public GUIButtonBase
 	{
 	public:
 		static const CM::String& getGUITypeName();
 
 		static GUIListBox* create(GUIWidget& parent, const CM::Vector<CM::WString>::type& elements, const GUIElementStyle* style = nullptr);
 		static GUIListBox* create(GUIWidget& parent, const CM::Vector<CM::WString>::type& elements, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
-	
+
 		boost::signal<void(CM::UINT32)> onSelectionChanged;
 	protected:
 		~GUIListBox();
 
-		/**
-		 * @copydoc GUIElement::getNumRenderElements()
-		 */
-		virtual CM::UINT32 getNumRenderElements() const;
-
-		/**
-		 * @copydoc GUIElement::getMaterial()
-		 */
-		virtual const CM::HMaterial& getMaterial(CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::getNumQuads()
-		 */
-		virtual CM::UINT32 getNumQuads(CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::fillBuffer()
-		 */
-		virtual void fillBuffer(CM::UINT8* vertices, CM::UINT8* uv, CM::UINT32* indices, CM::UINT32 startingQuad, 
-			CM::UINT32 maxNumQuads, CM::UINT32 vertexStride, CM::UINT32 indexStride, CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::updateRenderElementsInternal()
-		 */
-		virtual void updateRenderElementsInternal();
-
-		/**
-		 * @copydoc GUIElement::updateBounds()
-		 */
-		virtual void updateClippedBounds();
-
-		virtual CM::Int2 _getOptimalSize() const;
-
-		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 	private:
-		ImageSprite* mImageSprite;
-		TextSprite* mTextSprite;
-		CM::UINT32 mNumImageRenderElements;
 		CM::UINT32 mSelectedIdx;
-
-		IMAGE_SPRITE_DESC mImageDesc;
 		CM::Vector<CM::WString>::type mElements;
 		bool mIsListBoxOpen;
 
 		GUIListBox(GUIWidget& parent, const GUIElementStyle* style, const CM::Vector<CM::WString>::type& elements, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		TEXT_SPRITE_DESC getTextDesc() const;
 
 		void elementSelected(CM::UINT32 idx);
 

+ 2 - 44
BansheeEngine/Include/BsGUIToggle.h

@@ -1,7 +1,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
-#include "BsGUIElement.h"
+#include "BsGUIButtonBase.h"
 #include "BsGUIToggleGroup.h"
 #include "BsImageSprite.h"
 #include "BsTextSprite.h"
@@ -10,7 +10,7 @@
 
 namespace BansheeEngine
 {
-	class BS_EXPORT GUIToggle : public GUIElement
+	class BS_EXPORT GUIToggle : public GUIButtonBase
 	{
 	public:
 		static const CM::String& getGUITypeName();
@@ -38,55 +38,13 @@ namespace BansheeEngine
 	protected:
 		virtual ~GUIToggle();
 
-		/**
-		 * @copydoc GUIElement::getNumRenderElements()
-		 */
-		virtual CM::UINT32 getNumRenderElements() const;
-
-		/**
-		 * @copydoc GUIElement::getMaterial()
-		 */
-		virtual const CM::HMaterial& getMaterial(CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::getNumQuads()
-		 */
-		virtual CM::UINT32 getNumQuads(CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::fillBuffer()
-		 */
-		virtual void fillBuffer(CM::UINT8* vertices, CM::UINT8* uv, CM::UINT32* indices, CM::UINT32 startingQuad, 
-			CM::UINT32 maxNumQuads, CM::UINT32 vertexStride, CM::UINT32 indexStride, CM::UINT32 renderElementIdx) const;
-
-		/**
-		 * @copydoc GUIElement::updateRenderElementsInternal()
-		 */
-		virtual void updateRenderElementsInternal();
-
-		/**
-		 * @copydoc GUIElement::updateBounds()
-		 */
-		virtual void updateClippedBounds();
-
-		virtual CM::Int2 _getOptimalSize() const;
-
-		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
 	protected:
 		GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 
-		TEXT_SPRITE_DESC getTextDesc() const;
-
 	private:
 		std::shared_ptr<GUIToggleGroup> mToggleGroup;
-		ImageSprite* mImageSprite;
-		TextSprite* mTextSprite;
-		CM::UINT32 mNumImageRenderElements;
 		bool mIsToggled;
-
-		IMAGE_SPRITE_DESC mImageDesc;
-		GUIContent mContent;
 	};
 }

+ 6 - 0
BansheeEngine/Source/BsEngineGUI.cpp

@@ -434,6 +434,9 @@ namespace BansheeEngine
 		dropDownListStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(dropDownListNormal));
 		dropDownListStyle.hover.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(dropDownListHover));
 		dropDownListStyle.active.texture = dropDownListStyle.hover.texture;
+		dropDownListStyle.normalOn.texture = dropDownListStyle.hover.texture;
+		dropDownListStyle.hoverOn.texture = dropDownListStyle.hover.texture;
+		dropDownListStyle.activeOn.texture = dropDownListStyle.hover.texture;
 		dropDownListStyle.fixedHeight = true;
 		dropDownListStyle.fixedWidth = false;
 		dropDownListStyle.height = 13;
@@ -657,6 +660,9 @@ namespace BansheeEngine
 		menuBarBtnStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(menuBarBtnNormal));
 		menuBarBtnStyle.hover.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(menuBarBtnHover));
 		menuBarBtnStyle.active.texture = menuBarBtnStyle.hover.texture;
+		menuBarBtnStyle.normalOn.texture = menuBarBtnStyle.hover.texture;
+		menuBarBtnStyle.hoverOn.texture = menuBarBtnStyle.hover.texture;
+		menuBarBtnStyle.activeOn.texture = menuBarBtnStyle.hover.texture;
 		menuBarBtnStyle.fixedHeight = true;
 		menuBarBtnStyle.fixedWidth = false;
 		menuBarBtnStyle.height = 15;

+ 2 - 272
BansheeEngine/Source/BsGUIButton.cpp

@@ -20,37 +20,8 @@ namespace BansheeEngine
 	}
 
 	GUIButton::GUIButton(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, style, layoutOptions), mContent(content), mContentImageSprite(nullptr)
-	{
-		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
-		mTextSprite = cm_new<TextSprite, PoolAlloc>();
-
-		SpriteTexturePtr contentTex = content.getImage();
-		if(contentTex != nullptr)
-			mContentImageSprite = cm_new<ImageSprite, PoolAlloc>();
-
-		mImageDesc.texture = mStyle->normal.texture;
-
-		if(mImageDesc.texture != nullptr)
-		{
-			mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
-			mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
-		}
-
-		mImageDesc.borderLeft = mStyle->border.left;
-		mImageDesc.borderRight = mStyle->border.right;
-		mImageDesc.borderTop = mStyle->border.top;
-		mImageDesc.borderBottom = mStyle->border.bottom;
-	}
-
-	GUIButton::~GUIButton()
-	{
-		cm_delete<PoolAlloc>(mTextSprite);
-		cm_delete<PoolAlloc>(mImageSprite);
-
-		if(mContentImageSprite != nullptr)
-			cm_delete<PoolAlloc>(mContentImageSprite);
-	}
+		:GUIButtonBase(parent, style, content, layoutOptions)
+	{ }
 
 	GUIButton* GUIButton::create(GUIWidget& parent, const WString& text, const GUIElementStyle* style)
 	{
@@ -83,245 +54,4 @@ namespace BansheeEngine
 
 		return new (cm_alloc<GUIButton, PoolAlloc>()) GUIButton(parent, style, content, layoutOptions);
 	}
-
-	void GUIButton::setContent(const GUIContent& content)
-	{
-		mContent = content;
-
-		markContentAsDirty();
-	}
-
-	UINT32 GUIButton::getNumRenderElements() const
-	{
-		UINT32 numElements = mImageSprite->getNumRenderElements();
-		numElements += mTextSprite->getNumRenderElements();
-
-		if(mContentImageSprite != nullptr)
-			numElements += mContentImageSprite->getNumRenderElements();
-
-		return numElements;
-	}
-
-	const HMaterial& GUIButton::getMaterial(UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		if(renderElementIdx >= contentImgSpriteIdx)
-			return mContentImageSprite->getMaterial(contentImgSpriteIdx - renderElementIdx);
-		else if(renderElementIdx >= textSpriteIdx)
-			return mTextSprite->getMaterial(textSpriteIdx - renderElementIdx);
-		else
-			return mImageSprite->getMaterial(renderElementIdx);
-	}
-
-	UINT32 GUIButton::getNumQuads(UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		UINT32 numQuads = 0;
-		if(renderElementIdx >= contentImgSpriteIdx)
-			numQuads = mContentImageSprite->getNumQuads(contentImgSpriteIdx - renderElementIdx);
-		else if(renderElementIdx >= textSpriteIdx)
-			numQuads = mTextSprite->getNumQuads(textSpriteIdx - renderElementIdx);
-		else
-			numQuads = mImageSprite->getNumQuads(renderElementIdx);
-
-		return numQuads;
-	}
-
-	void GUIButton::updateRenderElementsInternal()
-	{		
-		mImageDesc.width = mWidth;
-		mImageDesc.height = mHeight;
-
-		mImageSprite->update(mImageDesc);
-
-		mTextSprite->update(getTextDesc());
-
-		if(mContentImageSprite != nullptr)
-		{
-			IMAGE_SPRITE_DESC contentImgDesc;
-			contentImgDesc.texture = mContent.getImage();
-			contentImgDesc.width = mContent.getImage()->getTexture()->getWidth();
-			contentImgDesc.height = mContent.getImage()->getTexture()->getHeight();
-
-			mContentImageSprite->update(contentImgDesc);
-		}
-
-		GUIElement::updateRenderElementsInternal();
-	}
-
-	void GUIButton::updateClippedBounds()
-	{
-		mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
-	}
-
-	Int2 GUIButton::_getOptimalSize() const
-	{
-		UINT32 imageWidth = 0;
-		UINT32 imageHeight = 0;
-		if(mImageDesc.texture != nullptr)
-		{
-			imageWidth = mImageDesc.texture->getTexture()->getWidth();
-			imageHeight = mImageDesc.texture->getTexture()->getHeight();
-		}
-
-		Int2 contentSize = GUIHelper::calcOptimalContentsSize(mContent, *mStyle, _getLayoutOptions());
-		UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
-		UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
-
-		return Int2(contentWidth, contentHeight);
-	}
-
-	UINT32 GUIButton::_getRenderElementDepth(UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		if(renderElementIdx >= contentImgSpriteIdx)
-			return _getDepth();
-		else if(renderElementIdx >= textSpriteIdx)
-			return _getDepth();
-		else
-			return _getDepth() + 1;
-	}
-
-	void GUIButton::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
-		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
-	{
-		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
-		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
-
-		if(renderElementIdx < textSpriteIdx)
-		{
-			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, renderElementIdx, mOffset, mClipRect);
-
-			return;
-		}
-
-		Rect contentBounds = getContentBounds();
-		Rect contentClipRect = getContentClipRect();
-		Rect textBounds = mTextSprite->getBounds(Int2(), Rect());
-
-		Int2 textOffset;
-		Rect textClipRect;
-
-		Int2 imageOffset;
-		Rect imageClipRect;
-		if(mContentImageSprite != nullptr)
-		{
-			Rect imageBounds = mContentImageSprite->getBounds(Int2(), Rect());
-			UINT32 freeWidth = (UINT32)std::max(0, contentBounds.width - textBounds.width - imageBounds.width);
-			INT32 imageXOffset = (INT32)(freeWidth / 2);
-
-			if(mStyle->imagePosition == GUIImagePosition::Right)
-			{
-				INT32 imageReservedWidth = std::max(0, contentBounds.width - textBounds.width);
-
-				textOffset = Int2(contentBounds.x, contentBounds.y);
-				textClipRect = contentClipRect;
-				textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
-
-				imageOffset = Int2(contentBounds.x + textBounds.width + imageXOffset, contentBounds.y);
-				imageClipRect = contentClipRect;
-				imageClipRect.x -= textBounds.width + imageXOffset;
-			}
-			else
-			{
-				INT32 imageReservedWidth = imageBounds.width + imageXOffset;
-
-				imageOffset = Int2(contentBounds.x + imageXOffset, contentBounds.y);
-				imageClipRect = contentClipRect;
-				imageClipRect.x -= imageXOffset;
-				imageClipRect.width = std::min(imageReservedWidth, imageClipRect.width);
-
-				textOffset = Int2(contentBounds.x + imageReservedWidth, contentBounds.y);
-				textClipRect = contentClipRect;
-				textClipRect.x -= imageReservedWidth;
-			}
-
-			INT32 imageYOffset = (contentBounds.height - imageBounds.height) / 2;
-			imageClipRect.y -= imageYOffset;
-			imageOffset.y += imageYOffset;
-		}
-		else
-		{
-			textOffset = Int2(contentBounds.x, contentBounds.y);
-			textClipRect = contentClipRect;
-		}
-
-		if(renderElementIdx >= contentImgSpriteIdx)
-		{
-			mContentImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, contentImgSpriteIdx - renderElementIdx, imageOffset, imageClipRect);
-		}
-		else
-		{
-			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-			vertexStride, indexStride, textSpriteIdx - renderElementIdx, textOffset, textClipRect);
-		}
-	}
-
-	bool GUIButton::mouseEvent(const GUIMouseEvent& ev)
-	{
-		if(ev.getType() == GUIMouseEventType::MouseOver)
-		{
-			mImageDesc.texture = mStyle->hover.texture;
-			markContentAsDirty();
-
-			if(!onHover.empty())
-				onHover();
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseOut)
-		{
-			mImageDesc.texture = mStyle->normal.texture;
-			markContentAsDirty();
-
-			if(!onOut.empty())
-				onOut();
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseDown)
-		{
-			mImageDesc.texture = mStyle->active.texture;
-			markContentAsDirty();
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseUp)
-		{
-			mImageDesc.texture = mStyle->hover.texture;
-			markContentAsDirty();
-
-			if(!onClick.empty())
-				onClick();
-
-			return true;
-		}
-		
-		return false;
-	}
-
-	TEXT_SPRITE_DESC GUIButton::getTextDesc() const
-	{
-		TEXT_SPRITE_DESC textDesc;
-		textDesc.text = mContent.getText();
-		textDesc.font = mStyle->font;
-		textDesc.fontSize = mStyle->fontSize;
-
-		Rect textBounds = getContentBounds();
-
-		textDesc.width = textBounds.width;
-		textDesc.height = textBounds.height;
-		textDesc.horzAlign = mStyle->textHorzAlign;
-		textDesc.vertAlign = mStyle->textVertAlign;
-
-		return textDesc;
-	}
 }

+ 332 - 0
BansheeEngine/Source/BsGUIButtonBase.cpp

@@ -0,0 +1,332 @@
+#include "BsGUIButtonBase.h"
+#include "BsImageSprite.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsSpriteTexture.h"
+#include "BsTextSprite.h"
+#include "BsGUILayoutOptions.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUIHelper.h"
+#include "CmTexture.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	GUIButtonBase::GUIButtonBase(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions)
+		:GUIElement(parent, style, layoutOptions), mContent(content), mContentImageSprite(nullptr), mActiveState(GUIButtonState::Normal)
+	{
+		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
+		mTextSprite = cm_new<TextSprite, PoolAlloc>();
+
+		SpriteTexturePtr contentTex = content.getImage();
+		if(contentTex != nullptr)
+			mContentImageSprite = cm_new<ImageSprite, PoolAlloc>();
+
+		mImageDesc.texture = mStyle->normal.texture;
+
+		if(mImageDesc.texture != nullptr)
+		{
+			mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
+			mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
+		}
+
+		mImageDesc.borderLeft = mStyle->border.left;
+		mImageDesc.borderRight = mStyle->border.right;
+		mImageDesc.borderTop = mStyle->border.top;
+		mImageDesc.borderBottom = mStyle->border.bottom;
+	}
+
+	GUIButtonBase::~GUIButtonBase()
+	{
+		cm_delete<PoolAlloc>(mTextSprite);
+		cm_delete<PoolAlloc>(mImageSprite);
+
+		if(mContentImageSprite != nullptr)
+			cm_delete<PoolAlloc>(mContentImageSprite);
+	}
+
+	void GUIButtonBase::setContent(const GUIContent& content)
+	{
+		mContent = content;
+
+		markContentAsDirty();
+	}
+
+	void GUIButtonBase::_setOn(bool on) 
+	{ 
+		if(on)
+			setState((GUIButtonState)((INT32)mActiveState | 0x10)); 
+		else
+			setState((GUIButtonState)((INT32)mActiveState & (~0x10))); 
+	}
+
+	bool GUIButtonBase::_isOn() const
+	{
+		return ((INT32)mActiveState & 0x10) != 0;
+	}
+
+	UINT32 GUIButtonBase::getNumRenderElements() const
+	{
+		UINT32 numElements = mImageSprite->getNumRenderElements();
+		numElements += mTextSprite->getNumRenderElements();
+
+		if(mContentImageSprite != nullptr)
+			numElements += mContentImageSprite->getNumRenderElements();
+
+		return numElements;
+	}
+
+	const HMaterial& GUIButtonBase::getMaterial(UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		if(renderElementIdx >= contentImgSpriteIdx)
+			return mContentImageSprite->getMaterial(contentImgSpriteIdx - renderElementIdx);
+		else if(renderElementIdx >= textSpriteIdx)
+			return mTextSprite->getMaterial(textSpriteIdx - renderElementIdx);
+		else
+			return mImageSprite->getMaterial(renderElementIdx);
+	}
+
+	UINT32 GUIButtonBase::getNumQuads(UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		UINT32 numQuads = 0;
+		if(renderElementIdx >= contentImgSpriteIdx)
+			numQuads = mContentImageSprite->getNumQuads(contentImgSpriteIdx - renderElementIdx);
+		else if(renderElementIdx >= textSpriteIdx)
+			numQuads = mTextSprite->getNumQuads(textSpriteIdx - renderElementIdx);
+		else
+			numQuads = mImageSprite->getNumQuads(renderElementIdx);
+
+		return numQuads;
+	}
+
+	void GUIButtonBase::updateRenderElementsInternal()
+	{		
+		mImageDesc.width = mWidth;
+		mImageDesc.height = mHeight;
+
+		mImageSprite->update(mImageDesc);
+
+		mTextSprite->update(getTextDesc());
+
+		if(mContentImageSprite != nullptr)
+		{
+			IMAGE_SPRITE_DESC contentImgDesc;
+			contentImgDesc.texture = mContent.getImage();
+			contentImgDesc.width = mContent.getImage()->getTexture()->getWidth();
+			contentImgDesc.height = mContent.getImage()->getTexture()->getHeight();
+
+			mContentImageSprite->update(contentImgDesc);
+		}
+
+		GUIElement::updateRenderElementsInternal();
+	}
+
+	void GUIButtonBase::updateClippedBounds()
+	{
+		mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
+	}
+
+	Int2 GUIButtonBase::_getOptimalSize() const
+	{
+		UINT32 imageWidth = 0;
+		UINT32 imageHeight = 0;
+		if(mImageDesc.texture != nullptr)
+		{
+			imageWidth = mImageDesc.texture->getTexture()->getWidth();
+			imageHeight = mImageDesc.texture->getTexture()->getHeight();
+		}
+
+		Int2 contentSize = GUIHelper::calcOptimalContentsSize(mContent, *mStyle, _getLayoutOptions());
+		UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
+		UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
+
+		return Int2(contentWidth, contentHeight);
+	}
+
+	UINT32 GUIButtonBase::_getRenderElementDepth(UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		if(renderElementIdx >= contentImgSpriteIdx)
+			return _getDepth();
+		else if(renderElementIdx >= textSpriteIdx)
+			return _getDepth();
+		else
+			return _getDepth() + 1;
+	}
+
+	void GUIButtonBase::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
+		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
+	{
+		UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
+		UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
+
+		if(renderElementIdx < textSpriteIdx)
+		{
+			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
+				vertexStride, indexStride, renderElementIdx, mOffset, mClipRect);
+
+			return;
+		}
+
+		Rect contentBounds = getContentBounds();
+		Rect contentClipRect = getContentClipRect();
+		Rect textBounds = mTextSprite->getBounds(Int2(), Rect());
+
+		Int2 textOffset;
+		Rect textClipRect;
+
+		Int2 imageOffset;
+		Rect imageClipRect;
+		if(mContentImageSprite != nullptr)
+		{
+			Rect imageBounds = mContentImageSprite->getBounds(Int2(), Rect());
+			UINT32 freeWidth = (UINT32)std::max(0, contentBounds.width - textBounds.width - imageBounds.width);
+			INT32 imageXOffset = (INT32)(freeWidth / 2);
+
+			if(mStyle->imagePosition == GUIImagePosition::Right)
+			{
+				INT32 imageReservedWidth = std::max(0, contentBounds.width - textBounds.width);
+
+				textOffset = Int2(contentBounds.x, contentBounds.y);
+				textClipRect = contentClipRect;
+				textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
+
+				imageOffset = Int2(contentBounds.x + textBounds.width + imageXOffset, contentBounds.y);
+				imageClipRect = contentClipRect;
+				imageClipRect.x -= textBounds.width + imageXOffset;
+			}
+			else
+			{
+				INT32 imageReservedWidth = imageBounds.width + imageXOffset;
+
+				imageOffset = Int2(contentBounds.x + imageXOffset, contentBounds.y);
+				imageClipRect = contentClipRect;
+				imageClipRect.x -= imageXOffset;
+				imageClipRect.width = std::min(imageReservedWidth, imageClipRect.width);
+
+				textOffset = Int2(contentBounds.x + imageReservedWidth, contentBounds.y);
+				textClipRect = contentClipRect;
+				textClipRect.x -= imageReservedWidth;
+			}
+
+			INT32 imageYOffset = (contentBounds.height - imageBounds.height) / 2;
+			imageClipRect.y -= imageYOffset;
+			imageOffset.y += imageYOffset;
+		}
+		else
+		{
+			textOffset = Int2(contentBounds.x, contentBounds.y);
+			textClipRect = contentClipRect;
+		}
+
+		if(renderElementIdx >= contentImgSpriteIdx)
+		{
+			mContentImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
+				vertexStride, indexStride, contentImgSpriteIdx - renderElementIdx, imageOffset, imageClipRect);
+		}
+		else
+		{
+			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
+				vertexStride, indexStride, textSpriteIdx - renderElementIdx, textOffset, textClipRect);
+		}
+	}
+
+	bool GUIButtonBase::mouseEvent(const GUIMouseEvent& ev)
+	{
+		if(ev.getType() == GUIMouseEventType::MouseOver)
+		{
+			setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
+
+			if(!onHover.empty())
+				onHover();
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseOut)
+		{
+			setState(_isOn() ? GUIButtonState::NormalOn : GUIButtonState::Normal);
+
+			if(!onOut.empty())
+				onOut();
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDown)
+		{
+			setState(_isOn() ? GUIButtonState::ActiveOn : GUIButtonState::Active);
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseUp)
+		{
+			setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
+
+			if(!onClick.empty())
+				onClick();
+
+			return true;
+		}
+
+		return false;
+	}
+
+	TEXT_SPRITE_DESC GUIButtonBase::getTextDesc() const
+	{
+		TEXT_SPRITE_DESC textDesc;
+		textDesc.text = mContent.getText();
+		textDesc.font = mStyle->font;
+		textDesc.fontSize = mStyle->fontSize;
+
+		Rect textBounds = getContentBounds();
+
+		textDesc.width = textBounds.width;
+		textDesc.height = textBounds.height;
+		textDesc.horzAlign = mStyle->textHorzAlign;
+		textDesc.vertAlign = mStyle->textVertAlign;
+
+		return textDesc;
+	}
+
+	void GUIButtonBase::setState(GUIButtonState state)
+	{
+		switch(state)
+		{
+		case GUIButtonState::Normal:
+			mImageDesc.texture = mStyle->normal.texture;
+			break;
+		case GUIButtonState::Hover:
+			mImageDesc.texture = mStyle->hover.texture;
+			break;
+		case GUIButtonState::Active:
+			mImageDesc.texture = mStyle->active.texture;
+			break;
+		case GUIButtonState::Focused:
+			mImageDesc.texture = mStyle->focused.texture;
+			break;
+		case GUIButtonState::NormalOn:
+			mImageDesc.texture = mStyle->normalOn.texture;
+			break;
+		case GUIButtonState::HoverOn:
+			mImageDesc.texture = mStyle->hoverOn.texture;
+			break;
+		case GUIButtonState::ActiveOn:
+			mImageDesc.texture = mStyle->activeOn.texture;
+			break;
+		case GUIButtonState::FocusedOn:
+			mImageDesc.texture = mStyle->focusedOn.texture;
+			break;
+		}
+
+		markContentAsDirty();
+		mActiveState = state;
+	}
+}

+ 17 - 154
BansheeEngine/Source/BsGUIListBox.cpp

@@ -22,30 +22,14 @@ namespace BansheeEngine
 	}
 
 	GUIListBox::GUIListBox(GUIWidget& parent, const GUIElementStyle* style, const Vector<WString>::type& elements, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, style, layoutOptions), mElements(elements), mNumImageRenderElements(0), mSelectedIdx(0), mIsListBoxOpen(false)
+		:GUIButtonBase(parent, style, GUIContent(L""), layoutOptions), mElements(elements), mSelectedIdx(0), mIsListBoxOpen(false)
 	{
-		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
-		mTextSprite = cm_new<TextSprite, PoolAlloc>();
-
-		mImageDesc.texture = mStyle->normal.texture;
-
-		if(mImageDesc.texture != nullptr)
-		{
-			mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
-			mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
-		}
-
-		mImageDesc.borderLeft = mStyle->border.left;
-		mImageDesc.borderRight = mStyle->border.right;
-		mImageDesc.borderTop = mStyle->border.top;
-		mImageDesc.borderBottom = mStyle->border.bottom;
+		if(elements.size() > 0)
+			setContent(GUIContent(mElements[mSelectedIdx]));
 	}
 
 	GUIListBox::~GUIListBox()
 	{
-		cm_delete<PoolAlloc>(mTextSprite);
-		cm_delete<PoolAlloc>(mImageSprite);
-
 		closeListBox();
 	}
 
@@ -71,129 +55,21 @@ namespace BansheeEngine
 		return new (cm_alloc<GUIListBox, PoolAlloc>()) GUIListBox(parent, style, elements, layoutOptions);
 	}
 
-	UINT32 GUIListBox::getNumRenderElements() const
-	{
-		UINT32 numElements = mImageSprite->getNumRenderElements();
-		numElements += mTextSprite->getNumRenderElements();
-
-		return numElements;
-	}
-
-	const HMaterial& GUIListBox::getMaterial(UINT32 renderElementIdx) const
-	{
-		if(renderElementIdx >= mNumImageRenderElements)
-			return mTextSprite->getMaterial(mNumImageRenderElements - renderElementIdx);
-		else
-			return mImageSprite->getMaterial(renderElementIdx);
-	}
-
-	UINT32 GUIListBox::getNumQuads(UINT32 renderElementIdx) const
-	{
-		UINT32 numQuads = 0;
-		if(renderElementIdx >= mNumImageRenderElements)
-			numQuads = mTextSprite->getNumQuads(mNumImageRenderElements - renderElementIdx);
-		else
-			numQuads = mImageSprite->getNumQuads(renderElementIdx);
-
-		return numQuads;
-	}
-
-	void GUIListBox::updateRenderElementsInternal()
-	{		
-		mImageDesc.width = mWidth;
-		mImageDesc.height = mHeight;
-
-		mImageSprite->update(mImageDesc);
-		mNumImageRenderElements = mImageSprite->getNumRenderElements();
-
-		mTextSprite->update(getTextDesc());
-
-		GUIElement::updateRenderElementsInternal();
-	}
-
-	void GUIListBox::updateClippedBounds()
-	{
-		mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
-	}
-
-	Int2 GUIListBox::_getOptimalSize() const
-	{
-		UINT32 imageWidth = 0;
-		UINT32 imageHeight = 0;
-		if(mImageDesc.texture != nullptr)
-		{
-			imageWidth = mImageDesc.texture->getTexture()->getWidth();
-			imageHeight = mImageDesc.texture->getTexture()->getHeight();
-		}
-
-		Int2 contentSize = GUIHelper::calcOptimalContentsSize(mElements[mSelectedIdx], *mStyle, _getLayoutOptions());
-		UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
-		UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
-
-		return Int2(contentWidth, contentHeight);
-	}
-
-	UINT32 GUIListBox::_getRenderElementDepth(UINT32 renderElementIdx) const
-	{
-		if(renderElementIdx >= mNumImageRenderElements)
-			return _getDepth();
-		else
-			return _getDepth() + 1;
-	}
-
-	void GUIListBox::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
-		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
-	{
-		if(renderElementIdx >= mNumImageRenderElements)
-		{
-			Rect textBounds = getContentBounds();
-			Int2 offset(textBounds.x, textBounds.y);
-			Rect textClipRect = getContentClipRect();
-
-			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, mNumImageRenderElements - renderElementIdx, offset, textClipRect);
-		}
-		else
-		{
-			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, renderElementIdx, mOffset, mClipRect);
-		}
-	}
-
 	bool GUIListBox::mouseEvent(const GUIMouseEvent& ev)
 	{
-		if(ev.getType() == GUIMouseEventType::MouseOver)
-		{
-			mImageDesc.texture = mStyle->hover.texture;
-			markContentAsDirty();
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseOut)
-		{
-			mImageDesc.texture = mStyle->normal.texture;
-			markContentAsDirty();
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseDown)
-		{
-			mImageDesc.texture = mStyle->active.texture;
-			markContentAsDirty();
+		bool processed = GUIButtonBase::mouseEvent(ev);
 
-			openListBox();
-
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseUp)
+		if(ev.getType() == GUIMouseEventType::MouseDown)
 		{
-			mImageDesc.texture = mStyle->hover.texture;
-			markContentAsDirty();
+			if(!mIsListBoxOpen)
+				openListBox();
+			else
+				closeListBox();
 
-			return true;
+			processed = true;
 		}
 
-		return false;
+		return processed;
 	}
 
 	void GUIListBox::elementSelected(CM::UINT32 idx)
@@ -202,10 +78,9 @@ namespace BansheeEngine
 			onSelectionChanged(idx);
 
 		mSelectedIdx = idx;
+		setContent(GUIContent(mElements[idx]));
 
 		closeListBox();
-
-		markContentAsDirty();
 	}
 
 	void GUIListBox::openListBox()
@@ -230,6 +105,7 @@ namespace BansheeEngine
 		GUIManager::instance().addSelectiveInputWidget(dropDownBox.get());
 		GUIManager::instance().addSelectiveInputElement(this);
 
+		_setOn(true);
 		mIsListBoxOpen = true;
 	}
 
@@ -239,6 +115,8 @@ namespace BansheeEngine
 		{
 			GUIDropDownBoxManager::instance().closeDropDownBox();
 			GUIManager::instance().disableSelectiveInput();
+
+			_setOn(false);
 			mIsListBoxOpen = false;
 		}
 	}
@@ -246,23 +124,8 @@ namespace BansheeEngine
 	void GUIListBox::onListBoxClosed()
 	{
 		GUIManager::instance().disableSelectiveInput();
-		mIsListBoxOpen = false;
-	}
-
-	TEXT_SPRITE_DESC GUIListBox::getTextDesc() const
-	{
-		TEXT_SPRITE_DESC textDesc;
-		textDesc.text = mElements[mSelectedIdx];
-		textDesc.font = mStyle->font;
-		textDesc.fontSize = mStyle->fontSize;
 
-		Rect textBounds = getContentBounds();
-
-		textDesc.width = textBounds.width;
-		textDesc.height = textBounds.height;
-		textDesc.horzAlign = mStyle->textHorzAlign;
-		textDesc.vertAlign = mStyle->textVertAlign;
-
-		return textDesc;
+		_setOn(false);
+		mIsListBoxOpen = false;
 	}
 }

+ 7 - 162
BansheeEngine/Source/BsGUIToggle.cpp

@@ -21,24 +21,8 @@ namespace BansheeEngine
 	}
 
 	GUIToggle::GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, style, layoutOptions), mContent(content), mNumImageRenderElements(0), mIsToggled(false), mToggleGroup(nullptr)
+		:GUIButtonBase(parent, style, content, layoutOptions), mIsToggled(false), mToggleGroup(nullptr)
 	{
-		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
-		mTextSprite = cm_new<TextSprite, PoolAlloc>();
-
-		mImageDesc.texture = mStyle->normal.texture;
-
-		if(mImageDesc.texture != nullptr)
-		{
-			mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
-			mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
-		}
-
-		mImageDesc.borderLeft = mStyle->border.left;
-		mImageDesc.borderRight = mStyle->border.right;
-		mImageDesc.borderTop = mStyle->border.top;
-		mImageDesc.borderBottom = mStyle->border.bottom;
-
 		if(toggleGroup != nullptr)
 			toggleGroup->add(this);
 	}
@@ -49,9 +33,6 @@ namespace BansheeEngine
 		{
 			mToggleGroup->remove(this);
 		}
-
-		cm_delete<PoolAlloc>(mTextSprite);
-		cm_delete<PoolAlloc>(mImageSprite);
 	}
 
 	GUIToggle* GUIToggle::create(GUIWidget& parent, const WString& text, const GUIElementStyle* style)
@@ -172,8 +153,7 @@ namespace BansheeEngine
 			}
 		}
 
-		mImageDesc.texture = mStyle->normalOn.texture;
-		markContentAsDirty();
+		_setOn(true);
 	}
 
 	void GUIToggle::toggleOff()
@@ -207,159 +187,24 @@ namespace BansheeEngine
 			if(!onToggled.empty())
 				onToggled(mIsToggled);
 
-			mImageDesc.texture = mStyle->normal.texture;
-			markContentAsDirty();
-		}
-	}
-
-	UINT32 GUIToggle::getNumRenderElements() const
-	{
-		UINT32 numElements = mImageSprite->getNumRenderElements();
-		numElements += mTextSprite->getNumRenderElements();
-
-		return numElements;
-	}
-
-	const HMaterial& GUIToggle::getMaterial(UINT32 renderElementIdx) const
-	{
-		if(renderElementIdx >= mNumImageRenderElements)
-			return mTextSprite->getMaterial(mNumImageRenderElements - renderElementIdx);
-		else
-			return mImageSprite->getMaterial(renderElementIdx);
-	}
-
-	UINT32 GUIToggle::getNumQuads(UINT32 renderElementIdx) const
-	{
-		UINT32 numQuads = 0;
-		if(renderElementIdx >= mNumImageRenderElements)
-			numQuads = mTextSprite->getNumQuads(mNumImageRenderElements - renderElementIdx);
-		else
-			numQuads = mImageSprite->getNumQuads(renderElementIdx);
-
-		return numQuads;
-	}
-
-	void GUIToggle::updateRenderElementsInternal()
-	{		
-		mImageDesc.width = mWidth;
-		mImageDesc.height = mHeight;
-
-		mImageSprite->update(mImageDesc);
-		mNumImageRenderElements = mImageSprite->getNumRenderElements();
-
-		mTextSprite->update(getTextDesc());
-
-		GUIElement::updateRenderElementsInternal();
-	}
-
-	void GUIToggle::updateClippedBounds()
-	{
-		mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
-	}
-
-	Int2 GUIToggle::_getOptimalSize() const
-	{
-		UINT32 imageWidth = 0;
-		UINT32 imageHeight = 0;
-		if(mImageDesc.texture != nullptr)
-		{
-			imageWidth = mImageDesc.texture->getTexture()->getWidth();
-			imageHeight = mImageDesc.texture->getTexture()->getHeight();
-		}
-
-		Int2 contentSize = GUIHelper::calcOptimalContentsSize(mContent, *mStyle, _getLayoutOptions());
-		UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
-		UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
-
-		return Int2(contentWidth, contentHeight);
-	}
-
-	UINT32 GUIToggle::_getRenderElementDepth(UINT32 renderElementIdx) const
-	{
-		if(renderElementIdx >= mNumImageRenderElements)
-			return _getDepth();
-		else
-			return _getDepth() + 1;
-	}
-
-	void GUIToggle::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
-		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
-	{
-		if(renderElementIdx >= mNumImageRenderElements)
-		{
-			Rect textBounds = getContentBounds();
-			Int2 offset(textBounds.x, textBounds.y);
-			Rect textClipRect = getContentClipRect();
-
-			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, mNumImageRenderElements - renderElementIdx, offset, textClipRect);
-		}
-		else
-		{
-			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, 
-				vertexStride, indexStride, renderElementIdx, mOffset, mClipRect);
+			_setOn(false);
 		}
 	}
 
 	bool GUIToggle::mouseEvent(const GUIMouseEvent& ev)
 	{
-		if(ev.getType() == GUIMouseEventType::MouseOver)
-		{
-			if(mIsToggled)
-				mImageDesc.texture = mStyle->hoverOn.texture;
-			else
-				mImageDesc.texture = mStyle->hover.texture;
+		bool processed = GUIButtonBase::mouseEvent(ev);
 
-			markContentAsDirty();
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseOut)
-		{
-			if(mIsToggled)
-				mImageDesc.texture = mStyle->normalOn.texture;
-			else
-				mImageDesc.texture = mStyle->normal.texture;
-
-			markContentAsDirty();
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseDown)
-		{
-			if(mIsToggled)
-				mImageDesc.texture = mStyle->activeOn.texture;
-			else
-				mImageDesc.texture = mStyle->active.texture;
-
-			markContentAsDirty();
-			return true;
-		}
-		else if(ev.getType() == GUIMouseEventType::MouseUp)
+		if(ev.getType() == GUIMouseEventType::MouseUp)
 		{
 			if(mIsToggled)
 				toggleOff();
 			else
 				toggleOn();
 
-			return true;
+			processed = true;
 		}
 
-		return false;
-	}
-
-	TEXT_SPRITE_DESC GUIToggle::getTextDesc() const
-	{
-		TEXT_SPRITE_DESC textDesc;
-		textDesc.text = mContent.getText();
-		textDesc.font = mStyle->font;
-		textDesc.fontSize = mStyle->fontSize;
-
-		Rect textBounds = getContentBounds();
-
-		textDesc.width = textBounds.width;
-		textDesc.height = textBounds.height;
-		textDesc.horzAlign = mStyle->textHorzAlign;
-		textDesc.vertAlign = mStyle->textVertAlign;
-
-		return textDesc;
+		return processed;
 	}
 }

+ 2 - 0
CamelotClient/Include/BsGUIMenuBar.h

@@ -32,6 +32,7 @@ namespace BansheeEditor
 
 		CM::Vector<GUIMenuBarData>::type mChildMenus;
 
+		BS::GUIButton* mSubMenuButton;
 		bool mSubMenuOpen;
 
 		const GUIMenuBarData* getSubMenu(const CM::WString& name) const;
@@ -48,6 +49,7 @@ namespace BansheeEditor
 		void openSubMenu(const CM::WString& name);
 		void closeSubMenu();
 
+		void onSubMenuHover(const CM::WString& name);
 		void onSubMenuClosed();
 	};
 }

+ 25 - 2
CamelotClient/Source/BsGUIMenuBar.cpp

@@ -17,7 +17,7 @@ using namespace BansheeEngine;
 namespace BansheeEditor
 {
 	GUIMenuBar::GUIMenuBar(BS::GUIWidget* parent)
-		:mParentWidget(parent), mMainArea(nullptr), mBackgroundArea(nullptr), mBgTexture(nullptr), mLogoTexture(nullptr), mSubMenuOpen(false)
+		:mParentWidget(parent), mMainArea(nullptr), mBackgroundArea(nullptr), mBgTexture(nullptr), mLogoTexture(nullptr), mSubMenuOpen(false), mSubMenuButton(nullptr)
 	{
 		mBackgroundArea = GUIArea::create(*parent, 0, 0, 1, 13, 9900);
 		mMainArea = GUIArea::create(*parent, 0, 0, 1, 13, 9899);
@@ -76,6 +76,7 @@ namespace BansheeEditor
 
 			GUIButton* newButton = GUIButton::create(*mParentWidget, rootName, EngineGUI::instance().getSkin().getStyle("MenuBarBtn"));
 			newButton->onClick.connect(boost::bind(&GUIMenuBar::openSubMenu, this, rootName));
+			newButton->onHover.connect(boost::bind(&GUIMenuBar::onSubMenuHover, this, rootName));
 			mMainArea->getLayout().insertElement(mMainArea->getLayout().getNumChildren() - 1, newButton);
 
 			newSubMenu.button = newButton;
@@ -105,6 +106,7 @@ namespace BansheeEditor
 
 			GUIButton* newButton = GUIButton::create(*mParentWidget, rootName, EngineGUI::instance().getSkin().getStyle("MenuBarBtn"));
 			newButton->onClick.connect(boost::bind(&GUIMenuBar::openSubMenu, this, rootName));
+			newButton->onHover.connect(boost::bind(&GUIMenuBar::onSubMenuHover, this, rootName));
 			mMainArea->getLayout().insertElement(mMainArea->getLayout().getNumChildren() - 1, newButton);
 
 			newSubMenu.button = newButton;
@@ -208,7 +210,15 @@ namespace BansheeEditor
 		if(subMenu == nullptr)
 			return;
 
-		closeSubMenu();
+		if(mSubMenuOpen)
+		{
+			bool closingExisting = subMenu->button == mSubMenuButton;
+
+			closeSubMenu();
+
+			if(closingExisting)
+				return;
+		}
 
 		Vector<GUIDropDownData>::type dropDownData = subMenu->menu->getDropDownData();
 		GUIWidget& widget = subMenu->button->_getParentWidget();
@@ -226,6 +236,9 @@ namespace BansheeEditor
 			GUIManager::instance().addSelectiveInputElement(childMenu.button);
 		}
 
+		subMenu->button->_setOn(true);
+
+		mSubMenuButton = subMenu->button;
 		mSubMenuOpen = true;
 	}
 
@@ -236,14 +249,24 @@ namespace BansheeEditor
 			GUIDropDownBoxManager::instance().closeDropDownBox();
 			GUIManager::instance().disableSelectiveInput();
 			
+			mSubMenuButton->_setOn(false);
 			mSubMenuOpen = false;
 		}		
 	}
 
+	void GUIMenuBar::onSubMenuHover(const CM::WString& name)
+	{
+		if(mSubMenuOpen)
+		{
+			openSubMenu(name);
+		}
+	}
+
 	void GUIMenuBar::onSubMenuClosed()
 	{
 		GUIManager::instance().disableSelectiveInput();
 
+		mSubMenuButton->_setOn(false);
 		mSubMenuOpen = false;
 	}
 }

+ 1 - 0
CamelotClient/Source/BsMainEditorWindow.cpp

@@ -30,6 +30,7 @@ namespace BansheeEditor
 		mMenuBar->addMenuItem(L"File/Recent projects/Project C", nullptr);
 		mMenuBar->addSeparator(L"File");
 		mMenuBar->addMenuItem(L"File/Exit", nullptr);
+		mMenuBar->addMenuItem(L"Window/Scene", nullptr);
 
 		// DEBUG ONLY
 

+ 7 - 8
DropDown.txt

@@ -2,13 +2,6 @@ GUI ignores image in GUIContent for most elements.
 
 Doesn't belong here but as an additional reminder: Remove Component::initialize and instead make multi paramter addComponent
 
-Attempt to get rid of selective input click callback. It's too specific.
- - Instead of using selective input I might be able to block all input using event filter (by adding return values to them)
-   - Some kind of GUISelectiveInput class? I think it makes sense to move it out of GUIManager
-
-I will need a GUIDropDownBoxManager because otherwise I have no way of ensuring only one drop down box is active at a time
- Principle remains the same, I call GUIDropDownBox::open and internally it calls the manager
-
 Once drop down menu is open the initial button (for listbox and menubar) should remain in active state
 Mousing over other buttons in menu bar should open their menus
 
@@ -17,4 +10,10 @@ ListBox: Clicking on ListBox once the DropDownBox is open should close the drop
 Add min/max/close buttons to GUIMenuBar
 Setup move areas in GUIMenuBar
 
-Test scrollUp/scrollDown
+Test scrollUp/scrollDown
+
+
+Design improvements:
+Attempt to get rid of selective input click callback in GUIManager. It's too specific.
+ - Instead of using selective input I might be able to block all input using event filter (by adding return values to them)
+   - Some kind of GUISelectiveInput class? I think it makes sense to move it out of GUIManager