Просмотр исходного кода

Added GUIInputBox
Made MouseUp event returns the element to hover state instead of normal state

Marko Pintera 12 лет назад
Родитель
Сommit
d66d278031

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -151,6 +151,7 @@
     <ClInclude Include="Include\BsEngineGUI.h" />
     <ClInclude Include="Include\BsGUIArea.h" />
     <ClInclude Include="Include\BsGUIButton.h" />
+    <ClInclude Include="Include\BsGUIInputBox.h" />
     <ClInclude Include="Include\BsGUILayoutOptions.h" />
     <ClInclude Include="Include\BsGUILayoutX.h" />
     <ClInclude Include="Include\BsGUILayout.h" />
@@ -192,6 +193,7 @@
     <ClCompile Include="Source\BsGUIArea.cpp" />
     <ClCompile Include="Source\BsGUIButton.cpp" />
     <ClCompile Include="Source\BsGUIElement.cpp" />
+    <ClCompile Include="Source\BsGUIInputBox.cpp" />
     <ClCompile Include="Source\BsGUILabel.cpp" />
     <ClCompile Include="Source\BsGUILayout.cpp" />
     <ClCompile Include="Source\BsGUILayoutY.cpp" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -150,6 +150,9 @@
     <ClInclude Include="Include\BsGUIWindowFrame.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIInputBox.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -245,5 +248,8 @@
     <ClCompile Include="Source\BsGUIWindowFrame.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIInputBox.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 4 - 0
BansheeEngine/Include/BsEngineGUI.h

@@ -38,5 +38,9 @@ namespace BansheeEngine
 
 		static const CM::String ButtonNormalTex;
 		static const CM::String ButtonHoverTex;
+
+		static const CM::String InputBoxNormalTex;
+		static const CM::String InputBoxHoverTex;
+		static const CM::String InputBoxFocusedTex;
 	};
 }

+ 61 - 0
BansheeEngine/Include/BsGUIInputBox.h

