Преглед изворни кода

Moved caret related sprite methods from TextSprite to INputCaret

Marko Pintera пре 12 година
родитељ
комит
e0458aed61

+ 0 - 2
BansheeEngine/Include/BsGUIInputBox.h

@@ -92,8 +92,6 @@ namespace BansheeEngine
 
 		void moveSelectionLeft(bool skipNewline);
 		void moveSelectionRight(bool skipnewLine);
-		void moveSelectionUp();
-		void moveSelectionDown();
 		void moveSelectionToCaret(CM::UINT32 caretPos);
 		bool isSelectionEmpty() const;
 		void selectAll();

+ 31 - 1
BansheeEngine/Include/BsGUIInputCaret.h

@@ -11,6 +11,26 @@ namespace BansheeEngine
 		CARET_AFTER
 	};
 
+	class BS_EXPORT GUIInputLineDesc
+	{
+	public:
+		GUIInputLineDesc(CM::UINT32 startChar, CM::UINT32 endChar, CM::UINT32 lineHeight, CM::INT32 lineYStart, bool includesNewline);
+
+		CM::UINT32 getEndChar(bool includeNewline = true) const;
+		CM::UINT32 getStartChar() const { return mStartChar; }
+		CM::UINT32 getLineHeight() const { return mLineHeight; }
+		CM::INT32 getLineYStart() const { return mLineYStart; }
+		bool isNewline(CM::UINT32 charIdx) const;
+		bool hasNewlineChar() const { return mIncludesNewline; }
+
+	private:
+		CM::UINT32 mStartChar;
+		CM::UINT32 mEndChar;
+		CM::UINT32 mLineHeight;
+		CM::INT32 mLineYStart;
+		bool mIncludesNewline;
+	};
+
 	class BS_EXPORT GUIInputCaret
 	{
 	public:
@@ -43,11 +63,21 @@ namespace BansheeEngine
 		CM::UINT32 getCaretPos() const { return mCaretPos; }
 	private:
 		CM::UINT32 mCaretPos;
-		TextSprite* mTextSprite; // TODO - Try to get rid of this and implement its methods internally?
 		ImageSprite* mCaretSprite;
 
+		CM::Vector2* mQuads;
+		CM::UINT32 mNumQuads;
+
 		TEXT_SPRITE_DESC mTextDesc;
 		CM::Int2 mTextOffset;
 		CM::Int2 mClipOffset;
+
+		CM::Vector<GUIInputLineDesc>::type mLineDescs;
+
+		CM::UINT32 getNumLines() const { return (CM::UINT32)mLineDescs.size(); }
+		const GUIInputLineDesc& getLineDesc(CM::UINT32 lineIdx) const { return mLineDescs.at(lineIdx); }
+		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;
 	};
 }

+ 31 - 31
BansheeEngine/Include/BsGUIInputSelection.h

@@ -11,45 +11,45 @@ namespace BansheeEngine
 		Right
 	};
 
-	class BS_EXPORT GUIInputSelection
-	{
-	public:
-		GUIInputSelection(const TEXT_SPRITE_DESC& textDesc);
-		~GUIInputSelection();
+	//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);
+	//	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();
+	//	void showSelection(CM::UINT32 anchorCaretPos, SelectionDir dir);
+	//	void clearSelection();
 
-		void moveSelectionToCaret(CM::UINT32 caretPos);
-		bool isSelectionEmpty() const;
-		void selectAll();
+	//	void moveSelectionToCaret(CM::UINT32 caretPos);
+	//	bool isSelectionEmpty() const;
+	//	void selectAll();
 
-		void selectionDragStart(CM::UINT32 caretPos);
-		void selectionDragUpdate(CM::UINT32 caretPos);
-		void selectionDragEnd();
+	//	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 getSelectionStart() const { return mSelectionStart; }
+	//	CM::UINT32 getSelectionEnd() const { return mSelectionEnd; }
 
-		CM::UINT32 caretPosToSelectionChar(CM::UINT32 caretPos, SelectionDir dir) const;
+	//	CM::UINT32 caretPosToSelectionChar(CM::UINT32 caretPos, SelectionDir dir) const;
 
-	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;
+	//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;
 
-		TEXT_SPRITE_DESC mTextDesc;
+	//	TEXT_SPRITE_DESC mTextDesc;
 
-		CM::Vector<CM::Rect>::type getSelectionRects() const;
-		bool isNewlineChar(CM::UINT32 charIdx) const;
+	//	CM::Vector<CM::Rect>::type getSelectionRects() const;
+	//	bool isNewlineChar(CM::UINT32 charIdx) const;
 
-		CM::UINT32 getCharIdxAtCaretPos(CM::UINT32 caretPos) const;
-	};
+	//	CM::UINT32 getCharIdxAtCaretPos(CM::UINT32 caretPos) const;
+	//};
 }

