BsGUIManager.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. #include "BsGUIManager.h"
  2. #include "BsGUIWidget.h"
  3. #include "BsGUIElement.h"
  4. #include "BsImageSprite.h"
  5. #include "BsSpriteTexture.h"
  6. #include "CmTime.h"
  7. #include "CmSceneObject.h"
  8. #include "CmMaterial.h"
  9. #include "CmMeshData.h"
  10. #include "CmMesh.h"
  11. #include "CmUtil.h"
  12. #include "CmRenderWindowManager.h"
  13. #include "CmPlatform.h"
  14. #include "CmRect.h"
  15. #include "CmApplication.h"
  16. #include "CmException.h"
  17. #include "CmInput.h"
  18. #include "CmPass.h"
  19. #include "CmDebug.h"
  20. #include "CmRenderQueue.h"
  21. #include "BsGUIInputCaret.h"
  22. #include "BsGUIInputSelection.h"
  23. #include "BsGUIListBox.h"
  24. #include "BsGUIButton.h"
  25. #include "BsGUIDropDownBox.h"
  26. #include "BsGUIContextMenu.h"
  27. #include "BsDragAndDropManager.h"
  28. #include "BsGUIDropDownBoxManager.h"
  29. #include "BsGUIContextMenu.h"
  30. using namespace CamelotFramework;
  31. namespace BansheeEngine
  32. {
  33. struct GUIGroupElement
  34. {
  35. GUIGroupElement()
  36. { }
  37. GUIGroupElement(GUIElement* _element, UINT32 _renderElement)
  38. :element(_element), renderElement(_renderElement)
  39. { }
  40. GUIElement* element;
  41. UINT32 renderElement;
  42. };
  43. struct GUIMaterialGroup
  44. {
  45. HMaterial material;
  46. UINT32 numQuads;
  47. UINT32 depth;
  48. Rect bounds;
  49. Vector<GUIGroupElement>::type elements;
  50. };
  51. GUIManager::GUIManager()
  52. :mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr),
  53. mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
  54. mCaretTexture(nullptr), mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(1.0f, 0.6588f, 0.0f), mIsCaretOn(false),
  55. mTextSelectionColor(1.0f, 0.6588f, 0.0f), mInputCaret(nullptr), mInputSelection(nullptr), mSelectiveInputActive(false)
  56. {
  57. mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
  58. mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
  59. mOnMouseMovedConn = gInput().onMouseMoved.connect(boost::bind(&GUIManager::onMouseMoved, this, _1));
  60. mOnTextInputConn = gInput().onCharInput.connect(boost::bind(&GUIManager::onTextInput, this, _1));
  61. mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
  62. mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
  63. mWindowMovedOrResizedConn = RenderWindowManager::instance().onMovedOrResized.connect(boost::bind(&GUIManager::onWindowMovedOrResized, this, _1));
  64. mMouseLeftWindowConn = Platform::onMouseLeftWindow.connect(boost::bind(&GUIManager::onMouseLeftWindow, this, _1));
  65. mInputCaret = cm_new<GUIInputCaret, PoolAlloc>();
  66. mInputSelection = cm_new<GUIInputSelection, PoolAlloc>();
  67. DragAndDropManager::startUp(cm_new<DragAndDropManager>());
  68. mMouseDragEndedConn = DragAndDropManager::instance().onDragEnded.connect(boost::bind(&GUIManager::onMouseDragEnded, this, _1));
  69. GUIDropDownBoxManager::startUp(cm_new<GUIDropDownBoxManager>());
  70. // Need to defer this call because I want to make sure all managers are initialized first
  71. deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
  72. deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
  73. }
  74. GUIManager::~GUIManager()
  75. {
  76. GUIDropDownBoxManager::shutDown();
  77. DragAndDropManager::shutDown();
  78. // Make a copy of widgets, since destroying them will remove them from mWidgets and
  79. // we can't iterate over an array thats getting modified
  80. Vector<WidgetInfo>::type widgetCopy = mWidgets;
  81. for(auto& widget : widgetCopy)
  82. widget.widget->destroy();
  83. mOnButtonDownConn.disconnect();
  84. mOnButtonUpConn.disconnect();
  85. mOnMouseMovedConn.disconnect();
  86. mOnTextInputConn.disconnect();
  87. mMouseDragEndedConn.disconnect();
  88. mWindowGainedFocusConn.disconnect();
  89. mWindowLostFocusConn.disconnect();
  90. mWindowMovedOrResizedConn.disconnect();
  91. mMouseLeftWindowConn.disconnect();
  92. cm_delete<PoolAlloc>(mInputCaret);
  93. cm_delete<PoolAlloc>(mInputSelection);
  94. }
  95. void GUIManager::registerWidget(GUIWidget* widget)
  96. {
  97. mWidgets.push_back(WidgetInfo(widget));
  98. const Viewport* renderTarget = widget->getTarget();
  99. auto findIter = mCachedGUIData.find(renderTarget);
  100. if(findIter == end(mCachedGUIData))
  101. mCachedGUIData[renderTarget] = GUIRenderData();
  102. GUIRenderData& windowData = mCachedGUIData[renderTarget];
  103. windowData.widgets.push_back(widget);
  104. windowData.isDirty = true;
  105. }
  106. void GUIManager::unregisterWidget(GUIWidget* widget)
  107. {
  108. auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
  109. if(findIter != end(mWidgets))
  110. {
  111. mWidgets.erase(findIter);
  112. }
  113. if(mMouseOverWidget == widget)
  114. {
  115. mMouseOverWidget = nullptr;
  116. mMouseOverElement = nullptr;
  117. }
  118. if(mKeyboardFocusWidget == widget)
  119. {
  120. mKeyboardFocusWidget = nullptr;
  121. mKeyboardFocusElement = nullptr;
  122. }
  123. if(mActiveWidget == widget)
  124. {
  125. mActiveWidget = nullptr;
  126. mActiveElement = nullptr;
  127. }
  128. const Viewport* renderTarget = widget->getTarget();
  129. GUIRenderData& renderData = mCachedGUIData[renderTarget];
  130. auto findIter2 = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
  131. if(findIter2 != end(renderData.widgets))
  132. renderData.widgets.erase(findIter2);
  133. if(renderData.widgets.size() == 0)
  134. mCachedGUIData.erase(renderTarget);
  135. else
  136. renderData.isDirty = true;
  137. }
  138. void GUIManager::update()
  139. {
  140. DragAndDropManager::instance().update();
  141. // Update layouts
  142. for(auto& widgetInfo : mWidgets)
  143. {
  144. widgetInfo.widget->_updateLayout();
  145. }
  146. // Blink caret
  147. if(mKeyboardFocusElement != nullptr)
  148. {
  149. float curTime = gTime().getTime();
  150. if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
  151. {
  152. mCaretLastBlinkTime = curTime;
  153. mIsCaretOn = !mIsCaretOn;
  154. mCommandEvent = GUICommandEvent();
  155. mCommandEvent.setRedrawData();
  156. mKeyboardFocusElement->commandEvent(mCommandEvent);
  157. }
  158. }
  159. updateMeshes();
  160. if(mMouseOverElement != nullptr && mMouseOverElement->_isDestroyed())
  161. {
  162. mMouseOverElement = nullptr;
  163. mMouseOverWidget = nullptr;
  164. }
  165. if(mActiveElement != nullptr && mActiveElement->_isDestroyed())
  166. {
  167. mActiveElement = nullptr;
  168. mActiveWidget = nullptr;
  169. }
  170. if(mKeyboardFocusElement != nullptr && mKeyboardFocusElement->_isDestroyed())
  171. {
  172. mKeyboardFocusElement = nullptr;
  173. mKeyboardFocusWidget = nullptr;
  174. }
  175. processDestroyQueue();
  176. }
  177. void GUIManager::render(ViewportPtr& target, CM::RenderQueue& renderQueue) const
  178. {
  179. auto findIter = mCachedGUIData.find(target.get());
  180. if(findIter == mCachedGUIData.end())
  181. return;
  182. const GUIRenderData& renderData = findIter->second;
  183. // Render the meshes
  184. if(mSeparateMeshesByWidget)
  185. {
  186. // TODO - Possible optimization. I currently divide by width/height inside the shader, while it
  187. // might be more optimal to just scale the mesh as the resolution changes?
  188. float invViewportWidth = 1.0f / (target->getWidth() * 0.5f);
  189. float invViewportHeight = 1.0f / (target->getHeight() * 0.5f);
  190. UINT32 meshIdx = 0;
  191. for(auto& mesh : renderData.cachedMeshes)
  192. {
  193. HMaterial material = renderData.cachedMaterials[meshIdx];
  194. GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
  195. if(material == nullptr || !material.isLoaded())
  196. continue;
  197. if(mesh == nullptr || !mesh.isLoaded())
  198. continue;
  199. material->setFloat("invViewportWidth", invViewportWidth);
  200. material->setFloat("invViewportHeight", invViewportHeight);
  201. material->setMat4("worldTransform", widget->SO()->getWorldTfrm());
  202. renderQueue.add(material, mesh->getSubMeshData(), Vector3::ZERO);
  203. meshIdx++;
  204. }
  205. }
  206. else
  207. {
  208. // TODO: I want to avoid separating meshes by widget in the future. On DX11 and GL I can set up a shader
  209. // that accepts multiple world transforms (one for each widget). Then I can add some instance information to vertices
  210. // and render elements using multiple different transforms with a single call.
  211. // Separating meshes can then be used as a compatibility mode for DX9
  212. CM_EXCEPT(NotImplementedException, "Not implemented");
  213. }
  214. }
  215. void GUIManager::updateMeshes()
  216. {
  217. for(auto& cachedMeshData : mCachedGUIData)
  218. {
  219. GUIRenderData& renderData = cachedMeshData.second;
  220. // Check if anything is dirty. If nothing is we can skip the update
  221. bool isDirty = renderData.isDirty;
  222. renderData.isDirty = false;
  223. for(auto& widget : renderData.widgets)
  224. {
  225. if(widget->isDirty(true))
  226. {
  227. isDirty = true;
  228. }
  229. }
  230. if(!isDirty)
  231. continue;
  232. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  233. auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
  234. {
  235. UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
  236. UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
  237. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  238. // requires all elements to be unique
  239. return (aDepth > bDepth) ||
  240. (aDepth == bDepth && a.element > b.element) ||
  241. (aDepth == bDepth && a.element == b.element && a.renderElement > b.renderElement);
  242. };
  243. Set<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>>::type allElements(elemComp);
  244. for(auto& widget : renderData.widgets)
  245. {
  246. const Vector<GUIElement*>::type& elements = widget->getElements();
  247. for(auto& element : elements)
  248. {
  249. if(element->_isDisabled())
  250. continue;
  251. UINT32 numRenderElems = element->getNumRenderElements();
  252. for(UINT32 i = 0; i < numRenderElems; i++)
  253. {
  254. allElements.insert(GUIGroupElement(element, i));
  255. }
  256. }
  257. }
  258. // Group the elements in such a way so that we end up with a smallest amount of
  259. // meshes, without breaking back to front rendering order
  260. UnorderedMap<UINT64, Vector<GUIMaterialGroup>::type>::type materialGroups;
  261. for(auto& elem : allElements)
  262. {
  263. GUIElement* guiElem = elem.element;
  264. UINT32 renderElemIdx = elem.renderElement;
  265. UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
  266. Rect tfrmedBounds = guiElem->_getClippedBounds();
  267. tfrmedBounds.transform(guiElem->_getParentWidget().SO()->getWorldTfrm());
  268. const HMaterial& mat = guiElem->getMaterial(renderElemIdx);
  269. UINT64 materialId = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
  270. // this system won't detect it. Find a better way of determining material similarity?
  271. // If this is a new material, add a new list of groups
  272. auto findIterMaterial = materialGroups.find(materialId);
  273. if(findIterMaterial == end(materialGroups))
  274. materialGroups[materialId] = Vector<GUIMaterialGroup>::type();
  275. // Try to find a group this material will fit in:
  276. // - Group that has a depth value same or one below elements depth will always be a match
  277. // - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
  278. // overlap the current elements bounds.
  279. Vector<GUIMaterialGroup>::type& allGroups = materialGroups[materialId];
  280. GUIMaterialGroup* foundGroup = nullptr;
  281. for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
  282. {
  283. // If we separate meshes by widget, ignore any groups with widget parents other than mine
  284. if(mSeparateMeshesByWidget)
  285. {
  286. if(groupIter->elements.size() > 0)
  287. {
  288. GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
  289. if(&otherElem->_getParentWidget() != &guiElem->_getParentWidget())
  290. continue;
  291. }
  292. }
  293. GUIMaterialGroup& group = *groupIter;
  294. if(group.depth == elemDepth || group.depth == (elemDepth - 1))
  295. {
  296. foundGroup = &group;
  297. break;
  298. }
  299. else
  300. {
  301. UINT32 startDepth = elemDepth;
  302. UINT32 endDepth = group.depth;
  303. bool foundOverlap = false;
  304. for(auto& material : materialGroups)
  305. {
  306. for(auto& group : material.second)
  307. {
  308. if(group.depth > startDepth && group.depth < endDepth)
  309. {
  310. if(group.bounds.overlaps(tfrmedBounds))
  311. {
  312. foundOverlap = true;
  313. break;
  314. }
  315. }
  316. }
  317. }
  318. if(!foundOverlap)
  319. {
  320. foundGroup = &group;
  321. break;
  322. }
  323. }
  324. }
  325. if(foundGroup == nullptr)
  326. {
  327. allGroups.push_back(GUIMaterialGroup());
  328. foundGroup = &allGroups[allGroups.size() - 1];
  329. foundGroup->depth = elemDepth;
  330. foundGroup->bounds = tfrmedBounds;
  331. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  332. foundGroup->material = mat;
  333. foundGroup->numQuads = guiElem->getNumQuads(renderElemIdx);
  334. }
  335. else
  336. {
  337. foundGroup->bounds.encapsulate(tfrmedBounds);
  338. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  339. foundGroup->numQuads += guiElem->getNumQuads(renderElemIdx);
  340. }
  341. }
  342. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  343. auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
  344. {
  345. return (a->depth > b->depth) || (a->depth == b->depth && a > b);
  346. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  347. // requires all elements to be unique
  348. };
  349. Set<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>>::type sortedGroups(groupComp);
  350. for(auto& material : materialGroups)
  351. {
  352. for(auto& group : material.second)
  353. {
  354. sortedGroups.insert(&group);
  355. }
  356. }
  357. UINT32 numMeshes = (UINT32)sortedGroups.size();
  358. UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
  359. if(numMeshes < oldNumMeshes)
  360. renderData.cachedMeshes.resize(numMeshes);
  361. renderData.cachedMaterials.resize(numMeshes);
  362. if(mSeparateMeshesByWidget)
  363. renderData.cachedWidgetsPerMesh.resize(numMeshes);
  364. // Fill buffers for each group and update their meshes
  365. UINT32 groupIdx = 0;
  366. for(auto& group : sortedGroups)
  367. {
  368. renderData.cachedMaterials[groupIdx] = group->material;
  369. if(mSeparateMeshesByWidget)
  370. {
  371. if(group->elements.size() == 0)
  372. renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
  373. else
  374. {
  375. GUIElement* elem = group->elements.begin()->element;
  376. renderData.cachedWidgetsPerMesh[groupIdx] = &elem->_getParentWidget();
  377. }
  378. }
  379. MeshDataPtr meshData = cm_shared_ptr<MeshData, PoolAlloc>(group->numQuads * 4);
  380. meshData->beginDesc();
  381. meshData->addVertElem(VET_FLOAT2, VES_POSITION);
  382. meshData->addVertElem(VET_FLOAT2, VES_TEXCOORD);
  383. meshData->addSubMesh(group->numQuads * 6);
  384. meshData->endDesc();
  385. UINT8* vertices = meshData->getElementData(VES_POSITION);
  386. UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
  387. UINT32* indices = meshData->getIndices32();
  388. UINT32 vertexStride = meshData->getVertexStride();
  389. UINT32 indexStride = meshData->getIndexElementSize();
  390. UINT32 quadOffset = 0;
  391. for(auto& matElement : group->elements)
  392. {
  393. matElement.element->fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
  394. UINT32 numQuads = matElement.element->getNumQuads(matElement.renderElement);
  395. UINT32 indexStart = quadOffset * 6;
  396. UINT32 indexEnd = indexStart + numQuads * 6;
  397. UINT32 vertOffset = quadOffset * 4;
  398. for(UINT32 i = indexStart; i < indexEnd; i++)
  399. indices[i] += vertOffset;
  400. quadOffset += numQuads;
  401. }
  402. if(groupIdx >= (UINT32)renderData.cachedMeshes.size())
  403. {
  404. renderData.cachedMeshes.push_back(Mesh::create());
  405. }
  406. gMainSyncedCA().writeSubresource(renderData.cachedMeshes[groupIdx].getInternalPtr(), 0, *meshData);
  407. gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
  408. groupIdx++;
  409. }
  410. }
  411. }
  412. void GUIManager::updateCaretTexture()
  413. {
  414. if(mCaretTexture == nullptr)
  415. {
  416. HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
  417. mCaretTexture = cm_shared_ptr<SpriteTexture>(newTex);
  418. }
  419. const HTexture& tex = mCaretTexture->getTexture();
  420. UINT32 subresourceIdx = tex->mapToSubresourceIdx(0, 0);
  421. PixelDataPtr data = tex->allocateSubresourceBuffer(subresourceIdx);
  422. data->setColorAt(mCaretColor, 0, 0);
  423. gMainSyncedCA().writeSubresource(tex.getInternalPtr(), tex->mapToSubresourceIdx(0, 0), *data);
  424. gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
  425. }
  426. void GUIManager::updateTextSelectionTexture()
  427. {
  428. if(mTextSelectionTexture == nullptr)
  429. {
  430. HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
  431. mTextSelectionTexture = cm_shared_ptr<SpriteTexture>(newTex);
  432. }
  433. const HTexture& tex = mTextSelectionTexture->getTexture();
  434. UINT32 subresourceIdx = tex->mapToSubresourceIdx(0, 0);
  435. PixelDataPtr data = tex->allocateSubresourceBuffer(subresourceIdx);
  436. data->setColorAt(mTextSelectionColor, 0, 0);
  437. gMainSyncedCA().writeSubresource(tex.getInternalPtr(), tex->mapToSubresourceIdx(0, 0), *data);
  438. gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
  439. }
  440. void GUIManager::onButtonDown(const ButtonEvent& event)
  441. {
  442. if(event.isUsed())
  443. return;
  444. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  445. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  446. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  447. if(event.isKeyboard())
  448. {
  449. if(mKeyboardFocusElement != nullptr)
  450. {
  451. mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
  452. mKeyEvent.setKeyDownData(event.buttonCode);
  453. if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
  454. event.markAsUsed();
  455. }
  456. }
  457. if(event.isMouse())
  458. {
  459. bool buttonStates[(int)GUIMouseButton::Count];
  460. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  461. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  462. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  463. mMouseEvent = GUIMouseEvent(buttonStates, shiftDown, ctrlDown, altDown);
  464. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  465. // Send out selective input callback when user clicks on non-selectable element
  466. if(mSelectiveInputActive && mMouseOverElement == nullptr)
  467. {
  468. if(mOnOutsideClickCallback != nullptr)
  469. mOnOutsideClickCallback();
  470. }
  471. // We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
  472. bool acceptMouseDown = mActiveElement == nullptr && mMouseOverElement != nullptr;
  473. if(acceptMouseDown)
  474. {
  475. Int2 localPos = getWidgetRelativePos(*mMouseOverWidget, gInput().getMousePosition());
  476. mMouseEvent.setMouseDownData(mMouseOverElement, localPos, guiButton);
  477. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  478. event.markAsUsed();
  479. if(guiButton == GUIMouseButton::Left)
  480. {
  481. mMouseEvent.setMouseDragStartData(mMouseOverElement, localPos);
  482. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  483. event.markAsUsed();
  484. }
  485. mActiveElement = mMouseOverElement;
  486. mActiveWidget = mMouseOverWidget;
  487. mActiveMouseButton = guiButton;
  488. }
  489. if(mMouseOverElement != nullptr && mMouseOverElement->_acceptsKeyboardFocus())
  490. {
  491. if(mKeyboardFocusElement != nullptr && mMouseOverElement != mKeyboardFocusElement)
  492. mKeyboardFocusElement->_setFocus(false);
  493. mMouseOverElement->_setFocus(true);
  494. mKeyboardFocusElement = mMouseOverElement;
  495. mKeyboardFocusWidget = mMouseOverWidget;
  496. event.markAsUsed();
  497. }
  498. // If right click try to open context menu
  499. if(mMouseOverElement != nullptr && buttonStates[2] == true)
  500. {
  501. GUIContextMenu* menu = mMouseOverElement->getContextMenu();
  502. if(menu != nullptr)
  503. {
  504. const RenderWindow* window = mMouseOverWidget->getOwnerWindow();
  505. Int2 windowPos = window->screenToWindowPos(gInput().getMousePosition());
  506. menu->open(windowPos, *mMouseOverWidget);
  507. event.markAsUsed();
  508. }
  509. }
  510. }
  511. }
  512. void GUIManager::onButtonUp(const ButtonEvent& event)
  513. {
  514. if(event.isUsed())
  515. return;
  516. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  517. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  518. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  519. if(event.isKeyboard())
  520. {
  521. if(mKeyboardFocusElement != nullptr)
  522. {
  523. mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
  524. mKeyEvent.setKeyUpData(event.buttonCode);
  525. if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
  526. event.markAsUsed();
  527. }
  528. }
  529. if(event.isMouse())
  530. {
  531. bool buttonStates[(int)GUIMouseButton::Count];
  532. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  533. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  534. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  535. mMouseEvent = GUIMouseEvent(buttonStates, shiftDown, ctrlDown, altDown);
  536. Int2 localPos;
  537. if(mMouseOverWidget != nullptr)
  538. {
  539. localPos = getWidgetRelativePos(*mMouseOverWidget, gInput().getMousePosition());
  540. }
  541. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  542. // Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
  543. // And only activate when a button that originally caused the active state is released, otherwise ignore it.
  544. bool acceptMouseUp = mActiveMouseButton == guiButton && (mMouseOverElement != nullptr && mActiveElement == mMouseOverElement);
  545. if(acceptMouseUp)
  546. {
  547. mMouseEvent.setMouseUpData(mMouseOverElement, localPos, guiButton);
  548. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  549. event.markAsUsed();
  550. }
  551. // Send DragEnd event to whichever element is active
  552. bool acceptEndDrag = mActiveMouseButton == guiButton && mActiveElement != nullptr;
  553. if(acceptEndDrag)
  554. {
  555. if(guiButton == GUIMouseButton::Left)
  556. {
  557. mMouseEvent.setMouseDragEndData(mMouseOverElement, localPos);
  558. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  559. event.markAsUsed();
  560. }
  561. mActiveElement = nullptr;
  562. mActiveWidget = nullptr;
  563. mActiveMouseButton = GUIMouseButton::Left;
  564. }
  565. }
  566. }
  567. bool GUIManager::onMouseDragEnded(const CM::ButtonEvent& event)
  568. {
  569. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  570. if(DragAndDropManager::instance().isDragInProgress() && guiButton == GUIMouseButton::Left)
  571. {
  572. if(mMouseOverElement != nullptr)
  573. {
  574. mMouseEvent.setDragAndDropDroppedData(mMouseOverElement, Int2(), DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  575. bool processed = sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent);
  576. return processed;
  577. }
  578. }
  579. return false;
  580. }
  581. void GUIManager::onMouseMoved(const MouseEvent& event)
  582. {
  583. if(event.isUsed())
  584. return;
  585. #if CM_DEBUG_MODE
  586. // Checks if all referenced windows actually exist
  587. Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
  588. for(auto& widgetInfo : mWidgets)
  589. {
  590. auto iterFind = std::find(begin(activeWindows), end(activeWindows), widgetInfo.widget->getOwnerWindow());
  591. if(iterFind == activeWindows.end())
  592. {
  593. CM_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
  594. Please detach all GUIWidgets from windows before destroying a window.");
  595. }
  596. }
  597. #endif
  598. GUIWidget* widgetInFocus = nullptr;
  599. GUIElement* topMostElement = nullptr;
  600. Int2 screenPos;
  601. Int2 localPos;
  602. for(auto& widgetInfo : mWidgets)
  603. {
  604. const RenderWindow* window = widgetInfo.widget->getOwnerWindow();
  605. if(window->hasFocus())
  606. {
  607. widgetInFocus = widgetInfo.widget;
  608. break;
  609. }
  610. }
  611. if(widgetInFocus != nullptr)
  612. {
  613. const RenderWindow* window = widgetInFocus->getOwnerWindow();
  614. Int2 screenPos = window->screenToWindowPos(event.screenPos);
  615. Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
  616. UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
  617. for(auto& widgetInfo : mWidgets)
  618. {
  619. GUIWidget* widget = widgetInfo.widget;
  620. if(widget->getOwnerWindow() == window && widget->inBounds(screenPos))
  621. {
  622. SelectiveInputData* selectiveInputData = nullptr;
  623. if(mSelectiveInputActive)
  624. {
  625. auto selectionIterFind = mSelectiveInputData.find(widget);
  626. if(selectionIterFind == mSelectiveInputData.end())
  627. continue;
  628. else
  629. selectiveInputData = &selectionIterFind->second;
  630. }
  631. const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
  632. Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
  633. localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  634. Vector<GUIElement*>::type sortedElements = widget->getElements();
  635. std::sort(sortedElements.begin(), sortedElements.end(),
  636. [](GUIElement* a, GUIElement* b)
  637. {
  638. return a->_getDepth() < b->_getDepth();
  639. });
  640. // Elements with lowest depth (most to the front) get handled first
  641. for(auto iter = sortedElements.begin(); iter != sortedElements.end(); ++iter)
  642. {
  643. GUIElement* element = *iter;
  644. if(!element->_isDisabled() && element->_isInBounds(localPos) && element->_getDepth() < topMostDepth)
  645. {
  646. if(mSelectiveInputActive && !selectiveInputData->acceptAllElements)
  647. {
  648. if(selectiveInputData->elements.find(element) == selectiveInputData->elements.end())
  649. continue;
  650. }
  651. topMostElement = element;
  652. topMostDepth = element->_getDepth();
  653. widgetInFocus = widget;
  654. break;
  655. }
  656. }
  657. }
  658. }
  659. }
  660. if(handleMouseOver(widgetInFocus, topMostElement, event.screenPos, event.mouseWheelScrollAmount))
  661. event.markAsUsed();
  662. }
  663. void GUIManager::onTextInput(const CM::TextInputEvent& event)
  664. {
  665. if(mKeyboardFocusElement != nullptr)
  666. {
  667. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  668. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  669. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  670. if(ctrlDown || altDown) // Ignore text input because key characters + alt/ctrl usually correspond to certain commands
  671. return;
  672. mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
  673. mKeyEvent.setTextInputData(event.textChar);
  674. if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
  675. event.markAsUsed();
  676. }
  677. }
  678. bool GUIManager::handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount)
  679. {
  680. bool eventProcessed = false;
  681. Int2 localPos;
  682. if(widget != nullptr)
  683. {
  684. const RenderWindow* window = widget->getOwnerWindow();
  685. Int2 screenPos = window->screenToWindowPos(screenMousePos);
  686. Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
  687. const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
  688. Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
  689. localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  690. }
  691. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  692. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  693. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  694. // TODO - Maybe avoid querying these for every event separately?
  695. bool buttonStates[(int)GUIMouseButton::Count];
  696. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  697. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  698. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  699. mMouseEvent = GUIMouseEvent(buttonStates, shiftDown, ctrlDown, altDown);
  700. // Send MouseOver/MouseOut events to any elements the mouse passes over, except when
  701. // mouse is being held down, in which we only send them to the active element
  702. if(element != mMouseOverElement)
  703. {
  704. if(mMouseOverElement != nullptr)
  705. {
  706. // Send MouseOut event
  707. if(mActiveElement == nullptr || mMouseOverElement == mActiveElement)
  708. {
  709. Int2 curLocalPos = getWidgetRelativePos(*mMouseOverWidget, screenMousePos);
  710. mMouseEvent.setMouseOutData(element, curLocalPos);
  711. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  712. eventProcessed = true;
  713. }
  714. }
  715. if(element != nullptr)
  716. {
  717. // Send MouseOver event
  718. if(mActiveElement == nullptr || element == mActiveElement)
  719. {
  720. mMouseEvent.setMouseOverData(element, localPos);
  721. if(sendMouseEvent(widget, element, mMouseEvent))
  722. eventProcessed = true;
  723. }
  724. }
  725. }
  726. // If mouse is being held down send MouseDrag events
  727. if(mActiveElement != nullptr && mActiveMouseButton == GUIMouseButton::Left)
  728. {
  729. Int2 curLocalPos = getWidgetRelativePos(*mActiveWidget, screenMousePos);
  730. if(mLastCursorLocalPos != curLocalPos)
  731. {
  732. mMouseEvent.setMouseDragData(element, curLocalPos, curLocalPos - mLastCursorLocalPos);
  733. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  734. eventProcessed = true;
  735. mLastCursorLocalPos = curLocalPos;
  736. }
  737. // Also if drag is in progress send DragAndDrop events
  738. if(DragAndDropManager::instance().isDragInProgress())
  739. {
  740. if(element != nullptr)
  741. {
  742. mMouseEvent.setDragAndDropDraggedData(element, localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  743. if(sendMouseEvent(widget, element, mMouseEvent))
  744. eventProcessed = true;
  745. }
  746. }
  747. }
  748. else // Otherwise, send MouseMove events if we are hovering over any element
  749. {
  750. if(element != nullptr)
  751. {
  752. // Send MouseMove event
  753. if(mLastCursorLocalPos != localPos)
  754. {
  755. mMouseEvent.setMouseMoveData(element, localPos);
  756. if(sendMouseEvent(widget, element, mMouseEvent))
  757. eventProcessed = true;
  758. mLastCursorLocalPos = localPos;
  759. }
  760. if(Math::Abs(wheelScrollAmount) > 0.00001f)
  761. {
  762. mMouseEvent.setMouseWheelScrollData(element, wheelScrollAmount);
  763. if(sendMouseEvent(widget, element, mMouseEvent))
  764. eventProcessed = true;
  765. }
  766. }
  767. }
  768. mMouseOverElement = element;
  769. mMouseOverWidget = widget;
  770. return false;
  771. }
  772. void GUIManager::onWindowFocusGained(RenderWindow& win)
  773. {
  774. for(auto& widgetInfo : mWidgets)
  775. {
  776. GUIWidget* widget = widgetInfo.widget;
  777. if(widget->getOwnerWindow() == &win)
  778. widget->ownerWindowFocusChanged();
  779. }
  780. }
  781. void GUIManager::onWindowFocusLost(RenderWindow& win)
  782. {
  783. for(auto& widgetInfo : mWidgets)
  784. {
  785. GUIWidget* widget = widgetInfo.widget;
  786. if(widget->getOwnerWindow() == &win)
  787. widget->ownerWindowFocusChanged();
  788. }
  789. }
  790. void GUIManager::onWindowMovedOrResized(RenderWindow& win)
  791. {
  792. for(auto& widgetInfo : mWidgets)
  793. {
  794. GUIWidget* widget = widgetInfo.widget;
  795. if(widget->getOwnerWindow() == &win)
  796. widget->ownerWindowResized();
  797. }
  798. }
  799. // We stop getting mouse move events once it leaves the window, so make sure
  800. // nothing stays in hover state
  801. void GUIManager::onMouseLeftWindow(CM::RenderWindow* win)
  802. {
  803. handleMouseOver(nullptr, nullptr, Int2());
  804. }
  805. void GUIManager::queueForDestroy(GUIElement* element)
  806. {
  807. mScheduledForDestruction.push(element);
  808. }
  809. void GUIManager::processDestroyQueue()
  810. {
  811. while(!mScheduledForDestruction.empty())
  812. {
  813. cm_delete<PoolAlloc>(mScheduledForDestruction.top());
  814. mScheduledForDestruction.pop();
  815. }
  816. }
  817. // HACK - This callback is very hackish and very specific. Attempt to replace it with a more
  818. // general purpose solution
  819. void GUIManager::enableSelectiveInput(std::function<void()> onOutsideClickCallback)
  820. {
  821. mSelectiveInputActive = true;
  822. mOnOutsideClickCallback = onOutsideClickCallback;
  823. }
  824. void GUIManager::disableSelectiveInput()
  825. {
  826. mSelectiveInputData.clear();
  827. mSelectiveInputActive = false;
  828. mOnOutsideClickCallback = nullptr;
  829. }
  830. void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)
  831. {
  832. auto findIter = mSelectiveInputData.find(widget);
  833. if(findIter == mSelectiveInputData.end())
  834. {
  835. SelectiveInputData& data = mSelectiveInputData[widget];
  836. data.acceptAllElements = true;
  837. }
  838. else
  839. {
  840. mSelectiveInputData[widget].acceptAllElements = true;
  841. }
  842. }
  843. void GUIManager::addSelectiveInputElement(const GUIElement* element)
  844. {
  845. mSelectiveInputData[&element->_getParentWidget()].elements.insert(element);
  846. }
  847. GUIMouseButton GUIManager::buttonToMouseButton(ButtonCode code) const
  848. {
  849. if(code == BC_MOUSE_LEFT)
  850. return GUIMouseButton::Left;
  851. else if(code == BC_MOUSE_MIDDLE)
  852. return GUIMouseButton::Middle;
  853. else if(code == BC_MOUSE_RIGHT)
  854. return GUIMouseButton::Right;
  855. CM_EXCEPT(InvalidParametersException, "Provided button code is not a GUI supported mouse button.");
  856. }
  857. Int2 GUIManager::getWidgetRelativePos(const GUIWidget& widget, const Int2& screenPos) const
  858. {
  859. const RenderWindow* window = widget.getOwnerWindow();
  860. Int2 windowPos = window->screenToWindowPos(screenPos);
  861. const Matrix4& worldTfrm = widget.SO()->getWorldTfrm();
  862. Vector4 vecLocalPos = worldTfrm.inverse() * Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
  863. Int2 curLocalPos(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  864. return curLocalPos;
  865. }
  866. bool GUIManager::sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event)
  867. {
  868. if(!mouseEventFilter.empty())
  869. mouseEventFilter(widget, element, event);
  870. return widget->_mouseEvent(element, event);
  871. }
  872. bool GUIManager::sendKeyEvent(GUIWidget* widget, GUIElement* element, const GUIKeyEvent& event)
  873. {
  874. if(!keyEventFilter.empty())
  875. keyEventFilter(widget, element, event);
  876. return widget->_keyEvent(element, event);
  877. }
  878. GUIManager& gGUIManager()
  879. {
  880. return GUIManager::instance();
  881. }
  882. }