فهرست منبع

Added VirtualInput (still WIP but working)

Marko Pintera 12 سال پیش
والد
کامیت
6824ae90d6
31فایلهای تغییر یافته به همراه663 افزوده شده و 138 حذف شده
  1. 1 0
      BansheeEngine.sln
  2. 5 0
      BansheeEngine/BansheeEngine.vcxproj
  3. 15 0
      BansheeEngine/BansheeEngine.vcxproj.filters
  4. 1 2
      BansheeEngine/Include/BsGUICommandEvent.h
  5. 1 0
      BansheeEngine/Include/BsGUIElement.h
  6. 7 0
      BansheeEngine/Include/BsGUIInputBox.h
  7. 5 0
      BansheeEngine/Include/BsGUIManager.h
  8. 22 0
      BansheeEngine/Include/BsGUIVirtualButtonEvent.h
  9. 6 0
      BansheeEngine/Include/BsGUIWidget.h
  10. 83 0
      BansheeEngine/Include/BsInputConfiguration.h
  11. 5 0
      BansheeEngine/Include/BsPrerequisites.h
  12. 61 0
      BansheeEngine/Include/BsVirtualInput.h
  13. 4 0
      BansheeEngine/Source/BsApplication.cpp
  14. 5 0
      BansheeEngine/Source/BsGUIElement.cpp
  15. 20 13
      BansheeEngine/Source/BsGUIInputBox.cpp
  16. 21 24
      BansheeEngine/Source/BsGUIManager.cpp
  17. 5 0
      BansheeEngine/Source/BsGUIWidget.cpp
  18. 105 0
      BansheeEngine/Source/BsInputConfiguration.cpp
  19. 183 0
      BansheeEngine/Source/BsVirtualInput.cpp
  20. 4 0
      CamelotClient/Include/BsGUISceneTreeView.h
  21. 13 0
      CamelotClient/Source/BsEditorApplication.cpp
  22. 19 11
      CamelotClient/Source/BsGUISceneTreeView.cpp
  23. 6 6
      CamelotClient/Source/CmDebugCamera.cpp
  24. 13 3
      CamelotCore/Include/CmInput.h
  25. 3 2
      CamelotCore/Include/CmInputFwd.h
  26. 2 2
      CamelotCore/Include/CmRawInputHandler.h
  27. 30 8
      CamelotCore/Source/CmInput.cpp
  28. 0 42
      CamelotCore/Source/CmPlatformWndProc.cpp
  29. 4 4
      CamelotOISInput/Source/CmInputHandlerOIS.cpp
  30. 1 21
      TreeView.txt
  31. 13 0
      VirtualInput.txt

+ 1 - 0
BansheeEngine.sln

@@ -54,6 +54,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		TODOEditor.txt = TODOEditor.txt
 		TreeView.txt = TreeView.txt
 		UndoRedo.txt = UndoRedo.txt
+		VirtualInput.txt = VirtualInput.txt
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CamelotFreeImgImporter", "CamelotFreeImgImporter\CamelotFreeImgImporter.vcxproj", "{122B7A22-0C62-4B35-B661-EBF3F394EA79}"

+ 5 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -226,6 +226,8 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="Source\BsInputConfiguration.cpp" />
+    <ClCompile Include="Source\BsVirtualInput.cpp" />
     <ClInclude Include="Include\BsApplication.h" />
     <ClInclude Include="Include\BsDrawHelper3D.h" />
     <ClInclude Include="Include\BsDrawHelperTemplate.h" />
@@ -268,6 +270,8 @@
     <ClInclude Include="Include\BsGUIToggle.h" />
     <ClInclude Include="Include\BsGUIToggleGroup.h" />
     <ClInclude Include="Include\BsGUIViewport.h" />
+    <ClInclude Include="Include\BsGUIVirtualButtonEvent.h" />
+    <ClInclude Include="Include\BsInputConfiguration.h" />
     <ClInclude Include="Include\BsPrerequisites.h" />
     <ClInclude Include="Include\BsGUIElement.h" />
     <ClInclude Include="Include\BsGUIElementStyle.h" />
@@ -299,6 +303,7 @@
     <ClInclude Include="Include\BsUpdateCallback.h" />
     <ClCompile Include="Source\BsGUIButtonBase.cpp" />
     <ClCompile Include="Source\BsGUIContextMenu.cpp" />
+    <ClInclude Include="Include\BsVirtualInput.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsApplication.cpp" />

+ 15 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -255,6 +255,15 @@
     <ClInclude Include="Include\BsGUIDropDownHitBox.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsVirtualInput.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsInputConfiguration.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsGUIVirtualButtonEvent.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -443,5 +452,11 @@
     <ClCompile Include="Source\BsGUIDropDownHitBox.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsVirtualInput.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsInputConfiguration.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 1 - 2
BansheeEngine/Include/BsGUICommandEvent.h

@@ -7,8 +7,7 @@ namespace BansheeEngine
 	enum class GUICommandEventType
 	{
 		Redraw, FocusLost, FocusGained, CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
-		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo, Rename,
-		Escape, Delete, Backspace, Return, SelectAll, Copy, Cut, Paste, Tab
+		SelectLeft, SelectRight, SelectUp, SelectDown, Escape, Delete, Backspace, Return
 	};
 
 	class BS_EXPORT GUICommandEvent

+ 1 - 0
BansheeEngine/Include/BsGUIElement.h

@@ -103,6 +103,7 @@ namespace BansheeEngine
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 		virtual bool textInputEvent(const GUITextInputEvent& ev);
 		virtual bool commandEvent(const GUICommandEvent& ev);
+		virtual bool virtualButtonEvent(const GUIVirtualButtonEvent& ev);
 
 		static void destroy(GUIElement* element);
 

+ 7 - 0
BansheeEngine/Include/BsGUIInputBox.h

@@ -4,6 +4,7 @@
 #include "BsGUIElement.h"
 #include "BsImageSprite.h"
 #include "BsTextSprite.h"
