Prechádzať zdrojové kódy

Reworked how input caret works
Started work on selection

Marko Pintera 12 rokov pred
rodič
commit
cc9f250d1a

+ 3 - 2
BansheeEngine/BansheeEngine.vcxproj

@@ -151,8 +151,9 @@
     <ClInclude Include="Include\BsEngineGUI.h" />
     <ClInclude Include="Include\BsGUIArea.h" />
     <ClInclude Include="Include\BsGUIButton.h" />
+    <ClInclude Include="Include\BsGUICommandEvent.h" />
     <ClInclude Include="Include\BsGUIInputBox.h" />
-    <ClInclude Include="Include\BsGUIKeyEvent.h" />
+    <ClInclude Include="Include\BsGUIButtonEvent.h" />
     <ClInclude Include="Include\BsGUILayoutOptions.h" />
     <ClInclude Include="Include\BsGUILayoutX.h" />
     <ClInclude Include="Include\BsGUILayout.h" />
@@ -196,7 +197,7 @@
     <ClCompile Include="Source\BsGUIButton.cpp" />
     <ClCompile Include="Source\BsGUIElement.cpp" />
     <ClCompile Include="Source\BsGUIInputBox.cpp" />
-    <ClCompile Include="Source\BsGUIKeyEvent.cpp" />
+    <ClCompile Include="Source\BsGUIButtonEvent.cpp" />
     <ClCompile Include="Source\BsGUILabel.cpp" />
     <ClCompile Include="Source\BsGUILayout.cpp" />
     <ClCompile Include="Source\BsGUILayoutY.cpp" />

+ 7 - 4
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -153,10 +153,13 @@
     <ClInclude Include="Include\BsGUIInputBox.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGUIKeyEvent.h">
+    <ClInclude Include="Include\BsGUIWindowMover.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGUIWindowMover.h">
+    <ClInclude Include="Include\BsGUICommandEvent.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsGUIButtonEvent.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
   </ItemGroup>
@@ -260,10 +263,10 @@
     <ClCompile Include="Source\BsGUIMouseEvent.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsGUIKeyEvent.cpp">
+    <ClCompile Include="Source\BsGUIWindowMover.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsGUIWindowMover.cpp">
+    <ClCompile Include="Source\BsGUIButtonEvent.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
   </ItemGroup>

+ 2 - 2
BansheeEngine/Include/BsGUIKeyEvent.h → BansheeEngine/Include/BsGUIButtonEvent.h

@@ -13,10 +13,10 @@ namespace BansheeEngine
 		TextInput
 	};
 
-	class BS_EXPORT GUIKeyEvent
+	class BS_EXPORT GUIButtonEvent
 	{
 	public:
-		GUIKeyEvent();
+		GUIButtonEvent();
 
 		GUIKeyEventType getType() const { return mType; }
 		CM::ButtonCode getKey() const { return mKey; }

+ 27 - 0
BansheeEngine/Include/BsGUICommandEvent.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+
+namespace BansheeEngine
+{
+	enum class GUICommandEventType
+	{
+		Redraw
+	};
+
+	class BS_EXPORT GUICommandEvent
+	{
+	public:
+		GUICommandEvent()
+			:mType(GUICommandEventType::Redraw)
+		{ }
+
+		GUICommandEventType getType() const { return mType; }
+	private:
+		friend class GUIManager;
+
+		GUICommandEventType mType;
+
+		void setRedrawData() { mType = GUICommandEventType::Redraw; }
+	};
+}

+ 2 - 1
BansheeEngine/Include/BsGUIElement.h

@@ -74,7 +74,8 @@ namespace BansheeEngine
 		void updateRenderElements();
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		virtual bool keyEvent(const GUIKeyEvent& ev);
+		virtual bool buttonEvent(const GUIButtonEvent& ev);
+		virtual bool commandEvent(const GUICommandEvent& ev);
 
 		static void destroy(GUIElement* element);
 

+ 23 - 1
BansheeEngine/Include/BsGUIInputBox.h

@@ -49,17 +49,39 @@ namespace BansheeEngine
 		virtual void _setFocus(bool focus);
 	private:
 		ImageSprite* mImageSprite;
+		ImageSprite* mCaretSprite;
 		TextSprite* mTextSprite;
+		CM::Vector<ImageSprite*>::type mSelectionSprites;
 		CM::UINT32 mNumImageRenderElements;
 		bool mInputCursorSet;
 		bool mDragInProgress;
 
+		CM::UINT32 mSelectionStart;
+		CM::UINT32 mSelectionEnd;
+		CM::UINT32 mCaretPos;
+		bool mCaretShown;
+		bool mSelectionShown;
+
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::WString mText;
 
 		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		virtual bool keyEvent(const GUIKeyEvent& ev);
+		virtual bool buttonEvent(const GUIButtonEvent& ev);
+		virtual bool commandEvent(const GUICommandEvent& ev);
+
+		Sprite* renderElemToSprite(CM::UINT32 renderElemIdx, CM::UINT32& localRenderElemIdx) const;
+
+		void showCaret(CM::UINT32 charIdx);
+		void clearCaret();
+		CM::Int2 getCaretPosition() const;
+		CM::UINT32 getCaretHeight() const;
+
+		void showSelection(CM::UINT32 startChar, CM::UINT32 endChar);
+		void clearSelection();
+		CM::Vector<CM::Rect>::type getSelectionRects() const;
+
+		CM::UINT32 getCharAtPosition(const CM::Int2& pos) const;
 	};
 }

