浏览代码

Added command events to Input, so I can more easily deal with text input commands and command repeat

Marko Pintera 12 年之前
父节点
当前提交
1ee7726962

+ 2 - 2
BansheeEngine/BansheeEngine.vcxproj

@@ -245,7 +245,7 @@
     <ClInclude Include="Include\BsGUIElementBase.h" />
     <ClInclude Include="Include\BsGUIInputTool.h" />
     <ClInclude Include="Include\BsGUIInputBox.h" />
-    <ClInclude Include="Include\BsGUIButtonEvent.h" />
+    <ClInclude Include="Include\BsGUITextInputEvent.h" />
     <ClInclude Include="Include\BsGUIInputCaret.h" />
     <ClInclude Include="Include\BsGUIInputSelection.h" />
     <ClInclude Include="Include\BsGUILayoutOptions.h" />
@@ -308,7 +308,7 @@
     <ClCompile Include="Source\BsGUIElement.cpp" />
     <ClCompile Include="Source\BsGUIElementBase.cpp" />
     <ClCompile Include="Source\BsGUIInputBox.cpp" />
-    <ClCompile Include="Source\BsGUIButtonEvent.cpp" />
+    <ClCompile Include="Source\BsGUITextInputEvent.cpp" />
     <ClCompile Include="Source\BsGUIInputCaret.cpp" />
     <ClCompile Include="Source\BsGUIInputSelection.cpp" />
     <ClCompile Include="Source\BsGUIInputTool.cpp" />

+ 9 - 9
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -54,9 +54,6 @@
     <ClInclude Include="Include\BsGUIMaterialManager.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGUIMouseEvent.h">
-      <Filter>Header Files\GUI</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsGUIWidget.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
@@ -150,9 +147,6 @@
     <ClInclude Include="Include\BsGUICommandEvent.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGUIButtonEvent.h">
-      <Filter>Header Files\GUI</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsGUIInputCaret.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
@@ -222,6 +216,12 @@
     <ClInclude Include="Include\BsGUIButtonBase.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUITextInputEvent.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsGUIMouseEvent.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -317,9 +317,6 @@
     <ClCompile Include="Source\BsGUIMouseEvent.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsGUIButtonEvent.cpp">
-      <Filter>Source Files\GUI</Filter>
-    </ClCompile>
     <ClCompile Include="Source\BsGUIInputCaret.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
@@ -389,5 +386,8 @@
     <ClCompile Include="Source\BsGUIButtonBase.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUITextInputEvent.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 0 - 44
BansheeEngine/Include/BsGUIButtonEvent.h

@@ -1,44 +0,0 @@
-#pragma once
-
-#include "BsPrerequisites.h"
-#include "CmInputFwd.h"
-#include "CmInt2.h"
-
-namespace BansheeEngine
-{
-	enum class GUIKeyEventType
-	{
-		KeyUp,
-		KeyDown,
-		TextInput
-	};
-
-	class BS_EXPORT GUIKeyEvent
-	{
-	public:
-		GUIKeyEvent();
-		GUIKeyEvent(bool shift, bool ctrl, bool alt);
-
-		GUIKeyEventType getType() const { return mType; }
-		CM::ButtonCode getKey() const { return mKey; }
-		const CM::UINT32& getInputChar() const { return mInputChar; }
-
-		bool isShiftDown() const { return mShift; }
-		bool isCtrlDown() const { return mCtrl; }
-		bool isAltDown() const { return mAlt; }
-	private:
-		friend class GUIManager;
-
-		GUIKeyEventType mType;
-		CM::ButtonCode mKey;
-		CM::UINT32 mInputChar;
-
-		bool mShift;
-		bool mCtrl;
-		bool mAlt;
-
-		void setKeyDownData(CM::ButtonCode key);
-		void setKeyUpData(CM::ButtonCode key);
-		void setTextInputData(CM::UINT32 inputChar);
-	};
-}

+ 4 - 2
BansheeEngine/Include/BsGUICommandEvent.h

@@ -6,7 +6,9 @@ namespace BansheeEngine
 {
 	enum class GUICommandEventType
 	{
-		Redraw
+		Redraw, CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
+		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo,
+		Escape, Delete, Backspace, Return, SelectAll, Copy, Cut, Paste, Tab
 	};
 
 	class BS_EXPORT GUICommandEvent
@@ -22,6 +24,6 @@ namespace BansheeEngine
 
 		GUICommandEventType mType;
 
-		void setRedrawData() { mType = GUICommandEventType::Redraw; }
+		void setType(GUICommandEventType type) { mType = type; }
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUIElement.h

@@ -80,7 +80,7 @@ namespace BansheeEngine
 		CM::Rect getBounds() const;
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		virtual bool keyEvent(const GUIKeyEvent& ev);
+		virtual bool textInputEvent(const GUITextInputEvent& ev);
 		virtual bool commandEvent(const GUICommandEvent& ev);
 
 		static void destroy(GUIElement* element);

+ 1 - 1
BansheeEngine/Include/BsGUIInputBox.h

@@ -76,7 +76,7 @@ namespace BansheeEngine
 		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		virtual bool keyEvent(const GUIKeyEvent& ev);
+		virtual bool textInputEvent(const GUITextInputEvent& ev);
 
 		virtual bool commandEvent(const GUICommandEvent& ev);
 

+ 7 - 4
BansheeEngine/Include/BsGUIManager.h

@@ -2,7 +2,7 @@
 
 #include "BsPrerequisites.h"
 #include "BsGUIMouseEvent.h"
-#include "BsGUIButtonEvent.h"
+#include "BsGUITextInputEvent.h"
 #include "BsGUICommandEvent.h"
 #include "CmModule.h"
 #include "CmColor.h"
@@ -78,7 +78,7 @@ namespace BansheeEngine
 		void addSelectiveInputElement(const GUIElement* element);
 
 		boost::signal<void(GUIWidget*, GUIElement*, const GUIMouseEvent&)> mouseEventFilter;
-		boost::signal<void(GUIWidget*, GUIElement*, const GUIKeyEvent&)> keyEventFilter;
+		boost::signal<void(GUIWidget*, GUIElement*, const GUITextInputEvent&)> textInputEventFilter;
 	private:
 		static float DOUBLE_CLICK_INTERVAL;
 
@@ -119,7 +119,7 @@ namespace BansheeEngine
 		CM::Int2 mLastCursorLocalPos;
 
 		GUIMouseEvent mMouseEvent;
-		GUIKeyEvent mKeyEvent;
+		GUITextInputEvent mTextInputEvent;
 		GUICommandEvent mCommandEvent;
 
 		SpriteTexturePtr mCaretTexture;
@@ -140,6 +140,7 @@ namespace BansheeEngine
 		boost::signals::connection mOnCursorPressedConn;
 		boost::signals::connection mOnCursorReleasedConn;
 		boost::signals::connection mOnTextInputConn;
+		boost::signals::connection mOnInputCommandConn;
 
 		boost::signals::connection mDragEndedConn;
 
@@ -162,6 +163,7 @@ namespace BansheeEngine
 		void onCursorReleased(const CM::PositionalInputEvent& event);
 		void onCursorPressed(const CM::PositionalInputEvent& event);
 		void onTextInput(const CM::TextInputEvent& event);
+		void onInputCommandEntered(CM::InputCommandType commandType);
 
 		bool onMouseDragEnded(const CM::PositionalInputEvent& event);
 
@@ -175,7 +177,8 @@ namespace BansheeEngine
 		CM::Int2 getWidgetRelativePos(const GUIWidget& widget, const CM::Int2& screenPos) const;
 
 		bool sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event);
