Browse Source

Most of work done for input caret

Marko Pintera 12 years ago
parent
commit
1c07c98c7e
3 changed files with 168 additions and 21 deletions
  1. 44 0
      BansheeEngine/Include/BsGUIManager.h
  2. 122 21
      BansheeEngine/Source/BsGUIManager.cpp
  3. 2 0
      TODO.txt

+ 44 - 0
BansheeEngine/Include/BsGUIManager.h

@@ -4,6 +4,7 @@
 #include "BsGUIMouseEvent.h"
 #include "BsGUIMouseEvent.h"
 #include "BsGUIKeyEvent.h"
 #include "BsGUIKeyEvent.h"
 #include "CmModule.h"
 #include "CmModule.h"
+#include "CmColor.h"
 #include "CmInput.h"
 #include "CmInput.h"
 #include <boost/signals/connection.hpp>
 #include <boost/signals/connection.hpp>
 
 
@@ -36,6 +37,30 @@ namespace BansheeEngine
 
 
 		void update();
 		void update();
 		void render(CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor);
 		void render(CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor);
+
+		/**
+		 * @brief	Starts rendering the input caret at the specified coordinates.
+		 * 			The coordinates represent top left corner of the caret, relative to the
+		 * 			provided widget.
+		 */
+		void showCaret(GUIWidget* widget, CM::INT32 x, CM::INT32 y, CM::UINT32 depth);
+
+		/**
+		 * @brief	Hides the input caret.
+		 */
+		void hideCaret() { mCaretShown = false; }
+
+		void setCaretWidth(CM::UINT32 width) { mCaretWidth = width; updateCaretSprite(); }
+		void setCaretHeight(CM::UINT32 height) { mCaretHeight = height; updateCaretSprite(); }
+
+		/**
+		 * @brief	Determines how fast the caret blinks.
+		 *
+		 * @param	interval	Blinking interval in seconds.
+		 */
+		void setCaretBlinkInterval(float interval) { mCaretBlinkInterval = interval; }
+		void setCaretColor(const CM::Color& color) { mCaretColor = color; updateCaretTexture(); }
+
 	private:
 	private:
 		CM::Vector<GUIWidget*>::type mWidgets;
 		CM::Vector<GUIWidget*>::type mWidgets;
 		CM::UnorderedMap<const CM::Viewport*, GUIRenderData>::type mCachedGUIData;
 		CM::UnorderedMap<const CM::Viewport*, GUIRenderData>::type mCachedGUIData;
@@ -59,6 +84,21 @@ namespace BansheeEngine
 		GUIMouseEvent mMouseEvent;
 		GUIMouseEvent mMouseEvent;
 		GUIKeyEvent mKeyEvent;
 		GUIKeyEvent mKeyEvent;
 
 
+		// Caret related
+		ImageSprite* mCaretSprite;
+		SpriteTexturePtr mCaretTexture;
+		CM::HMesh mCaretMesh;
+		CM::HMaterial mCaretMaterial;
+
+		GUIWidget* mCaretOwnerWidget;
+		bool mCaretShown;
+		CM::INT32 mCaretX, mCaretY;
+		CM::UINT32 mCaretDepth;
+		CM::UINT32 mCaretWidth, mCaretHeight;
+		CM::Color mCaretColor;
+		float mCaretBlinkInterval;
+		float mCaretLastBlinkTime;
+
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonDownConn;
 		boost::signals::connection mOnButtonUpConn;
 		boost::signals::connection mOnButtonUpConn;
 		boost::signals::connection mOnMouseMovedConn;
 		boost::signals::connection mOnMouseMovedConn;
@@ -68,7 +108,11 @@ namespace BansheeEngine
 		boost::signals::connection mWindowLostFocusConn;
 		boost::signals::connection mWindowLostFocusConn;
 		boost::signals::connection mWindowMovedOrResizedConn;
 		boost::signals::connection mWindowMovedOrResizedConn;
 
 
+		void renderMesh(const CM::HMesh& mesh, const CM::HMaterial& material, const CM::Matrix4& tfrm, CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor);
+
 		void updateMeshes();
 		void updateMeshes();
+		void updateCaretSprite();
+		void updateCaretTexture();
 
 
 		void onButtonDown(const CM::ButtonEvent& event);
 		void onButtonDown(const CM::ButtonEvent& event);
 		void onButtonUp(const CM::ButtonEvent& event);
 		void onButtonUp(const CM::ButtonEvent& event);

+ 122 - 21
BansheeEngine/Source/BsGUIManager.cpp

@@ -1,6 +1,9 @@
 #include "BsGUIManager.h"
 #include "BsGUIManager.h"
 #include "BsGUIWidget.h"
 #include "BsGUIWidget.h"
 #include "BsGUIElement.h"
 #include "BsGUIElement.h"
+#include "BsImageSprite.h"
+#include "BsSpriteTexture.h"
+#include "CmTime.h"
 #include "CmSceneObject.h"
 #include "CmSceneObject.h"
 #include "CmMaterial.h"
 #include "CmMaterial.h"
 #include "CmMeshData.h"
 #include "CmMeshData.h"