+ 7 - 34
BansheeEngine/Include/BsGUIManager.h

@@ -2,7 +2,8 @@
 
 #include "BsPrerequisites.h"
 #include "BsGUIMouseEvent.h"
-#include "BsGUIKeyEvent.h"
+#include "BsGUIButtonEvent.h"
+#include "BsGUICommandEvent.h"
 #include "CmModule.h"
 #include "CmColor.h"
 #include "CmInput.h"
@@ -38,28 +39,9 @@ namespace BansheeEngine
 		void update();
 		void render(CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor);
 
-		/**
-		 * @brief	Starts rendering the input caret at the specified coordinates.
-		 * 			The coordinates represent top left corner of the caret, relative to the
-		 * 			provided widget.
-		 */
-		void showCaret(GUIWidget* widget, CM::INT32 x, CM::INT32 y, CM::UINT32 depth);
-
-		/**
-		 * @brief	Hides the input caret.
-		 */
-		void hideCaret() { mCaretShown = false; }
-
-		void setCaretWidth(CM::UINT32 width) { mCaretWidth = width; updateCaretSprite(); }
-		void setCaretHeight(CM::UINT32 height) { mCaretHeight = height; updateCaretSprite(); }
-
-		/**
-		 * @brief	Determines how fast the caret blinks.
-		 *
-		 * @param	interval	Blinking interval in seconds.
-		 */
-		void setCaretBlinkInterval(float interval) { mCaretBlinkInterval = interval; }
 		void setCaretColor(const CM::Color& color) { mCaretColor = color; updateCaretTexture(); }
+		const SpriteTexturePtr& getCaretTexture() const { return mCaretTexture; }
+		bool getCaretBlinkState() const { return mIsCaretOn; }
 
 	private:
 		CM::Vector<GUIWidget*>::type mWidgets;
@@ -82,22 +64,14 @@ namespace BansheeEngine
 		CM::Int2 mLastCursorLocalPos;
 
 		GUIMouseEvent mMouseEvent;
-		GUIKeyEvent mKeyEvent;
+		GUIButtonEvent mKeyEvent;
+		GUICommandEvent mCommandEvent;
 
-		// Caret related
-		ImageSprite* mCaretSprite;
 		SpriteTexturePtr mCaretTexture;
-		CM::HMesh mCaretMesh;
-		CM::HMaterial mCaretMaterial;
-
-		GUIWidget* mCaretOwnerWidget;
-		bool mCaretShown;
-		CM::INT32 mCaretX, mCaretY;
-		CM::UINT32 mCaretDepth;
-		CM::UINT32 mCaretWidth, mCaretHeight;
 		CM::Color mCaretColor;
 		float mCaretBlinkInterval;
 		float mCaretLastBlinkTime;
