2
0

BsGUIManager.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  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. // DragStart is for all intents and purposes same as mouse down but since I need a DragEnd event, I feel a separate DragStart
  480. // event was also needed to make things clearer.
  481. mMouseEvent.setMouseDragStartData(mMouseOverElement, localPos);
  482. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  483. event.markAsUsed();
  484. mActiveElement = mMouseOverElement;
  485. mActiveWidget = mMouseOverWidget;
  486. mActiveMouseButton = guiButton;
  487. }
  488. if(mMouseOverElement != nullptr && mMouseOverElement->_acceptsKeyboardFocus())
  489. {
  490. if(mKeyboardFocusElement != nullptr && mMouseOverElement != mKeyboardFocusElement)
  491. mKeyboardFocusElement->_setFocus(false);
  492. mMouseOverElement->_setFocus(true);
  493. mKeyboardFocusElement = mMouseOverElement;
  494. mKeyboardFocusWidget = mMouseOverWidget;
  495. event.markAsUsed();
  496. }
  497. // If right click try to open context menu
  498. if(mMouseOverElement != nullptr && buttonStates[2] == true)
  499. {
  500. GUIContextMenu* menu = mMouseOverElement->getContextMenu();
  501. if(menu != nullptr)
  502. {
  503. const RenderWindow* window = mMouseOverWidget->getOwnerWindow();
  504. Int2 windowPos = window->screenToWindowPos(gInput().getMousePosition());
  505. menu->open(windowPos, *mMouseOverWidget);
  506. event.markAsUsed();
  507. }
  508. }
  509. }
  510. }
  511. void GUIManager::onButtonUp(const ButtonEvent& event)
  512. {
  513. if(event.isUsed())
  514. return;
  515. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  516. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  517. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  518. if(event.isKeyboard())
  519. {
  520. if(mKeyboardFocusElement != nullptr)
  521. {
  522. mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
  523. mKeyEvent.setKeyUpData(event.buttonCode);
  524. if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
  525. event.markAsUsed();
  526. }
  527. }
  528. if(event.isMouse())
  529. {
  530. bool buttonStates[(int)GUIMouseButton::Count];
  531. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  532. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  533. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  534. mMouseEvent = GUIMouseEvent(buttonStates, shiftDown, ctrlDown, altDown);
  535. Int2 localPos;
  536. if(mMouseOverWidget != nullptr)
  537. {
  538. localPos = getWidgetRelativePos(*mMouseOverWidget, gInput().getMousePosition());
  539. }
  540. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  541. // Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
  542. // And only activate when a button that originally caused the active state is released, otherwise ignore it.
  543. bool acceptMouseUp = mActiveMouseButton == guiButton && (mMouseOverElement != nullptr && mActiveElement == mMouseOverElement);
  544. if(acceptMouseUp)
  545. {
  546. mMouseEvent.setMouseUpData(mMouseOverElement, localPos, guiButton);
  547. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  548. event.markAsUsed();
  549. }
  550. // Send DragEnd event to whichever element is active
  551. bool acceptEndDrag = mActiveMouseButton == guiButton && mActiveElement != nullptr;
  552. if(acceptEndDrag)
  553. {
  554. mMouseEvent.setMouseDragEndData(mMouseOverElement, localPos);
  555. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  556. event.markAsUsed();
  557. mActiveElement = nullptr;
  558. mActiveWidget = nullptr;
  559. mActiveMouseButton = GUIMouseButton::Left;
  560. }
  561. }
  562. }
  563. bool GUIManager::onMouseDragEnded(const CM::ButtonEvent& event)
  564. {
  565. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  566. if(DragAndDropManager::instance().isDragInProgress() && guiButton == GUIMouseButton::Left)
  567. {
  568. if(mMouseOverElement != nullptr)
  569. {
  570. mMouseEvent.setDragAndDropDroppedData(mMouseOverElement, Int2(), DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  571. bool processed = sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent);
  572. return processed;
  573. }
  574. }
  575. return false;
  576. }
  577. void GUIManager::onMouseMoved(const MouseEvent& event)
  578. {
  579. if(event.isUsed())
  580. return;
  581. #if CM_DEBUG_MODE
  582. // Checks if all referenced windows actually exist
  583. Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
  584. for(auto& widgetInfo : mWidgets)
  585. {
  586. auto iterFind = std::find(begin(activeWindows), end(activeWindows), widgetInfo.widget->getOwnerWindow());
  587. if(iterFind == activeWindows.end())
  588. {
  589. CM_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
  590. Please detach all GUIWidgets from windows before destroying a window.");
  591. }
  592. }
  593. #endif
  594. GUIWidget* widgetInFocus = nullptr;
  595. GUIElement* topMostElement = nullptr;
  596. Int2 screenPos;
  597. Int2 localPos;
  598. for(auto& widgetInfo : mWidgets)
  599. {
  600. const RenderWindow* window = widgetInfo.widget->getOwnerWindow();
  601. if(window->hasFocus())
  602. {
  603. widgetInFocus = widgetInfo.widget;
  604. break;
  605. }
  606. }
  607. if(widgetInFocus != nullptr)
  608. {
  609. const RenderWindow* window = widgetInFocus->getOwnerWindow();
  610. Int2 screenPos = window->screenToWindowPos(event.screenPos);
  611. Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
  612. UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
  613. for(auto& widgetInfo : mWidgets)
  614. {
  615. GUIWidget* widget = widgetInfo.widget;
  616. if(widget->getOwnerWindow() == window && widget->inBounds(screenPos))
  617. {
  618. SelectiveInputData* selectiveInputData = nullptr;
  619. if(mSelectiveInputActive)
  620. {
  621. auto selectionIterFind = mSelectiveInputData.find(widget);
  622. if(selectionIterFind == mSelectiveInputData.end())
  623. continue;
  624. else
  625. selectiveInputData = &selectionIterFind->second;
  626. }
  627. const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
  628. Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
  629. localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  630. Vector<GUIElement*>::type sortedElements = widget->getElements();
  631. std::sort(sortedElements.begin(), sortedElements.end(),
  632. [](GUIElement* a, GUIElement* b)
  633. {
  634. return a->_getDepth() < b->_getDepth();
  635. });
  636. // Elements with lowest depth (most to the front) get handled first
  637. for(auto iter = sortedElements.begin(); iter != sortedElements.end(); ++iter)
  638. {
  639. GUIElement* element = *iter;
  640. if(!element->_isDisabled() && element->_isInBounds(localPos) && element->_getDepth() < topMostDepth)
  641. {
  642. if(mSelectiveInputActive && !selectiveInputData->acceptAllElements)
  643. {
  644. if(selectiveInputData->elements.find(element) == selectiveInputData->elements.end())
  645. continue;
  646. }
  647. topMostElement = element;
  648. topMostDepth = element->_getDepth();
  649. widgetInFocus = widget;
  650. break;
  651. }
  652. }
  653. }
  654. }
  655. }
  656. if(handleMouseOver(widgetInFocus, topMostElement, event.screenPos, event.mouseWheelScrollAmount))
  657. event.markAsUsed();
  658. }
  659. void GUIManager::onTextInput(const CM::TextInputEvent& event)
  660. {
  661. if(mKeyboardFocusElement != nullptr)
  662. {
  663. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  664. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  665. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  666. if(ctrlDown || altDown) // Ignore text input because key characters + alt/ctrl usually correspond to certain commands
  667. return;
  668. mKeyEvent = GUIKeyEvent(shiftDown, ctrlDown, altDown);
  669. mKeyEvent.setTextInputData(event.textChar);
  670. if(sendKeyEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mKeyEvent))
  671. event.markAsUsed();
  672. }
  673. }
  674. bool GUIManager::handleMouseOver(GUIWidget* widget, GUIElement* element, const CM::Int2& screenMousePos, float wheelScrollAmount)
  675. {
  676. bool eventProcessed = false;
  677. Int2 localPos;
  678. if(widget != nullptr)
  679. {
  680. const RenderWindow* window = widget->getOwnerWindow();
  681. Int2 screenPos = window->screenToWindowPos(screenMousePos);
  682. Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
  683. const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
  684. Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
  685. localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  686. }
  687. bool shiftDown = gInput().isButtonDown(BC_LSHIFT) || gInput().isButtonDown(BC_RSHIFT);
  688. bool ctrlDown = gInput().isButtonDown(BC_LCONTROL) || gInput().isButtonDown(BC_RCONTROL);
  689. bool altDown = gInput().isButtonDown(BC_LMENU) || gInput().isButtonDown(BC_RMENU);
  690. // TODO - Maybe avoid querying these for every event separately?
  691. bool buttonStates[(int)GUIMouseButton::Count];
  692. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  693. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  694. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  695. mMouseEvent = GUIMouseEvent(buttonStates, shiftDown, ctrlDown, altDown);
  696. // Send MouseOver/MouseOut events to any elements the mouse passes over, except when
  697. // mouse is being held down, in which we only send them to the active element
  698. if(element != mMouseOverElement)
  699. {
  700. if(mMouseOverElement != nullptr)
  701. {
  702. // Send MouseOut event
  703. if(mActiveElement == nullptr || mMouseOverElement == mActiveElement)
  704. {
  705. Int2 curLocalPos = getWidgetRelativePos(*mMouseOverWidget, screenMousePos);
  706. mMouseEvent.setMouseOutData(element, curLocalPos);
  707. if(sendMouseEvent(mMouseOverWidget, mMouseOverElement, mMouseEvent))
  708. eventProcessed = true;
  709. }
  710. }
  711. if(element != nullptr)
  712. {
  713. // Send MouseOver event
  714. if(mActiveElement == nullptr || element == mActiveElement)
  715. {
  716. mMouseEvent.setMouseOverData(element, localPos);
  717. if(sendMouseEvent(widget, element, mMouseEvent))
  718. eventProcessed = true;
  719. }
  720. }
  721. }
  722. // If mouse is being held down send MouseDrag events
  723. if(mActiveElement != nullptr)
  724. {
  725. Int2 curLocalPos = getWidgetRelativePos(*mActiveWidget, screenMousePos);
  726. if(mLastCursorLocalPos != curLocalPos)
  727. {
  728. mMouseEvent.setMouseDragData(element, curLocalPos, curLocalPos - mLastCursorLocalPos);
  729. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  730. eventProcessed = true;
  731. mLastCursorLocalPos = curLocalPos;
  732. }
  733. // Also if drag is in progress send DragAndDrop events
  734. if(DragAndDropManager::instance().isDragInProgress())
  735. {
  736. if(element != nullptr)
  737. {
  738. mMouseEvent.setDragAndDropDraggedData(element, localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  739. if(sendMouseEvent(widget, element, mMouseEvent))
  740. eventProcessed = true;
  741. }
  742. }
  743. }
  744. else // Otherwise, send MouseMove events if we are hovering over any element
  745. {
  746. if(element != nullptr)
  747. {
  748. // Send MouseMove event
  749. if(mLastCursorLocalPos != localPos)
  750. {
  751. mMouseEvent.setMouseMoveData(element, localPos);
  752. if(sendMouseEvent(widget, element, mMouseEvent))
  753. eventProcessed = true;
  754. mLastCursorLocalPos = localPos;
  755. }
  756. if(Math::Abs(wheelScrollAmount) > 0.00001f)
  757. {
  758. mMouseEvent.setMouseWheelScrollData(element, wheelScrollAmount);
  759. if(sendMouseEvent(widget, element, mMouseEvent))
  760. eventProcessed = true;
  761. }
  762. }
  763. }
  764. mMouseOverElement = element;
  765. mMouseOverWidget = widget;
  766. return false;
  767. }
  768. void GUIManager::onWindowFocusGained(RenderWindow& win)
  769. {
  770. for(auto& widgetInfo : mWidgets)
  771. {
  772. GUIWidget* widget = widgetInfo.widget;
  773. if(widget->getOwnerWindow() == &win)
  774. widget->ownerWindowFocusChanged();
  775. }
  776. }
  777. void GUIManager::onWindowFocusLost(RenderWindow& win)
  778. {
  779. for(auto& widgetInfo : mWidgets)
  780. {
  781. GUIWidget* widget = widgetInfo.widget;
  782. if(widget->getOwnerWindow() == &win)
  783. widget->ownerWindowFocusChanged();
  784. }
  785. }
  786. void GUIManager::onWindowMovedOrResized(RenderWindow& win)
  787. {
  788. for(auto& widgetInfo : mWidgets)
  789. {
  790. GUIWidget* widget = widgetInfo.widget;
  791. if(widget->getOwnerWindow() == &win)
  792. widget->ownerWindowResized();
  793. }
  794. }
  795. // We stop getting mouse move events once it leaves the window, so make sure
  796. // nothing stays in hover state
  797. void GUIManager::onMouseLeftWindow(CM::RenderWindow* win)
  798. {
  799. handleMouseOver(nullptr, nullptr, Int2());
  800. }
  801. void GUIManager::queueForDestroy(GUIElement* element)
  802. {
  803. mScheduledForDestruction.push(element);
  804. }
  805. void GUIManager::processDestroyQueue()
  806. {
  807. while(!mScheduledForDestruction.empty())
  808. {
  809. cm_delete<PoolAlloc>(mScheduledForDestruction.top());
  810. mScheduledForDestruction.pop();
  811. }
  812. }
  813. // HACK - This callback is very hackish and very specific. Attempt to replace it with a more
  814. // general purpose solution
  815. void GUIManager::enableSelectiveInput(std::function<void()> onOutsideClickCallback)
  816. {
  817. mSelectiveInputActive = true;
  818. mOnOutsideClickCallback = onOutsideClickCallback;
  819. }
  820. void GUIManager::disableSelectiveInput()
  821. {
  822. mSelectiveInputData.clear();
  823. mSelectiveInputActive = false;
  824. mOnOutsideClickCallback = nullptr;
  825. }
  826. void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)
  827. {
  828. auto findIter = mSelectiveInputData.find(widget);
  829. if(findIter == mSelectiveInputData.end())
  830. {
  831. SelectiveInputData& data = mSelectiveInputData[widget];
  832. data.acceptAllElements = true;
  833. }
  834. else
  835. {
  836. mSelectiveInputData[widget].acceptAllElements = true;
  837. }
  838. }
  839. void GUIManager::addSelectiveInputElement(const GUIElement* element)
  840. {
  841. mSelectiveInputData[&element->_getParentWidget()].elements.insert(element);
  842. }
  843. GUIMouseButton GUIManager::buttonToMouseButton(ButtonCode code) const
  844. {
  845. if(code == BC_MOUSE_LEFT)
  846. return GUIMouseButton::Left;
  847. else if(code == BC_MOUSE_MIDDLE)
  848. return GUIMouseButton::Middle;
  849. else if(code == BC_MOUSE_RIGHT)
  850. return GUIMouseButton::Right;
  851. CM_EXCEPT(InvalidParametersException, "Provided button code is not a GUI supported mouse button.");
  852. }
  853. Int2 GUIManager::getWidgetRelativePos(const GUIWidget& widget, const Int2& screenPos) const
  854. {
  855. const RenderWindow* window = widget.getOwnerWindow();
  856. Int2 windowPos = window->screenToWindowPos(screenPos);
  857. const Matrix4& worldTfrm = widget.SO()->getWorldTfrm();
  858. Vector4 vecLocalPos = worldTfrm.inverse() * Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
  859. Int2 curLocalPos(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  860. return curLocalPos;
  861. }
  862. bool GUIManager::sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event)
  863. {
  864. if(!mouseEventFilter.empty())
  865. mouseEventFilter(widget, element, event);
  866. return widget->_mouseEvent(element, event);
  867. }
  868. bool GUIManager::sendKeyEvent(GUIWidget* widget, GUIElement* element, const GUIKeyEvent& event)
  869. {
  870. if(!keyEventFilter.empty())
  871. keyEventFilter(widget, element, event);
  872. return widget->_keyEvent(element, event);
  873. }
  874. GUIManager& gGUIManager()
  875. {
  876. return GUIManager::instance();
  877. }
  878. }