Explorar o código

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 %!s(int64=12) %!d(string=hai) anos
pai
achega
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