+#include "BsVirtualInput.h"
 
 namespace BansheeEngine
 {
@@ -60,6 +61,7 @@ namespace BansheeEngine
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 		virtual bool textInputEvent(const GUITextInputEvent& ev);
 		virtual bool commandEvent(const GUICommandEvent& ev);
+		virtual bool virtualButtonEvent(const GUIVirtualButtonEvent& ev);
 
 		virtual CM::Vector2I _getTextInputOffset() const;
 		virtual CM::RectI _getTextInputRect() const;
@@ -68,6 +70,11 @@ namespace BansheeEngine
 
 		virtual GUIContextMenu* getContextMenu() const;
 	private:
+		static VirtualButton mCopyVB;
+		static VirtualButton mPasteVB;
+		static VirtualButton mCutVB;
+		static VirtualButton mSelectAllVB;
+
 		// Sprites
 		ImageSprite* mImageSprite;
 		TextSprite* mTextSprite;

+ 5 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -4,6 +4,7 @@
 #include "BsGUIMouseEvent.h"
 #include "BsGUITextInputEvent.h"
 #include "BsGUICommandEvent.h"
+#include "BsGUIVirtualButtonEvent.h"
 #include "BsGUIMaterialInfo.h"
 #include "CmModule.h"
 #include "CmColor.h"
@@ -164,6 +165,7 @@ namespace BansheeEngine
 		GUIMouseEvent mMouseEvent;
 		GUITextInputEvent mTextInputEvent;
 		GUICommandEvent mCommandEvent;
+		GUIVirtualButtonEvent mVirtualButtonEvent;
 
 		HSpriteTexture mCaretTexture;
 		CM::Color mCaretColor;
@@ -182,6 +184,7 @@ namespace BansheeEngine
 		boost::signals::connection mOnCursorDoubleClick;
 		boost::signals::connection mOnTextInputConn;
 		boost::signals::connection mOnInputCommandConn;
+		boost::signals::connection mOnVirtualButtonDown;
 
 		boost::signals::connection mDragEndedConn;
 
@@ -203,6 +206,7 @@ namespace BansheeEngine
 		void onCursorDoubleClick(const CM::PositionalInputEvent& event);
 		void onTextInput(const CM::TextInputEvent& event);
 		void onInputCommandEntered(CM::InputCommandType commandType);
+		void onVirtualButtonDown(const VirtualButton& button);
 
 		bool onMouseDragEnded(const CM::PositionalInputEvent& event);
 
@@ -219,6 +223,7 @@ namespace BansheeEngine
 		bool sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event);
 		bool sendTextInputEvent(GUIWidget* widget, GUIElement* element, const GUITextInputEvent& event);
 		bool sendCommandEvent(GUIWidget* widget, GUIElement* element, const GUICommandEvent& event);
+		bool sendVirtualButtonEvent(GUIWidget* widget, GUIElement* element, const GUIVirtualButtonEvent& event);
 	};
 
 	BS_EXPORT GUIManager& gGUIManager();

+ 22 - 0
BansheeEngine/Include/BsGUIVirtualButtonEvent.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsVirtualInput.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIVirtualButtonEvent
+	{
+	public:
+		GUIVirtualButtonEvent()
+		{ }
+
+		const VirtualButton& getButton() const { return mButton; }
+	private:
+		friend class GUIManager;
+
+		VirtualButton mButton;
+
+		void setButton(const VirtualButton& button) { mButton = button; }
+	};
+}

+ 6 - 0
BansheeEngine/Include/BsGUIWidget.h

@@ -55,6 +55,12 @@ namespace BansheeEngine
 		 */
 		virtual bool _commandEvent(GUIElement* element, const GUICommandEvent& ev);
 
+		/**
+		 * @brief	Forwards the specified virtual button event to the specified element. The element
+		 * 			must be a child of this widget.
+		 */
+		virtual bool _virtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& ev);
+
 		static GUISkin DefaultSkin;
 	protected:
 		friend class CM::SceneObject;

+ 83 - 0
BansheeEngine/Include/BsInputConfiguration.h

@@ -0,0 +1,83 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "CmInputFwd.h"
+
+namespace BansheeEngine
+{
+	enum class VButtonModifier
+	{
+		None = 0x00,
+		Shift = 0x01,
+		Ctrl = 0x02,
+		Alt = 0x04,
+		ShiftCtrl = 0x03,
+		CtrlAlt = 0x06,
+		ShiftAlt = 0x05,
+		ShiftCtrlAlt = 0x07
+	};
+
+	struct VIRTUAL_BUTTON_DESC
+	{
+		VIRTUAL_BUTTON_DESC();
+		VIRTUAL_BUTTON_DESC(CM::ButtonCode buttonCode, VButtonModifier modifiers = VButtonModifier::None, bool repeatable = false);
+
+		CM::ButtonCode buttonCode;
+		VButtonModifier modifiers;
+		bool repeatable;
+	};
+
+	/**
+	 * @brief	Identifier for a virtual button. 
+	 * 			
+	 * @note	Primary purpose of this class is to avoid expensive string compare (i.e. button names),
+	 * 			and instead use a unique button identifier for compare. Generally you want to create 
+	 * 			one of these using the button name, and then store it for later use. 
+	 * 			
+	 *			This class is not thread safe and should only be used on the sim thread.
+	 */
+	class BS_EXPORT VirtualButton 
+	{
+	public:
+		VirtualButton();
+		VirtualButton(const CM::String& name);
+
+		CM::UINT32 buttonIdentifier;
+
+		bool operator== (const VirtualButton& rhs) const
+		{
+			return (buttonIdentifier == rhs.buttonIdentifier);
+		}
+
+	private:
+		static CM::Map<CM::String, CM::UINT32>::type UniqueButtonIds;
+		static CM::UINT32 NextButtonId;
+	};
+
+	class BS_EXPORT InputConfiguration
+	{
+		struct VirtualButtonData
+		{
+			CM::String name;
+			VirtualButton button;
+			VIRTUAL_BUTTON_DESC desc;
+		};
+
+	public:
+		InputConfiguration();
+
+		void registerButton(const CM::String& name, CM::ButtonCode buttonCode, VButtonModifier modifiers = VButtonModifier::None, bool repeatable = false);
+		void unregisterButton(const CM::String& name);
+
+		void setRepeatInterval(float seconds) { mRepeatInterval = seconds; }
+		float getRepeatInterval() const { return mRepeatInterval; }
+
+		bool getButton(CM::ButtonCode code, CM::UINT32 modifiers, VirtualButton& btn, VIRTUAL_BUTTON_DESC& btnDesc) const;
+
+		// TODO - registerAxis
+	private:
+		CM::Vector<VirtualButtonData>::type mButtons[CM::BC_Count];
+
+		float mRepeatInterval;
+	};
+}