+		bool mIsCaretOn;
 
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
@@ -111,7 +85,6 @@ namespace BansheeEngine
 		void renderMesh(const CM::HMesh& mesh, const CM::HMaterial& material, const CM::Matrix4& tfrm, CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor);
 
 		void updateMeshes();
-		void updateCaretSprite();
 		void updateCaretTexture();
 
 		void onButtonDown(const CM::ButtonEvent& event);

+ 1 - 1
BansheeEngine/Include/BsGUIWidget.h

@@ -55,7 +55,7 @@ namespace BansheeEngine
 		 * @brief	Forwards the specified key event to the specified element. The element
 		 * 			must be a child of this widget.
 		 */
-		virtual bool _keyEvent(GUIElement* element, const GUIKeyEvent& ev);
+		virtual bool _keyEvent(GUIElement* element, const GUIButtonEvent& ev);
 	protected:
 		friend class CM::SceneObject;
 		friend class GUIElement;

+ 2 - 1
BansheeEngine/Include/BsPrerequisites.h

@@ -39,7 +39,8 @@ namespace BansheeEngine
 	class GUISkin;
 	struct GUIElementStyle;
 	class GUIMouseEvent;
-	class GUIKeyEvent;
+	class GUIButtonEvent;
+	class GUICommandEvent;
 	class GUIArea;
 	class GUILayout;
 	class GUILayoutX;

+ 5 - 5
BansheeEngine/Source/BsGUIKeyEvent.cpp → BansheeEngine/Source/BsGUIButtonEvent.cpp

@@ -1,30 +1,30 @@
-#include "BsGUIKeyEvent.h"
+#include "BsGUIButtonEvent.h"
 
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	GUIKeyEvent::GUIKeyEvent()
+	GUIButtonEvent::GUIButtonEvent()
 		:mType(GUIKeyEventType::KeyDown), mKey(BC_0)
 	{
 
 	}
 
-	void GUIKeyEvent::setKeyDownData(ButtonCode key)
+	void GUIButtonEvent::setKeyDownData(ButtonCode key)
 	{
 		mType = GUIKeyEventType::KeyDown;
 		mKey = key;
 		mInputChar = 0;
 	}
 
-	void GUIKeyEvent::setKeyUpData(ButtonCode key)
+	void GUIButtonEvent::setKeyUpData(ButtonCode key)
 	{
 		mType = GUIKeyEventType::KeyUp;
 		mKey = key;
 		mInputChar = 0;
 	}
 
-	void GUIKeyEvent::setTextInputData(UINT32 inputChar)
+	void GUIButtonEvent::setTextInputData(UINT32 inputChar)
 	{
 		mType = GUIKeyEventType::TextInput;
 		mKey = BC_0;

+ 6 - 1
BansheeEngine/Source/BsGUIElement.cpp

@@ -50,7 +50,12 @@ namespace BansheeEngine
 		return false;
 	}
 
-	bool GUIElement::keyEvent(const GUIKeyEvent& ev)
+	bool GUIElement::buttonEvent(const GUIButtonEvent& ev)
+	{
+		return false;
+	}
+
+	bool GUIElement::commandEvent(const GUICommandEvent& ev)
 	{
 		return false;
 	}

+ 204 - 22
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -1,12 +1,14 @@
 #include "BsGUIInputBox.h"
+#include "BsGUIManager.h"
 #include "BsImageSprite.h"
 #include "BsGUIWidget.h"
 #include "BsGUISkin.h"
 #include "BsSpriteTexture.h"
 #include "BsTextSprite.h"
 #include "BsGUILayoutOptions.h"
-#include "BsGUIKeyEvent.h"
+#include "BsGUIButtonEvent.h"
 #include "BsGUIMouseEvent.h"
+#include "BsGUICommandEvent.h"
 #include "CmTexture.h"
 #include "CmCursor.h"
 
@@ -21,9 +23,11 @@ namespace BansheeEngine
 	}
 
 	GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, style, layoutOptions), mNumImageRenderElements(0), mInputCursorSet(false), mDragInProgress(false)