@@ -43,7 +46,9 @@ namespace BansheeEngine
 
 
 	GUIManager::GUIManager()
 	GUIManager::GUIManager()
 		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
 		:mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
-		mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr)
+		mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
+		mCaretSprite(nullptr), mCaretTexture(nullptr), mCaretX(0), mCaretY(0), mCaretDepth(0), mCaretWidth(1), mCaretHeight(8), 
+		mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretShown(false), mCaretColor(Color::Black), mCaretOwnerWidget(nullptr)
 	{
 	{
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
 		mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
@@ -53,6 +58,9 @@ namespace BansheeEngine
 		mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
 		mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
 		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
 		mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
 		mWindowMovedOrResizedConn = RenderWindowManager::instance().onMovedOrResized.connect(boost::bind(&GUIManager::onWindowMovedOrResized, this, _1));
 		mWindowMovedOrResizedConn = RenderWindowManager::instance().onMovedOrResized.connect(boost::bind(&GUIManager::onWindowMovedOrResized, this, _1));
+
+		updateCaretTexture();
+		updateCaretSprite();
 	}
 	}
 
 
 	GUIManager::~GUIManager()
 	GUIManager::~GUIManager()
@@ -65,6 +73,9 @@ namespace BansheeEngine
 		mWindowGainedFocusConn.disconnect();
 		mWindowGainedFocusConn.disconnect();
 		mWindowLostFocusConn.disconnect();
 		mWindowLostFocusConn.disconnect();
 		mWindowMovedOrResizedConn.disconnect();
 		mWindowMovedOrResizedConn.disconnect();
+
+		if(mCaretSprite != nullptr)
+			cm_delete(mCaretSprite);
 	}
 	}
 
 
 	void GUIManager::registerWidget(GUIWidget* widget)
 	void GUIManager::registerWidget(GUIWidget* widget)
@@ -120,6 +131,16 @@ namespace BansheeEngine
 		updateMeshes();
 		updateMeshes();
 	}
 	}
 
 