-		bool sendKeyEvent(GUIWidget* widget, GUIElement* element, const GUIKeyEvent& event);
+		bool sendTextInputEvent(GUIWidget* widget, GUIElement* element, const GUITextInputEvent& event);
+		bool sendCommandEvent(GUIWidget* widget, GUIElement* element, const GUICommandEvent& event);
 	};
 
 	BS_EXPORT GUIManager& gGUIManager();

+ 22 - 0
BansheeEngine/Include/BsGUITextInputEvent.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "CmInputFwd.h"
+#include "CmInt2.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUITextInputEvent
+	{
+	public:
+		GUITextInputEvent();
+
+		const CM::UINT32& getInputChar() const { return mInputChar; }
+	private:
+		friend class GUIManager;
+
+		CM::UINT32 mInputChar;
+
+		void setData(CM::UINT32 inputChar);
+	};
+}

+ 7 - 1
BansheeEngine/Include/BsGUIWidget.h

@@ -47,7 +47,13 @@ namespace BansheeEngine
 		 * @brief	Forwards the specified key event to the specified element. The element
 		 * 			must be a child of this widget.
 		 */
-		virtual bool _keyEvent(GUIElement* element, const GUIKeyEvent& ev);
+		virtual bool _textInputEvent(GUIElement* element, const GUITextInputEvent& ev);
+
+		/**
+		 * @brief	Forwards the specified key event to the specified element. The element
+		 * 			must be a child of this widget.
+		 */
+		virtual bool _commandEvent(GUIElement* element, const GUICommandEvent& ev);
 
 		static GUISkin DefaultSkin;
 	protected:

+ 1 - 1
BansheeEngine/Include/BsPrerequisites.h

@@ -41,7 +41,7 @@ namespace BansheeEngine
 	class GUISkin;
 	struct GUIElementStyle;
 	class GUIMouseEvent;
-	class GUIKeyEvent;
+	class GUITextInputEvent;
 	class GUICommandEvent;
 	class GUIArea;
 	class GUILayout;

+ 0 - 39
BansheeEngine/Source/BsGUIButtonEvent.cpp

@@ -1,39 +0,0 @@
-#include "BsGUIButtonEvent.h"
-
-using namespace CamelotFramework;
-
-namespace BansheeEngine
-{
-	GUIKeyEvent::GUIKeyEvent()
-		:mType(GUIKeyEventType::KeyDown), mKey(BC_0), mShift(false), mCtrl(false), mAlt(false)
-	{
-
-	}
-
-	GUIKeyEvent::GUIKeyEvent(bool shift, bool ctrl, bool alt)
-		:mType(GUIKeyEventType::KeyDown), mKey(BC_0), mShift(shift), mCtrl(ctrl), mAlt(alt)
-	{
-
-	}
-
-	void GUIKeyEvent::setKeyDownData(ButtonCode key)
-	{
-		mType = GUIKeyEventType::KeyDown;
-		mKey = key;
-		mInputChar = 0;
-	}
-
-	void GUIKeyEvent::setKeyUpData(ButtonCode key)
-	{
-		mType = GUIKeyEventType::KeyUp;
-		mKey = key;
-		mInputChar = 0;
-	}
-
-	void GUIKeyEvent::setTextInputData(UINT32 inputChar)
-	{
-		mType = GUIKeyEventType::TextInput;
-		mKey = BC_0;
-		mInputChar = inputChar;
-	}
-}

+ 1 - 1
BansheeEngine/Source/BsGUIElement.cpp

@@ -59,7 +59,7 @@ namespace BansheeEngine
 		return false;
 	}
 
-	bool GUIElement::keyEvent(const GUIKeyEvent& ev)
+	bool GUIElement::textInputEvent(const GUITextInputEvent& ev)
 	{
 		return false;
 	}

+ 177 - 172
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -6,7 +6,7 @@
 #include "BsSpriteTexture.h"
 #include "BsTextSprite.h"
 #include "BsGUILayoutOptions.h"
-#include "BsGUIButtonEvent.h"
+#include "BsGUITextInputEvent.h"
 #include "BsGUIMouseEvent.h"
 #include "BsGUICommandEvent.h"
 #include "CmFont.h"
@@ -470,247 +470,252 @@ namespace BansheeEngine
 		return false;
 	}
 