+		:GUIElement(parent, style, layoutOptions), mNumImageRenderElements(0), mInputCursorSet(false), mDragInProgress(false),
+		mSelectionStart(0), mSelectionEnd(0), mCaretSprite(nullptr), mCaretShown(false), mSelectionShown(false), mCaretPos(0)
 	{
 		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
+		mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
 		mTextSprite = cm_new<TextSprite, PoolAlloc>();
 
 		mImageDesc.texture = mStyle->normal.texture;
@@ -43,6 +47,7 @@ namespace BansheeEngine
 	GUIInputBox::~GUIInputBox()
 	{
 		cm_delete<PoolAlloc>(mTextSprite);
+		cm_delete<PoolAlloc>(mCaretSprite);
 		cm_delete<PoolAlloc>(mImageSprite);
 	}
 
@@ -73,26 +78,34 @@ namespace BansheeEngine
 		UINT32 numElements = mImageSprite->getNumRenderElements();
 		numElements += mTextSprite->getNumRenderElements();
 
+		if(mCaretShown && GUIManager::instance().getCaretBlinkState())
+			numElements += mTextSprite->getNumRenderElements();
+
+		if(mSelectionShown)
+		{
+			for(auto& selectionSprite : mSelectionSprites)
+			{
+				numElements += selectionSprite->getNumRenderElements();
+			}
+		}
+
 		return numElements;
 	}
 
 	const HMaterial& GUIInputBox::getMaterial(UINT32 renderElementIdx) const
 	{
-		if(renderElementIdx >= mNumImageRenderElements)
-			return mTextSprite->getMaterial(mNumImageRenderElements - renderElementIdx);
-		else
-			return mImageSprite->getMaterial(renderElementIdx);
+		UINT32 localRenderElementIdx;
+		Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
+
+		return sprite->getMaterial(localRenderElementIdx);
 	}
 
 	UINT32 GUIInputBox::getNumQuads(UINT32 renderElementIdx) const
 	{
-		UINT32 numQuads = 0;
-		if(renderElementIdx >= mNumImageRenderElements)
-			numQuads = mTextSprite->getNumQuads(mNumImageRenderElements - renderElementIdx);
-		else
-			numQuads = mImageSprite->getNumQuads(renderElementIdx);
+		UINT32 localRenderElementIdx;
+		Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
 
-		return numQuads;
+		return sprite->getNumQuads(localRenderElementIdx);
 	}
 
 	void GUIInputBox::updateRenderElementsInternal()
@@ -128,6 +141,74 @@ namespace BansheeEngine
 		textDesc.vertAlign = mStyle->textVertAlign;
 
 		mTextSprite->update(textDesc);
+
+		if(mCaretShown && GUIManager::instance().getCaretBlinkState())
+		{
+			IMAGE_SPRITE_DESC mCaretDesc;
+			mCaretDesc.offset = getCaretPosition();
+			mCaretDesc.width = 1;
+			mCaretDesc.height = getCaretHeight();
+			mCaretDesc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
+			mCaretDesc.texture = GUIManager::instance().getCaretTexture();
+
+			mCaretSprite->update(mCaretDesc);
+		}
+
+		if(mSelectionShown)
+		{
+			Vector<Rect>::type selectionRects = getSelectionRects();
+			// TODO - Update sprites
+		}
+	}
+
+	Sprite* GUIInputBox::renderElemToSprite(UINT32 renderElemIdx, UINT32& localRenderElemIdx) const
+	{
+		UINT32 oldNumElements = 0;
+		UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
+		if(renderElemIdx < newNumElements)
+		{
+			localRenderElemIdx = renderElemIdx - oldNumElements;
+			return mTextSprite;
+		}
+
+		oldNumElements = newNumElements;
+		newNumElements += mImageSprite->getNumRenderElements();
+
+		if(renderElemIdx < newNumElements)
+		{
+			localRenderElemIdx = renderElemIdx - oldNumElements;
+			return mImageSprite;
+		}
+
+		if(mCaretShown && GUIManager::instance().getCaretBlinkState())
+		{
+			oldNumElements = newNumElements;
+			newNumElements += mImageSprite->getNumRenderElements();
+
+			if(renderElemIdx < newNumElements)
+			{
+				localRenderElemIdx = renderElemIdx - oldNumElements;
+				return mImageSprite;
+			}
+		}
+
+		if(mSelectionShown)
+		{
+			for(auto& selectionSprite : mSelectionSprites)
+			{
+				oldNumElements = newNumElements;
+				newNumElements += selectionSprite->getNumRenderElements();
+
+				if(renderElemIdx < newNumElements)
+				{
+					localRenderElemIdx = renderElemIdx - oldNumElements;
+					return selectionSprite;
+				}
+			}
+		}
+
+		localRenderElemIdx = renderElemIdx;
+		return nullptr;
 	}
 
 	UINT32 GUIInputBox::_getOptimalWidth() const