+ 5 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -25,6 +25,10 @@ namespace CM = CamelotFramework;
 
 namespace BansheeEngine
 {
+	class VirtualButton;
+	class VirtualInput;
+	class InputConfiguration;
+
 	// GUI
 	class GUIManager;
 	class GUIWidget;
@@ -43,6 +47,7 @@ namespace BansheeEngine
 	class GUIMouseEvent;
 	class GUITextInputEvent;
 	class GUICommandEvent;
+	class GUIVirtualButtonEvent;
 	class GUIArea;
 	class GUILayout;
 	class GUILayoutX;

+ 61 - 0
BansheeEngine/Include/BsVirtualInput.h

@@ -0,0 +1,61 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "CmModule.h"
+#include "BsInputConfiguration.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT VirtualInput : public CM::Module<VirtualInput>
+	{
+		enum class ButtonState
+		{
+			Off,
+			On,
+			ToggledOn,
+			ToggledOff
+		};
+
+		struct ButtonData
+		{
+			VirtualButton button;
+			ButtonState state;
+			float timestamp;
+			bool allowRepeat;
+		};
+
+		struct VirtualButtonEvent
+		{
+			VirtualButton button;
+			ButtonState state;
+		};
+
+	public:
+		VirtualInput();
+
+		static std::shared_ptr<InputConfiguration> createConfiguration();
+
+		void setConfiguration(const std::shared_ptr<InputConfiguration>& input);
+		const std::shared_ptr<InputConfiguration>& getConfiguration() const { return mInputConfiguration; }
+
+		bool isButtonDown(const VirtualButton& button) const;
+		bool isButtonUp(const VirtualButton& button) const;
+		bool isButtonHeld(const VirtualButton& button) const;
+
+		void update();
+
+		boost::signal<void(const VirtualButton&)> onButtonDown;
+		boost::signal<void(const VirtualButton&)> onButtonUp;
+		boost::signal<void(const VirtualButton&)> onButtonHeld;
+	private:
+		friend class VirtualButton;
+
+		std::shared_ptr<InputConfiguration> mInputConfiguration;
+		CM::Map<CM::UINT32, ButtonData>::type mCachedStates;
+		CM::Queue<VirtualButtonEvent>::type mEvents;
+		CM::UINT32 mActiveModifiers;
+
+		void buttonDown(const CM::ButtonEvent& event);
+		void buttonUp(const CM::ButtonEvent& event);
+	};
+}

+ 4 - 0
BansheeEngine/Source/BsApplication.cpp

@@ -12,6 +12,7 @@
 #include "BsScriptManager.h"
 #include "CmApplication.h"
 #include "CmProfiler.h"
+#include "BsVirtualInput.h"
 
 using namespace CamelotFramework;
 
@@ -38,6 +39,7 @@ namespace BansheeEngine
 		
 		CM::gApplication().startUp(desc);
 
+		VirtualInput::startUp(cm_new<VirtualInput>());
 		ScriptManager::startUp(cm_new<ScriptManager>());
 		GUIManager::startUp(cm_new<GUIManager>());
 		GUIMaterialManager::startUp(cm_new<GUIMaterialManager>());
@@ -82,12 +84,14 @@ namespace BansheeEngine
 		GUIManager::shutDown();
 		GUIMaterialManager::shutDown();
 		ScriptManager::shutDown();
+		VirtualInput::shutDown();
 		
 		CM::gApplication().shutDown();
 	}
 
 	void Application::update()
 	{
+		VirtualInput::instance().update();
 		PROFILE_CALL(GUIManager::instance().update(), "GUI");
 	}
 

+ 5 - 0
BansheeEngine/Source/BsGUIElement.cpp

@@ -66,6 +66,11 @@ namespace BansheeEngine
 		return false;
 	}
 
+	bool GUIElement::virtualButtonEvent(const GUIVirtualButtonEvent& ev)
+	{
+		return false;
+	}
+
 	void GUIElement::_setWidgetDepth(UINT8 depth) 
 	{ 
 		mDepth |= depth << 24; 

+ 20 - 13
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -23,6 +23,11 @@ using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
+	VirtualButton GUIInputBox::mCopyVB = VirtualButton("Copy");
+	VirtualButton GUIInputBox::mPasteVB = VirtualButton("Paste");
+	VirtualButton GUIInputBox::mCutVB = VirtualButton("Cut");
+	VirtualButton GUIInputBox::mSelectAllVB = VirtualButton("SelectAll");
+
 	const String& GUIInputBox::getGUITypeName()
 	{
 		static String name = "InputBox";
@@ -750,37 +755,39 @@ namespace BansheeEngine
 
 		}
 
-		if(ev.getType() == GUICommandEventType::SelectAll)
-		{
-			showSelection(0);
-			gGUIManager().getInputSelectionTool()->selectAll();
-
-			markContentAsDirty();
-			return true;
-		}
+		return false;
+	}
 
-		if(ev.getType() == GUICommandEventType::Cut)
+	bool GUIInputBox::virtualButtonEvent(const GUIVirtualButtonEvent& ev)
+	{
+		if(ev.getButton() == mCutVB)
 		{
 			cutText();
 
 			markContentAsDirty();
 			return true;
 		}
-
-		if(ev.getType() == GUICommandEventType::Copy)
+		else if(ev.getButton() == mCopyVB)
 		{
 			copyText();
 
 			return true;
 		}
-
-		if(ev.getType() == GUICommandEventType::Paste)
+		else if(ev.getButton() == mPasteVB)
 		{
 			pasteText();
 
 			markContentAsDirty();
 			return true;
 		}
+		else if(ev.getButton() == mSelectAllVB)
+		{
+			showSelection(0);
+			gGUIManager().getInputSelectionTool()->selectAll();
+
+			markContentAsDirty();
+			return true;
+		}
 
 		return false;
 	}

