Browse Source

Selection using Ctrl + A works

Marko Pintera 12 years ago
parent
commit
a83a991e7b

+ 3 - 3
BansheeEngine/Include/BsGUIButtonEvent.h

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

+ 1 - 1
BansheeEngine/Include/BsGUIElement.h

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

+ 3 - 1
BansheeEngine/Include/BsGUIInputBox.h

@@ -64,6 +64,7 @@ namespace BansheeEngine
 		// Selection & input caret
 		CM::UINT32 mSelectionStart;
 		CM::UINT32 mSelectionEnd;
+		CM::UINT32 mSelectionAnchor;
 		CM::UINT32 mCaretPos;
 		bool mCaretShown;
 		bool mSelectionShown;
@@ -71,7 +72,7 @@ namespace BansheeEngine
 		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
-		virtual bool buttonEvent(const GUIButtonEvent& ev);
+		virtual bool keyEvent(const GUIKeyEvent& ev);
 		virtual bool commandEvent(const GUICommandEvent& ev);
 
 		Sprite* renderElemToSprite(CM::UINT32 renderElemIdx, CM::UINT32& localRenderElemIdx) const;
@@ -87,5 +88,6 @@ namespace BansheeEngine
 
 		CM::Rect getTextBounds() const;
 		TEXT_SPRITE_DESC getTextDesc() const;
+		CM::UINT32 getValidCharCount() const;
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUIManager.h

@@ -66,7 +66,7 @@ namespace BansheeEngine
 		CM::Int2 mLastCursorLocalPos;
 
 		GUIMouseEvent mMouseEvent;
-		GUIButtonEvent mKeyEvent;
+		GUIKeyEvent mKeyEvent;
 		GUICommandEvent mCommandEvent;
 
 		SpriteTexturePtr mCaretTexture;

+ 1 - 1
BansheeEngine/Include/BsGUIWidget.h

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

+ 1 - 1
BansheeEngine/Include/BsPrerequisites.h

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

+ 5 - 5
BansheeEngine/Source/BsGUIButtonEvent.cpp

@@ -4,33 +4,33 @@ using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	GUIButtonEvent::GUIButtonEvent()
+	GUIKeyEvent::GUIKeyEvent()
 		:mType(GUIKeyEventType::KeyDown), mKey(BC_0), mShift(false), mCtrl(false), mAlt(false)
 	{
 
 	}
 
-	GUIButtonEvent::GUIButtonEvent(bool shift, bool ctrl, bool alt)
+	GUIKeyEvent::GUIKeyEvent(bool shift, bool ctrl, bool alt)
 		:mType(GUIKeyEventType::KeyDown), mKey(BC_0), mShift(shift), mCtrl(ctrl), mAlt(alt)
 	{
 
 	}
 
-	void GUIButtonEvent::setKeyDownData(ButtonCode key)
+	void GUIKeyEvent::setKeyDownData(ButtonCode key)
 	{
 		mType = GUIKeyEventType::KeyDown;
 		mKey = key;
 		mInputChar = 0;
 	}
 
-	void GUIButtonEvent::setKeyUpData(ButtonCode key)
+	void GUIKeyEvent::setKeyUpData(ButtonCode key)
 	{
 		mType = GUIKeyEventType::KeyUp;
 		mKey = key;
 		mInputChar = 0;
 	}
 