@@ -152,19 +233,24 @@ namespace BansheeEngine
 
 	UINT32 GUIInputBox::_getRenderElementDepth(UINT32 renderElementIdx) const
 	{
-		if(renderElementIdx >= mNumImageRenderElements)
+		UINT32 localRenderElementIdx;
+		Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
+
+		if(sprite == mImageSprite)
 			return _getDepth();
-		else
+		else if(sprite == mTextSprite || sprite == mCaretSprite)
+			return _getDepth() + 2;
+		else // Selection sprites
 			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);
+		UINT32 localRenderElementIdx;
+		Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
+
+		sprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, localRenderElementIdx);
 	}
 
 	bool GUIInputBox::mouseEvent(const GUIMouseEvent& ev)
@@ -198,6 +284,8 @@ namespace BansheeEngine
 		else if(ev.getType() == GUIMouseEventType::MouseDown)
 		{
 			mImageDesc.texture = mStyle->active.texture;
+			showCaret(getCharAtPosition(ev.getPosition()));
+			clearSelection();
 			markAsDirty();
 
 			return true;
@@ -227,11 +315,18 @@ namespace BansheeEngine
 
 			return true;
 		}
+		else if(ev.getType() == GUIMouseEventType::MouseDrag)
+		{
+			// TODO - Update selection
+			//  - If mouse is over control, place selection marker there (make sure start < end)
+			//  - Else move the selection by a certain amount of pixels depending on drag amount
+			markAsDirty();
+		}
 
 		return false;
 	}
 