+ 21 - 24
BansheeEngine/Source/BsGUIManager.cpp

@@ -31,6 +31,7 @@
 #include "CmProfiler.h"
 #include "CmMeshHeap.h"
 #include "CmTransientMesh.h"
+#include "BsVirtualInput.h"
 
 using namespace CamelotFramework;
 namespace BansheeEngine
@@ -72,6 +73,7 @@ namespace BansheeEngine
 		mOnCursorDoubleClick = gInput().onDoubleClick.connect(boost::bind(&GUIManager::onCursorDoubleClick, this, _1));
 		mOnTextInputConn = gInput().onCharInput.connect(boost::bind(&GUIManager::onTextInput, this, _1)); 
 		mOnInputCommandConn = gInput().onInputCommand.connect(boost::bind(&GUIManager::onInputCommandEntered, this, _1)); 
+		mOnVirtualButtonDown = VirtualInput::instance().onButtonDown.connect(boost::bind(&GUIManager::onVirtualButtonDown, this, _1));
 
 		mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
 		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
@@ -114,6 +116,7 @@ namespace BansheeEngine
 		mOnCursorDoubleClick.disconnect();
 		mOnTextInputConn.disconnect();
 		mOnInputCommandConn.disconnect();
+		mOnVirtualButtonDown.disconnect();
 
 		mDragEndedConn.disconnect();
 
@@ -951,36 +954,12 @@ namespace BansheeEngine
 		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::Rename:
-			mCommandEvent.setType(GUICommandEventType::Rename);
-			break;
-		case InputCommandType::SelectAll:
-			mCommandEvent.setType(GUICommandEventType::SelectAll);
-			break;
 		case InputCommandType::CursorMoveLeft:
 			mCommandEvent.setType(GUICommandEventType::CursorMoveLeft);
 			break;
@@ -1013,6 +992,19 @@ namespace BansheeEngine
 		}		
 	}
 
+	void GUIManager::onVirtualButtonDown(const VirtualButton& button)
+	{
+		mVirtualButtonEvent.setButton(button);
+		
+		for(auto& elementInFocus : mElementsInFocus)
+		{
+			bool processed = sendVirtualButtonEvent(elementInFocus.widget, elementInFocus.element, mVirtualButtonEvent);
+
+			if(processed)
+				break;
+		}
+	}
+
 	bool GUIManager::findElementUnderCursor(const CM::Vector2I& cursorScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
 	{
 		Vector<const RenderWindow*>::type widgetWindows;
@@ -1385,6 +1377,11 @@ namespace BansheeEngine
 		return widget->_commandEvent(element, event);
 	}
 
+	bool GUIManager::sendVirtualButtonEvent(GUIWidget* widget, GUIElement* element, const GUIVirtualButtonEvent& event)
+	{
+		return widget->_virtualButtonEvent(element, event);
+	}
+
 	GUIManager& gGUIManager()
 	{
 		return GUIManager::instance();

+ 5 - 0
BansheeEngine/Source/BsGUIWidget.cpp

@@ -127,6 +127,11 @@ namespace BansheeEngine
 		return element->commandEvent(ev);
 	}
 
+	bool GUIWidget::_virtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& ev)
+	{
+		return element->virtualButtonEvent(ev);
+	}
+
 	void GUIWidget::registerElement(GUIElement* elem)
 	{
 		assert(elem != nullptr);

+ 105 - 0
BansheeEngine/Source/BsInputConfiguration.cpp

@@ -0,0 +1,105 @@
+#include "BsInputConfiguration.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	Map<String, UINT32>::type VirtualButton::UniqueButtonIds;
+	UINT32 VirtualButton::NextButtonId = 0;
+
+	VIRTUAL_BUTTON_DESC::VIRTUAL_BUTTON_DESC()
+		:buttonCode(BC_0), modifiers(VButtonModifier::None), repeatable(false)
+	{ }
+
+	VIRTUAL_BUTTON_DESC::VIRTUAL_BUTTON_DESC(ButtonCode buttonCode, VButtonModifier modifiers, bool repeatable)
+		:buttonCode(buttonCode), modifiers(modifiers), repeatable(repeatable)
+	{ }
+
+	VirtualButton::VirtualButton()
+		:buttonIdentifier(0)
+	{ }
+
+	VirtualButton::VirtualButton(const CM::String& name)
+	{
+		auto findIter = UniqueButtonIds.find(name);
+
+		if(findIter != UniqueButtonIds.end())
+			buttonIdentifier = findIter->second;
+		else
+		{
+			buttonIdentifier = NextButtonId;
+			UniqueButtonIds[name] = NextButtonId++;
+		}
+	}
+
+	InputConfiguration::InputConfiguration()
+		:mRepeatInterval(1.0f)
+	{ }
+
+	void InputConfiguration::registerButton(const CM::String& name, CM::ButtonCode buttonCode, VButtonModifier modifiers, bool repeatable)
+	{
+		Vector<VirtualButtonData>::type& btnData = mButtons[buttonCode & 0x0000FFFF];
+
+		INT32 idx = -1;
+		for(UINT32 i = 0; i < (UINT32)btnData.size(); i++)
+		{
+			if(btnData[i].desc.modifiers == modifiers)
+			{
+				idx = (INT32)i;
+				break;
+			}
+		}
+
+		if(idx == -1)
+		{
+			idx = (INT32)btnData.size();
+			btnData.push_back(VirtualButtonData());
+		}
+
+		VirtualButtonData& btn = btnData[idx];
+		btn.name = name;
+		btn.desc = VIRTUAL_BUTTON_DESC(buttonCode, modifiers, repeatable);
+		btn.button = VirtualButton(name);
+	}
+
+	void InputConfiguration::unregisterButton(const CM::String& name)
+	{
+		Vector<UINT32>::type toRemove;
+
+		for(UINT32 i = 0; i < BC_Count; i++)
+		{
+			for(UINT32 j = 0; j < (UINT32)mButtons[i].size(); j++)
+			{
+				if(mButtons[i][j].name == name)
+					toRemove.push_back(j);
+			}
+
+			UINT32 numRemoved = 0;
+			for(auto& toRemoveIdx : toRemove)
+			{
+				mButtons[i].erase(mButtons[i].begin() + toRemoveIdx - numRemoved);
+
+				numRemoved++;
+			}
+
+			toRemove.clear();
+		}
+	}
+
+	bool InputConfiguration::getButton(CM::ButtonCode code, UINT32 modifiers, VirtualButton& btn, VIRTUAL_BUTTON_DESC& btnDesc) const
+	{
+		const Vector<VirtualButtonData>::type& btnData = mButtons[code & 0x0000FFFF];
+
+		for(UINT32 i = 0; i < (UINT32)btnData.size(); i++)
+		{
+			if((((UINT32)btnData[i].desc.modifiers) & modifiers) == ((UINT32)btnData[i].desc.modifiers))
+			{
+				btn = btnData[i].button;
+				btnDesc = btnData[i].desc;
+				return true;
+			}
+		}
+
+		return false;
+	}
+}