-	void GUIButtonEvent::setTextInputData(UINT32 inputChar)
+	void GUIKeyEvent::setTextInputData(UINT32 inputChar)
 	{
 		mType = GUIKeyEventType::TextInput;
 		mKey = BC_0;

+ 1 - 1
BansheeEngine/Source/BsGUIElement.cpp

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

+ 74 - 20
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -26,8 +26,8 @@ namespace BansheeEngine
 
 	GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
 		:GUIElement(parent, style, layoutOptions), mInputCursorSet(false), mDragInProgress(false),
-		mSelectionStart(0), mSelectionEnd(0), mCaretSprite(nullptr), mCaretShown(false), mSelectionShown(false), mCaretPos(0),
-		mIsMultiline(false)
+		mSelectionStart(0), mSelectionEnd(0), mSelectionAnchor(0), mCaretSprite(nullptr), mCaretShown(false), 
+		mSelectionShown(false), mCaretPos(0), mIsMultiline(false)
 	{
 		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
 		mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
@@ -306,7 +306,7 @@ namespace BansheeEngine
 		{
 			mImageDesc.texture = mStyle->active.texture;
 
-			if(mText.size() > 0)
+			if(getValidCharCount() > 0)
 			{
 				UINT32 charIdx = mTextSprite->getCharIdxAtPos(ev.getPosition());
 				Rect charRect = mTextSprite->getCharRect(charIdx);
@@ -361,18 +361,18 @@ namespace BansheeEngine
 		return false;
 	}
 
-	bool GUIInputBox::buttonEvent(const GUIButtonEvent& ev)
+	bool GUIInputBox::keyEvent(const GUIKeyEvent& ev)
 	{
 		if(ev.getType() == GUIKeyEventType::KeyDown)
 		{
 			if(ev.getKey() == BC_BACK)
 			{
-				if(mText.size() > 0)
+				if(getValidCharCount() > 0)
 				{
 					if(mSelectionShown)
 					{
-						mText.erase(mSelectionStart, mSelectionEnd);
-						mCaretPos = mSelectionStart + 1;
+						mText.erase(mText.begin() + mSelectionStart, mText.begin() + mSelectionEnd);
+						mCaretPos = mSelectionStart;
 						clearSelection();
 					}
 					else
@@ -389,15 +389,46 @@ namespace BansheeEngine
 
 				return true;
 			}
+
+			if(ev.getKey() == BC_DELETE)
+			{
+				if(getValidCharCount() > 0)
+				{
+					if(mSelectionShown)
+					{
+						mText.erase(mText.begin() + mSelectionStart, mText.begin() + mSelectionEnd);
+						mCaretPos = mSelectionStart;
+						clearSelection();
+					}
+					else
+					{
+						mText.erase(mCaretPos, 1);
+					}
+
+					markAsDirty();
+				}
+
+				return true;
+			}
 			
 			if(ev.getKey() == BC_LEFT)
 			{
 				if(ev.isShiftDown())
 				{
-					// TODO
+					if(!mSelectionShown)
+						showSelection(mCaretPos, mCaretPos);
+
+					if(mSelectionAnchor == mSelectionEnd)
+						mSelectionStart = (UINT32)std::max(0, (INT32)mSelectionStart - 1);
+					else
+						mSelectionEnd = (UINT32)std::max(0, (INT32)mSelectionEnd - 1);
+
+					markAsDirty();
+					return true;
 				}
 				else
 				{
+					clearSelection();
 					mCaretPos = (UINT32)std::max(0, (INT32)mCaretPos - 1);
 
 					markAsDirty();
@@ -409,11 +440,21 @@ namespace BansheeEngine
 			{
 				if(ev.isShiftDown())
 				{
-					// TODO
+					if(!mSelectionShown)
+						showSelection(mCaretPos, mCaretPos);
+
+					if(mSelectionAnchor == mSelectionStart)
+						mSelectionEnd = std::min(getValidCharCount(), mSelectionEnd + 1);
+					else
+						mSelectionStart = std::min(getValidCharCount(), mSelectionStart + 1);
+
+					markAsDirty();
+					return true;
 				}
 				else
 				{
-					mCaretPos = std::min((UINT32)mText.size(), mCaretPos + 1);
+					clearSelection();
+					mCaretPos = std::min(getValidCharCount(), mCaretPos + 1);
 
 					markAsDirty();
 					return true;
@@ -424,7 +465,14 @@ namespace BansheeEngine
 			{
 				if(mIsMultiline)
 				{
-					mText += '\n';
+					if(mSelectionShown)
+					{
+						mText.erase(mText.begin() + mSelectionStart, mText.begin() + mSelectionEnd);
+						mCaretPos = mSelectionStart;
+						clearSelection();
+					}
+
+					mText.insert(mText.begin() + mCaretPos, '\n');
 
 					markAsDirty();
 					return true;
@@ -432,16 +480,17 @@ namespace BansheeEngine
 				
 			}
 
-			// TODO - shift + left + right arrow to select
-			// TODO - ctrl + a to select all
-			// TODO - ctrl + c, ctrl + v, ctrl + x to copy/cut/paste
+			if(ev.getKey() == BC_A && ev.isCtrlDown())
+			{
+				showSelection(0, getValidCharCount());
+			}
 		}
 		else if(ev.getType() == GUIKeyEventType::TextInput)
 		{
 			if(mSelectionShown)
 			{
-				mText.erase(mSelectionStart, mSelectionEnd);
-				mCaretPos = mSelectionStart + 1;
+				mText.erase(mText.begin() + mSelectionStart, mText.begin() + mSelectionEnd);
+				mCaretPos = mSelectionStart;
 				clearSelection();
 			}
 
@@ -449,7 +498,6 @@ namespace BansheeEngine
 			mCaretPos++;
 
 			markAsDirty();
-
 			return true;
 		}
 
@@ -523,6 +571,7 @@ namespace BansheeEngine
 	{
 		mSelectionStart = startChar;
 		mSelectionEnd = endChar;
+		mSelectionAnchor = startChar;
 		mSelectionShown = true;
 		markAsDirty();
 	}
@@ -545,12 +594,12 @@ namespace BansheeEngine
 			return selectionRects;
 
 		UINT32 startLine = mTextSprite->getLineForChar(mSelectionStart);
-		UINT32 endLine = mTextSprite->getLineForChar(mSelectionEnd);
+		UINT32 endLine = mTextSprite->getLineForChar(mSelectionEnd - 1);
 
 		{
 			const SpriteLineDesc& lineDesc = mTextSprite->getLineDesc(startLine);
 			Rect startChar = mTextSprite->getCharRect(mSelectionStart);
-			UINT32 endCharIdx = mSelectionEnd;
+			UINT32 endCharIdx = mSelectionEnd - 1;
 			if(endCharIdx >= lineDesc.endChar)
 				endCharIdx = lineDesc.endChar - 1;
 			
@@ -590,7 +639,7 @@ namespace BansheeEngine
 			if(lineDesc.startChar != lineDesc.endChar)
 			{
 				Rect startChar = mTextSprite->getCharRect(lineDesc.startChar);
-				Rect endChar = mTextSprite->getCharRect(mSelectionEnd);
+				Rect endChar = mTextSprite->getCharRect(mSelectionEnd - 1);
 
 				Rect selectionRect;
 				selectionRect.x = startChar.x;
@@ -637,6 +686,11 @@ namespace BansheeEngine
 		return textDesc;
 	}
 
+	UINT32 GUIInputBox::getValidCharCount() const
+	{
+		return (UINT32)mText.size(); // TODO - Need to ignore newline chars
+	}
+
 	void GUIInputBox::_setFocus(bool focus)
 	{
 		if(focus)

+ 20 - 4
BansheeEngine/Source/BsGUIManager.cpp

@@ -497,7 +497,7 @@ namespace BansheeEngine
 				bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 				bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
 
-				mKeyEvent = GUIButtonEvent(shiftDown, ctrlDown, altDown);
+				mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
 
 				mKeyEvent.setKeyDownData(event.buttonCode);
 				mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);
@@ -506,7 +506,6 @@ namespace BansheeEngine
 
 		if(event.isMouse())
 		{
-			// TODO - Maybe avoid querying these for every event separately?
 			bool buttonStates[(int)GUIMouseButton::Count];
 			buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
 			buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
@@ -565,7 +564,21 @@ namespace BansheeEngine
 		if(event.isUsed())
 			return;
 
-		// TODO - Maybe avoid querying these for every event separately?
+		if(event.isKeyboard())
+		{
+			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);
+
+				mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
+
+				mKeyEvent.setKeyUpData(event.buttonCode);
+				mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);
+			}
+		}
+
 		if(event.isMouse())
 		{
 			bool buttonStates[(int)GUIMouseButton::Count];
@@ -765,7 +778,10 @@ namespace BansheeEngine
 			bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
 			bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
 
-			mKeyEvent = GUIButtonEvent(shiftDown, ctrlDown, altDown);
+			if(ctrlDown || altDown) // Ignore text input because key characters + alt/ctrl usually correspond to certain commands
+				return;
+
+			mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
 
 			mKeyEvent.setTextInputData(event.textChar);
 			mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);

+ 2 - 2
BansheeEngine/Source/BsGUIWidget.cpp

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

+ 6 - 6
CamelotCore/Source/CmInput.cpp

@@ -62,21 +62,21 @@ namespace CamelotFramework
 
 	void Input::update()
 	{
-		if(mOSInputHandler == nullptr)
+		if(mRawInputHandler == nullptr)
 		{
-			LOGERR("OS input handler not initialized!");
+			LOGERR("Raw input handler not initialized!");
 			return;
 		}
 		else
-			mOSInputHandler->update();
+			mRawInputHandler->update();
 
-		if(mRawInputHandler == nullptr)
+		if(mOSInputHandler == nullptr)
 		{
-			LOGERR("Raw input handler not initialized!");
+			LOGERR("OS input handler not initialized!");
 			return;
 		}
 		else
-			mRawInputHandler->update();
+			mOSInputHandler->update();
 
 		Vector<ButtonCode>::type simulatedUp;
 		Vector<ButtonCode>::type simulatedDown;

+ 4 - 3
TODO.txt

@@ -26,11 +26,12 @@ IMMEDIATE:
 	- SpriteTexture keeps a static reference to DUmmyTexture which I need to release before shutdown
 
 TextBox needed elements:
- - Clicking to the left of a letter should move the cursor to position BEFORE the letter, 
-   and clicking to the right should move it to the position AFTER the letter. Right now 
-   it is always moved after which makes it impossible to move the cursor before the first letter.
  - Implement and test multiline text control
  - Test selection (especially multiline selection)
+ - I need to replace mText with clean text without \n because newline will break my char indexes
+ - Ctrl+A doesn't work. Only TextInput event is sent, without button press
+ - Text scroll. Typing outside of textbox should scroll the text so caret is visible.
+ - Get DebugCamera to ignore input if GUI has already processed it
  - Cut/Copy/Paste
  - LATER
   - TAB between input elements