-	bool GUIInputBox::keyEvent(const GUIKeyEvent& ev)
+	bool GUIInputBox::buttonEvent(const GUIButtonEvent& ev)
 	{
 		if(ev.getType() == GUIKeyEventType::KeyDown)
 		{
@@ -239,17 +334,40 @@ namespace BansheeEngine
 			{
 				if(mText.size() > 0)
 				{
-					mText.erase(mText.end() - 1);
+					if(mSelectionShown)
+					{
+						mText.erase(mSelectionStart, mSelectionEnd);
+						mCaretPos = mSelectionStart;
+						clearSelection();
+					}
+					else
+					{
+						mText.erase(mCaretPos);
+					}
+
 					markAsDirty();
 				}
 
 				return true;
 			}
+
+			// TODO - Handle newline if it's a multiline control
+			// TODO - left+right arrow to move the cursor
+			// TODO - shift + left + right arrow to select
+			// TODO - ctrl + a to select all
+			// TODO - ctrl + c, ctrl + v, ctrl + x to copy/cut/paste
 		}
 		else if(ev.getType() == GUIKeyEventType::TextInput)
 		{
-			// TODO - How are backspace, caps and enter handled?
-			mText += ev.getInputChar();
+			if(mSelectionShown)
+			{
+				mText.erase(mSelectionStart, mSelectionEnd);
+				mCaretPos = mSelectionStart;
+				clearSelection();
+			}
+
+			mText.insert(mText.begin() + mCaretPos, ev.getInputChar());
+			mCaretPos++;
 
 			markAsDirty();
 
@@ -259,6 +377,68 @@ namespace BansheeEngine
 		return false;
 	}
 
+	bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
+	{
+		if(ev.getType() == GUICommandEventType::Redraw)
+		{
+			markAsDirty();
+			return true;
+		}
+
+		return false;
+	}
+
+	void GUIInputBox::showCaret(CM::UINT32 charIdx)
+	{
+		mCaretPos = charIdx;
+		mCaretShown = true;
+		markAsDirty();
+	}
+
+	void GUIInputBox::clearCaret()
+	{
+		mCaretShown = false;
+		markAsDirty();
+	}
+
+	Int2 GUIInputBox::getCaretPosition() const
+	{
+		// TODO
+		return Int2(0, 0);
+	}
+
+	UINT32 GUIInputBox::getCaretHeight() const
+	{
+		// TODO
+		return 8;
+	}
+
+	void GUIInputBox::showSelection(UINT32 startChar, UINT32 endChar)
+	{
+		mSelectionStart = startChar;
+		mSelectionEnd = endChar;
+		mSelectionShown = true;
+		markAsDirty();
+	}
+
+	void GUIInputBox::clearSelection()
+	{
+		mSelectionShown = false;
+		markAsDirty();
+	}
+
+	Vector<Rect>::type GUIInputBox::getSelectionRects() const
+	{
+		// TODO
+		return Vector<Rect>::type();
+	}
+
+	UINT32 GUIInputBox::getCharAtPosition(const Int2& pos) const
+	{
+		// TODO
+		return 0;
+	}
+
 	void GUIInputBox::_setFocus(bool focus)
 	{
 		if(focus)
@@ -269,6 +449,8 @@ namespace BansheeEngine
 		else
 		{
 			mImageDesc.texture = mStyle->normal.texture;
+			clearCaret();
+			clearSelection();
 			markAsDirty();
 		}
 	}

+ 20 - 76
BansheeEngine/Source/BsGUIManager.cpp

@@ -46,8 +46,7 @@ namespace BansheeEngine
 	GUIManager::GUIManager()
 		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
 		mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
-		mCaretSprite(nullptr), mCaretTexture(nullptr), mCaretX(0), mCaretY(0), mCaretDepth(0), mCaretWidth(1), mCaretHeight(8), 
-		mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretShown(false), mCaretColor(Color::Black), mCaretOwnerWidget(nullptr)
+		mCaretTexture(nullptr), mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(Color::Black), mIsCaretOn(false)
 	{
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
@@ -58,9 +57,8 @@ namespace BansheeEngine
 		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
 		mWindowMovedOrResizedConn = RenderWindowManager::instance().onMovedOrResized.connect(boost::bind(&GUIManager::onWindowMovedOrResized, this, _1));
 
-		// Need to defer these calls because I want to make sure all managers are initialized first
+		// Need to defer this call because I want to make sure all managers are initialized first
 		deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
-		deferredCall(std::bind(&GUIManager::updateCaretSprite, this));
 	}
 
 	GUIManager::~GUIManager()
@@ -79,9 +77,6 @@ namespace BansheeEngine
 		mWindowGainedFocusConn.disconnect();
 		mWindowLostFocusConn.disconnect();
 		mWindowMovedOrResizedConn.disconnect();
-
-		if(mCaretSprite != nullptr)
-			cm_delete(mCaretSprite);
 	}
 
 	void GUIManager::registerWidget(GUIWidget* widget)
@@ -134,17 +129,23 @@ namespace BansheeEngine
 			widget->_updateLayout();
 		}
 
-		updateMeshes();
-	}
+		// Blink caret
+		if(mKeyboardFocusElement != nullptr)
+		{
+			float curTime = gTime().getTime();
 
-	void GUIManager::showCaret(GUIWidget* widget, INT32 x, INT32 y, UINT32 depth)
-	{
-		mCaretX = x;
-		mCaretY = y;
-		mCaretDepth = depth;
-		mCaretLastBlinkTime = 0.0f;
-		mCaretShown = true;
-		mCaretOwnerWidget = widget;
+			if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
+			{
+				mCaretLastBlinkTime = curTime;
+				mIsCaretOn = !mIsCaretOn;
+
+				mCommandEvent = GUICommandEvent();
+				mCommandEvent.setRedrawData();
+				mKeyboardFocusElement->commandEvent(mCommandEvent);
+			}
+		}
+
+		updateMeshes();
 	}
 
 	void GUIManager::render(ViewportPtr& target, CoreAccessor& coreAccessor)