+ 183 - 0
BansheeEngine/Source/BsVirtualInput.cpp

@@ -0,0 +1,183 @@
+#include "BsVirtualInput.h"
+#include "CmInput.h"
+#include "CmTime.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	VirtualInput::VirtualInput()
+		:mActiveModifiers((UINT32)VButtonModifier::None)
+	{
+		mInputConfiguration = createConfiguration();
+
+		Input::instance().onButtonDown.connect(boost::bind(&VirtualInput::buttonDown, this, _1));
+		Input::instance().onButtonUp.connect(boost::bind(&VirtualInput::buttonUp, this, _1));
+	}
+
+	std::shared_ptr<InputConfiguration> VirtualInput::createConfiguration()
+	{
+		return cm_shared_ptr<InputConfiguration>();
+	}
+
+	void VirtualInput::setConfiguration(const std::shared_ptr<InputConfiguration>& input)
+	{
+		mInputConfiguration = input;
+
+		mCachedStates.clear(); // Note: Technically this is slightly wrong as it will
+		// "forget" any buttons currently held down, but shouldn't matter much in practice.
+	}
+
+	bool VirtualInput::isButtonDown(const VirtualButton& button) const
+	{
+		auto iterFind = mCachedStates.find(button.buttonIdentifier);
+
+		if(iterFind != mCachedStates.end())
+			return iterFind->second.state == ButtonState::ToggledOn;
+		
+		return false;
+	}
+
+	bool VirtualInput::isButtonUp(const VirtualButton& button) const
+	{
+		auto iterFind = mCachedStates.find(button.buttonIdentifier);
+
+		if(iterFind != mCachedStates.end())
+			return iterFind->second.state == ButtonState::ToggledOff;
+
+		return false;
+	}
+
+	bool VirtualInput::isButtonHeld(const VirtualButton& button) const
+	{
+		auto iterFind = mCachedStates.find(button.buttonIdentifier);
+
+		if(iterFind != mCachedStates.end())
+			return iterFind->second.state == ButtonState::On || iterFind->second.state == ButtonState::ToggledOn;
+
+		return false;
+	}
+
+	void VirtualInput::update()
+	{
+		for(auto& state : mCachedStates)
+		{
+			if(state.second.state == ButtonState::ToggledOff)
+				state.second.state = ButtonState::Off;
+			else if(state.second.state == ButtonState::ToggledOn)
+				state.second.state = ButtonState::On;
+		}
+
+		bool hasEvents = true;
+		float repeatInternal = mInputConfiguration->getRepeatInterval();
+		float currentTime = gTime().getTime();
+
+		// Trigger all events
+		while(hasEvents)
+		{
+			while(!mEvents.empty())
+			{
+				VirtualButtonEvent& event = mEvents.front();
+
+				if(event.state == ButtonState::On)
+				{
+					if(!onButtonDown.empty())
+						onButtonDown(event.button);
+				}
+				else if(event.state == ButtonState::Off)
+				{
+					if(!onButtonUp.empty())
+						onButtonUp(event.button);
+				}
+
+				mEvents.pop();
+			}
+
+			// Queue up any repeatable events
+			hasEvents = false;
+			
+			for(auto& state : mCachedStates)
+			{
+				if(state.second.state != ButtonState::On)
+					continue;
+
+				if(!state.second.allowRepeat)
+					continue;
+
+				float diff = currentTime - state.second.timestamp;
+				if(diff >= repeatInternal)
+				{
+					state.second.timestamp += repeatInternal;
+
+					VirtualButtonEvent event;
+					event.button = state.second.button;
+					event.state = ButtonState::On;
+
+					mEvents.push(event);
+				}
+
+				hasEvents = true;
+			}
+		}
+	}
+
+	void VirtualInput::buttonDown(const ButtonEvent& event)
+	{
+		if(event.buttonCode == BC_LSHIFT || event.buttonCode == BC_RSHIFT)
+			mActiveModifiers |= (UINT32)VButtonModifier::Shift;
+		else if(event.buttonCode == BC_LCONTROL || event.buttonCode == BC_RCONTROL)
+			mActiveModifiers |= (UINT32)VButtonModifier::Ctrl;
+		else if(event.buttonCode == BC_LMENU || event.buttonCode == BC_RMENU)
+			mActiveModifiers |= (UINT32)VButtonModifier::Alt;
+		else
+		{
+			VirtualButton btn;
+			VIRTUAL_BUTTON_DESC btnDesc;
+			if(mInputConfiguration->getButton(event.buttonCode, mActiveModifiers, btn, btnDesc))
+			{
+				ButtonData& data = mCachedStates[btn.buttonIdentifier];
+
+				data.button = btn;
+				data.state = ButtonState::ToggledOn;
+				data.timestamp = event.timestamp;
+				data.allowRepeat = btnDesc.repeatable;
+
+				VirtualButtonEvent virtualEvent;
+				virtualEvent.button = btn;
+				virtualEvent.state = ButtonState::On;
+
+				mEvents.push(virtualEvent);
+			}
+		}
+	}
+
+	void VirtualInput::buttonUp(const ButtonEvent& event)
+	{
+		if(event.buttonCode == BC_LSHIFT || event.buttonCode == BC_RSHIFT)
+			mActiveModifiers &= ~(UINT32)VButtonModifier::Shift;
+		else if(event.buttonCode == BC_LCONTROL || event.buttonCode == BC_RCONTROL)
+			mActiveModifiers &= ~(UINT32)VButtonModifier::Ctrl;
+		else if(event.buttonCode == BC_LMENU || event.buttonCode == BC_RMENU)
+			mActiveModifiers &= ~(UINT32)VButtonModifier::Alt;
+		else
+		{
+			VirtualButton btn;
+			VIRTUAL_BUTTON_DESC btnDesc;
+			if(mInputConfiguration->getButton(event.buttonCode, mActiveModifiers, btn, btnDesc))
+			{
+				ButtonData& data = mCachedStates[btn.buttonIdentifier];
+
+				data.button = btn;
+				data.state = ButtonState::ToggledOff;
+				data.timestamp = event.timestamp;
+				data.allowRepeat = btnDesc.repeatable;
+
+				VirtualButtonEvent virtualEvent;
+				virtualEvent.button = btn;
+				virtualEvent.state = ButtonState::Off;
+
+				mEvents.push(virtualEvent);
+			}
+		}
+	}
+}