+	void GUIManager::showCaret(GUIWidget* widget, INT32 x, INT32 y, UINT32 depth)
+	{
+		mCaretX = x;
+		mCaretY = y;
+		mCaretDepth = depth;
+		mCaretLastBlinkTime = 0.0f;
+		mCaretShown = true;
+		mCaretOwnerWidget = widget;
+	}
+
 	void GUIManager::render(ViewportPtr& target, CoreAccessor& coreAccessor)
 	void GUIManager::render(ViewportPtr& target, CoreAccessor& coreAccessor)
 	{
 	{
 		auto findIter = mCachedGUIData.find(target.get());
 		auto findIter = mCachedGUIData.find(target.get());
@@ -140,30 +161,24 @@ namespace BansheeEngine
 				HMaterial material = renderData.cachedMaterials[meshIdx];
 				HMaterial material = renderData.cachedMaterials[meshIdx];
 				GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
 				GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
 
 
-				// TODO - Possible optimization. I currently divide by width/height inside the shader, while it
-				// might be more optimal to just scale the mesh as the resolution changes?
-				float invViewportWidth = 1.0f / (target->getWidth() * 0.5f);
-				float invViewportHeight = 1.0f / (target->getHeight() * 0.5f);
-
-				material->setFloat("invViewportWidth", invViewportWidth);
-				material->setFloat("invViewportHeight", invViewportHeight);
-				material->setMat4("worldTransform", widget->SO()->getWorldTfrm());
+				renderMesh(mesh, material, widget->SO()->getWorldTfrm(), target, coreAccessor);
 
 
-				if(material == nullptr || !material.isLoaded())
-					continue;
-
-				if(mesh == nullptr || !mesh.isLoaded())
-					continue;
-
-				for(UINT32 i = 0; i < material->getNumPasses(); i++)
+				// Draw caret
+				// TODO: Caret is always drawn on top of a single widget. This means it will ignore
+				// depth of individual elements within the widget, so it will draw in front of them.
+				// Having it draw correctly would require updating the mesh whenever caret blinks,
+				// which I don't feel is worth it considering that elements within a widget shouldn't be overlapping
+				// so this is unlikely to be an issue.
+				if(mCaretShown && widget == mCaretOwnerWidget)
 				{
 				{
-					PassPtr pass = material->getPass(i);
-					pass->activate(coreAccessor);
+					float curTime = gTime().getTime();
 
 
-					PassParametersPtr paramsPtr = material->getPassParameters(i);
-					pass->bindParameters(coreAccessor, paramsPtr);
+					if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
+					{
+						mCaretLastBlinkTime = curTime;
 
 
-					coreAccessor.render(mesh->getRenderOperation());
+						renderMesh(mCaretMesh, mCaretMaterial, widget->SO()->getWorldTfrm(), target, coreAccessor);
+					}
 				}
 				}
 
 
 				meshIdx++;
 				meshIdx++;
@@ -180,6 +195,35 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void GUIManager::renderMesh(const CM::HMesh& mesh, const CM::HMaterial& material, const CM::Matrix4& tfrm, CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor)
+	{
+		// TODO - Possible optimization. I currently divide by width/height inside the shader, while it
+		// might be more optimal to just scale the mesh as the resolution changes?
+		float invViewportWidth = 1.0f / (target->getWidth() * 0.5f);
+		float invViewportHeight = 1.0f / (target->getHeight() * 0.5f);
+
+		material->setFloat("invViewportWidth", invViewportWidth);
+		material->setFloat("invViewportHeight", invViewportHeight);
+		material->setMat4("worldTransform", tfrm);
+
+		if(material == nullptr || !material.isLoaded())
+			return;
+
+		if(mesh == nullptr || !mesh.isLoaded())
+			return;
+
+		for(UINT32 i = 0; i < material->getNumPasses(); i++)
+		{
+			PassPtr pass = material->getPass(i);
+			pass->activate(coreAccessor);
+
+			PassParametersPtr paramsPtr = material->getPassParameters(i);
+			pass->bindParameters(coreAccessor, paramsPtr);
+
+			coreAccessor.render(mesh->getRenderOperation());
+		}
+	}
+
 	void GUIManager::updateMeshes()
 	void GUIManager::updateMeshes()
 	{
 	{
 		for(auto& cachedMeshData : mCachedGUIData)
 		for(auto& cachedMeshData : mCachedGUIData)
@@ -411,6 +455,63 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void GUIManager::updateCaretSprite()
+	{
+		if(mCaretSprite == nullptr)
+			mCaretSprite = cm_new<ImageSprite>();
+
+		IMAGE_SPRITE_DESC desc;
+		desc.offset = Int2(mCaretX, mCaretY);
+		desc.width = mCaretWidth;
+		desc.height = mCaretHeight;
+		desc.texture = mCaretTexture;
+
+		mCaretSprite->update(desc);
+
+		assert(mCaretSprite->getNumRenderElements() == 1);
+
+		UINT32 numQuads = mCaretSprite->getNumQuads(0);
+		MeshDataPtr meshData = cm_shared_ptr<MeshData, PoolAlloc>(numQuads * 4);
+
+		meshData->beginDesc();
+		meshData->addVertElem(VET_FLOAT2, VES_POSITION);
+		meshData->addVertElem(VET_FLOAT2, VES_TEXCOORD);
+		meshData->addSubMesh(numQuads * 6);
+		meshData->endDesc();
+
+		UINT8* vertices = meshData->getElementData(VES_POSITION);
+		UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
+		UINT32* indices = meshData->getIndices32();
+		UINT32 vertexStride = meshData->getVertexStride();
+		UINT32 indexStride = meshData->getIndexElementSize();
+
+		mCaretSprite->fillBuffer(vertices, uvs, indices, 0, numQuads, vertexStride, indexStride, 0);
+
+		if(!mCaretMesh)
+			mCaretMesh = Mesh::create();
+
+		gMainSyncedCA().writeSubresource(mCaretMesh.getInternalPtr(), 0, *meshData);
+		gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
+	}
+
+	void GUIManager::updateCaretTexture()
+	{
+		if(mCaretTexture == nullptr)
+		{
+			HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
+			mCaretTexture = cm_shared_ptr<SpriteTexture>(newTex);
+		}
+
+		const HTexture& tex = mCaretTexture->getTexture();
+		UINT32 subresourceIdx = tex->mapToSubresourceIdx(0, 0);
+		PixelDataPtr data = tex->allocateSubresourceBuffer(subresourceIdx);
+
+		data->setColorAt(Color::Red, 0, 0);
+
+		gMainSyncedCA().writeSubresource(tex.getInternalPtr(), tex->mapToSubresourceIdx(0, 0), *data);
+		gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
+	}
+
 	void GUIManager::onButtonDown(const ButtonEvent& event)
 	void GUIManager::onButtonDown(const ButtonEvent& event)
 	{
 	{
 		if(event.isUsed())
 		if(event.isUsed())

+ 2 - 0
TODO.txt

@@ -12,6 +12,8 @@ I call waitUntilLoaded too many times. Sometimes 5-6 times in a single function.
 GUIWidget::updateMeshes leaks. If I leave the game running I can see memory continously going up
 GUIWidget::updateMeshes leaks. If I leave the game running I can see memory continously going up
 
 
 IMMEDIATE:
 IMMEDIATE:
+ - Clicking on a window to focus and immediately trying to drag/resize it, doesn't work. I first need to click, then click again to drag/resize.
+ - OpenGL rendering slows to extremely with time (seems to be related to rendering, possibly GUI, possibly in general Pass/Material/Shader/PassParams)
  - Update debug camera so it uses callbacks
  - Update debug camera so it uses callbacks
  - Add support for diacritical marks
  - Add support for diacritical marks
  - onMovedOrResized is still used by Viewport
  - onMovedOrResized is still used by Viewport