Browse Source

Lots more refactoring

Marko Pintera 12 years ago
parent
commit
1699c2482c

+ 7 - 31
BansheeEngine/Include/BsGUIInputBox.h

@@ -54,23 +54,18 @@ namespace BansheeEngine
 		// Sprites
 		ImageSprite* mImageSprite;
 		TextSprite* mTextSprite;
-		CM::Vector<ImageSprite*>::type mSelectionSprites;
 		GUIInputCaret* mInputCaret;
-		bool mInputCursorSet;
-		bool mDragInProgress;
+		GUIInputSelection* mInputSelection;
 		bool mIsMultiline;
 		CM::Int2 mTextOffset;
 
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::WString mText;
 
-		// Selection & input caret
-		CM::UINT32 mSelectionStart;
-		CM::UINT32 mSelectionEnd;
-		CM::UINT32 mSelectionAnchor;
-		CM::UINT32 mSelectionDragAnchor;
 		bool mCaretShown;
 		bool mSelectionShown;
+		bool mInputCursorSet;
+		bool mDragInProgress;
 
 		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline);
 
@@ -83,33 +78,14 @@ namespace BansheeEngine
 		CM::Int2 renderElemToOffset(CM::UINT32 renderElemIdx) const;
 		CM::Rect renderElemToClipRect(CM::UINT32 renderElemIdx) const;
 
+		void insertChar(CM::UINT32 charIdx, CM::UINT32 charCode);
+		void eraseChar(CM::UINT32 charIdx);
+		void deleteSelectedText();
+
 		void showCaret();
 		void hideCaret();
 		void scrollTextToCaret();
 
-		void showSelection(CM::UINT32 caretPos, SelectionDir dir);
-		void clearSelection();
-
-		void moveSelectionLeft(bool skipNewline);
-		void moveSelectionRight(bool skipnewLine);
-		void moveSelectionToCaret(CM::UINT32 caretPos);
-		bool isSelectionEmpty() const;
-		void selectAll();
-
-		void selectionDragStart(CM::UINT32 caretPos);
-		void selectionDragUpdate(CM::UINT32 caretPos);
-		void selectionDragEnd();
-
-		CM::UINT32 getSelectionStart() const { return mSelectionStart; }
-		CM::UINT32 getSelectionEnd() const { return mSelectionEnd; }
-
-		CM::UINT32 caretPosToSelectionChar(CM::UINT32 caretPos, SelectionDir dir) const;
-		bool isNewlineChar(CM::UINT32 charIdx) const;
-		CM::Vector<CM::Rect>::type getSelectionRects() const;
-
-		CM::Int2 getSelectionSpriteOffset(CM::UINT32 spriteIdx) const;
-		CM::Rect getSelectionSpriteClipRect(CM::UINT32 spriteIdx) const;
-
 		CM::Int2 getTextOffset() const;
 		CM::Rect getTextClipRect() const;
 		CM::Rect getTextBounds() const;

+ 1 - 2
BansheeEngine/Include/BsGUIInputCaret.h

@@ -22,7 +22,7 @@ namespace BansheeEngine
 		CM::Int2 getSpriteOffset() const;
 		CM::Rect getSpriteClipRect() const;
 
-		void updateSprite(const CM::Int2& offset);
+		void updateSprite();
 
 		void moveCaretToStart();
 		void moveCaretToEnd();
@@ -34,7 +34,6 @@ namespace BansheeEngine
 		void moveCaretToChar(CM::UINT32 charIdx, CaretPos caretPos);
 
 		CM::UINT32 getCharIdxAtCaretPos() const;
-		CM::UINT32 getCharIdxAtCaretPos(CM::UINT32 caretPos) const;
 		CM::Int2 getCaretPosition(const CM::Int2& offset) const;
 		CM::UINT32 getCaretHeight() const;
 

+ 31 - 33
BansheeEngine/Include/BsGUIInputSelection.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
+#include "BsGUIInputTool.h"
 #include "BsTextSprite.h"
 
 namespace BansheeEngine
@@ -11,45 +12,42 @@ namespace BansheeEngine
 		Right
 	};
 
-	//class BS_EXPORT GUIInputSelection
-	//{
-	//public:
-	//	GUIInputSelection(const TEXT_SPRITE_DESC& textDesc);
-	//	~GUIInputSelection();
-
-	//	CM::Vector<ImageSprite*>::type getSprites() const { return mSprites; }
-	//	void updateText(const TEXT_SPRITE_DESC& textDesc);
-	//	void updateSprite(const CM::Int2& offset);
-
-	//	void showSelection(CM::UINT32 anchorCaretPos, SelectionDir dir);
-	//	void clearSelection();
+	class BS_EXPORT GUIInputSelection : public GUIInputTool
+	{
+	public:
+		GUIInputSelection(const TEXT_SPRITE_DESC& textDesc, const CM::Int2& offset, const CM::Int2 clipOffset);
+		~GUIInputSelection();
 
-	//	void moveSelectionToCaret(CM::UINT32 caretPos);
-	//	bool isSelectionEmpty() const;
-	//	void selectAll();
+		const CM::Vector<ImageSprite*>::type& getSprites() const { return mSprites; }
+		CM::Int2 GUIInputSelection::getSelectionSpriteOffset(CM::UINT32 spriteIdx) const;
+		CM::Rect GUIInputSelection::getSelectionSpriteClipRect(CM::UINT32 spriteIdx) const;
+		void updateSprite();
 
-	//	void selectionDragStart(CM::UINT32 caretPos);
-	//	void selectionDragUpdate(CM::UINT32 caretPos);
-	//	void selectionDragEnd();
+		void showSelection(CM::UINT32 anchorCaretPos, SelectionDir dir);
+		void clearSelection();
 
-	//	CM::UINT32 getSelectionStart() const { return mSelectionStart; }
-	//	CM::UINT32 getSelectionEnd() const { return mSelectionEnd; }
+		void moveSelectionToCaret(CM::UINT32 caretPos);
+		bool isSelectionEmpty() const;
+		void selectAll();
 
-	//	CM::UINT32 caretPosToSelectionChar(CM::UINT32 caretPos, SelectionDir dir) const;
+		void selectionDragStart(CM::UINT32 caretPos);
+		void selectionDragUpdate(CM::UINT32 caretPos);
+		void selectionDragEnd();
 
-	//private:
-	//	CM::UINT32 mSelectionStart;
-	//	CM::UINT32 mSelectionEnd;
-	//	CM::UINT32 mSelectionAnchor;
-	//	CM::UINT32 mSelectionDragAnchor;
-	//	TextSprite* mTextSprite; // TODO - Try to get rid of this and implement its methods internally?
-	//	CM::Vector<ImageSprite*>::type mSprites;
+		CM::UINT32 getSelectionStart() const { return mSelectionStart; }
+		CM::UINT32 getSelectionEnd() const { return mSelectionEnd; }
 
-	//	TEXT_SPRITE_DESC mTextDesc;
+	private:
+		CM::UINT32 mSelectionStart;
+		CM::UINT32 mSelectionEnd;
+		CM::UINT32 mSelectionAnchor;
+		CM::UINT32 mSelectionDragAnchor;
 
-	//	CM::Vector<CM::Rect>::type getSelectionRects() const;
-	//	bool isNewlineChar(CM::UINT32 charIdx) const;
+		CM::Vector<CM::Rect>::type mSelectionRects;
+		CM::Vector<ImageSprite*>::type mSprites;
 
-	//	CM::UINT32 getCharIdxAtCaretPos(CM::UINT32 caretPos) const;
-	//};
+		CM::UINT32 caretPosToSelectionChar(CM::UINT32 caretPos, SelectionDir dir) const;
+		CM::Vector<CM::Rect>::type getSelectionRects() const;
+		bool isNewlineChar(CM::UINT32 charIdx) const;
+	};
 }