+ 0 - 28
BansheeEngine/Include/BsTextSprite.h

@@ -35,26 +35,6 @@ namespace BansheeEngine
 		bool wordWrap;
 	};
 
-	class BS_EXPORT SpriteLineDesc
-	{
-	public:
-		SpriteLineDesc(CM::UINT32 startChar, CM::UINT32 endChar, CM::UINT32 lineHeight, CM::INT32 lineYStart, bool includesNewline);
-
-		CM::UINT32 getEndChar(bool includeNewline = true) const;
-		CM::UINT32 getStartChar() const { return mStartChar; }
-		CM::UINT32 getLineHeight() const { return mLineHeight; }
-		CM::INT32 getLineYStart() const { return mLineYStart; }
-		bool isNewline(CM::UINT32 charIdx) const;
-		bool hasNewlineChar() const { return mIncludesNewline; }
-
-	private:
-		CM::UINT32 mStartChar;
-		CM::UINT32 mEndChar;
-		CM::UINT32 mLineHeight;
-		CM::INT32 mLineYStart;
-		bool mIncludesNewline;
-	};
-
 	class BS_EXPORT TextSprite : public Sprite
 	{
 	public:
@@ -62,12 +42,6 @@ namespace BansheeEngine
 
 		void update(const TEXT_SPRITE_DESC& desc);
 
-		CM::UINT32 getNumLines() const { return (CM::UINT32)mLineDescs.size(); }
-		const SpriteLineDesc& getLineDesc(CM::UINT32 lineIdx) const { return mLineDescs.at(lineIdx); }
-		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;
-
 		static CM::Vector<CM::Int2>::type getAlignmentOffsets(const CM::Vector<CM::TextUtility::TextLine>::type& lines, 
 			CM::UINT32 width, CM::UINT32 height, TextHorzAlign horzAlign, TextVertAlign vertAlign);
 
@@ -99,7 +73,5 @@ namespace BansheeEngine
 		static CM::UINT32 genTextQuads(const CM::TextUtility::TextData& textData, CM::UINT32 width, CM::UINT32 height, 
 			TextHorzAlign horzAlign, TextVertAlign vertAlign, SpriteAnchor anchor, CM::Vector2* vertices, CM::Vector2* uv, CM::UINT32* indices, 
 			CM::UINT32 bufferSizeQuads);
-	private:
-		CM::Vector<SpriteLineDesc>::type mLineDescs;
 	};
 }

+ 2 - 72
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -964,76 +964,6 @@ namespace BansheeEngine
 			clearSelection();
 	}
 