-	bool GUIInputBox::keyEvent(const GUIKeyEvent& ev)
+	bool GUIInputBox::textInputEvent(const GUITextInputEvent& ev)
 	{
-		if(ev.getType() == GUIKeyEventType::KeyDown)
-		{
-			if(ev.getKey() == BC_BACK)
-			{
-				if(mText.size() > 0)
-				{
-					if(mSelectionShown)
-					{
-						deleteSelectedText();
-					}
-					else
-					{
-						UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos() - 1;
-
-						if(charIdx < (UINT32)mText.size())
-						{
-							eraseChar(charIdx);
+		if(mSelectionShown)
+			deleteSelectedText();
 
-							if(charIdx > 0)
-								charIdx--;
+		UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
+		insertChar(charIdx, ev.getInputChar());
 
-							gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+		gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
 
-							scrollTextToCaret();
-						}
-					}
+		scrollTextToCaret();
 
-					markContentAsDirty();
-				}
+		markContentAsDirty();
+		return true;
+	}
 
-				return true;
-			}
+	bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
+	{
+		if(ev.getType() == GUICommandEventType::Redraw)
+		{
+			markMeshAsDirty();
+			return true;
+		}
 
-			if(ev.getKey() == BC_DELETE)
+		if(ev.getType() == GUICommandEventType::Backspace)
+		{
+			if(mText.size() > 0)
 			{
-				if(mText.size() > 0)
+				if(mSelectionShown)
 				{
-					if(mSelectionShown)
-					{
-						deleteSelectedText();
-					}
-					else
+					deleteSelectedText();
+				}
+				else
+				{
+					UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos() - 1;
+
+					if(charIdx < (UINT32)mText.size())
 					{
-						UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
-						if(charIdx < (UINT32)mText.size())
-						{
-							eraseChar(charIdx);
+						eraseChar(charIdx);
 
-							if(charIdx > 0)
-								charIdx--;
+						if(charIdx > 0)
+							charIdx--;
 
-							gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+						gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
 
-							scrollTextToCaret();
-						}
+						scrollTextToCaret();
 					}
-
-					markContentAsDirty();
 				}
 
-				return true;
+				markContentAsDirty();
 			}
-			
-			if(ev.getKey() == BC_LEFT)
+
+			return true;
+		}
+
+		if(ev.getType() == GUICommandEventType::Delete)
+		{
+			if(mText.size() > 0)
 			{
-				if(ev.isShiftDown())
+				if(mSelectionShown)
 				{
-					if(!mSelectionShown)
-						showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
-
-					gGUIManager().getInputCaretTool()->moveCaretLeft();
-					gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
+					deleteSelectedText();
 				}
 				else
 				{
-					if(mSelectionShown)
+					UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
+					if(charIdx < (UINT32)mText.size())
 					{
-						UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
-						clearSelection();
+						eraseChar(charIdx);
+
+						if(charIdx > 0)
+							charIdx--;
+
+						gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
 
-						if(selStart > 0)
-							gGUIManager().getInputCaretTool()->moveCaretToChar(selStart - 1, CARET_AFTER);
-						else
-							gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
+						scrollTextToCaret();
 					}
-					else
-						gGUIManager().getInputCaretTool()->moveCaretLeft();
 				}
 
-				scrollTextToCaret();
 				markContentAsDirty();
-				return true;
 			}
 
-			if(ev.getKey() == BC_RIGHT)
+			return true;
+		}
+
+		if(ev.getType() == GUICommandEventType::CursorMoveLeft)
+		{
+			if(mSelectionShown)
 			{
-				if(ev.isShiftDown())
-				{
-					if(!mSelectionShown)
-						showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
+				UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
+				clearSelection();
 
-					gGUIManager().getInputCaretTool()->moveCaretRight();
-					gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
-				}
+				if(selStart > 0)
+					gGUIManager().getInputCaretTool()->moveCaretToChar(selStart - 1, CARET_AFTER);
 				else
-				{
-					if(mSelectionShown)
-					{
-						UINT32 selEnd = gGUIManager().getInputSelectionTool()->getSelectionEnd();
-						clearSelection();
+					gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
+			}
+			else
+				gGUIManager().getInputCaretTool()->moveCaretLeft();
 
-						if(selEnd > 0)
-							gGUIManager().getInputCaretTool()->moveCaretToChar(selEnd - 1, CARET_AFTER);
-						else
-							gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
-					}
-					else
-						gGUIManager().getInputCaretTool()->moveCaretRight();
-				}
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-				scrollTextToCaret();
-				markContentAsDirty();
-				return true;
-			}
+		if(ev.getType() == GUICommandEventType::SelectLeft)
+		{
+			if(!mSelectionShown)
+				showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
+
+			gGUIManager().getInputCaretTool()->moveCaretLeft();
+			gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
 
-			if(ev.getKey() == BC_UP)
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
+
+		if(ev.getType() == GUICommandEventType::CursorMoveRight)
+		{
+			if(mSelectionShown)
 			{
-				if(ev.isShiftDown())
-				{
-					if(!mSelectionShown)
-						showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
-				}
+				UINT32 selEnd = gGUIManager().getInputSelectionTool()->getSelectionEnd();
+				clearSelection();
+
+				if(selEnd > 0)
+					gGUIManager().getInputCaretTool()->moveCaretToChar(selEnd - 1, CARET_AFTER);
 				else
-					clearSelection();
+					gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
+			}
+			else
+				gGUIManager().getInputCaretTool()->moveCaretRight();
 
-				gGUIManager().getInputCaretTool()->moveCaretUp();
-				
-				if(ev.isShiftDown())
-					gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-				scrollTextToCaret();
-				markContentAsDirty();
-				return true;
-			}
+		if(ev.getType() == GUICommandEventType::SelectRight)
+		{
+			if(!mSelectionShown)
+				showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
 
-			if(ev.getKey() == BC_DOWN)
-			{
-				if(ev.isShiftDown())
-				{
-					if(!mSelectionShown)
-						showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
-				}
-				else
-					clearSelection();
+			gGUIManager().getInputCaretTool()->moveCaretRight();
+			gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
 
-				gGUIManager().getInputCaretTool()->moveCaretDown();
-				
-				if(ev.isShiftDown())
-					gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-				scrollTextToCaret();
-				markContentAsDirty();
-				return true;
-			}
+		if(ev.getType() == GUICommandEventType::CursorMoveUp)
+		{
+			clearSelection();
 
-			if(ev.getKey() == BC_RETURN)
-			{
-				if(mIsMultiline)
-				{
-					if(mSelectionShown)
-						deleteSelectedText();
+			gGUIManager().getInputCaretTool()->moveCaretUp();
 
-					insertChar(gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos(), '\n');
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-					gGUIManager().getInputCaretTool()->moveCaretRight();
-					scrollTextToCaret();
+		if(ev.getType() == GUICommandEventType::SelectUp)
+		{
+			if(!mSelectionShown)
+				showSelection(gGUIManager().getInputCaretTool()->getCaretPos());;
 
-					markContentAsDirty();
-					return true;
-				}
-				
-			}
+			gGUIManager().getInputCaretTool()->moveCaretUp();
+			gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
 
-			if(ev.getKey() == BC_A && ev.isCtrlDown())
-			{
-				showSelection(0);
-				gGUIManager().getInputSelectionTool()->selectAll();
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-				markContentAsDirty();
-				return true;
-			}
+		if(ev.getType() == GUICommandEventType::CursorMoveDown)
+		{
+			clearSelection();
 
-			if(ev.getKey() == BC_X && ev.isCtrlDown())
-			{
-				cutText();
+			gGUIManager().getInputCaretTool()->moveCaretDown();
 
-				markContentAsDirty();
-				return true;
-			}
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-			if(ev.getKey() == BC_C && ev.isCtrlDown())
-			{
-				copyText();
+		if(ev.getType() == GUICommandEventType::SelectDown)
+		{
+			if(!mSelectionShown)
+				showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
 
-				return true;
-			}
+			gGUIManager().getInputCaretTool()->moveCaretDown();
+			gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
+
+			scrollTextToCaret();
+			markContentAsDirty();
+			return true;
+		}
 
-			if(ev.getKey() == BC_V && ev.isCtrlDown())
+		if(ev.getType() == GUICommandEventType::Return)
+		{
+			if(mIsMultiline)
 			{
-				pasteText();
+				if(mSelectionShown)
+					deleteSelectedText();
+
+				insertChar(gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos(), '\n');
+
+				gGUIManager().getInputCaretTool()->moveCaretRight();
+				scrollTextToCaret();
 
 				markContentAsDirty();
 				return true;
 			}
+
 		}
-		else if(ev.getType() == GUIKeyEventType::TextInput)
-		{
-			if(mSelectionShown)
-				deleteSelectedText();
 
-			UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
-			insertChar(charIdx, ev.getInputChar());
+		if(ev.getType() == GUICommandEventType::SelectAll)
+		{
+			showSelection(0);
+			gGUIManager().getInputSelectionTool()->selectAll();
 
-			gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+			markContentAsDirty();
+			return true;
+		}
 
-			scrollTextToCaret();
+		if(ev.getType() == GUICommandEventType::Cut)
+		{
+			cutText();
 
 			markContentAsDirty();
 			return true;
 		}
 
-		return false;
-	}
+		if(ev.getType() == GUICommandEventType::Copy)
+		{
+			copyText();
 
-	bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
-	{
-		if(ev.getType() == GUICommandEventType::Redraw)
+			return true;
+		}
+
+		if(ev.getType() == GUICommandEventType::Paste)
 		{
-			markMeshAsDirty();
+			pasteText();
+
+			markContentAsDirty();
 			return true;
 		}
 

+ 88 - 16
BansheeEngine/Source/BsGUIManager.cpp

@@ -65,6 +65,7 @@ namespace BansheeEngine
 		mOnCursorPressedConn = gInput().onCursorPressed.connect(boost::bind(&GUIManager::onCursorPressed, this, _1));
 		mOnCursorReleasedConn = gInput().onCursorReleased.connect(boost::bind(&GUIManager::onCursorReleased, this, _1));
 		mOnTextInputConn = gInput().onCharInput.connect(boost::bind(&GUIManager::onTextInput, this, _1)); 
+		mOnInputCommandConn = gInput().onInputCommand.connect(boost::bind(&GUIManager::onInputCommandEntered, this, _1)); 
 
 		mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
 		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
@@ -100,6 +101,7 @@ namespace BansheeEngine
 		mOnCursorReleasedConn.disconnect();
 		mOnCursorMovedConn.disconnect();
 		mOnTextInputConn.disconnect();
+		mOnInputCommandConn.disconnect();
 
 		mDragEndedConn.disconnect();
 
@@ -190,8 +192,9 @@ namespace BansheeEngine
 				mIsCaretOn = !mIsCaretOn;
 
 				mCommandEvent = GUICommandEvent();
-				mCommandEvent.setRedrawData();
-				mKeyboardFocusElement->commandEvent(mCommandEvent);
+				mCommandEvent.setType(GUICommandEventType::Redraw);
+
+				sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
 			}
 		}
 
@@ -761,6 +764,77 @@ namespace BansheeEngine
 		}
 	}
 
+	void GUIManager::onInputCommandEntered(CM::InputCommandType commandType)
+	{
+		if(mKeyboardFocusElement == nullptr)
+			return;
+
+		mCommandEvent = GUICommandEvent();
+
+		switch(commandType)
+		{
+		case InputCommandType::Backspace:
+			mCommandEvent.setType(GUICommandEventType::Backspace);
+			break;
+		case InputCommandType::Delete:
+			mCommandEvent.setType(GUICommandEventType::Delete);
+			break;
+		case InputCommandType::Copy:
+			mCommandEvent.setType(GUICommandEventType::Copy);
+			break;
+		case InputCommandType::Cut:
+			mCommandEvent.setType(GUICommandEventType::Cut);
+			break;
+		case InputCommandType::Paste:
+			mCommandEvent.setType(GUICommandEventType::Paste);
+			break;
+		case InputCommandType::Undo:
+			mCommandEvent.setType(GUICommandEventType::Undo);
+			break;
+		case InputCommandType::Redo:
+			mCommandEvent.setType(GUICommandEventType::Redo);
+			break;
+		case InputCommandType::Return:
+			mCommandEvent.setType(GUICommandEventType::Return);
+			break;
+		case InputCommandType::Escape:
+			mCommandEvent.setType(GUICommandEventType::Escape);
+			break;
+		case InputCommandType::Tab:
+			mCommandEvent.setType(GUICommandEventType::Tab);
+			break;
+		case InputCommandType::SelectAll:
+			mCommandEvent.setType(GUICommandEventType::SelectAll);
+			break;
+		case InputCommandType::CursorMoveLeft:
+			mCommandEvent.setType(GUICommandEventType::CursorMoveLeft);
+			break;
+		case InputCommandType::CursorMoveRight:
+			mCommandEvent.setType(GUICommandEventType::CursorMoveRight);
+			break;
+		case InputCommandType::CursorMoveUp:
+			mCommandEvent.setType(GUICommandEventType::CursorMoveUp);
+			break;
+		case InputCommandType::CursorMoveDown:
+			mCommandEvent.setType(GUICommandEventType::CursorMoveDown);
+			break;
+		case InputCommandType::SelectLeft:
+			mCommandEvent.setType(GUICommandEventType::SelectLeft);
+			break;
+		case InputCommandType::SelectRight:
+			mCommandEvent.setType(GUICommandEventType::SelectRight);
+			break;
+		case InputCommandType::SelectUp:
+			mCommandEvent.setType(GUICommandEventType::SelectUp);
+			break;
+		case InputCommandType::SelectDown:
+			mCommandEvent.setType(GUICommandEventType::SelectDown);
+			break;
+		}
+
+		sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
+	}
+
 	bool GUIManager::findElementUnderCursor(const CM::Int2& cursorScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
 	{
 #if CM_DEBUG_MODE
@@ -910,17 +984,10 @@ namespace BansheeEngine
 	{
 		if(mKeyboardFocusElement != nullptr)
 		{
-			bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
-			bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
-			bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
-
-			if(ctrlDown || altDown) // Ignore text input because key characters + alt/ctrl usually correspond to certain commands
-				return;
-
-			mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
+			mTextInputEvent = GUITextInputEvent();
 
-			mKeyEvent.setTextInputData(event.textChar);
-			if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
+			mTextInputEvent.setData(event.textChar);
+			if(sendTextInputEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mTextInputEvent))
 				event.markAsUsed();
 		}
 	}
@@ -1049,12 +1116,17 @@ namespace BansheeEngine
 		return widget->_mouseEvent(element, event);
 	}
 
-	bool GUIManager::sendKeyEvent(GUIWidget* widget, GUIElement* element, const GUIKeyEvent& event)
+	bool GUIManager::sendTextInputEvent(GUIWidget* widget, GUIElement* element, const GUITextInputEvent& event)
 	{
-		if(!keyEventFilter.empty())
-			keyEventFilter(widget, element, event);
+		if(!textInputEventFilter.empty())
+			textInputEventFilter(widget, element, event);
 
-		return widget->_keyEvent(element, event);
+		return widget->_textInputEvent(element, event);
+	}
+
+	bool GUIManager::sendCommandEvent(GUIWidget* widget, GUIElement* element, const GUICommandEvent& event)
+	{
+		return widget->_commandEvent(element, event);
 	}
 
 	GUIManager& gGUIManager()

+ 17 - 0
BansheeEngine/Source/BsGUITextInputEvent.cpp

@@ -0,0 +1,17 @@
+#include "BsGUITextInputEvent.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	GUITextInputEvent::GUITextInputEvent()
+		:mInputChar(0)
+	{
+
+	}
+
+	void GUITextInputEvent::setData(UINT32 inputChar)
+	{
+		mInputChar = inputChar;
+	}
+}

+ 31 - 2
BansheeEngine/Source/BsGUIWidget.cpp

@@ -140,7 +140,7 @@ namespace BansheeEngine
 		return false;
 	}
 
-	bool GUIWidget::_keyEvent(GUIElement* element, const GUIKeyEvent& ev)
+	bool GUIWidget::_textInputEvent(GUIElement* element, const GUITextInputEvent& ev)
 	{
 		// If an element has any parents we send the events to all parents first and only then to the children unless
 		// the parents process them
@@ -159,7 +159,36 @@ namespace BansheeEngine
 			GUIElement* elem = todo.top();
 			todo.pop();
 
-			if(elem->keyEvent(ev))
+			if(elem->textInputEvent(ev))
+				return true;
+
+			if(todo.size() == 0)
+				return false;
+		}
+
+		return false;
+	}
+
+	bool GUIWidget::_commandEvent(GUIElement* element, const GUICommandEvent& ev)
+	{
+		// If an element has any parents we send the events to all parents first and only then to the children unless
+		// the parents process them
+		Stack<GUIElement*>::type todo;
+		GUIElementBase* curElement = element;
+		do
+		{
+			if(curElement->_getType() == GUIElementBase::Type::Element)
+				todo.push(static_cast<GUIElement*>(curElement));
+
+			curElement = curElement->_getParent();
+		} while(curElement != nullptr);
+
+		while(true)
+		{
+			GUIElement* elem = todo.top();
+			todo.pop();
+
+			if(elem->commandEvent(ev))
 				return true;
 
 			if(todo.size() == 0)

+ 7 - 0
CamelotCore/Include/CmInput.h

@@ -23,6 +23,8 @@ namespace CamelotFramework
 		boost::signal<void(const PositionalInputEvent&)> onCursorPressed;
 		boost::signal<void(const PositionalInputEvent&)> onCursorReleased;
 
+		boost::signal<void(InputCommandType)> onInputCommand;
+
 		void registerRawInputHandler(std::shared_ptr<RawInputHandler> inputHandler);
 
 		/**
@@ -90,6 +92,11 @@ namespace CamelotFramework
 		 */
 		void cursorReleased(const PositionalInputEvent& event);
 		
+		/**
+		 * @brief	Input commands as OS reports them.
+		 */
+		void inputCommandEntered(InputCommandType commandType);
+
 		/**
 		 * @brief	Updates the axis input values that need smoothing.
 		 */

+ 4 - 2
CamelotCore/Include/CmInputFwd.h

@@ -298,9 +298,11 @@ namespace CamelotFramework
 		mutable bool mIsUsed;
 	};
 
-	struct InputCommandEvent
+	enum class InputCommandType
 	{
-		// TODO
+		CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
+		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo,
+		Escape, Delete, Backspace, Return, SelectAll, Copy, Cut, Paste, Tab
 	};
 
 	struct TextInputEvent

+ 8 - 0
CamelotCore/Include/CmOSInputHandler.h

@@ -33,6 +33,7 @@ namespace CamelotFramework
 		boost::signal<void(const PositionalInputEvent&)> onCursorMoved;
 		boost::signal<void(const PositionalInputEvent&)> onCursorPressed;
 		boost::signal<void(const PositionalInputEvent&)> onCursorReleased;
+		boost::signal<void(InputCommandType)> onInputCommand;
 
 		/**
 			* @brief	Called every frame by InputManager. Capture input here if needed.
@@ -51,12 +52,14 @@ namespace CamelotFramework
 		float mMouseScroll;
 		WString mInputString;
 		Queue<ButtonStateChange>::type mButtonStates;
+		Queue<InputCommandType>::type mInputCommands;
 		OSPositionalInputButtonStates mMouseMoveBtnState;
 
 		boost::signals::connection mCharInputConn;
 		boost::signals::connection mCursorMovedConn;
 		boost::signals::connection mCursorPressedConn;
 		boost::signals::connection mCursorReleasedConn;
+		boost::signals::connection mInputCommandConn;
 		boost::signals::connection mMouseWheelScrolledConn;
 
 		/**
@@ -79,6 +82,11 @@ namespace CamelotFramework
 		 */
 		void cursorReleased(const Int2& cursorPos, OSMouseButton button, OSPositionalInputButtonStates& btnStates);
 
+		/**
+		 * @brief	Called from the message loop.
+		 */
+		void inputCommandEntered(InputCommandType commandType);
+
 		/**
 		 * @brief	Called from the message loop.
 		 */

+ 48 - 12
CamelotCore/Include/CmPlatform.h

@@ -2,19 +2,55 @@
 
 #include "CmPrerequisites.h"
 
-enum class CursorType
+namespace CamelotFramework
 {
-	Arrow,
-	Wait,
-	IBeam,
-	Help,
-	Hand,
-	SizeAll,
-	SizeNESW,
-	SizeNS,
-	SizeNWSE,
-	SizeWE
-};
+	enum class CursorType
+	{
+		Arrow,
+		Wait,
+		IBeam,
+		Help,
+		Hand,
+		SizeAll,
+		SizeNESW,
+		SizeNS,
+		SizeNWSE,
+		SizeWE
+	};
+
+	enum class NonClientAreaBorderType
+	{
+		TopLeft,
+		Top,
+		TopRight,
+		Left,
+		Right,
+		BottomLeft,
+		Bottom,
+		BottomRight	
+	};
+
+	enum class OSMouseButton
+	{
+		Left, Middle, Right, Count
+	};
+
+	struct CM_EXPORT OSPositionalInputButtonStates
+	{
+		OSPositionalInputButtonStates()
+		{
+			mouseButtons[0] = false;
+			mouseButtons[1] = false;
+			mouseButtons[2] = false;
+
+			shift = false;
+			ctrl = false;
+		}
+
+		bool mouseButtons[OSMouseButton::Count];
+		bool shift, ctrl;
+	};
+}
 
 //Bring in the specific platform's header file
 #if CM_PLATFORM == CM_PLATFORM_WIN32

+ 4 - 0
CamelotCore/Include/CmPlatformWndProc.h

@@ -11,8 +11,12 @@ namespace CamelotFramework
 		static LRESULT CALLBACK _win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
 	private:
+		static bool isShiftPressed;
+		static bool isCtrlPressed;
+
 		static LRESULT translateNonClientAreaType(NonClientAreaBorderType type);
 
 		static void getMouseData(HWND hWnd, WPARAM wParam, LPARAM lParam, Int2& mousePos, OSPositionalInputButtonStates& btnStates);
+		static bool getCommand(unsigned int virtualKeyCode, InputCommandType& command);
 	};
 }