+ 9 - 0
BansheeEngine/Include/BsGUIInputTool.h

@@ -48,5 +48,14 @@ namespace BansheeEngine
 		CM::UINT32 getLineForChar(CM::UINT32 charIdx, bool newlineCountsOnNextLine = false) const;
 		CM::Rect getCharRect(CM::UINT32 charIdx) const;
 		CM::INT32 getCharIdxAtPos(const CM::Int2& pos) const;
+
+		/**
+		 * @brief	Gets a character index AFTER the input index. 
+		 * 			"Input index" represents the empty areas between the characters. Newline counts as a character.
+		 * 			(e.g. 0 is before the first character, 1 is after the first character but before the second, etc.)
+		 * 			
+		 * @note	This can return an out of range character index, in case the input index is specified after the last character.
+		 */
+		CM::UINT32 getCharIdxAtInputIdx(CM::UINT32 inputIdx) const;
 	};
 }

+ 210 - 454
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -26,12 +26,12 @@ namespace BansheeEngine
 
 	GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline)
 		:GUIElement(parent, style, layoutOptions), mInputCursorSet(false), mDragInProgress(false),
-		mSelectionStart(0), mSelectionEnd(0), mSelectionAnchor(0), mInputCaret(nullptr), mCaretShown(false), 
-		mSelectionShown(false), mIsMultiline(multiline), mSelectionDragAnchor(0)
+		mInputCaret(nullptr), mInputSelection(nullptr), mCaretShown(false), mSelectionShown(false), mIsMultiline(multiline)
 	{
 		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
 		mTextSprite = cm_new<TextSprite, PoolAlloc>();
 		mInputCaret = cm_new<GUIInputCaret, PoolAlloc>(getTextDesc(), getTextOffset(), mTextOffset);
+		mInputSelection = cm_new<GUIInputSelection, PoolAlloc>(getTextDesc(), getTextOffset(), mTextOffset);
 
 		mImageDesc.texture = mStyle->normal.texture;
 
@@ -53,9 +53,7 @@ namespace BansheeEngine
 		cm_delete<PoolAlloc>(mImageSprite);
 
 		cm_delete<PoolAlloc>(mInputCaret);
-
-		for(auto& sprite : mSelectionSprites)
-			cm_delete(sprite);
+		cm_delete<PoolAlloc>(mInputSelection);
 	}
 
 	GUIInputBox* GUIInputBox::create(GUIWidget& parent, bool multiline, const GUIElementStyle* style)
@@ -90,7 +88,8 @@ namespace BansheeEngine
 
 		if(mSelectionShown)
 		{
-			for(auto& selectionSprite : mSelectionSprites)
+			const Vector<ImageSprite*>::type& sprites = mInputSelection->getSprites();
+			for(auto& selectionSprite : sprites)
 			{
 				numElements += selectionSprite->getNumRenderElements();
 			}
@@ -128,43 +127,14 @@ namespace BansheeEngine
 
 		if(mCaretShown && GUIManager::instance().getCaretBlinkState())
 		{
-			mInputCaret->updateText(textDesc, getTextOffset(), mTextOffset);
-			mInputCaret->updateSprite(mTextOffset);
+			mInputCaret->updateText(textDesc, getTextOffset(), mTextOffset); // TODO - These shouldn't be here. Only call this when one of these parameters changes.
+			mInputCaret->updateSprite();
 		}
 
 		if(mSelectionShown)
 		{
-			Vector<Rect>::type selectionRects = getSelectionRects();
-
-			INT32 diff = (INT32)(mSelectionSprites.size() - selectionRects.size());
-
-			if(diff > 0)
-			{
-				for(UINT32 i = (UINT32)selectionRects.size(); i < (UINT32)mSelectionSprites.size(); i++)
-					cm_delete(mSelectionSprites[i]);
-
-				mSelectionSprites.erase(mSelectionSprites.begin() + selectionRects.size(), mSelectionSprites.end());
-			}
-			else if(diff < 0)
-			{
-				for(INT32 i = diff; i < 0; i++)
-				{
-					ImageSprite* newSprite = cm_new<ImageSprite>();
-					mSelectionSprites.push_back(newSprite);
-				}
-			}
-
-			UINT32 idx = 0;
-			for(auto& sprite : mSelectionSprites)
-			{
-				IMAGE_SPRITE_DESC desc;
-				desc.width = selectionRects[idx].width;
-				desc.height = selectionRects[idx].height;
-				desc.texture = GUIManager::instance().getTextSelectionTexture();
-
-				sprite->update(desc);
-				idx++;
-			}
+			mInputSelection->updateText(textDesc, getTextOffset(), mTextOffset); // TODO - These shouldn't be here. Only call this when one of these parameters changes.
+			mInputCaret->updateSprite();
 		}
 	}
 