+ 4 - 0
CamelotClient/Include/BsGUISceneTreeView.h

@@ -2,6 +2,7 @@
 
 #include "BsEditorPrerequisites.h"
 #include "BsGUIElementContainer.h"
+#include "BsVirtualInput.h"
 #include <boost/signal.hpp>
 
 namespace BansheeEditor
@@ -143,6 +144,8 @@ namespace BansheeEditor
 		TreeElement* mMouseOverDragElement;
 		float mMouseOverDragElementTime;
 
+		static BS::VirtualButton mRenameVB;
+
 		GUISceneTreeView(BS::GUIWidget& parent, BS::GUIElementStyle* backgroundStyle, BS::GUIElementStyle* elementBtnStyle, 
 			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, BS::GUIElementStyle* editBoxStyle, 
 			BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const BS::GUILayoutOptions& layoutOptions);
@@ -156,6 +159,7 @@ namespace BansheeEditor
 
 		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
 		virtual bool commandEvent(const BS::GUICommandEvent& ev);
+		virtual bool virtualButtonEvent(const BS::GUIVirtualButtonEvent& ev);
 		void elementToggled(TreeElement* element, bool toggled);
 
 		bool isSelectionActive() const;

+ 13 - 0
CamelotClient/Source/BsEditorApplication.cpp

@@ -22,6 +22,7 @@
 #include "CmPass.h"
 #include "BsRenderable.h"
 #include "BsDbgTestGameObjectRef.h"
+#include "BsVirtualInput.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -41,6 +42,18 @@ namespace BansheeEditor
 		const String& renderSystemLibraryName = getLibraryNameForRenderSystem(renderSystemPlugin);
 		gBansheeApp().startUp(renderWindowDesc, renderSystemLibraryName, "BansheeForwardRenderer", "D:\\CamelotResourceMetas"); // TODO - Make renderer and resource cache dir customizable
 		EditorGUI::startUp(cm_new<EditorGUI>());
+
+		{
+			auto inputConfig = VirtualInput::instance().getConfiguration();
+
+			inputConfig->registerButton("Rename", BC_F2);
+			inputConfig->registerButton("Undo", BC_Z, VButtonModifier::Ctrl);
+			inputConfig->registerButton("Redo", BC_Y, VButtonModifier::Ctrl);
+			inputConfig->registerButton("Copy", BC_C, VButtonModifier::Ctrl);
+			inputConfig->registerButton("Cut", BC_X, VButtonModifier::Ctrl);
+			inputConfig->registerButton("Paste", BC_V, VButtonModifier::Ctrl);
+		}
+
 		//gApplication().loadPlugin("SBansheeEditor"); // Managed part of the editor
 
 		/************************************************************************/

+ 19 - 11
CamelotClient/Source/BsGUISceneTreeView.cpp

@@ -11,6 +11,7 @@
 #include "BsGUIMouseEvent.h"
 #include "BsGUISkin.h"
 #include "BsGUICommandEvent.h"
+#include "BsGUIVirtualButtonEvent.h"
 #include "CmSceneObject.h"
 #include "CmSceneManager.h"
 #include "BsCmdEditPlainFieldGO.h"
@@ -32,6 +33,8 @@ namespace BansheeEditor
 	const float GUISceneTreeView::SCROLL_AREA_HEIGHT_PCT = 0.1f;
 	const UINT32 GUISceneTreeView::SCROLL_SPEED_PX_PER_SEC = 25;
 