@@ -169,24 +170,6 @@ namespace BansheeEngine
 
 				renderMesh(mesh, material, widget->SO()->getWorldTfrm(), target, coreAccessor);
 
-				// Draw caret
-				// TODO: Caret is always drawn on top of a single widget. This means it will ignore
-				// depth of individual elements within the widget, so it will draw in front of them.
-				// Having it draw correctly would require updating the mesh whenever caret blinks,
-				// which I don't feel is worth it considering that elements within a widget shouldn't be overlapping
-				// so this is unlikely to be an issue.
-				if(mCaretShown && widget == mCaretOwnerWidget)
-				{
-					float curTime = gTime().getTime();
-
-					if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
-					{
-						mCaretLastBlinkTime = curTime;
-
-						renderMesh(mCaretMesh, mCaretMaterial, widget->SO()->getWorldTfrm(), target, coreAccessor);
-					}
-				}
-
 				meshIdx++;
 			}
 		}
@@ -461,45 +444,6 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUIManager::updateCaretSprite()
-	{
-		if(mCaretSprite == nullptr)
-			mCaretSprite = cm_new<ImageSprite>();
-
-		IMAGE_SPRITE_DESC desc;
-		desc.offset = Int2(mCaretX, mCaretY);
-		desc.width = mCaretWidth;
-		desc.height = mCaretHeight;
-		desc.texture = mCaretTexture;
-
-		mCaretSprite->update(desc);
-
-		assert(mCaretSprite->getNumRenderElements() == 1);
-
-		UINT32 numQuads = mCaretSprite->getNumQuads(0);
-		MeshDataPtr meshData = cm_shared_ptr<MeshData, PoolAlloc>(numQuads * 4);
-
-		meshData->beginDesc();
-		meshData->addVertElem(VET_FLOAT2, VES_POSITION);
-		meshData->addVertElem(VET_FLOAT2, VES_TEXCOORD);
-		meshData->addSubMesh(numQuads * 6);
-		meshData->endDesc();
-
-		UINT8* vertices = meshData->getElementData(VES_POSITION);
-		UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
-		UINT32* indices = meshData->getIndices32();
-		UINT32 vertexStride = meshData->getVertexStride();
-		UINT32 indexStride = meshData->getIndexElementSize();
-
-		mCaretSprite->fillBuffer(vertices, uvs, indices, 0, numQuads, vertexStride, indexStride, 0);
-
-		if(!mCaretMesh)
-			mCaretMesh = Mesh::create();
-
-		gMainSyncedCA().writeSubresource(mCaretMesh.getInternalPtr(), 0, *meshData);
-		gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
-	}
-
 	void GUIManager::updateCaretTexture()
 	{
 		if(mCaretTexture == nullptr)
@@ -527,7 +471,7 @@ namespace BansheeEngine
 		{
 			if(mKeyboardFocusElement != nullptr)
 			{
-				mKeyEvent = GUIKeyEvent();
+				mKeyEvent = GUIButtonEvent();
 
 				mKeyEvent.setKeyDownData(event.buttonCode);
 				mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);
@@ -789,7 +733,7 @@ namespace BansheeEngine
 	{
 		if(mKeyboardFocusElement != nullptr)
 		{
-			mKeyEvent = GUIKeyEvent();
+			mKeyEvent = GUIButtonEvent();
 
 			mKeyEvent.setTextInputData(event.textChar);
 			mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);

+ 2 - 2
BansheeEngine/Source/BsGUIWidget.cpp

@@ -116,9 +116,9 @@ namespace BansheeEngine
 		return element->mouseEvent(ev);
 	}
 
-	bool GUIWidget::_keyEvent(GUIElement* element, const GUIKeyEvent& ev)
+	bool GUIWidget::_keyEvent(GUIElement* element, const GUIButtonEvent& ev)
 	{
-		return element->keyEvent(ev);
+		return element->buttonEvent(ev);
 	}
 
 	void GUIWidget::registerElement(GUIElement* elem)