@@ -201,7 +171,8 @@ namespace BansheeEngine
 
 		if(mSelectionShown)
 		{
-			for(auto& selectionSprite : mSelectionSprites)
+			const Vector<ImageSprite*>::type& sprites = mInputSelection->getSprites();
+			for(auto& selectionSprite : sprites)
 			{
 				oldNumElements = newNumElements;
 				newNumElements += selectionSprite->getNumRenderElements();
@@ -243,13 +214,14 @@ namespace BansheeEngine
 		if(mSelectionShown)
 		{
 			UINT32 spriteIdx = 0;
-			for(auto& selectionSprite : mSelectionSprites)
+			const Vector<ImageSprite*>::type& sprites = mInputSelection->getSprites();
+			for(auto& selectionSprite : sprites)
 			{
 				oldNumElements = newNumElements;
 				newNumElements += selectionSprite->getNumRenderElements();
 
 				if(renderElemIdx < newNumElements)
-					return getSelectionSpriteOffset(spriteIdx);
+					return mInputSelection->getSelectionSpriteOffset(spriteIdx);
 
 				spriteIdx++;
 			}
@@ -283,13 +255,14 @@ namespace BansheeEngine
 		if(mSelectionShown)
 		{
 			UINT32 spriteIdx = 0;
-			for(auto& selectionSprite : mSelectionSprites)
+			const Vector<ImageSprite*>::type& sprites = mInputSelection->getSprites();
+			for(auto& selectionSprite : sprites)
 			{
 				oldNumElements = newNumElements;
 				newNumElements += selectionSprite->getNumRenderElements();
 
 				if(renderElemIdx < newNumElements)
-					return getSelectionSpriteClipRect(spriteIdx);
+					return mInputSelection->getSelectionSpriteClipRect(spriteIdx);
 
 				spriteIdx++;
 			}
@@ -385,7 +358,7 @@ namespace BansheeEngine
 
 			scrollTextToCaret();
 
-			clearSelection();
+			mInputSelection->clearSelection();
 			markAsDirty();
 
 			return true;
@@ -402,7 +375,7 @@ namespace BansheeEngine
 		{
 			mDragInProgress = true;
 
-			selectionDragStart(mInputCaret->getCaretPos());
+			mInputSelection->selectionDragStart(mInputCaret->getCaretPos());
 
 			return true;
 		}
@@ -416,7 +389,7 @@ namespace BansheeEngine
 				mInputCursorSet = false;
 			}
 
-			selectionDragEnd();
+			mInputSelection->selectionDragEnd();
 
 			return true;
 		}
@@ -428,7 +401,7 @@ namespace BansheeEngine
 			else
 				mInputCaret->moveCaretToStart();
 
-			selectionDragUpdate(mInputCaret->getCaretPos());
+			mInputSelection->selectionDragUpdate(mInputCaret->getCaretPos());
 
 			scrollTextToCaret();
 
@@ -449,22 +422,7 @@ namespace BansheeEngine
 				{
 					if(mSelectionShown)
 					{
-						UINT32 selStart = getSelectionStart();
-						mText.erase(mText.begin() + selStart, mText.begin() + getSelectionEnd());
-						mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
-
-						if(selStart > 0)
-						{
-							UINT32 newCaretPos = selStart - 1;
-							mInputCaret->moveCaretToChar(newCaretPos, CARET_AFTER);
-						}
-						else
-						{
-							mInputCaret->moveCaretToChar(0, CARET_BEFORE);
-						}
-
-						scrollTextToCaret();
-						clearSelection();
+						deleteSelectedText();
 					}
 					else
 					{
@@ -472,8 +430,7 @@ namespace BansheeEngine
 
 						if(charIdx < (UINT32)mText.size())
 						{
-							mText.erase(charIdx, 1);
-							mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
+							eraseChar(charIdx);
 
 							if(charIdx > 0)
 								charIdx--;
@@ -496,30 +453,14 @@ namespace BansheeEngine
 				{
 					if(mSelectionShown)
 					{
-						UINT32 selStart = getSelectionStart();
-						mText.erase(mText.begin() + selStart, mText.begin() + getSelectionEnd());
-						mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
-
-						if(selStart > 0)
-						{
-							UINT32 newCaretPos = selStart - 1;
-							mInputCaret->moveCaretToChar(newCaretPos, CARET_AFTER);
-						}
-						else
-						{
-							mInputCaret->moveCaretToChar(0, CARET_BEFORE);
-						}
-
-						scrollTextToCaret();
-						clearSelection();
+						deleteSelectedText();
 					}
 					else
 					{
 						UINT32 charIdx = mInputCaret->getCharIdxAtCaretPos();
 						if(charIdx < (UINT32)mText.size())
 						{
-							mText.erase(charIdx, 1);
-							mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
+							eraseChar(charIdx);
 
 							if(charIdx > 0)
 								charIdx--;
@@ -540,27 +481,27 @@ namespace BansheeEngine
 			{
 				if(ev.isShiftDown())
 				{
-					bool caretMovedDueToNewline = false;
-					if(!mSelectionShown)
-					{
-						if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Right)))
-						{
-							mInputCaret->moveCaretLeft();
-							caretMovedDueToNewline = true;
-						}
-
-						showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
-					}
-
-					moveSelectionLeft(caretMovedDueToNewline);
-					scrollTextToCaret();
-
-					markAsDirty();
-					return true;
+					//bool caretMovedDueToNewline = false;
+					//if(!mSelectionShown)
+					//{
+					//	if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Right)))
+					//	{
+					//		mInputCaret->moveCaretLeft();
+					//		caretMovedDueToNewline = true;
+					//	}
+
+					//	showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
+					//}
+
+					//moveSelectionLeft(caretMovedDueToNewline);
+					//scrollTextToCaret();
+
+					//markAsDirty();
+					//return true;
 				}
 				else
 				{
-					clearSelection();
+					mInputSelection->clearSelection();
 					mInputCaret->moveCaretLeft();
 					scrollTextToCaret();
 
@@ -573,27 +514,27 @@ namespace BansheeEngine
 			{
 				if(ev.isShiftDown())
 				{
-					bool caretMovedDueToNewline = false;
-					if(!mSelectionShown)
-					{
-						if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left)))
-						{
-							mInputCaret->moveCaretRight();
-							caretMovedDueToNewline = true;
-						}
-
-						showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
-					}
-						
-					moveSelectionRight(caretMovedDueToNewline);
-					scrollTextToCaret();
-
-					markAsDirty();
-					return true;
+					//bool caretMovedDueToNewline = false;
+					//if(!mSelectionShown)
+					//{
+					//	if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left)))
+					//	{
+					//		mInputCaret->moveCaretRight();
+					//		caretMovedDueToNewline = true;
+					//	}
+
+					//	showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
+					//}
+					//	
+					//moveSelectionRight(caretMovedDueToNewline);
+					//scrollTextToCaret();
+
+					//markAsDirty();
+					//return true;
 				}
 				else
 				{
-					clearSelection();
+					mInputSelection->clearSelection();
 					mInputCaret->moveCaretRight();
 					scrollTextToCaret();
 
@@ -606,19 +547,19 @@ namespace BansheeEngine
 			{
 				if(ev.isShiftDown())
 				{
-					if(!mSelectionShown)
-					{
-						if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Right)))
-						{
-							mInputCaret->moveCaretLeft();
-						}
-
-						showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
-					}
+					//if(!mSelectionShown)
+					//{
+					//	if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Right)))
+					//	{
+					//		mInputCaret->moveCaretLeft();
+					//	}
+
+					//	showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
+					//}
 				}
 				else
 				{
-					clearSelection();
+					mInputSelection->clearSelection();
 				}
 
 				mInputCaret->moveCaretUp();
@@ -626,7 +567,7 @@ namespace BansheeEngine
 
 				if(ev.isShiftDown())
 				{
-					moveSelectionToCaret(mInputCaret->getCaretPos());
+					mInputSelection->moveSelectionToCaret(mInputCaret->getCaretPos());
 				}
 
 				markAsDirty();
@@ -637,19 +578,19 @@ namespace BansheeEngine
 			{
 				if(ev.isShiftDown())
 				{
-					if(!mSelectionShown)
-					{
-						if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left)))
-						{
-							mInputCaret->moveCaretRight();
-						}
-
-						showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
-					}
+					//if(!mSelectionShown)
+					//{
+					//	if(isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left)))
+					//	{
+					//		mInputCaret->moveCaretRight();
+					//	}
+
+					//	showSelection(mInputCaret->getCaretPos(), SelectionDir::Left);
+					//}
 				}
 				else
 				{
-					clearSelection();
+					mInputSelection->clearSelection();
 				}
 
 				mInputCaret->moveCaretDown();
@@ -657,7 +598,7 @@ namespace BansheeEngine
 
 				if(ev.isShiftDown())
 				{
-					moveSelectionToCaret(mInputCaret->getCaretPos());
+					mInputSelection->moveSelectionToCaret(mInputCaret->getCaretPos());
 				}
 
 				markAsDirty();
@@ -669,18 +610,9 @@ namespace BansheeEngine
 				if(mIsMultiline)
 				{
 					if(mSelectionShown)
-					{
-						UINT32 selStart = getSelectionStart();
-						mText.erase(mText.begin() + selStart, mText.begin() + getSelectionEnd());
-						mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
+						deleteSelectedText();
 
-						mInputCaret->moveCaretToChar(selStart, CARET_BEFORE);
-						scrollTextToCaret();
-						clearSelection();
-					}
-
-					mText.insert(mText.begin() + mInputCaret->getCharIdxAtCaretPos(), '\n');
-					mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
+					insertChar(mInputCaret->getCharIdxAtCaretPos(), '\n');
 
 					mInputCaret->moveCaretRight();
 					scrollTextToCaret();
@@ -693,8 +625,8 @@ namespace BansheeEngine
 
 			if(ev.getKey() == BC_A && ev.isCtrlDown())
 			{
-				showSelection(0, SelectionDir::Left);
-				selectAll();
+				mInputSelection->showSelection(0, SelectionDir::Left);
+				mInputSelection->selectAll();
 
 				markAsDirty();
 				return true;
@@ -703,18 +635,10 @@ namespace BansheeEngine
 		else if(ev.getType() == GUIKeyEventType::TextInput)
 		{
 			if(mSelectionShown)
-			{
-				UINT32 selStart = getSelectionStart();
-				mText.erase(mText.begin() + selStart, mText.begin() + getSelectionEnd());
-				mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
-
-				mInputCaret->moveCaretToChar(selStart, CARET_BEFORE);
-				clearSelection();
-			}
+				deleteSelectedText();
 
 			UINT32 charIdx = mInputCaret->getCharIdxAtCaretPos();
-			mText.insert(mText.begin() + charIdx, ev.getInputChar());
-			mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
+			insertChar(charIdx, ev.getInputChar());
 
 			mInputCaret->moveCaretToChar(charIdx, CARET_AFTER);
 
@@ -789,316 +713,148 @@ namespace BansheeEngine
 		}
 
 		mTextOffset += offset;
-		mInputCaret->updateText(getTextDesc(), getTextOffset(), mTextOffset);
-
-		markAsDirty();
-	}
 
-	void GUIInputBox::showSelection(CM::UINT32 caretPos, SelectionDir dir)
-	{
-		UINT32 charIdx = mInputCaret->getCharIdxAtCaretPos(caretPos);
+		Int2 newOffset = getTextOffset();
+		mInputCaret->updateText(textDesc, newOffset, mTextOffset);
+		mInputSelection->updateText(textDesc, newOffset, mTextOffset);
 
-		if(dir == SelectionDir::Right)
-			charIdx = (UINT32)std::max(0, (INT32)(charIdx - 1));
-
-		mSelectionStart = charIdx;
-		mSelectionEnd = charIdx;
-		mSelectionAnchor = charIdx;
-		mSelectionShown = true;
-		markAsDirty();
-	}
-
-	void GUIInputBox::clearSelection()
-	{
-		for(auto& sprite : mSelectionSprites)
-			cm_delete(sprite);
-		
-		mSelectionSprites.clear();
-		mSelectionShown = false;
 		markAsDirty();
 	}
 
-	UINT32 GUIInputBox::caretPosToSelectionChar(UINT32 caretPos, SelectionDir dir) const
+	void GUIInputBox::insertChar(CM::UINT32 charIdx, CM::UINT32 charCode)
 	{
-		UINT32 charIdx = mInputCaret->getCharIdxAtCaretPos(caretPos);
+		mText.insert(mText.begin() + charIdx, charCode);
 
-		if(dir == SelectionDir::Right)
-			charIdx = (UINT32)std::max(0, (INT32)(charIdx - 1));
-
-		return charIdx;
-	}
-
-	bool GUIInputBox::isNewlineChar(CM::UINT32 charIdx) const
-	{
-		if(mText[charIdx] == '\n')
-			return true;
-
-		return false;
-	}
-
-	void GUIInputBox::selectionDragStart(UINT32 caretPos)
-	{
-		clearSelection();
-
-		showSelection(caretPos, SelectionDir::Left); 
-		mSelectionDragAnchor = caretPos;
-	}
-
-	void GUIInputBox::selectionDragUpdate(UINT32 caretPos)
-	{
-		if(caretPos < mSelectionDragAnchor)
-		{
-			mSelectionStart = mInputCaret->getCharIdxAtCaretPos(caretPos);
-			mSelectionEnd = mInputCaret->getCharIdxAtCaretPos(mSelectionDragAnchor);
-
-			mSelectionAnchor = mSelectionStart;
-		}
-
-		if(caretPos > mSelectionDragAnchor)
-		{
-			mSelectionStart = mInputCaret->getCharIdxAtCaretPos(mSelectionDragAnchor);
-			mSelectionEnd = mInputCaret->getCharIdxAtCaretPos(caretPos);
-
-			mSelectionAnchor = mSelectionEnd;
-		}
-
-		if(caretPos == mSelectionDragAnchor)
-		{
-			mSelectionStart = mSelectionAnchor;
-			mSelectionEnd = mSelectionAnchor;
-		}
-	}
+		TEXT_SPRITE_DESC textDesc = getTextDesc();
+		Int2 offset = getTextOffset();
 
-	void GUIInputBox::selectionDragEnd()
-	{
-		if(isSelectionEmpty())
-			clearSelection();
+		mInputCaret->updateText(textDesc, offset, mTextOffset);
+		mInputSelection->updateText(textDesc, offset, mTextOffset);
 	}
 
-	void GUIInputBox::moveSelectionLeft(bool skipNewline) 
+	void GUIInputBox::eraseChar(CM::UINT32 charIdx)
 	{
-		SelectionDir newlineTestSelectionDir;
-		if(mSelectionAnchor == mSelectionEnd)
-			newlineTestSelectionDir = SelectionDir::Left;
-		else
-			newlineTestSelectionDir = SelectionDir::Right;
-
-		if(mInputCaret->getCaretPos() > 0)
-		{
-			mInputCaret->moveCaretLeft();
-
-			if(!skipNewline) // Move one more if we moved to a new line (we can't select newline char so we skip it)
-			{
-				if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir)) && mInputCaret->getCaretPos() > 0)
-				{
-					mInputCaret->moveCaretLeft();
-
-					// Reverse caret movement if previous char was a newline, and this one is as well.
-					// Otherwise we skip an entire line which is not what we want.
-					if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
-						mInputCaret->moveCaretRight();
-				} 
-			}
-			else
-			{
-				// Reverse caret movement if previous char was a newline, and this one is as well
-				// so we don't skip a line
-				if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
-					mInputCaret->moveCaretRight();
-			}
-		}
+		mText.erase(charIdx, 1);
 
-		UINT32 charIdx = caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left);
-		
-		if(mSelectionAnchor == mSelectionEnd)
-			mSelectionStart = std::min(mSelectionEnd, charIdx); 
-		else
-			mSelectionEnd = std::max(mSelectionStart, charIdx);
+		TEXT_SPRITE_DESC textDesc = getTextDesc();
+		Int2 offset = getTextOffset();
 
