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

Added a common class for input caret and input selection

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

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -152,6 +152,7 @@
     <ClInclude Include="Include\BsGUIArea.h" />
     <ClInclude Include="Include\BsGUIButton.h" />
     <ClInclude Include="Include\BsGUICommandEvent.h" />
+    <ClInclude Include="Include\BsGUIInputTool.h" />
     <ClInclude Include="Include\BsGUIInputBox.h" />
     <ClInclude Include="Include\BsGUIButtonEvent.h" />
     <ClInclude Include="Include\BsGUIInputCaret.h" />
@@ -202,6 +203,7 @@
     <ClCompile Include="Source\BsGUIButtonEvent.cpp" />
     <ClCompile Include="Source\BsGUIInputCaret.cpp" />
     <ClCompile Include="Source\BsGUIInputSelection.cpp" />
+    <ClCompile Include="Source\BsGUIInputTool.cpp" />
     <ClCompile Include="Source\BsGUILabel.cpp" />
     <ClCompile Include="Source\BsGUILayout.cpp" />
     <ClCompile Include="Source\BsGUILayoutY.cpp" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -168,6 +168,9 @@
     <ClInclude Include="Include\BsGUIInputSelection.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIInputTool.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -281,5 +284,8 @@
     <ClCompile Include="Source\BsGUIInputSelection.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIInputTool.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 2 - 37
BansheeEngine/Include/BsGUIInputCaret.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
+#include "BsGUIInputTool.h"
 #include "BsTextSprite.h"
 
 namespace BansheeEngine
@@ -11,27 +12,7 @@ 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
+	class BS_EXPORT GUIInputCaret : public GUIInputTool
 	{
 	public:
 		GUIInputCaret(const TEXT_SPRITE_DESC& textDesc, const CM::Int2& offset, const CM::Int2 clipOffset);
@@ -41,7 +22,6 @@ namespace BansheeEngine
 		CM::Int2 getSpriteOffset() const;
 		CM::Rect getSpriteClipRect() const;
 
-		void updateText(const TEXT_SPRITE_DESC& textDesc, const CM::Int2& offset, const CM::Int2 clipOffset);
 		void updateSprite(const CM::Int2& offset);
 
 		void moveCaretToStart();
@@ -64,20 +44,5 @@ namespace BansheeEngine
 	private:
 		CM::UINT32 mCaretPos;
 		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;
 	};
 }

+ 52 - 0
BansheeEngine/Include/BsGUIInputTool.h

@@ -0,0 +1,52 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsTextSprite.h"
+
+namespace BansheeEngine
+{
+	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 GUIInputTool
+	{
+	public:
+		GUIInputTool(const TEXT_SPRITE_DESC& textDesc, const CM::Int2& offset, const CM::Int2 clipOffset);
+		~GUIInputTool();
+
+		void updateText(const TEXT_SPRITE_DESC& textDesc, const CM::Int2& offset, const CM::Int2 clipOffset);
+
+	protected:
+		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;
+	};
+}

+ 1 - 197
BansheeEngine/Source/BsGUIInputCaret.cpp

@@ -8,7 +8,7 @@ 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), mQuads(nullptr), mNumQuads(0)
+		:GUIInputTool(textDesc, offset, clipOffset), mCaretPos(0)
 	{
 		mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
 
@@ -20,66 +20,6 @@ namespace BansheeEngine
 		cm_delete<PoolAlloc>(mCaretSprite);
 	}
 
-	void GUIInputCaret::updateText(const TEXT_SPRITE_DESC& textDesc, const Int2& offset, const Int2 clipOffset)
-	{
-		mTextDesc = textDesc;
-		mTextOffset = offset;
-		mClipOffset = clipOffset;
-
-		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
 	{
 		return getCaretPosition(mTextOffset);
@@ -403,140 +343,4 @@ 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;
-	}
 }

+ 214 - 0
BansheeEngine/Source/BsGUIInputTool.cpp

@@ -0,0 +1,214 @@
+#include "BsGUIInputTool.h"
+#include "CmMath.h"
+#include "CmVector2.h"
+#include "CmFont.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	GUIInputTool::GUIInputTool(const TEXT_SPRITE_DESC& textDesc, const Int2& offset, const Int2 clipOffset)
+		:mTextDesc(textDesc), mTextOffset(offset), mClipOffset(clipOffset), mQuads(nullptr), mNumQuads(0)
+	{
+		updateText(textDesc, offset, clipOffset);
+	}
+
+	GUIInputTool::~GUIInputTool()
+	{ }
+
+	void GUIInputTool::updateText(const TEXT_SPRITE_DESC& textDesc, const Int2& offset, const Int2 clipOffset)
+	{
+		mTextDesc = textDesc;
+		mTextOffset = offset;
+		mClipOffset = clipOffset;
+
+		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((float)mTextOffset.x, (float)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++;
+		}
+	}
+
+	CM::Rect GUIInputTool::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 GUIInputTool::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 GUIInputTool::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;
+	}
+}

+ 4 - 10
TODO.txt

@@ -10,6 +10,8 @@ MAJOR ISSUE: writeSubresource/readSubresoure doesn't require a shared ptr to Gpu
   - When fixed, make sure I remove blocking calls after writeSubresource where they're not needed (GUIManager for example)
 I call waitUntilLoaded too many times. Sometimes 5-6 times in a single function. Each of those calls will be extremely slow.
 GUIWidget::updateMeshes leaks. If I leave the game running I can see memory continously going up
+ - Resizing from the top doesn't work
+ - Resizing from the left actually resizes the right side
 
 IMMEDIATE:
  - Clicking on a window to focus and immediately trying to drag/resize it, doesn't work. I first need to click, then click again to drag/resize.
@@ -27,22 +29,14 @@ TextBox needed elements:
   - Add special text input commands, which will get repeatedly sent as long as their corresponding key is set in GUIManager
  - Get DebugCamera to ignore input if GUI has already processed it
  - Cut/Copy/Paste
- - Make a separate class for InputSelection like I have for InputCaret. 
-  - Make them use their own text rendering methods instead of relying on TextSprite.
-  - Make them a singleton so that any input element can use them, and not every element needs to have its own copy.
-  - Since caret and selection are so connected make sure their classes share a common base class.
  - All classes dealing with text (Button, Toggle, Label, InputBox) determine text field size based on mBounds which is calculated from 
     clipped mImageSprite coordinates. Which is wrong because text word wrap will change depending how clipped the sprite is.
+ - Re-enable selection
  - Add GUIInputTextBase which is a parent to both caret and selection
  - Don't forget to make the classes input/selection singletons
  - Elements need TWO different dirty flags. Right now setting dirty causes both content update and fillBuffer update.
    - But I'd like only fillBuffer update when element is moved, or clipped as content update may be expensive
-
-TextData::getVertexData(vertex, uv, bool splitByPages)
- - Called by both TextSprite, InputCaret and InputSelection
- - I move clipping/offset outside of TEXT_SPRITE_DESC and IMAGE_SPRITE_DESC and into fillBuffer()
-   - make sure that I don't call markDirty when clipping and/or offset changes
- - Move SpriteLineDesc out of TextSprite and into InputCaret (I don't think InputSelection needs it)
+   - I added separate clip/offset params to sprites but haven't added these dirty flags yet!
 
  - LATER
   - TAB between input elements