-	void GUIInputBox::moveSelectionUp()
-	{
-		UINT32 charIdx = mInputCaret->getCharIdxAtCaretPos();
-		if(charIdx > 0)
-			charIdx--;
-
-		UINT32 lineIdx = mTextSprite->getLineForChar(charIdx, true);
-
-		if(lineIdx == 0)
-		{
-			mInputCaret->moveCaretToStart();
-			mSelectionStart = 0; 
-			mSelectionEnd = mSelectionAnchor;
-		}
-		else
-		{
-			mInputCaret->moveCaretUp();
-			UINT32 charIdx = caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left);
-
-			if(charIdx > mSelectionAnchor)
-			{
-				mSelectionStart = mSelectionAnchor;
-				mSelectionEnd = charIdx;
-			}
-			else
-			{
-				mSelectionStart = charIdx;
-				mSelectionEnd = mSelectionAnchor;
-			}
-		}
-
-		if(mSelectionStart == mSelectionEnd)
-			clearSelection();
-	}
-
-	void GUIInputBox::moveSelectionDown()
-	{
-		UINT32 charIdx = mInputCaret->getCharIdxAtCaretPos();
-		if(charIdx > 0)
-			charIdx--;
-
-		UINT32 lineIdx = mTextSprite->getLineForChar(charIdx, true);
-
-		if(lineIdx == (mTextSprite->getNumLines() - 1))
-		{
-			mInputCaret->moveCaretToEnd();
-			mSelectionStart = mSelectionAnchor;
-			mSelectionEnd = (UINT32)mText.size();
-		}
-		else
-		{
-			mInputCaret->moveCaretDown();
-			UINT32 charIdx = caretPosToSelectionChar(mInputCaret->getCaretPos(), SelectionDir::Left);
-
-			if(charIdx > mSelectionAnchor)
-			{
-				mSelectionStart = mSelectionAnchor;
-				mSelectionEnd = charIdx;
-			}
-			else
-			{
-				mSelectionStart = charIdx;
-				mSelectionEnd = mSelectionAnchor;
-			}
-		}
-
-		if(mSelectionStart == mSelectionEnd)
-			clearSelection();
-	}
-
 	void GUIInputBox::moveSelectionToCaret(UINT32 caretPos)
 	{
 		UINT32 charIdx = caretPosToSelectionChar(caretPos, SelectionDir::Left);
@@ -1071,7 +1001,7 @@ namespace BansheeEngine
 		if(mSelectionStart == mSelectionEnd)
 			return selectionRects;
 
-		UINT32 startLine = mTextSprite->getLineForChar(mSelectionStart);
+		/*UINT32 startLine = mTextSprite->getLineForChar(mSelectionStart);
 
 		UINT32 endLine = startLine;
 		if(mSelectionEnd > 0)
@@ -1149,7 +1079,7 @@ namespace BansheeEngine
 					selectionRects.push_back(selectionRect);
 				}
 			}
-		}
+		}*/
 		
 		return selectionRects;
 	}

+ 215 - 30
BansheeEngine/Source/BsGUIInputCaret.cpp

@@ -8,18 +8,16 @@ using namespace CamelotFramework;
 namespace BansheeEngine
 {
 	GUIInputCaret::GUIInputCaret(const TEXT_SPRITE_DESC& textDesc, const Int2& offset, const Int2 clipOffset)
-		:mCaretPos(0), mTextDesc(textDesc), mTextOffset(offset), mClipOffset(clipOffset)
+		:mCaretPos(0), mTextDesc(textDesc), mTextOffset(offset), mClipOffset(clipOffset), mQuads(nullptr), mNumQuads(0)
 	{
 		mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
-		mTextSprite = cm_new<TextSprite, PoolAlloc>();
 
-		mTextSprite->update(mTextDesc);
+		updateText(textDesc, offset, clipOffset);
 	}
 
 	GUIInputCaret::~GUIInputCaret()
 	{
 		cm_delete<PoolAlloc>(mCaretSprite);
-		cm_delete<PoolAlloc>(mTextSprite);
 	}
 
 	void GUIInputCaret::updateText(const TEXT_SPRITE_DESC& textDesc, const Int2& offset, const Int2 clipOffset)
@@ -28,7 +26,58 @@ namespace BansheeEngine
 		mTextOffset = offset;
 		mClipOffset = clipOffset;
 
-		mTextSprite->update(mTextDesc);
+		mLineDescs.clear();
+
+		std::shared_ptr<TextUtility::TextData> textData = TextUtility::getTextData(mTextDesc.text, mTextDesc.font, mTextDesc.fontSize, 
+			mTextDesc.width, mTextDesc.height, mTextDesc.wordWrap);
+
+		if(textData == nullptr)
+			return;
+
+		const CM::Vector<TextUtility::TextLine>::type& lines = textData->getLines();
+		const CM::Vector<UINT32>::type& quadsPerPage = textData->getNumQuadsPerPage();
+
+		mNumQuads = 0;
+		for(auto& numQuads : quadsPerPage)
+			mNumQuads += numQuads;
+
+		if(mQuads != nullptr)
+			cm_delete<ScratchAlloc>(mQuads);
+		
+		mQuads = cm_newN<Vector2, ScratchAlloc>(mNumQuads * 4);
+
+		TextSprite::genTextQuads(*textData, mTextDesc.width, mTextDesc.height, mTextDesc.horzAlign, mTextDesc.vertAlign, mTextDesc.anchor, 
+			mQuads, nullptr, nullptr, mNumQuads);
+
+		UINT32 numVerts = mNumQuads * 4;
+		Vector2 vecOffset(mTextOffset.x, mTextOffset.y);
+		for(UINT32 i = 0; i < numVerts; i++)
+			mQuads[i] = mQuads[i] + vecOffset;
+
+		// Store cached line data
+		UINT32 curCharIdx = 0;
+		UINT32 cachedLineY = 0;
+		UINT32 curLineIdx = 0;
+		Vector<Int2>::type alignmentOffsets = TextSprite::getAlignmentOffsets(lines, mTextDesc.width, 
+			mTextDesc.height, mTextDesc.horzAlign, mTextDesc.vertAlign);
+
+		for(auto& line : lines)
+		{
+			// Line has a newline char only if it wasn't created by word wrap and it isn't the last line
+			bool hasNewline = line.hasNewlineChar() && (curLineIdx != ((UINT32)lines.size() - 1));
+
+			UINT32 startChar = curCharIdx;
+			UINT32 endChar = curCharIdx + line.getNumChars() + (hasNewline ? 1 : 0);
+			UINT32 lineHeight = line.getYOffset();
+			INT32 lineYStart = alignmentOffsets[curLineIdx].y + mTextOffset.y;
+
+			GUIInputLineDesc lineDesc(startChar, endChar, lineHeight, lineYStart, hasNewline);
+			mLineDescs.push_back(lineDesc);
+
+			curCharIdx = lineDesc.getEndChar();
+			cachedLineY += lineDesc.getLineHeight();
+			curLineIdx++;
+		}
 	}
 
 	Int2 GUIInputCaret::getSpriteOffset() const