-		if(mSelectionStart == mSelectionEnd)
-			clearSelection();
+		mInputCaret->updateText(textDesc, offset, mTextOffset);
+		mInputSelection->updateText(textDesc, offset, mTextOffset);
 	}
 
-	void GUIInputBox::moveSelectionRight(bool skipNewline) 
+	void GUIInputBox::deleteSelectedText()
 	{
-		SelectionDir newlineTestSelectionDir;
-		if(mSelectionAnchor == mSelectionStart)
-			newlineTestSelectionDir = SelectionDir::Right;
-		else
-			newlineTestSelectionDir = SelectionDir::Left;
+		UINT32 selStart = mInputSelection->getSelectionStart();
+		mText.erase(mText.begin() + selStart, mText.begin() + mInputSelection->getSelectionEnd());
 
-		UINT32 maxCaretPos = mInputCaret->getMaxCaretPos();
-		if(mInputCaret->getCaretPos() < maxCaretPos)
-		{
-			mInputCaret->moveCaretRight();
-
-			if(!skipNewline) // Move one more if we moved to a new line (we can't select newline char so we skip it)
-			{
-				if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir)) && mInputCaret->getCaretPos() < maxCaretPos)
-				{
-					mInputCaret->moveCaretRight();
-
-					// Reverse caret movement if previous char was a newline, and this one is as well.
-					// Otherwise we skip an entire line which is not what we want.
-					if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
-						mInputCaret->moveCaretLeft();
-				} 
-			}
-			else
-			{
-				// Reverse caret movement if previous char was a newline, and this one is as well.
-				// Otherwise we skip an entire line which is not what we want.
-				if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
-					mInputCaret->moveCaretLeft();
-			}
-		}
-
-		UINT32 charIdx = caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left);
-
-		if(mSelectionAnchor == mSelectionStart)
-			mSelectionEnd = std::max(mSelectionStart, charIdx);
-		else
-			mSelectionStart = std::min(mSelectionEnd, charIdx);
-
-		if(mSelectionStart == mSelectionEnd)
-			clearSelection();
-	}
-
-	void GUIInputBox::moveSelectionToCaret(UINT32 caretPos)
-	{
-		UINT32 charIdx = caretPosToSelectionChar(caretPos, SelectionDir::Left);
+		TEXT_SPRITE_DESC textDesc = getTextDesc();
+		Int2 offset = getTextOffset();
+		mInputCaret->updateText(textDesc, offset, mTextOffset);
+		mInputSelection->updateText(textDesc, offset, mTextOffset);
 
-		if(charIdx > mSelectionAnchor)
+		if(selStart > 0)
 		{
-			mSelectionStart = mSelectionAnchor;
-			mSelectionEnd = charIdx;
+			UINT32 newCaretPos = selStart - 1;
+			mInputCaret->moveCaretToChar(newCaretPos, CARET_AFTER);
 		}
 		else
 		{
-			mSelectionStart = charIdx;
-			mSelectionEnd = mSelectionAnchor;
-		}
-
-		if(mSelectionStart == mSelectionEnd)
-			clearSelection();
-	}
-
-	void GUIInputBox::selectAll()
-	{
-		mSelectionStart = 0;
-		mSelectionEnd = (UINT32)mText.size();
-	}
-
-	bool GUIInputBox::isSelectionEmpty() const
-	{
-		return mSelectionStart == mSelectionEnd;
-	}
-
-	Vector<Rect>::type GUIInputBox::getSelectionRects() const
-	{
-		Vector<Rect>::type selectionRects;
-
-		if(mSelectionStart == mSelectionEnd)
-			return selectionRects;
-
-		/*UINT32 startLine = mTextSprite->getLineForChar(mSelectionStart);
-
-		UINT32 endLine = startLine;
-		if(mSelectionEnd > 0)
-			endLine = mTextSprite->getLineForChar(mSelectionEnd - 1, true);
-
-		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(startLine);
-			
-			UINT32 startCharIdx = mSelectionStart;
-
-			UINT32 endCharIdx = mSelectionEnd - 1;
-			if(startLine != endLine)
-			{
-				endCharIdx = lineDesc.getEndChar(false);
-				if(endCharIdx > 0)
-					endCharIdx = endCharIdx - 1;
-			}
-
-			if(!isNewlineChar(startCharIdx) && !isNewlineChar(endCharIdx))
-			{
-				Rect startChar = mTextSprite->getCharRect(startCharIdx);
-				Rect endChar = mTextSprite->getCharRect(endCharIdx);
-
-				Rect selectionRect;
-				selectionRect.x = startChar.x;
-				selectionRect.y = lineDesc.getLineYStart();
-				selectionRect.height = lineDesc.getLineHeight();
-				selectionRect.width = (endChar.x + endChar.width) - startChar.x;
-
-				selectionRects.push_back(selectionRect);
-			}
-		}
-
-		for(UINT32 i = startLine + 1; i < endLine; i++)
-		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
-			if(lineDesc.getStartChar() == lineDesc.getEndChar() || isNewlineChar(lineDesc.getStartChar()))
-				continue;
-
-			UINT32 endCharIdx = lineDesc.getEndChar(false);
-			if(endCharIdx > 0)
-				endCharIdx = endCharIdx - 1;
-
-			Rect startChar = mTextSprite->getCharRect(lineDesc.getStartChar());
-			Rect endChar = mTextSprite->getCharRect(endCharIdx);
-
-			Rect selectionRect;
-			selectionRect.x = startChar.x;
-			selectionRect.y = lineDesc.getLineYStart();
-			selectionRect.height = lineDesc.getLineHeight();
-			selectionRect.width = (endChar.x + endChar.width) - startChar.x;
-
-			selectionRects.push_back(selectionRect);
-		}
-
-		if(startLine != endLine)
-		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(endLine);
-
-			if(lineDesc.getStartChar() != lineDesc.getEndChar() && !isNewlineChar(lineDesc.getStartChar()))
-			{
-				UINT32 endCharIdx = mSelectionEnd - 1;
-
-				if(!isNewlineChar(endCharIdx))
-				{
-					Rect startChar = mTextSprite->getCharRect(lineDesc.getStartChar());
-					Rect endChar = mTextSprite->getCharRect(endCharIdx);
-
-					Rect selectionRect;
-					selectionRect.x = startChar.x;
-					selectionRect.y = lineDesc.getLineYStart();
-					selectionRect.height = lineDesc.getLineHeight();
-					selectionRect.width = (endChar.x + endChar.width) - startChar.x;
-
-					selectionRects.push_back(selectionRect);
-				}
-			}
-		}*/
-		
-		return selectionRects;
-	}
-
-	Int2 GUIInputBox::getSelectionSpriteOffset(UINT32 spriteIdx) const
-	{
-		Vector<Rect>::type selectionRects = getSelectionRects(); // TODO - Cache these?
-
-		return Int2(selectionRects[spriteIdx].x, selectionRects[spriteIdx].y);
-	}
-
-	Rect GUIInputBox::getSelectionSpriteClipRect(UINT32 spriteIdx) const
-	{
-		Vector<Rect>::type selectionRects = getSelectionRects(); // TODO - Cache these?
-
-		return Rect(getTextBounds().x - selectionRects[spriteIdx].x, 
-			getTextBounds().y - selectionRects[spriteIdx].y, 
-			selectionRects[spriteIdx].width, selectionRects[spriteIdx].height);
-	}
+			mInputCaret->moveCaretToChar(0, CARET_BEFORE);
+		}
+
+		scrollTextToCaret();
+		mInputSelection->clearSelection();
+	}
+
+	//void GUIInputBox::moveSelectionLeft(bool skipNewline) 
+	//{
+	//	SelectionDir newlineTestSelectionDir;
+	//	if(mSelectionAnchor == mSelectionEnd)
+	//		newlineTestSelectionDir = SelectionDir::Left;
+	//	else
+	//		newlineTestSelectionDir = SelectionDir::Right;
+
+	//	if(mInputCaret->getCaretPos() > 0)
+	//	{
+	//		mInputCaret->moveCaretLeft();
+
+	//		if(!skipNewline) // Move one more if we moved to a new line (we can't select newline char so we skip it)
+	//		{
+	//			if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir)) && mInputCaret->getCaretPos() > 0)
+	//			{
+	//				mInputCaret->moveCaretLeft();
+
+	//				// Reverse caret movement if previous char was a newline, and this one is as well.
+	//				// Otherwise we skip an entire line which is not what we want.
+	//				if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
+	//					mInputCaret->moveCaretRight();
+	//			} 
+	//		}
+	//		else
+	//		{
+	//			// Reverse caret movement if previous char was a newline, and this one is as well
+	//			// so we don't skip a line
+	//			if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
+	//				mInputCaret->moveCaretRight();
+	//		}
+	//	}
+
+	//	UINT32 charIdx = caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left);
+	//	
+	//	if(mSelectionAnchor == mSelectionEnd)
+	//		mSelectionStart = std::min(mSelectionEnd, charIdx); 
+	//	else
+	//		mSelectionEnd = std::max(mSelectionStart, charIdx);
+
+	//	if(mSelectionStart == mSelectionEnd)
+	//		clearSelection();
+	//}
+
+	//void GUIInputBox::moveSelectionRight(bool skipNewline) 
+	//{
+	//	SelectionDir newlineTestSelectionDir;
+	//	if(mSelectionAnchor == mSelectionStart)
+	//		newlineTestSelectionDir = SelectionDir::Right;
+	//	else
+	//		newlineTestSelectionDir = SelectionDir::Left;
+
+	//	UINT32 maxCaretPos = mInputCaret->getMaxCaretPos();
+	//	if(mInputCaret->getCaretPos() < maxCaretPos)
+	//	{
+	//		mInputCaret->moveCaretRight();
+
+	//		if(!skipNewline) // Move one more if we moved to a new line (we can't select newline char so we skip it)
+	//		{
+	//			if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir)) && mInputCaret->getCaretPos() < maxCaretPos)
+	//			{
+	//				mInputCaret->moveCaretRight();
+
+	//				// Reverse caret movement if previous char was a newline, and this one is as well.
+	//				// Otherwise we skip an entire line which is not what we want.
+	//				if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
+	//					mInputCaret->moveCaretLeft();
+	//			} 
+	//		}
+	//		else
+	//		{
+	//			// Reverse caret movement if previous char was a newline, and this one is as well.
+	//			// Otherwise we skip an entire line which is not what we want.
+	//			if (isNewlineChar(caretPosToSelectionChar(mInputCaret->getCaretPos(), newlineTestSelectionDir))) 
+	//				mInputCaret->moveCaretLeft();
+	//		}
+	//	}
+
+	//	UINT32 charIdx = caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left);
+
+	//	if(mSelectionAnchor == mSelectionStart)
+	//		mSelectionEnd = std::max(mSelectionStart, charIdx);
+	//	else
+	//		mSelectionStart = std::min(mSelectionEnd, charIdx);
+
+	//	if(mSelectionStart == mSelectionEnd)
+	//		clearSelection();
+	//}
 
 	CM::Int2 GUIInputBox::getTextOffset() const
 	{
@@ -1154,7 +910,7 @@ namespace BansheeEngine
 		{
 			mImageDesc.texture = mStyle->normal.texture;
 			hideCaret();
-			clearSelection();
+			mInputSelection->clearSelection();
 			markAsDirty();
 		}
 	}