+	VirtualButton GUISceneTreeView::mRenameVB = VirtualButton("Rename");
+
 	GUISceneTreeView::DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
 		:numObjects(numObjects)
 	{
@@ -635,17 +638,6 @@ namespace BansheeEditor
 
 	bool GUISceneTreeView::commandEvent(const GUICommandEvent& ev)
 	{
-		if(ev.getType() == GUICommandEventType::Rename)
-		{
-			if(isSelectionActive() && mEditElement == nullptr)
-			{
-				unselectAll();
-				enableEdit(mSelectedElements[0].element);
-			}
-
-			return true;
-		}
-		
 		if(ev.getType() == GUICommandEventType::CursorMoveUp || ev.getType() == GUICommandEventType::SelectUp)
 		{
 			TreeElement* topMostElement = getTopMostSelectedElement();
@@ -698,6 +690,22 @@ namespace BansheeEditor
 		return false;
 	}
 
+	bool GUISceneTreeView::virtualButtonEvent(const BS::GUIVirtualButtonEvent& ev)
+	{
+		if(ev.getButton() == mRenameVB)
+		{
+			if(isSelectionActive() && mEditElement == nullptr)
+			{
+				enableEdit(mSelectedElements[0].element);
+				unselectAll();
+			}
+
+			return true;
+		}
+
+		return false;
+	}
+
 	void GUISceneTreeView::dragAndDropEnded()
 	{
 		mDragInProgress = false;

+ 6 - 6
CamelotClient/Source/CmDebugCamera.cpp

@@ -29,12 +29,12 @@ namespace CamelotFramework
 
 	void DebugCamera::update()
 	{
-		bool goingForward = gInput().isButtonDown(BC_W) || gInput().isButtonDown(BC_UP);
-		bool goingBack = gInput().isButtonDown(BC_S) || gInput().isButtonDown(BC_DOWN);
-		bool goingLeft = gInput().isButtonDown(BC_A) || gInput().isButtonDown(BC_LEFT);
-		bool goingRight = gInput().isButtonDown(BC_D) || gInput().isButtonDown(BC_RIGHT);
-		bool fastMove = gInput().isButtonDown(BC_LSHIFT);
-		bool camRotating = gInput().isButtonDown(BC_MOUSE_RIGHT);
+		bool goingForward = gInput().isButtonHeld(BC_W) || gInput().isButtonHeld(BC_UP);
+		bool goingBack = gInput().isButtonHeld(BC_S) || gInput().isButtonHeld(BC_DOWN);
+		bool goingLeft = gInput().isButtonHeld(BC_A) || gInput().isButtonHeld(BC_LEFT);
+		bool goingRight = gInput().isButtonHeld(BC_D) || gInput().isButtonHeld(BC_RIGHT);
+		bool fastMove = gInput().isButtonHeld(BC_LSHIFT);
+		bool camRotating = gInput().isButtonHeld(BC_MOUSE_RIGHT);
 
 		if(camRotating != mLastButtonState)
 		{

+ 13 - 3
CamelotCore/Include/CmInput.h

@@ -11,6 +11,14 @@ namespace CamelotFramework
 {
 	class CM_EXPORT Input : public Module<Input>
 	{
+		enum class ButtonState
+		{
+			Off,
+			On,
+			ToggledOn,
+			ToggledOff
+		};
+
 	public:
 		Input();
 		~Input();
@@ -47,6 +55,8 @@ namespace CamelotFramework
 		 */
 		float getVerticalAxis() const;
 
+		bool isButtonHeld(ButtonCode keyCode) const;
+		bool isButtonUp(ButtonCode keyCode) const;
 		bool isButtonDown(ButtonCode keyCode) const;
 
 		Vector2I getCursorPosition() const { return mMouseAbsPos; }
@@ -66,10 +76,10 @@ namespace CamelotFramework
 		Vector2I mMouseAbsPos;
 
 		RawAxisState mAxes[RawInputAxis::Count];
-		bool mKeyState[BC_Count];
+		ButtonState mKeyState[BC_Count];
 
-		void buttonDown(ButtonCode code);
-		void buttonUp(ButtonCode code);
+		void buttonDown(ButtonCode code, float timestamp);
+		void buttonUp(ButtonCode code, float timestamp);
 
 		void charInput(UINT32 chr);
 

+ 3 - 2
CamelotCore/Include/CmInputFwd.h

@@ -237,6 +237,7 @@ namespace CamelotFramework
 		{ }
 
 		ButtonCode buttonCode;
+		float timestamp;
 
 		bool isKeyboard() const { return (buttonCode & 0xC0000000) == 0; }
 		bool isMouse() const { return (buttonCode & 0x80000000) != 0; }
@@ -302,8 +303,8 @@ namespace CamelotFramework
 	enum class InputCommandType
 	{
 		CursorMoveLeft, CursorMoveRight, CursorMoveUp, CursorMoveDown, 
-		SelectLeft, SelectRight, SelectUp, SelectDown, Undo, Redo, Rename,
-		Escape, Delete, Backspace, Return, SelectAll, Copy, Cut, Paste, Tab
+		SelectLeft, SelectRight, SelectUp, SelectDown,
+		Escape, Delete, Backspace, Return
 	};
 
 	struct TextInputEvent

+ 2 - 2
CamelotCore/Include/CmRawInputHandler.h

@@ -46,8 +46,8 @@ namespace CamelotFramework
 		RawInputHandler() {}
 		virtual ~RawInputHandler() {}
 
-		boost::signal<void(ButtonCode)> onButtonDown;
-		boost::signal<void(ButtonCode)> onButtonUp;
+		boost::signal<void(ButtonCode, float)> onButtonDown;
+		boost::signal<void(ButtonCode, float)> onButtonUp;
 
 		boost::signal<void(const RawAxisState&, RawInputAxis)> onAxisMoved;
 

+ 30 - 8
CamelotCore/Source/CmInput.cpp

@@ -27,7 +27,7 @@ namespace CamelotFramework
 		}
 
 		for(int i = 0; i < BC_Count; i++)
-			mKeyState[i] = false;
+			mKeyState[i] = ButtonState::Off;
 
 		mOSInputHandler = cm_shared_ptr<OSInputHandler>();
 
@@ -56,8 +56,8 @@ namespace CamelotFramework
 
 			if(mRawInputHandler != nullptr)
 			{
-				mRawInputHandler->onButtonDown.connect(boost::bind(&Input::buttonDown, this, _1));
-				mRawInputHandler->onButtonUp.connect(boost::bind(&Input::buttonUp, this, _1));
+				mRawInputHandler->onButtonDown.connect(boost::bind(&Input::buttonDown, this, _1, _2));
+				mRawInputHandler->onButtonUp.connect(boost::bind(&Input::buttonUp, this, _1, _2));
 
 				mRawInputHandler->onAxisMoved.connect(boost::bind(&Input::axisMoved, this, _1, _2));
 			}
@@ -66,6 +66,16 @@ namespace CamelotFramework
 
 	void Input::update()
 	{
+		// Toggle states only remain active for a single frame before they are transitioned
+		// into permanent state
+		for(UINT32 i = 0; i < BC_Count; i++)
+		{
+			if(mKeyState[i] == ButtonState::ToggledOff)
+				mKeyState[i] = ButtonState::Off;
+			else if(mKeyState[i] == ButtonState::ToggledOn)
+				mKeyState[i] = ButtonState::On;
+		}
+
 		if(mRawInputHandler == nullptr)
 		{
 			LOGERR("Raw input handler not initialized!");
@@ -94,27 +104,29 @@ namespace CamelotFramework
 			mOSInputHandler->inputWindowChanged(win);
 	}
 
-	void Input::buttonDown(ButtonCode code)
+	void Input::buttonDown(ButtonCode code, float timestamp)
 	{
-		mKeyState[code & 0x0000FFFF] = true;
+		mKeyState[code & 0x0000FFFF] = ButtonState::ToggledOn;
 
 		if(!onButtonDown.empty())
 		{
 			ButtonEvent btnEvent;
 			btnEvent.buttonCode = code;
+			btnEvent.timestamp = timestamp;
 
 			onButtonDown(btnEvent);
 		}
 	}
 
-	void Input::buttonUp(ButtonCode code)
+	void Input::buttonUp(ButtonCode code, float timestamp)
 	{
-		mKeyState[code & 0x0000FFFF] = false;
+		mKeyState[code & 0x0000FFFF] = ButtonState::ToggledOff;
 
 		if(!onButtonUp.empty())
 		{
 			ButtonEvent btnEvent;
 			btnEvent.buttonCode = code;
+			btnEvent.timestamp = timestamp;
 
 			onButtonUp(btnEvent);
 		}
@@ -185,9 +197,19 @@ namespace CamelotFramework
 		return mSmoothVerticalAxis;
 	}
 
+	bool Input::isButtonHeld(ButtonCode button) const
+	{
+		return mKeyState[button & 0x0000FFFF] == ButtonState::On || mKeyState[button & 0x0000FFFF] == ButtonState::ToggledOn;
+	}
+
+	bool Input::isButtonUp(ButtonCode button) const
+	{
+		return mKeyState[button & 0x0000FFFF] == ButtonState::ToggledOff;
+	}
+
 	bool Input::isButtonDown(ButtonCode button) const
 	{
-		return mKeyState[button & 0x0000FFFF];
+		return mKeyState[button & 0x0000FFFF] == ButtonState::ToggledOn;
 	}
 
 	void Input::updateSmoothInput()

+ 0 - 42
CamelotCore/Source/CmPlatformWndProc.cpp

@@ -456,48 +456,6 @@ namespace CamelotFramework
 		case VK_DELETE:
 			command = InputCommandType::Delete;
 			return true;
-		case VK_TAB:
-			command = InputCommandType::Tab;
-			return true;
-		case VK_F2:
-			command = InputCommandType::Rename;
-			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;

+ 4 - 4
CamelotOISInput/Source/CmInputHandlerOIS.cpp

@@ -73,13 +73,13 @@ namespace CamelotFramework
 
 	bool InputHandlerOIS::keyPressed(const OIS::KeyEvent &arg)
 	{
-		onButtonDown(keyCodeToButtonCode(arg.key));
+		onButtonDown(keyCodeToButtonCode(arg.key), 0.0f);
 		return true;
 	}
 
 	bool InputHandlerOIS::keyReleased(const OIS::KeyEvent& arg)
 	{
-		onButtonUp(keyCodeToButtonCode(arg.key));
+		onButtonUp(keyCodeToButtonCode(arg.key), 0.0f);
 		return true;
 	}
 
@@ -102,14 +102,14 @@ namespace CamelotFramework
 
 	bool InputHandlerOIS::mousePressed(const OIS::MouseEvent& arg, OIS::MouseButtonID id)
 	{
-		onButtonDown(mouseButtonToButtonCode(id));
+		onButtonDown(mouseButtonToButtonCode(id), 0.0f);
 
 		return true;
 	}
 
 	bool InputHandlerOIS::mouseReleased(const OIS::MouseEvent& arg, OIS::MouseButtonID id)
 	{
-		onButtonUp(mouseButtonToButtonCode(id));
+		onButtonUp(mouseButtonToButtonCode(id), 0.0f);
 
 		return true;
 	}

+ 1 - 21
TreeView.txt

@@ -6,27 +6,7 @@ TODO:
  - Clicking on an already selectecd element starts rename 
    - Will likely need some kind of check to ignore double-clicks?
 
-MenuBar problems:
- - Clicking the top menu item closes and immediately opens the menu again (it should only close it)
-   - Something similar probably also happens with ListBox
-
  Other:
  - "Ping" effect
  - Copy/Paste/Duplicate
-   - This is more of a problem with actual copying of SceneObjects (It's not implemented). I should be able to serialize, and then de-serialize with a new parent as a form of copy.
-
---------------------------------------------------
-LOW PRIORITY:
-
-Shortcut plan:
-Replace all GUICommandEventType with two things:
- - Events like CursorMoveUp should just be received a keys + modifiers (shift/ctrl/alt) and the individual element can then decide what to do with them and how to interpret them.
- - Events like Rename, Undo, Redo, Copy, Paste, etc. should be globally set shortcut keys
-    - Shortcut keys are globally registered on program start in ShortcutManager
-    - Objects can then register callbacks for certain shortcut using mechanism like: addCallback("Undo", onUndo).
-     - They should also be able to unregister those callbacks.
-    - GUIElement can choose to listen to certain shortcut keys
-        - getUsedShortcut is a overridable method that returns a list of shortcuts and callbacks
-        - When element comes into focus or loses focus GUIManager will register/unregister those shortcuts with ShortcutManager
-          - I should probably prevent one shortcut from possibly triggering multiple callbacks
-    - Whenever some key combination or a single key is pressed all elements in focus get send the shortcut key (if that shortcut exists)
+   - This is more of a problem with actual copying of SceneObjects (It's not implemented). I should be able to serialize, and then de-serialize with a new parent as a form of copy.

+ 13 - 0
VirtualInput.txt

@@ -0,0 +1,13 @@
+TODO:
+Extend CamelotOIS so it returns a timestamp of each event
+ - INputHandlerOIS currently sents timestamp to 0. Make sure to update that.
+ - I need to ensure that time value returned from OIS can be compared to time returned from gTime() (In BsVirtualInput::update)
+
+IMPORTANT: Was there an issue with raw input events vs. OS input events?
+ - It might not be possible to use raw input events to manage the UI events?
+  - Or was the only reason lack of auto-repeat?
+
+ - Change how I handle axes in CmInput
+   - Right now there is only single horizontal and single vertical axis, which makes little sense
+   - Plus a lot of axis functionality should probably be moved to VirtualInput
+ - Register InputConfiguration containing default keys during Editor start-up