@@ -82,8 +131,8 @@ namespace BansheeEngine
 		if(charIdx > 0)
 			charIdx -= 1;	
 
-		UINT32 lineIdx = mTextSprite->getLineForChar(charIdx);
-		const SpriteLineDesc& desc = mTextSprite->getLineDesc(lineIdx);
+		UINT32 lineIdx = getLineForChar(charIdx);
+		const GUIInputLineDesc& desc = getLineDesc(lineIdx);
 		// If char is a newline, I want that to count as being on the next line because that's
 		// how user sees it
 		if(desc.isNewline(charIdx))
@@ -107,14 +156,14 @@ namespace BansheeEngine
 		if(charIdx > 0)
 			charIdx -= 1;	
 
-		UINT32 lineIdx = mTextSprite->getLineForChar(charIdx);
-		const SpriteLineDesc& desc = mTextSprite->getLineDesc(lineIdx);
+		UINT32 lineIdx = getLineForChar(charIdx);
+		const GUIInputLineDesc& desc = getLineDesc(lineIdx);
 		// If char is a newline, I want that to count as being on the next line because that's
 		// how user sees it
 		if(desc.isNewline(charIdx))
 			lineIdx++;					  
 
-		if(lineIdx == (mTextSprite->getNumLines() - 1))
+		if(lineIdx == (getNumLines() - 1))
 		{
 			moveCaretToEnd();
 			return;
@@ -128,11 +177,11 @@ namespace BansheeEngine
 
 	void GUIInputCaret::moveCaretToPos(const CM::Int2& pos)
 	{
-		INT32 charIdx = mTextSprite->getCharIdxAtPos(pos);
+		INT32 charIdx = getCharIdxAtPos(pos);
 
 		if(charIdx != -1)
 		{
-			Rect charRect = mTextSprite->getCharRect(charIdx);
+			Rect charRect = getCharRect(charIdx);
 
 			float xCenter = charRect.x + charRect.width * 0.5f;
 			if(pos.x <= xCenter)
@@ -142,7 +191,7 @@ namespace BansheeEngine
 		}
 		else
 		{
-			UINT32 numLines = mTextSprite->getNumLines();
+			UINT32 numLines = getNumLines();
 
 			if(numLines == 0)
 			{
@@ -153,7 +202,7 @@ namespace BansheeEngine
 			UINT32 curPos = 0;
 			for(UINT32 i = 0; i < numLines; i++)
 			{
-				const SpriteLineDesc& line = mTextSprite->getLineDesc(i);
+				const GUIInputLineDesc& line = getLineDesc(i);
 
 				if(pos.y >= line.getLineYStart() && pos.y < (line.getLineYStart() + (INT32)line.getLineHeight()))
 				{
@@ -165,7 +214,7 @@ namespace BansheeEngine
 				curPos += numChars;
 			}
 
-			const SpriteLineDesc& firstLine = mTextSprite->getLineDesc(0);
+			const GUIInputLineDesc& firstLine = getLineDesc(0);
 
 			if(pos.y < firstLine.getLineYStart()) // Before first line
 				mCaretPos = 0;
@@ -182,12 +231,12 @@ namespace BansheeEngine
 			return;
 		}
 
-		UINT32 numLines = mTextSprite->getNumLines();
+		UINT32 numLines = getNumLines();
 		UINT32 curPos = 0;
 		UINT32 curCharIdx = 0;
 		for(UINT32 i = 0; i < numLines; i++)
 		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
+			const GUIInputLineDesc& lineDesc = getLineDesc(i);
 		
 			curPos++; // Move past line start position
 
@@ -223,12 +272,12 @@ namespace BansheeEngine
 		if(mTextDesc.text.size() == 0)
 			return 0;
 
-		UINT32 numLines = mTextSprite->getNumLines();
+		UINT32 numLines = getNumLines();
 		UINT32 curPos = 0;
 		UINT32 curCharIdx = 0;
 		for(UINT32 i = 0; i < numLines; i++)
 		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
+			const GUIInputLineDesc& lineDesc = getLineDesc(i);
 
 			if(curPos == caretPos)
 				return lineDesc.getStartChar();
@@ -258,10 +307,10 @@ namespace BansheeEngine
 		if(mTextDesc.text.size() > 0)
 		{
 			UINT32 curPos = 0;
-			UINT32 numLines = mTextSprite->getNumLines();
+			UINT32 numLines = getNumLines();
 			for(UINT32 i = 0; i < numLines; i++)
 			{
-				const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
+				const GUIInputLineDesc& lineDesc = getLineDesc(i);
 
 				if(mCaretPos == curPos)
 				{
@@ -278,9 +327,9 @@ namespace BansheeEngine
 
 			charIdx = std::min((UINT32)(mTextDesc.text.size() - 1), charIdx);
 
-			Rect charRect = mTextSprite->getCharRect(charIdx);
-			UINT32 lineIdx = mTextSprite->getLineForChar(charIdx);
-			UINT32 yOffset = mTextSprite->getLineDesc(lineIdx).getLineYStart();
+			Rect charRect = getCharRect(charIdx);
+			UINT32 lineIdx = getLineForChar(charIdx);
+			UINT32 yOffset = getLineDesc(lineIdx).getLineYStart();
 
 			return Int2(charRect.x + charRect.width, yOffset);
 		}
@@ -298,8 +347,8 @@ namespace BansheeEngine
 
 		if(charIdx < (UINT32)mTextDesc.text.size())
 		{
-			UINT32 lineIdx = mTextSprite->getLineForChar(charIdx);
-			return mTextSprite->getLineDesc(lineIdx).getLineHeight();
+			UINT32 lineIdx = getLineForChar(charIdx);
+			return getLineDesc(lineIdx).getLineHeight();
 		}
 		else
 		{
@@ -321,11 +370,11 @@ namespace BansheeEngine
 		if(mTextDesc.text.size() == 0)
 			return true;
 
-		UINT32 numLines = mTextSprite->getNumLines();
+		UINT32 numLines = getNumLines();
 		UINT32 curPos = 0;
 		for(UINT32 i = 0; i < numLines; i++)
 		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
+			const GUIInputLineDesc& lineDesc = getLineDesc(i);
 
 			if(curPos == mCaretPos)
 				return true;
@@ -342,11 +391,11 @@ namespace BansheeEngine
 		if(mTextDesc.text.size() == 0)
 			return 0;
 
-		UINT32 numLines = mTextSprite->getNumLines();
+		UINT32 numLines = getNumLines();
 		UINT32 maxPos = 0;
 		for(UINT32 i = 0; i < numLines; i++)
 		{
-			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(i);
+			const GUIInputLineDesc& lineDesc = getLineDesc(i);
 
 			UINT32 numChars = lineDesc.getEndChar(false) - lineDesc.getStartChar() + 1; // + 1 for special line start position
 			maxPos += numChars;
@@ -354,4 +403,140 @@ namespace BansheeEngine
 
 		return maxPos - 1;
 	}
+
+	CM::Rect GUIInputCaret::getCharRect(UINT32 charIdx) const
+	{
+		UINT32 lineIdx = getLineForChar(charIdx);
+
+		// If char is newline we don't have any geometry to return
+		const GUIInputLineDesc& lineDesc = getLineDesc(lineIdx);
+		if(lineDesc.isNewline(charIdx))
+			return Rect();
+
+		UINT32 numNewlineChars = 0;
+		for(UINT32 i = 0; i < lineIdx; i++)
+			numNewlineChars += (getLineDesc(i).hasNewlineChar() ? 1 : 0);
+
+		UINT32 quadIdx = charIdx - numNewlineChars;
+		if(quadIdx >= 0 && quadIdx < mNumQuads)
+		{
+			UINT32 vertIdx = quadIdx * 4;
+
+			Rect charRect;
+			charRect.x = Math::RoundToInt(mQuads[vertIdx + 0].x);
+			charRect.y = Math::RoundToInt(mQuads[vertIdx + 0].y);
+			charRect.width = Math::RoundToInt(mQuads[vertIdx + 3].x - charRect.x);
+			charRect.height = Math::RoundToInt(mQuads[vertIdx + 3].y - charRect.y);
+
+			return charRect;
+		}
+
+		CM_EXCEPT(InternalErrorException, "Invalid character index: " + toString(charIdx));
+	}
+
+	INT32 GUIInputCaret::getCharIdxAtPos(const Int2& pos) const
+	{
+		Vector2 vecPos((float)pos.x, (float)pos.y);
+
+		UINT32 lineStartChar = 0;
+		UINT32 lineEndChar = 0;
+		UINT32 numNewlineChars = 0;
+		UINT32 lineIdx = 0;
+		for(auto& line : mLineDescs)
+		{
+			if(pos.y >= line.getLineYStart() && pos.y < (line.getLineYStart() + (INT32)line.getLineHeight()))
+			{
+				lineStartChar = line.getStartChar();
+				lineEndChar = line.getEndChar(false);
+				break;
+			}
+
+			// Newline chars count in the startChar/endChar variables, but don't actually exist in the buffers
+			// so we need to filter them out
+			numNewlineChars += (line.hasNewlineChar() ? 1 : 0); 
+
+			lineIdx++;
+		}
+
+		UINT32 lineStartQuad = lineStartChar - numNewlineChars;
+		UINT32 lineEndQuad = lineEndChar - numNewlineChars;
+
+		float nearestDist = std::numeric_limits<float>::max();
+		UINT32 nearestChar = 0;
+		bool foundChar = false;
+
+		for(UINT32 i = lineStartQuad; i < lineEndQuad; i++)
+		{
+			UINT32 curVert = i * 4;
+
+			float centerX = mQuads[curVert + 0].x + mQuads[curVert + 1].x;
+			centerX *= 0.5f;
+
+			float dist = Math::Abs(centerX - vecPos.x);
+			if(dist < nearestDist)
+			{
+				nearestChar = i + numNewlineChars;
+				nearestDist = dist;
+				foundChar = true;
+			}
+		}
+
+		if(!foundChar)
+			return -1;
+
+		return nearestChar;
+	}
+
+	CM::UINT32 GUIInputCaret::getLineForChar(CM::UINT32 charIdx, bool newlineCountsOnNextLine) const
+	{
+		UINT32 idx = 0;
+		for(auto& line : mLineDescs)
+		{
+			if(charIdx >= line.getStartChar() && charIdx < line.getEndChar())
+			{
+				if(line.isNewline(charIdx) && newlineCountsOnNextLine)
+					return idx + 1; // Incrementing is safe because next line must exist, since we just found a newline char
+
+				return idx;
+			}
+
+			idx++;
+		}
+
+		CM_EXCEPT(InternalErrorException, "Invalid character index: " + toString(charIdx));
+	}
+
+	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)
+	{
+
+	}
+
+	UINT32 GUIInputLineDesc::getEndChar(bool includeNewline) const
+	{
+		if(mIncludesNewline)
+		{
+			if(includeNewline)
+				return mEndChar;
+			else
+			{
+				if(mEndChar > 0)
+					return mEndChar - 1;
+				else
+					return mStartChar;
+			}
+		}
+		else
+			return mEndChar;
+	}
+
+	bool GUIInputLineDesc::isNewline(UINT32 charIdx) const
+	{
+		if(mIncludesNewline)
+		{
+			return (mEndChar - 1) == charIdx;
+		}
+		else
+			return false;
+	}
 }

+ 258 - 258
BansheeEngine/Source/BsGUIInputSelection.cpp

@@ -6,262 +6,262 @@ 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)
+	//	: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;
+	//}
 }

+ 0 - 182
BansheeEngine/Source/BsTextSprite.cpp

@@ -15,8 +15,6 @@ namespace BansheeEngine
 
 	void TextSprite::update(const TEXT_SPRITE_DESC& desc)
 	{
-		mLineDescs.clear();
-
 		std::shared_ptr<TextUtility::TextData> textData = TextUtility::getTextData(desc.text, desc.font, desc.fontSize, desc.width, desc.height, desc.wordWrap);
 
 		if(textData == nullptr)
@@ -95,29 +93,6 @@ namespace BansheeEngine
 				renderElem.vertices, renderElem.uvs, renderElem.indexes, renderElem.numQuads);
 		}
 
-		// Store cached line data
-		UINT32 curCharIdx = 0;
-		UINT32 cachedLineY = 0;
-		UINT32 curLineIdx = 0;
-		Vector<Int2>::type alignmentOffsets = getAlignmentOffsets(lines, desc.width, desc.height, desc.horzAlign, desc.vertAlign);
-		for(auto& line : lines)
-		{
-			// Line has a newline char only if it wasn't created by word wrap and it isn't the last line
-			bool hasNewline = line.hasNewlineChar() && (curLineIdx != ((UINT32)lines.size() - 1));
-
-			UINT32 startChar = curCharIdx;
-			UINT32 endChar = curCharIdx + line.getNumChars() + (hasNewline ? 1 : 0);
-			UINT32 lineHeight = line.getYOffset();
-			INT32 lineYStart = alignmentOffsets[curLineIdx].y + 0; // TODO - Ignoring offset
-
-			SpriteLineDesc lineDesc(startChar, endChar, lineHeight, lineYStart, hasNewline);
-			mLineDescs.push_back(lineDesc);
-
-			curCharIdx = lineDesc.getEndChar();
-			cachedLineY += lineDesc.getLineHeight();
-			curLineIdx++;
-		}
-
 		updateBounds();
 	}
 
@@ -185,129 +160,6 @@ namespace BansheeEngine
 		return quadOffset;
 	}
 