+ 2 - 37
BansheeEngine/Source/BsGUIInputCaret.cpp

@@ -33,7 +33,7 @@ namespace BansheeEngine
 			mTextDesc.width + 1, mTextDesc.height); // Increase clip size by 1, so we can fit the caret in case it is fully at the end of the text
 	}
 
-	void GUIInputCaret::updateSprite(const CM::Int2& offset)
+	void GUIInputCaret::updateSprite()
 	{
 		IMAGE_SPRITE_DESC mCaretDesc;
 		mCaretDesc.width = 1;
@@ -204,42 +204,7 @@ namespace BansheeEngine
 
 	UINT32 GUIInputCaret::getCharIdxAtCaretPos() const
 	{
-		return getCharIdxAtCaretPos(mCaretPos);
-	}
-
-	UINT32 GUIInputCaret::getCharIdxAtCaretPos(UINT32 caretPos) const
-	{
-		if(mTextDesc.text.size() == 0)
-			return 0;
-
-		UINT32 numLines = getNumLines();
-		UINT32 curPos = 0;
-		UINT32 curCharIdx = 0;
-		for(UINT32 i = 0; i < numLines; i++)
-		{
-			const GUIInputLineDesc& lineDesc = getLineDesc(i);
-
-			if(curPos == caretPos)
-				return lineDesc.getStartChar();
-
-			curPos++; // Move past line start position
-
-			UINT32 numChars = lineDesc.getEndChar() - lineDesc.getStartChar();
-			UINT32 numCaretPositions = lineDesc.getEndChar(false) - lineDesc.getStartChar();
-			if(caretPos >= (curPos + numCaretPositions))
-			{
-				curCharIdx += numChars;
-				curPos += numCaretPositions;
-				continue;
-			}
-
-			UINT32 diff = caretPos - curPos; 
-			curCharIdx += diff + 1; // Character after the caret
-
-			return curCharIdx;
-		}
-
-		return 0;
+		return getCharIdxAtInputIdx(mCaretPos);
 	}
 
 	Int2 GUIInputCaret::getCaretPosition(const CM::Int2& offset) const

+ 250 - 258
BansheeEngine/Source/BsGUIInputSelection.cpp

@@ -6,262 +6,254 @@ using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	//GUIInputSelection::GUIInputSelection(const TEXT_SPRITE_DESC& textDesc)
-	//	:mSelectionStart(0), mSelectionEnd(0), mSelectionAnchor(0), mSelectionDragAnchor(0),
-	//	mTextSprite(nullptr)
-	//{
-	//	mTextSprite = cm_new<TextSprite, PoolAlloc>();
-
-	//	mTextSprite->update(mTextDesc);
-	//}
-
-	//GUIInputSelection::~GUIInputSelection()
-	//{
-	//	for(auto& sprite : mSprites)
-	//		cm_delete<PoolAlloc>(sprite);
-
-	//	cm_delete<PoolAlloc>(mTextSprite);
-	//}
-
-	//void GUIInputSelection::updateText(const TEXT_SPRITE_DESC& textDesc)
-	//{
-	//	mTextDesc = textDesc;
-
-	//	mTextSprite->update(mTextDesc);
-	//}
-
-	//void GUIInputSelection::updateSprite(const CM::Int2& offset)
-	//{
-	//	Vector<Rect>::type selectionRects = getSelectionRects();
-
-	//	INT32 diff = (INT32)(mSprites.size() - selectionRects.size());
-
-	//	if(diff > 0)
-	//	{
-	//		for(UINT32 i = (UINT32)selectionRects.size(); i < (UINT32)mSprites.size(); i++)
-	//			cm_delete(mSprites[i]);
-
-	//		mSprites.erase(mSprites.begin() + selectionRects.size(), mSprites.end());
-	//	}
-	//	else if(diff < 0)
-	//	{
-	//		for(INT32 i = diff; i < 0; i++)
-	//		{
-	//			ImageSprite* newSprite = cm_new<ImageSprite>();
-	//			mSprites.push_back(newSprite);
-	//		}
-	//	}
-
-	//	UINT32 idx = 0;
-	//	for(auto& sprite : mSprites)
-	//	{
-	//		IMAGE_SPRITE_DESC desc;
-	//		desc.width = selectionRects[idx].width;
-	//		desc.height = selectionRects[idx].height;
-	//		desc.texture = GUIManager::instance().getTextSelectionTexture();
-
-	//		sprite->update(desc);
-	//		idx++;
-	//	}
-	//}
-
-	//Vector<Rect>::type GUIInputSelection::getSelectionRects() const
-	//{
-	//	Vector<Rect>::type selectionRects;
-
-	//	if(mSelectionStart == mSelectionEnd)
-	//		return selectionRects;
-
-	//	UINT32 startLine = mTextSprite->getLineForChar(mSelectionStart);
-
-	//	UINT32 endLine = startLine;
-	//	if(mSelectionEnd > 0)
-	//		endLine = mTextSprite->getLineForChar(mSelectionEnd - 1, true);
-
-	//	{
-	//		const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(startLine);
-
-	//		UINT32 startCharIdx = mSelectionStart;
-
-	//		UINT32 endCharIdx = mSelectionEnd - 1;
-	//		if(startLine != endLine)
-	//		{
-	//			endCharIdx = lineDesc.getEndChar(false);
-	//			if(endCharIdx > 0)
-	//				endCharIdx = endCharIdx - 1;
-	//		}
-
-	//		if(!isNewlineChar(startCharIdx) && !isNewlineChar(endCharIdx))
-	//		{
-	//			Rect startChar = mTextSprite->getCharRect(startCharIdx);
-	//			Rect endChar = mTextSprite->getCharRect(endCharIdx);
-
-	//			Rect selectionRect;
-	//			selectionRect.x = startChar.x;
-	//			selectionRect.y = lineDesc.getLineYStart();
-	//			selectionRect.height = lineDesc.getLineHeight();
-	//			selectionRect.width = (endChar.x + endChar.width) - startChar.x;
-
-	//			selectionRects.push_back(selectionRect);
-	//		}
-	//	}
-
-	//	for(UINT32 i = startLine + 1; i < endLine; i++)
-	//	{
-	//		const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
-	//		if(lineDesc.getStartChar() == lineDesc.getEndChar() || isNewlineChar(lineDesc.getStartChar()))
-	//			continue;
-
-	//		UINT32 endCharIdx = lineDesc.getEndChar(false);
-	//		if(endCharIdx > 0)
-	//			endCharIdx = endCharIdx - 1;
-
-	//		Rect startChar = mTextSprite->getCharRect(lineDesc.getStartChar());
-	//		Rect endChar = mTextSprite->getCharRect(endCharIdx);
-
-	//		Rect selectionRect;
-	//		selectionRect.x = startChar.x;
-	//		selectionRect.y = lineDesc.getLineYStart();
-	//		selectionRect.height = lineDesc.getLineHeight();
-	//		selectionRect.width = (endChar.x + endChar.width) - startChar.x;
-
-	//		selectionRects.push_back(selectionRect);
-	//	}
-
-	//	if(startLine != endLine)
-	//	{
-	//		const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(endLine);
-
-	//		if(lineDesc.getStartChar() != lineDesc.getEndChar() && !isNewlineChar(lineDesc.getStartChar()))
-	//		{
-	//			UINT32 endCharIdx = mSelectionEnd - 1;
-
-	//			if(!isNewlineChar(endCharIdx))
-	//			{
-	//				Rect startChar = mTextSprite->getCharRect(lineDesc.getStartChar());
-	//				Rect endChar = mTextSprite->getCharRect(endCharIdx);
-
-	//				Rect selectionRect;
-	//				selectionRect.x = startChar.x;
-	//				selectionRect.y = lineDesc.getLineYStart();
-	//				selectionRect.height = lineDesc.getLineHeight();
-	//				selectionRect.width = (endChar.x + endChar.width) - startChar.x;
-
-	//				selectionRects.push_back(selectionRect);
-	//			}
-	//		}
-	//	}
-
-	//	return selectionRects;
-	//}
-
-	//void GUIInputSelection::showSelection(CM::UINT32 anchorCaretPos, SelectionDir dir)
-	//{
-	//	UINT32 charIdx = caretPosToSelectionChar(anchorCaretPos, dir);
-
-	//	mSelectionStart = charIdx;
-	//	mSelectionEnd = charIdx;
-	//	mSelectionAnchor = charIdx;
-	//}
-
-	//void GUIInputSelection::clearSelection()
-	//{
-	//	for(auto& sprite : mSprites)
-	//		cm_delete(sprite);
-
-	//	mSprites.clear();
-	//}
-
-	//UINT32 GUIInputSelection::caretPosToSelectionChar(UINT32 caretPos, SelectionDir dir) const
-	//{
-	//	UINT32 charIdx = getCharIdxAtCaretPos(caretPos);
-
-	//	if(dir == SelectionDir::Right)
-	//		charIdx = (UINT32)std::max(0, (INT32)(charIdx - 1));
-
-	//	return charIdx;
-	//}
-
-	//void GUIInputSelection::selectionDragStart(UINT32 caretPos)
-	//{
-	//	clearSelection();
-
-	//	showSelection(caretPos, SelectionDir::Left); 
-	//	mSelectionDragAnchor = caretPos;
-	//}
-
-	//void GUIInputSelection::selectionDragUpdate(UINT32 caretPos)
-	//{
-	//	if(caretPos < mSelectionDragAnchor)
-	//	{
-	//		mSelectionStart = getCharIdxAtCaretPos(caretPos);
-	//		mSelectionEnd = getCharIdxAtCaretPos(mSelectionDragAnchor);
-
-	//		mSelectionAnchor = mSelectionStart;
-	//	}
-
-	//	if(caretPos > mSelectionDragAnchor)
-	//	{
-	//		mSelectionStart = getCharIdxAtCaretPos(mSelectionDragAnchor);
-	//		mSelectionEnd = getCharIdxAtCaretPos(caretPos);
-
-	//		mSelectionAnchor = mSelectionEnd;
-	//	}
-
-	//	if(caretPos == mSelectionDragAnchor)
-	//	{
-	//		mSelectionStart = mSelectionAnchor;
-	//		mSelectionEnd = mSelectionAnchor;
-	//	}
-	//}
-
-	//void GUIInputSelection::selectionDragEnd()
-	//{
-	//	if(isSelectionEmpty())
-	//		clearSelection();
-	//}
-
-	//void GUIInputSelection::moveSelectionToCaret(UINT32 caretPos)
-	//{
-	//	UINT32 charIdx = caretPosToSelectionChar(caretPos, SelectionDir::Left);
-
-	//	if(charIdx > mSelectionAnchor)
-	//	{
-	//		mSelectionStart = mSelectionAnchor;
-	//		mSelectionEnd = charIdx;
-	//	}
-	//	else
-	//	{
-	//		mSelectionStart = charIdx;
-	//		mSelectionEnd = mSelectionAnchor;
-	//	}
-
-	//	if(mSelectionStart == mSelectionEnd)
-	//		clearSelection();
-	//}
-
-	//void GUIInputSelection::selectAll()
-	//{
-	//	mSelectionStart = 0;
-	//	mSelectionEnd = (UINT32)mTextDesc.text.size();
-	//}
-
-	//bool GUIInputSelection::isSelectionEmpty() const
-	//{
-	//	return mSelectionStart == mSelectionEnd;
-	//}
-
-	//bool GUIInputSelection::isNewlineChar(CM::UINT32 charIdx) const
-	//{
-	//	if(mTextDesc.text[charIdx] == '\n')
-	//		return true;
-
-	//	return false;
-	//}
-
-	//CM::UINT32 GUIInputSelection::getCharIdxAtCaretPos(CM::UINT32 caretPos) const
-	//{
-	//	// TODO - Needs to be implemented in the base class
-	//	return 0;
-	//}
+	GUIInputSelection::GUIInputSelection(const TEXT_SPRITE_DESC& textDesc, const CM::Int2& offset, const CM::Int2 clipOffset)
+		:GUIInputTool(textDesc, offset, clipOffset), mSelectionStart(0), mSelectionEnd(0), mSelectionAnchor(0), mSelectionDragAnchor(0)
+	{ }
+
+	GUIInputSelection::~GUIInputSelection()
+	{
+		for(auto& sprite : mSprites)
+			cm_delete<PoolAlloc>(sprite);
+	}
+
+	void GUIInputSelection::updateSprite()
+	{
+		mSelectionRects = getSelectionRects();
+
+		INT32 diff = (INT32)(mSprites.size() - mSelectionRects.size());
+
+		if(diff > 0)
+		{
+			for(UINT32 i = (UINT32)mSelectionRects.size(); i < (UINT32)mSprites.size(); i++)
+				cm_delete(mSprites[i]);
+
+			mSprites.erase(mSprites.begin() + mSelectionRects.size(), mSprites.end());
+		}
+		else if(diff < 0)
+		{
+			for(INT32 i = diff; i < 0; i++)
+			{
+				ImageSprite* newSprite = cm_new<ImageSprite>();
+				mSprites.push_back(newSprite);
+			}
+		}
+
+		UINT32 idx = 0;
+		for(auto& sprite : mSprites)
+		{
+			IMAGE_SPRITE_DESC desc;
+			desc.width = mSelectionRects[idx].width;
+			desc.height = mSelectionRects[idx].height;
+			desc.texture = GUIManager::instance().getTextSelectionTexture();
+
+			sprite->update(desc);
+			idx++;
+		}
+	}
+
+	Int2 GUIInputSelection::getSelectionSpriteOffset(UINT32 spriteIdx) const
+	{
+		return Int2(mTextOffset.x + mSelectionRects[spriteIdx].x, mTextOffset.y + mSelectionRects[spriteIdx].y);
+	}
+
+	Rect GUIInputSelection::getSelectionSpriteClipRect(UINT32 spriteIdx) const
+	{
+		return Rect(mClipOffset.x - mSelectionRects[spriteIdx].x, 
+			mClipOffset.y - mSelectionRects[spriteIdx].y, 
+			mSelectionRects[spriteIdx].width, mSelectionRects[spriteIdx].height);
+	}
+
+	Vector<Rect>::type GUIInputSelection::getSelectionRects() const
+	{
+		Vector<Rect>::type selectionRects;
+
+		if(mSelectionStart == mSelectionEnd)
+			return selectionRects;
+
+		UINT32 startLine = getLineForChar(mSelectionStart);
+
+		UINT32 endLine = startLine;
+		if(mSelectionEnd > 0)
+			endLine = getLineForChar(mSelectionEnd - 1, true);
+
+		{
+			const GUIInputLineDesc& lineDesc = getLineDesc(startLine);
+
+			UINT32 startCharIdx = mSelectionStart;
+
+			UINT32 endCharIdx = mSelectionEnd - 1;
+			if(startLine != endLine)
+			{
+				endCharIdx = lineDesc.getEndChar(false);
+				if(endCharIdx > 0)
+					endCharIdx = endCharIdx - 1;
+			}
+
+			if(!isNewlineChar(startCharIdx) && !isNewlineChar(endCharIdx))
+			{
+				Rect startChar = getCharRect(startCharIdx);
+				Rect endChar = getCharRect(endCharIdx);
+
+				Rect selectionRect;
+				selectionRect.x = startChar.x;
+				selectionRect.y = lineDesc.getLineYStart();
+				selectionRect.height = lineDesc.getLineHeight();
+				selectionRect.width = (endChar.x + endChar.width) - startChar.x;
+
+				selectionRects.push_back(selectionRect);
+			}
+		}
+
+		for(UINT32 i = startLine + 1; i < endLine; i++)
+		{
+			const GUIInputLineDesc& lineDesc = getLineDesc(i);
+			if(lineDesc.getStartChar() == lineDesc.getEndChar() || isNewlineChar(lineDesc.getStartChar()))
+				continue;
+
+			UINT32 endCharIdx = lineDesc.getEndChar(false);
+			if(endCharIdx > 0)
+				endCharIdx = endCharIdx - 1;
+
+			Rect startChar = getCharRect(lineDesc.getStartChar());
+			Rect endChar = getCharRect(endCharIdx);
+
+			Rect selectionRect;
+			selectionRect.x = startChar.x;
+			selectionRect.y = lineDesc.getLineYStart();
+			selectionRect.height = lineDesc.getLineHeight();
+			selectionRect.width = (endChar.x + endChar.width) - startChar.x;
+
+			selectionRects.push_back(selectionRect);
+		}
+
+		if(startLine != endLine)
+		{
+			const GUIInputLineDesc& lineDesc = getLineDesc(endLine);
+
+			if(lineDesc.getStartChar() != lineDesc.getEndChar() && !isNewlineChar(lineDesc.getStartChar()))
+			{
+				UINT32 endCharIdx = mSelectionEnd - 1;
+
+				if(!isNewlineChar(endCharIdx))
+				{
+					Rect startChar = getCharRect(lineDesc.getStartChar());
+					Rect endChar = getCharRect(endCharIdx);
+
+					Rect selectionRect;
+					selectionRect.x = startChar.x;
+					selectionRect.y = lineDesc.getLineYStart();
+					selectionRect.height = lineDesc.getLineHeight();
+					selectionRect.width = (endChar.x + endChar.width) - startChar.x;
+
+					selectionRects.push_back(selectionRect);
+				}
+			}
+		}
+
+		return selectionRects;
+	}
+
+	void GUIInputSelection::showSelection(CM::UINT32 anchorCaretPos, SelectionDir dir)
+	{
+		UINT32 charIdx = caretPosToSelectionChar(anchorCaretPos, dir);
+
+		mSelectionStart = charIdx;
+		mSelectionEnd = charIdx;
+		mSelectionAnchor = charIdx;
+	}
+
+	void GUIInputSelection::clearSelection()
+	{
+		for(auto& sprite : mSprites)
+			cm_delete(sprite);
+
+		mSprites.clear();
+	}
+
+	UINT32 GUIInputSelection::caretPosToSelectionChar(UINT32 caretPos, SelectionDir dir) const
+	{
+		UINT32 charIdx = getCharIdxAtInputIdx(caretPos);
+
+		if(dir == SelectionDir::Right)
+			charIdx = (UINT32)std::max(0, (INT32)(charIdx - 1));
+
+		return charIdx;
+	}
+
+	void GUIInputSelection::selectionDragStart(UINT32 caretPos)
+	{
+		clearSelection();
+
+		showSelection(caretPos, SelectionDir::Left); 
+		mSelectionDragAnchor = caretPos;
+	}
+
+	void GUIInputSelection::selectionDragUpdate(UINT32 caretPos)
+	{
+		if(caretPos < mSelectionDragAnchor)
+		{
+			mSelectionStart = getCharIdxAtInputIdx(caretPos);
+			mSelectionEnd = getCharIdxAtInputIdx(mSelectionDragAnchor);
+
+			mSelectionAnchor = mSelectionStart;
+		}
+
+		if(caretPos > mSelectionDragAnchor)
+		{
+			mSelectionStart = getCharIdxAtInputIdx(mSelectionDragAnchor);
+			mSelectionEnd = getCharIdxAtInputIdx(caretPos);
+
+			mSelectionAnchor = mSelectionEnd;
+		}
+
+		if(caretPos == mSelectionDragAnchor)
+		{
+			mSelectionStart = mSelectionAnchor;
+			mSelectionEnd = mSelectionAnchor;
+		}
+	}
+
+	void GUIInputSelection::selectionDragEnd()
+	{
+		if(isSelectionEmpty())
+			clearSelection();
+	}
+
+	void GUIInputSelection::moveSelectionToCaret(UINT32 caretPos)
+	{
+		UINT32 charIdx = caretPosToSelectionChar(caretPos, SelectionDir::Left);
+
+		if(charIdx > mSelectionAnchor)
+		{
+			mSelectionStart = mSelectionAnchor;
+			mSelectionEnd = charIdx;
+		}
+		else
+		{
+			mSelectionStart = charIdx;
+			mSelectionEnd = mSelectionAnchor;
+		}
+
+		if(mSelectionStart == mSelectionEnd)
+			clearSelection();
+	}
+
+	void GUIInputSelection::selectAll()
+	{
+		mSelectionStart = 0;
+		mSelectionEnd = (UINT32)mTextDesc.text.size();
+	}
+
+	bool GUIInputSelection::isSelectionEmpty() const
+	{
+		return mSelectionStart == mSelectionEnd;
+	}
+
+	bool GUIInputSelection::isNewlineChar(CM::UINT32 charIdx) const
+	{
+		if(mTextDesc.text[charIdx] == '\n')
+			return true;
+
+		return false;
+	}
 }