+ 2 - 33
CamelotCore/Include/Win32/CmPlatformImpl.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "CmPrerequisites.h"
+#include "CmInputFwd.h"
 #include "CmInt2.h"
 #include "CmRect.h"
 #include <boost/signals.hpp>
@@ -17,18 +18,6 @@ namespace CamelotFramework
 
 		Pimpl* data;
 	};
-	
-	enum class NonClientAreaBorderType
-	{
-		TopLeft,
-		Top,
-		TopRight,
-		Left,
-		Right,
-		BottomLeft,
-		Bottom,
-		BottomRight	
-	};
 
 	struct CM_EXPORT NonClientResizeArea
 	{
@@ -42,27 +31,6 @@ namespace CamelotFramework
 		Vector<Rect>::type moveAreas;
 	};
 
-	enum class OSMouseButton
-	{
-		Left, Middle, Right, Count
-	};
-
-	struct CM_EXPORT OSPositionalInputButtonStates
-	{
-		OSPositionalInputButtonStates()
-		{
-			mouseButtons[0] = false;
-			mouseButtons[1] = false;
-			mouseButtons[2] = false;
-
-			shift = false;
-			ctrl = false;
-		}
-
-		bool mouseButtons[OSMouseButton::Count];
-		bool shift, ctrl;
-	};
-
 	/**
 	 * @brief	Provides access to various Windows operating system functions, including
 	 * 			the main message pump.
@@ -220,6 +188,7 @@ namespace CamelotFramework
 		static boost::signal<void(const Int2&, OSPositionalInputButtonStates)> onCursorMoved;
 		static boost::signal<void(const Int2&, OSMouseButton button, OSPositionalInputButtonStates)> onCursorButtonPressed;
 		static boost::signal<void(const Int2&, OSMouseButton button, OSPositionalInputButtonStates)> onCursorButtonReleased;
+		static boost::signal<void(InputCommandType)> onInputCommand;
 		static boost::signal<void(float)> onMouseWheelScrolled;
 		static boost::signal<void(UINT32)> onCharInput;
 		static boost::signal<void(RenderWindow*)> onWindowFocusReceived;

+ 7 - 0
CamelotCore/Source/CmInput.cpp

@@ -35,6 +35,7 @@ namespace CamelotFramework
 		mOSInputHandler->onCursorMoved.connect(boost::bind(&Input::cursorMoved, this, _1));
 		mOSInputHandler->onCursorPressed.connect(boost::bind(&Input::cursorPressed, this, _1));
 		mOSInputHandler->onCursorReleased.connect(boost::bind(&Input::cursorReleased, this, _1));
+		mOSInputHandler->onInputCommand.connect(boost::bind(&Input::inputCommandEntered, this, _1));
 
 		RenderWindowManager::instance().onFocusGained.connect(boost::bind(&Input::inputWindowChanged, this, _1));
 	}
@@ -150,6 +151,12 @@ namespace CamelotFramework
 			onCursorReleased(event);
 	}
 
+	void Input::inputCommandEntered(InputCommandType commandType)
+	{
+		if(!onInputCommand.empty())
+			onInputCommand(commandType);
+	}
+
 	void Input::charInput(UINT32 chr)
 	{
 		if(!onCharInput.empty())

+ 21 - 0
CamelotCore/Source/CmOSInputHandler.cpp

@@ -12,6 +12,7 @@ namespace CamelotFramework
 		mCursorMovedConn = Platform::onCursorMoved.connect(boost::bind(&OSInputHandler::cursorMoved, this, _1, _2));
 		mCursorPressedConn = Platform::onCursorButtonPressed.connect(boost::bind(&OSInputHandler::cursorPressed, this, _1, _2, _3));
 		mCursorReleasedConn = Platform::onCursorButtonReleased.connect(boost::bind(&OSInputHandler::cursorReleased, this, _1, _2, _3));
+		mInputCommandConn = Platform::onInputCommand.connect(boost::bind(&OSInputHandler::inputCommandEntered, this, _1));
 
 		mMouseWheelScrolledConn  = Platform::onMouseWheelScrolled.connect(boost::bind(&OSInputHandler::mouseWheelScrolled, this, _1));
 	}
@@ -22,6 +23,7 @@ namespace CamelotFramework
 		mCursorMovedConn.disconnect();
 		mCursorPressedConn.disconnect();
 		mCursorReleasedConn.disconnect();
+		mInputCommandConn.disconnect();
 		mMouseWheelScrolledConn.disconnect();
 	}
 
@@ -32,6 +34,7 @@ namespace CamelotFramework
 		float mouseScroll;
 		OSPositionalInputButtonStates mouseMoveBtnState;
 		Queue<ButtonStateChange>::type buttonStates;
+		Queue<InputCommandType>::type inputCommands;
 
 		{
 			CM_LOCK_MUTEX(mOSInputMutex);
@@ -46,6 +49,9 @@ namespace CamelotFramework
 
 			buttonStates = mButtonStates;
 			mButtonStates = Queue<ButtonStateChange>::type();
+
+			inputCommands = mInputCommands;
+			mInputCommands = Queue<InputCommandType>::type();
 		}
 
 		if(mousePosition != mLastCursorPos || (Math::Abs(mouseScroll) > 0.00001f))
@@ -115,6 +121,14 @@ namespace CamelotFramework
 			buttonStates.pop();
 		}
 
+		while(!inputCommands.empty())
+		{
+			if(!onInputCommand.empty())
+				onInputCommand(inputCommands.front());
+
+			inputCommands.pop();
+		}
+
 		if(!onCharInput.empty())
 		{
 			for(auto& curChar : inputString)
@@ -167,6 +181,13 @@ namespace CamelotFramework
 		btnState.btnStates = btnStates;
 	}
 
+	void OSInputHandler::inputCommandEntered(InputCommandType commandType)
+	{
+		CM_LOCK_MUTEX(mOSInputMutex);
+
+		mInputCommands.push(commandType);
+	}
+
 	void OSInputHandler::mouseWheelScrolled(float scrollPos)
 	{
 		CM_LOCK_MUTEX(mOSInputMutex);

+ 122 - 23
CamelotCore/Source/CmPlatformWndProc.cpp

@@ -2,10 +2,12 @@
 #include "CmRenderWindow.h"
 #include "CmApplication.h"
 #include "CmInput.h"
+#include "CmDebug.h"
 
 namespace CamelotFramework
 {
-	UINT32 PlatformWndProc::mMoveResizeMouseUpState = 0;
+	bool PlatformWndProc::isShiftPressed = false;
+	bool PlatformWndProc::isCtrlPressed = false;
 
 	LRESULT CALLBACK PlatformWndProc::_win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 	{
@@ -41,27 +43,6 @@ namespace CamelotFramework
 
 				break;
 			}
-		case WM_SYSKEYDOWN:
-			switch( wParam )
-			{
-			case VK_CONTROL:
-			case VK_SHIFT:
-			case VK_MENU: //ALT
-				//return zero to bypass defProc and signal we processed the message
-				return 0;
-			}
-			break;
-		case WM_SYSKEYUP:
-			switch( wParam )
-			{
-			case VK_CONTROL:
-			case VK_SHIFT:
-			case VK_MENU: //ALT
-			case VK_F10:
-				//return zero to bypass defProc and signal we processed the message
-				return 0;
-			}
-			break;
 		case WM_SYSCHAR:
 			// return zero to bypass defProc and signal we processed the message, unless it's an ALT-space
 			if (wParam != VK_SPACE)
@@ -271,9 +252,52 @@ namespace CamelotFramework
 
 				return true;
 			}
+		case WM_SYSKEYDOWN:
+		case WM_KEYDOWN:
+			{
+				if(wParam == VK_SHIFT)
+				{
+					isShiftPressed = true;
+					break;
+				}
+
+				if(wParam == VK_CONTROL)
+				{
+					isCtrlPressed = true;
+					break;
+				}
+
+				InputCommandType command = InputCommandType::Backspace;
+				if(getCommand((unsigned int)wParam, command))
+				{
+					if(!onInputCommand.empty())
+						onInputCommand(command);
+
+					return 0;
+				}
+
+				break;
+			}
+		case WM_SYSKEYUP:
+		case WM_KEYUP:
+			{
+				if(wParam == VK_SHIFT)
+				{
+					isShiftPressed = false;
+				}
+
+				if(wParam == VK_CONTROL)
+				{
+					isCtrlPressed = false;
+				}
+
+				break;
+			}
 		case WM_DEADCHAR:
 		case WM_CHAR:
 			{
+				// TODO - Not handling IME input
+
 				switch (wParam) 
 				{ 
 				case VK_BACK:
@@ -282,7 +306,6 @@ namespace CamelotFramework
 				case VK_ESCAPE:
 				case VK_TAB: 
 					break; 
-
 				default:    // displayable character 
 					{
 						UINT8 scanCode = (lParam >> 16) & 0xFF;
@@ -296,6 +319,10 @@ namespace CamelotFramework
 						if(vk == 0)
 							return 0;
 
+						InputCommandType command = InputCommandType::Backspace;
+						if(getCommand(vk, command)) // We ignore character combinations that are special commands
+							return 0;
+
 						bool isDeadKey = (MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, layout) & (1 << 31)) != 0;
 						if(isDeadKey)
 							return 0;
@@ -389,4 +416,76 @@ namespace CamelotFramework
 		btnStates.shift = (wParam & MK_SHIFT) != 0;
 		btnStates.ctrl = (wParam & MK_CONTROL) != 0;
 	}
+
+	bool PlatformWndProc::getCommand(unsigned int virtualKeyCode, InputCommandType& command)
+	{
+		switch (virtualKeyCode) 
+		{ 
+		case VK_LEFT:
+			command = isShiftPressed ? InputCommandType::SelectLeft : InputCommandType::CursorMoveLeft;
+			return true;
+		case VK_RIGHT:
+			command = isShiftPressed ? InputCommandType::SelectRight : InputCommandType::CursorMoveRight;
+			return true;
+		case VK_UP:
+			command = isShiftPressed ? InputCommandType::SelectUp : InputCommandType::CursorMoveUp;
+			return true;
+		case VK_DOWN:
+			command = isShiftPressed ? InputCommandType::SelectDown : InputCommandType::CursorMoveDown;
+			return true;
+		case VK_ESCAPE:
+			command = InputCommandType::Escape;
+			return true;
+		case VK_RETURN:
+			command = InputCommandType::Return;
+			return true;
+		case VK_BACK:
+			command = InputCommandType::Backspace;
+			return true;
+		case VK_DELETE:
+			command = InputCommandType::Delete;
+			return true;
+		case VK_TAB:
+			command = InputCommandType::Tab;
+			return true;
+		case 0x41: // A
+			if(isCtrlPressed)
+			{
+				command = InputCommandType::SelectAll;
+				return true;
+			}
+		case 0x43: // C
+			if(isCtrlPressed)
+			{
+				command = InputCommandType::Copy;
+				return true;
+			}
+		case 0x56: // V
+			if(isCtrlPressed)
+			{
+				command = InputCommandType::Paste;
+				return true;
+			}
+		case 0x58: // X
+			if(isCtrlPressed)
+			{
+				command = InputCommandType::Cut;
+				return true;
+			}
+		case 0x5A: // Z
+			if(isCtrlPressed)
+			{
+				command = InputCommandType::Undo;
+				return true;
+			}
+		case 0x59: // Y
+			if(isCtrlPressed)
+			{
+				command = InputCommandType::Redo;
+				return true;
+			}
+		}
+
+		return false;
+	}
 }

+ 1 - 0
CamelotCore/Source/Win32/CmPlatformImpl.cpp

@@ -11,6 +11,7 @@ namespace CamelotFramework
 	boost::signal<void(const Int2&, OSPositionalInputButtonStates)> Platform::onCursorMoved;
 	boost::signal<void(const Int2&, OSMouseButton button, OSPositionalInputButtonStates)> Platform::onCursorButtonPressed;
 	boost::signal<void(const Int2&, OSMouseButton button, OSPositionalInputButtonStates)> Platform::onCursorButtonReleased;
+	boost::signal<void(InputCommandType)> Platform::onInputCommand;
 	boost::signal<void(float)> Platform::onMouseWheelScrolled;
 	boost::signal<void(UINT32)> Platform::onCharInput;
 

+ 0 - 6
TODO.txt

@@ -10,18 +10,12 @@ GUIWidget::updateMeshes leaks. If I leave the game running I can see memory cont
    gApplication and gBansheeApp, which is non-intuitive (e.g. retrieving a window can be done on gApplication, but running main loop can happen on both
 
 GUI INPUT REFACTOR:
-Add support for commands like cut/copy/paste/select-all/arrow movement
-  - Ensure that arrow movement, and backspace/delete commands properly handle repeat
-simulateButtonUp call in _win32WndProc might not be needed anymore?
 Possibly also forward doubleclick from OS?
-Rename/Get-rid-off mMouseEvent, mKeyEvent, mCommandEvent in GUIManager
 
 GUI SYSTEM WRAP UP:
  GUIManager drag events should only trigger after a mouse moves a few pixels
   - Right now it is breaking double click as start/end drag events get sent out twice
 
- Key repeat
-  - Add special text input commands, which will get repeatedly sent as long as their corresponding key is set in GUIManager
  Double click (Input box select all)
  Windows drag and drop detect
   - http://www.codeguru.com/cpp/misc/misc/draganddrop/article.php/c349/Drag-And-Drop-between-Window-Controls.htm