-	CM::Rect TextSprite::getCharRect(UINT32 charIdx) const
-	{
-		UINT32 lineIdx = getLineForChar(charIdx);
-
-		// If char is newline we don't have any geometry to return
-		const SpriteLineDesc& lineDesc = getLineDesc(lineIdx);
-		if(lineDesc.isNewline(charIdx))
-			return Rect();
-
-		UINT32 numNewlineChars = 0;
-		for(UINT32 i = 0; i < lineIdx; i++)
-			numNewlineChars += (getLineDesc(i).hasNewlineChar() ? 1 : 0);
-
-		UINT32 quadIdx = charIdx - numNewlineChars;
-
-		UINT32 curQuadIdx = 0;
-		for(auto& renderElem : mCachedRenderElements)
-		{
-			if(quadIdx >= curQuadIdx && quadIdx < renderElem.numQuads)
-			{
-				UINT32 localIdx = (quadIdx - curQuadIdx) * 4;
-
-				Rect charRect;
-				charRect.x = Math::RoundToInt(renderElem.vertices[localIdx + 0].x);
-				charRect.y = Math::RoundToInt(renderElem.vertices[localIdx + 0].y);
-				charRect.width = Math::RoundToInt(renderElem.vertices[localIdx + 3].x - charRect.x);
-				charRect.height = Math::RoundToInt(renderElem.vertices[localIdx + 3].y - charRect.y);
-
-				return charRect;
-			}
-
-			curQuadIdx += renderElem.numQuads;
-		}
-
-		CM_EXCEPT(InternalErrorException, "Invalid character index: " + toString(charIdx));
-	}
-
-	INT32 TextSprite::getCharIdxAtPos(const Int2& pos) const
-	{
-		Vector2 vecPos((float)pos.x, (float)pos.y);
-
-		UINT32 lineStartChar = 0;
-		UINT32 lineEndChar = 0;
-		UINT32 numNewlineChars = 0;
-		UINT32 lineIdx = 0;
-		for(auto& line : mLineDescs)
-		{
-			if(pos.y >= line.getLineYStart() && pos.y < (line.getLineYStart() + (INT32)line.getLineHeight()))
-			{
-				lineStartChar = line.getStartChar();
-				lineEndChar = line.getEndChar(false);
-				break;
-			}
-
-			// Newline chars count in the startChar/endChar variables, but don't actually exist in the buffers
-			// so we need to filter them out
-			numNewlineChars += (line.hasNewlineChar() ? 1 : 0); 
-
-			lineIdx++;
-		}
-
-		UINT32 lineStartQuad = lineStartChar - numNewlineChars;
-		UINT32 lineEndQuad = lineEndChar - numNewlineChars;
-
-		float nearestDist = std::numeric_limits<float>::max();
-		UINT32 nearestChar = 0;
-		UINT32 quadIdx = 0;
-		bool foundChar = false;
-		for(auto& renderElem : mCachedRenderElements)
-		{
-			for(UINT32 i = 0; i < renderElem.numQuads; i++)
-			{
-				if(quadIdx < lineStartQuad)
-				{
-					quadIdx++;
-					continue;
-				}
-
-				if(quadIdx >= lineEndQuad)
-					break;
-
-				UINT32 curVert = quadIdx * 4;
-
-				float centerX = renderElem.vertices[curVert + 0].x + renderElem.vertices[curVert + 1].x;
-				centerX *= 0.5f;
-
-				float dist = Math::Abs(centerX - vecPos.x);
-				if(dist < nearestDist)
-				{
-					nearestChar = quadIdx + numNewlineChars;
-					nearestDist = dist;
-					foundChar = true;
-				}
-
-				quadIdx++;
-			}
-		}
-
-		if(!foundChar)
-			return -1;
-
-		return nearestChar;
-	}
-
-	CM::UINT32 TextSprite::getLineForChar(CM::UINT32 charIdx, bool newlineCountsOnNextLine) const
-	{
-		UINT32 idx = 0;
-		for(auto& line : mLineDescs)
-		{
-			if(charIdx >= line.getStartChar() && charIdx < line.getEndChar())
-			{
-				if(line.isNewline(charIdx) && newlineCountsOnNextLine)
-					return idx + 1; // Incrementing is safe because next line must exist, since we just found a newline char
-
-				return idx;
-			}
-
-			idx++;
-		}
-		
-		CM_EXCEPT(InternalErrorException, "Invalid character index: " + toString(charIdx));
-	}
-
 	Vector<Int2>::type TextSprite::getAlignmentOffsets(const Vector<TextUtility::TextLine>::type& lines, 
 		UINT32 width, UINT32 height, TextHorzAlign horzAlign, TextVertAlign vertAlign)
 	{
@@ -356,38 +208,4 @@ namespace BansheeEngine
 
 		return lineOffsets;
 	}
-
-	SpriteLineDesc::SpriteLineDesc(CM::UINT32 startChar, CM::UINT32 endChar, CM::UINT32 lineHeight, CM::INT32 lineYStart, bool includesNewline)
-		:mStartChar(startChar), mEndChar(endChar), mLineHeight(lineHeight), mLineYStart(lineYStart), mIncludesNewline(includesNewline)
-	{
-
-	}
-
-	UINT32 SpriteLineDesc::getEndChar(bool includeNewline) const
-	{
-		if(mIncludesNewline)
-		{
-			if(includeNewline)
-				return mEndChar;
-			else
-			{
-				if(mEndChar > 0)
-					return mEndChar - 1;
-				else
-					return mStartChar;
-			}
-		}
-		else
-			return mEndChar;
-	}
-
-	bool SpriteLineDesc::isNewline(UINT32 charIdx) const
-	{
-		if(mIncludesNewline)
-		{
-			return (mEndChar - 1) == charIdx;
-		}
-		else
-			return false;
-	}
 }