+ 35 - 0
BansheeEngine/Source/BsGUIInputTool.cpp

@@ -178,6 +178,41 @@ namespace BansheeEngine
 		CM_EXCEPT(InternalErrorException, "Invalid character index: " + toString(charIdx));
 	}
 
+	UINT32 GUIInputTool::getCharIdxAtInputIdx(UINT32 inputIdx) const
+	{
+		if(mTextDesc.text.size() == 0)
+			return 0;
+
+		UINT32 numLines = getNumLines();
+		UINT32 curPos = 0;
+		UINT32 curCharIdx = 0;
+		for(UINT32 i = 0; i < numLines; i++)
+		{
+			const GUIInputLineDesc& lineDesc = getLineDesc(i);
+
+			if(curPos == inputIdx)
+				return lineDesc.getStartChar();
+
+			curPos++; // Move past line start position
+
+			UINT32 numChars = lineDesc.getEndChar() - lineDesc.getStartChar();
+			UINT32 numCaretPositions = lineDesc.getEndChar(false) - lineDesc.getStartChar();
+			if(inputIdx >= (curPos + numCaretPositions))
+			{
+				curCharIdx += numChars;
+				curPos += numCaretPositions;
+				continue;
+			}
+
+			UINT32 diff = inputIdx - curPos; 
+			curCharIdx += diff + 1; // Character after the caret
+
+			return curCharIdx;
+		}
+
+		return 0;
+	}
+
 	GUIInputLineDesc::GUIInputLineDesc(CM::UINT32 startChar, CM::UINT32 endChar, CM::UINT32 lineHeight, CM::INT32 lineYStart, bool includesNewline)
 		:mStartChar(startChar), mEndChar(endChar), mLineHeight(lineHeight), mLineYStart(lineYStart), mIncludesNewline(includesNewline)
 	{