@@ -0,0 +1,61 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIElement.h"
+#include "BsImageSprite.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIInputBox : public GUIElement
+	{
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUIInputBox* create(GUIWidget& parent, const GUIElementStyle* style = nullptr);
+		static GUIInputBox* create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
+	protected:
+		~GUIInputBox();
+
+		/**
+		 * @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();
+
+		virtual CM::UINT32 _getOptimalWidth() const;
+		virtual CM::UINT32 _getOptimalHeight() const;
+
+		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
+	private:
+		ImageSprite* mImageSprite;
+		TextSprite* mTextSprite;
+		CM::UINT32 mNumImageRenderElements;
+
+		IMAGE_SPRITE_DESC mImageDesc;
+		CM::String mText;
+
+		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions);
+
+		virtual bool mouseEvent(const GUIMouseEvent& ev);
+	};
+}

+ 1 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -34,6 +34,7 @@ namespace BansheeEngine
 	class GUITexture;
 	class GUIToggle;
 	class GUIWindowFrame;
+	class GUIInputBox;
 	class GUISkin;
 	struct GUIElementStyle;
 	class GUIMouseEvent;

+ 35 - 0
BansheeEngine/Source/BsEngineGUI.cpp

@@ -3,6 +3,7 @@
 
 #include "BsGUILabel.h"
 #include "BsGUIButton.h"
+#include "BsGUIInputBox.h"
 #include "BsTextSprite.h"
 #include "BsSpriteTexture.h"
 
@@ -37,6 +38,10 @@ namespace BansheeEngine
 	const String EngineGUI::TabbedBarBtnNormal = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\TabbedButtonNormal.psd";
 	const String EngineGUI::TabbedBarBtnActive = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\TabbedButtonActive.psd";
 
+	const String EngineGUI::InputBoxNormalTex = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\InputBoxNormal.psd";
+	const String EngineGUI::InputBoxHoverTex = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\InputBoxHover.psd";
+	const String EngineGUI::InputBoxFocusedTex = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\InputBoxFocused.psd";
+
 	EngineGUI::EngineGUI()
 	{
 		// TODO - Normally I want to load this from some file
@@ -189,6 +194,36 @@ namespace BansheeEngine
 		winCloseButtonStyle.width = 8;
 
 		mSkin.setStyle("WinCloseBtn", winCloseButtonStyle);
+
+		// Input box
+		HTexture inputBoxNormalTex = static_resource_cast<Texture>(Importer::instance().import(InputBoxNormalTex));
+		HTexture inputBoxHoverTex = static_resource_cast<Texture>(Importer::instance().import(InputBoxHoverTex));
+		HTexture inputBoxFocusedTex = static_resource_cast<Texture>(Importer::instance().import(InputBoxFocusedTex));
+
+		GUIElementStyle inputBoxStyle;
+		inputBoxStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(inputBoxNormalTex));
+		inputBoxStyle.hover.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(inputBoxHoverTex));
+		inputBoxStyle.focused.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(inputBoxFocusedTex));
+		inputBoxStyle.active.texture = inputBoxStyle.focused.texture;
+		inputBoxStyle.border.left = 5;
+		inputBoxStyle.border.right = 5;
+		inputBoxStyle.border.top = 5;
+		inputBoxStyle.border.bottom = 5;
+		inputBoxStyle.margins.left = 4;
+		inputBoxStyle.margins.right = 4;
+		inputBoxStyle.margins.top = 4;
+		inputBoxStyle.margins.bottom = 4;
+		inputBoxStyle.contentOffset.left = 2;
+		inputBoxStyle.contentOffset.right = 2;
+		inputBoxStyle.fixedHeight = true;
+		inputBoxStyle.height = 21;
+		inputBoxStyle.minWidth = 10;
+		inputBoxStyle.font = font;
+		inputBoxStyle.fontSize = DefaultFontSize;
+		inputBoxStyle.textHorzAlign = THA_Left;
+		inputBoxStyle.textVertAlign = TVA_Top;
+
+		mSkin.setStyle(GUIInputBox::getGUITypeName(), inputBoxStyle);
 	}
 
 }

+ 1 - 1
BansheeEngine/Source/BsGUIButton.cpp

@@ -190,7 +190,7 @@ namespace BansheeEngine
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseUp)
 		{
-			mImageDesc.texture = mStyle->normal.texture;
+			mImageDesc.texture = mStyle->hover.texture;
 			markAsDirty();
 
 			return true;

+ 201 - 0
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -0,0 +1,201 @@
+#include "BsGUIInputBox.h"
+#include "BsImageSprite.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsSpriteTexture.h"
+#include "BsTextSprite.h"
+#include "BsGUILayoutOptions.h"
+#include "BsGUIMouseEvent.h"
+#include "CmTexture.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	const String& GUIInputBox::getGUITypeName()
+	{
+		static String name = "InputBox";
+		return name;
+	}
+
+	GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
+		:GUIElement(parent, style, layoutOptions), mNumImageRenderElements(0)
+	{
+		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;
+	}
+
+	GUIInputBox::~GUIInputBox()
+	{
+		cm_delete<PoolAlloc>(mTextSprite);
+		cm_delete<PoolAlloc>(mImageSprite);
+	}
+
+	GUIInputBox* GUIInputBox::create(GUIWidget& parent, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, getDefaultLayoutOptions(style));
+	}
+
+	GUIInputBox* GUIInputBox::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, layoutOptions);
+	}
+
+	UINT32 GUIInputBox::getNumRenderElements() const
+	{
+		UINT32 numElements = mImageSprite->getNumRenderElements();
+		numElements += mTextSprite->getNumRenderElements();
+
+		return numElements;
+	}
+
+	const HMaterial& GUIInputBox::getMaterial(UINT32 renderElementIdx) const
+	{
+		if(renderElementIdx >= mNumImageRenderElements)
+			return mTextSprite->getMaterial(mNumImageRenderElements - renderElementIdx);
+		else
+			return mImageSprite->getMaterial(renderElementIdx);
+	}
+
+	UINT32 GUIInputBox::getNumQuads(UINT32 renderElementIdx) const
+	{
+		UINT32 numQuads = 0;
+		if(renderElementIdx >= mNumImageRenderElements)
+			numQuads = mTextSprite->getNumQuads(mNumImageRenderElements - renderElementIdx);
+		else
+			numQuads = mImageSprite->getNumQuads(renderElementIdx);
+
+		return numQuads;
+	}
+
+	void GUIInputBox::updateRenderElementsInternal()
+	{		
+		mImageDesc.offset = mOffset;
+		mImageDesc.width = mWidth;
+		mImageDesc.height = mHeight;
+		mImageDesc.clipRect = mClipRect;
+
+		mImageSprite->update(mImageDesc);
+		mBounds = mImageSprite->getBounds();
+		mNumImageRenderElements = mImageSprite->getNumRenderElements();
+
+		TEXT_SPRITE_DESC textDesc;
+		textDesc.text = mText;
+		textDesc.font = mStyle->font;
+		textDesc.fontSize = mStyle->fontSize;
+
+		Rect contentBounds = mBounds;
+
+		contentBounds.x += mStyle->margins.left + mStyle->contentOffset.left;
+		contentBounds.y += mStyle->margins.top + mStyle->contentOffset.top;
+		contentBounds.width = (UINT32)std::max(0, (INT32)contentBounds.width - 
+			(INT32)(mStyle->margins.left + mStyle->margins.right + mStyle->contentOffset.left + mStyle->contentOffset.right));
+		contentBounds.height = (UINT32)std::max(0, (INT32)contentBounds.height - 
+			(INT32)(mStyle->margins.top + mStyle->margins.bottom + mStyle->contentOffset.top + mStyle->contentOffset.bottom));
+
+		textDesc.offset = Int2(contentBounds.x, contentBounds.y);
+		textDesc.width = contentBounds.width;
+		textDesc.height = contentBounds.height;
+		textDesc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
+		textDesc.horzAlign = mStyle->textHorzAlign;
+		textDesc.vertAlign = mStyle->textVertAlign;
+
+		mTextSprite->update(textDesc);
+	}
+
+	UINT32 GUIInputBox::_getOptimalWidth() const
+	{
+		if(mImageDesc.texture != nullptr)
+		{
+			return mImageDesc.texture->getTexture()->getWidth();
+		}
+
+		return 0;
+	}
+
+	UINT32 GUIInputBox::_getOptimalHeight() const
+	{
+		if(mImageDesc.texture != nullptr)
+		{
+			return mImageDesc.texture->getTexture()->getHeight();
+		}
+
+		return 0;
+	}
+
+	UINT32 GUIInputBox::_getRenderElementDepth(UINT32 renderElementIdx) const
+	{
+		if(renderElementIdx >= mNumImageRenderElements)
+			return _getDepth();
+		else
+			return _getDepth() + 1;
+	}
+
+	void GUIInputBox::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
+		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
+	{
+		if(renderElementIdx >= mNumImageRenderElements)
+			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, mNumImageRenderElements - renderElementIdx);
+		else
+			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx);
+	}
+
+	bool GUIInputBox::mouseEvent(const GUIMouseEvent& ev)
+	{
+		if(ev.getType() == GUIMouseEventType::MouseOver)
+		{
+			mImageDesc.texture = mStyle->hover.texture;
+			markAsDirty();
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseOut)
+		{
+			mImageDesc.texture = mStyle->normal.texture;
+			markAsDirty();
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDown)
+		{
+			mImageDesc.texture = mStyle->active.texture;
+			markAsDirty();
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseUp)
+		{
+			mImageDesc.texture = mStyle->hover.texture;
+			markAsDirty();
+
+			return true;
+		}
+
+		return false;
+	}
+}

+ 4 - 2
CamelotClient/CmEditorWindow.cpp

@@ -8,6 +8,7 @@
 #include "BsGUILabel.h"
 #include "BsGUIWindowFrameWidget.h"
 #include "BsGUIButton.h"
+#include "BsGUIInputBox.h"
 #include "BsGUITexture.h"
 #include "BsGUISkin.h"
 #include "BsGUILayout.h"
@@ -64,12 +65,13 @@ namespace BansheeEditor
 
 		//// DEBUG
 		
-		//GUIArea* dbgArea = GUIArea::create(*mGUI, 0, 13, 0, 0, 1998);
-		//GUILayout& layout = dbgArea->getLayout();
+		GUIArea* dbgArea = GUIArea::create(*mGUI, 0, 13, 0, 0, 475);
+		GUILayout& layout = dbgArea->getLayout();
 		//
 		//mDbgLabel = GUILabel::create(*mGUI, "Testing test");
 		//layout.addElement(mDbgLabel);
 
+		layout.addElement(GUIInputBox::create(*mGUI));
 		
 		//GUIFlexibleSpace& space4 = otherLayout.addFlexibleSpace();
 		//otherLayout.addElement(mDbgLabel);

+ 10 - 0
TODO.txt

@@ -32,6 +32,16 @@ IMMEDIATE:
     - Make sure GUI system uses a dummy texture if one isn't available
 	- SpriteTexture keeps a static reference to DUmmyTexture which I need to release before shutdown
 
+TextBox needed elements:
+ - Render element: background texture and a text sprite
+ - Keyboard focus: Mouse click gives focus, tabbing between elements gives focus (TODO: explore how will tabbing work)
+ - Input: All characters get forwarded to element with focus
+ - Input cursor - Mouse click within box moves input, arrow keys move input. If cursor goes out of clip limits, move text.
+   - CURSOR needs to flash. Implement it as a GUIManager::showInputCursor(depth, position) method instead of doing it in GUITextBox.
+ - Text selection - Shift + arrow keys select text. Mouse drag selects text, and scrolls the text if needed. Text selection is drawn using another single color image sprite. Selection height is determined by line height.
+ - Text operations - Select all, copy, paste (using shorcuts, possibly also context menu?)
+ - Make sure multi-line text boxes work fine as well.
+
 Window needed systems:
 EditorWindowContainer
  - TabbedTitleBar widget