BsGUIManager.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  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 "CmCursor.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. using namespace CamelotFramework;
  21. namespace BansheeEngine
  22. {
  23. struct GUIGroupElement
  24. {
  25. GUIGroupElement()
  26. { }
  27. GUIGroupElement(GUIElement* _element, UINT32 _renderElement)
  28. :element(_element), renderElement(_renderElement)
  29. { }
  30. GUIElement* element;
  31. UINT32 renderElement;
  32. };
  33. struct GUIMaterialGroup
  34. {
  35. HMaterial material;
  36. UINT32 numQuads;
  37. UINT32 depth;
  38. Rect bounds;
  39. Vector<GUIGroupElement>::type elements;
  40. };
  41. GUIManager::GUIManager()
  42. :mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr),
  43. mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
  44. mCaretTexture(nullptr), mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(Color::Black), mIsCaretOn(false)
  45. {
  46. mOnButtonDownConn = gInput().onButtonDown.connect(boost::bind(&GUIManager::onButtonDown, this, _1));
  47. mOnButtonUpConn = gInput().onButtonUp.connect(boost::bind(&GUIManager::onButtonUp, this, _1));
  48. mOnMouseMovedConn = gInput().onMouseMoved.connect(boost::bind(&GUIManager::onMouseMoved, this, _1));
  49. mOnTextInputConn = gInput().onCharInput.connect(boost::bind(&GUIManager::onTextInput, this, _1));
  50. mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
  51. mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
  52. mWindowMovedOrResizedConn = RenderWindowManager::instance().onMovedOrResized.connect(boost::bind(&GUIManager::onWindowMovedOrResized, this, _1));
  53. // Need to defer this call because I want to make sure all managers are initialized first
  54. deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
  55. }
  56. GUIManager::~GUIManager()
  57. {
  58. // Make a copy of widgets, since destroying them will remove them from mWidgets and
  59. // we can't iterate over an array thats getting modified
  60. Vector<GUIWidget*>::type widgetCopy = mWidgets;
  61. for(auto& widget : widgetCopy)
  62. widget->destroy();
  63. mOnButtonDownConn.disconnect();
  64. mOnButtonUpConn.disconnect();
  65. mOnMouseMovedConn.disconnect();
  66. mOnTextInputConn.disconnect();
  67. mWindowGainedFocusConn.disconnect();
  68. mWindowLostFocusConn.disconnect();
  69. mWindowMovedOrResizedConn.disconnect();
  70. }
  71. void GUIManager::registerWidget(GUIWidget* widget)
  72. {
  73. mWidgets.push_back(widget);
  74. const Viewport* renderTarget = widget->getTarget();
  75. auto findIter = mCachedGUIData.find(renderTarget);
  76. if(findIter == end(mCachedGUIData))
  77. mCachedGUIData[renderTarget] = GUIRenderData();
  78. GUIRenderData& windowData = mCachedGUIData[renderTarget];
  79. windowData.widgets.push_back(widget);
  80. windowData.isDirty = true;
  81. }
  82. void GUIManager::unregisterWidget(GUIWidget* widget)
  83. {
  84. auto findIter = std::find(begin(mWidgets), end(mWidgets), widget);
  85. if(findIter != end(mWidgets))
  86. mWidgets.erase(findIter);
  87. if(mMouseOverWidget == widget)
  88. {
  89. mMouseOverWidget = nullptr;
  90. mMouseOverElement = nullptr;
  91. }
  92. const Viewport* renderTarget = widget->getTarget();
  93. GUIRenderData& renderData = mCachedGUIData[renderTarget];
  94. findIter = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
  95. if(findIter != end(renderData.widgets))
  96. renderData.widgets.erase(findIter);
  97. if(renderData.widgets.size() == 0)
  98. mCachedGUIData.erase(renderTarget);
  99. else
  100. renderData.isDirty = true;
  101. }
  102. void GUIManager::update()
  103. {
  104. // Update layouts
  105. for(auto& widget : mWidgets)
  106. {
  107. widget->_updateLayout();
  108. }
  109. // Blink caret
  110. if(mKeyboardFocusElement != nullptr)
  111. {
  112. float curTime = gTime().getTime();
  113. if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
  114. {
  115. mCaretLastBlinkTime = curTime;
  116. mIsCaretOn = !mIsCaretOn;
  117. mCommandEvent = GUICommandEvent();
  118. mCommandEvent.setRedrawData();
  119. mKeyboardFocusElement->commandEvent(mCommandEvent);
  120. }
  121. }
  122. updateMeshes();
  123. }
  124. void GUIManager::render(ViewportPtr& target, CoreAccessor& coreAccessor)
  125. {
  126. auto findIter = mCachedGUIData.find(target.get());
  127. if(findIter == mCachedGUIData.end())
  128. return;
  129. coreAccessor.setViewport(target);
  130. GUIRenderData& renderData = findIter->second;
  131. // Render the meshes
  132. if(mSeparateMeshesByWidget)
  133. {
  134. UINT32 meshIdx = 0;
  135. for(auto& mesh : renderData.cachedMeshes)
  136. {
  137. HMaterial material = renderData.cachedMaterials[meshIdx];
  138. GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
  139. renderMesh(mesh, material, widget->SO()->getWorldTfrm(), target, coreAccessor);
  140. meshIdx++;
  141. }
  142. }
  143. else
  144. {
  145. // TODO: I want to avoid separating meshes by widget in the future. On DX11 and GL I can set up a shader
  146. // that accepts multiple world transforms (one for each widget). Then I can add some instance information to vertices
  147. // and render elements using multiple different transforms with a single call.
  148. // Separating meshes can then be used as a compatibility mode for DX9
  149. CM_EXCEPT(NotImplementedException, "Not implemented");
  150. }
  151. }
  152. void GUIManager::renderMesh(const CM::HMesh& mesh, const CM::HMaterial& material, const CM::Matrix4& tfrm, CM::ViewportPtr& target, CM::CoreAccessor& coreAccessor)
  153. {
  154. // TODO - Possible optimization. I currently divide by width/height inside the shader, while it
  155. // might be more optimal to just scale the mesh as the resolution changes?
  156. float invViewportWidth = 1.0f / (target->getWidth() * 0.5f);
  157. float invViewportHeight = 1.0f / (target->getHeight() * 0.5f);
  158. material->setFloat("invViewportWidth", invViewportWidth);
  159. material->setFloat("invViewportHeight", invViewportHeight);
  160. material->setMat4("worldTransform", tfrm);
  161. if(material == nullptr || !material.isLoaded())
  162. return;
  163. if(mesh == nullptr || !mesh.isLoaded())
  164. return;
  165. for(UINT32 i = 0; i < material->getNumPasses(); i++)
  166. {
  167. PassPtr pass = material->getPass(i);
  168. pass->activate(coreAccessor);
  169. PassParametersPtr paramsPtr = material->getPassParameters(i);
  170. pass->bindParameters(coreAccessor, paramsPtr);
  171. coreAccessor.render(mesh->getRenderOperation());
  172. }
  173. }
  174. void GUIManager::updateMeshes()
  175. {
  176. for(auto& cachedMeshData : mCachedGUIData)
  177. {
  178. GUIRenderData& renderData = cachedMeshData.second;
  179. // Check if anything is dirty. If nothing is we can skip the update
  180. bool isDirty = renderData.isDirty;
  181. renderData.isDirty = false;
  182. for(auto& widget : renderData.widgets)
  183. {
  184. if(widget->isDirty(true))
  185. {
  186. isDirty = true;
  187. }
  188. }
  189. if(!isDirty)
  190. continue;
  191. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  192. auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
  193. {
  194. UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
  195. UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
  196. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  197. // requires all elements to be unique
  198. return aDepth > bDepth || (aDepth ==bDepth && a.element > b.element);
  199. };
  200. Set<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>>::type allElements(elemComp);
  201. for(auto& widget : renderData.widgets)
  202. {
  203. const Vector<GUIElement*>::type& elements = widget->getElements();
  204. for(auto& element : elements)
  205. {
  206. UINT32 numRenderElems = element->getNumRenderElements();
  207. for(UINT32 i = 0; i < numRenderElems; i++)
  208. {
  209. allElements.insert(GUIGroupElement(element, i));
  210. }
  211. }
  212. }
  213. // Group the elements in such a way so that we end up with a smallest amount of
  214. // meshes, without breaking back to front rendering order
  215. UnorderedMap<UINT64, Vector<GUIMaterialGroup>::type>::type materialGroups;
  216. for(auto& elem : allElements)
  217. {
  218. GUIElement* guiElem = elem.element;
  219. UINT32 renderElemIdx = elem.renderElement;
  220. UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
  221. Rect tfrmedBounds = guiElem->_getBounds();
  222. tfrmedBounds.transform(guiElem->_getParentWidget().SO()->getWorldTfrm());
  223. const HMaterial& mat = guiElem->getMaterial(renderElemIdx);
  224. UINT64 materialId = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
  225. // this system won't detect it. Find a better way of determining material similarity?
  226. // If this is a new material, add a new list of groups
  227. auto findIterMaterial = materialGroups.find(materialId);
  228. if(findIterMaterial == end(materialGroups))
  229. materialGroups[materialId] = Vector<GUIMaterialGroup>::type();
  230. // Try to find a group this material will fit in:
  231. // - Group that has a depth value same or one below elements depth will always be a match
  232. // - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
  233. // overlap the current elements bounds.
  234. Vector<GUIMaterialGroup>::type& allGroups = materialGroups[materialId];
  235. GUIMaterialGroup* foundGroup = nullptr;
  236. for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
  237. {
  238. // If we separate meshes by widget, ignore any groups with widget parents other than mine
  239. if(mSeparateMeshesByWidget)
  240. {
  241. if(groupIter->elements.size() > 0)
  242. {
  243. GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
  244. if(&otherElem->_getParentWidget() != &guiElem->_getParentWidget())
  245. continue;
  246. }
  247. }
  248. GUIMaterialGroup& group = *groupIter;
  249. if(group.depth == elemDepth || group.depth == (elemDepth - 1))
  250. {
  251. foundGroup = &group;
  252. break;
  253. }
  254. else
  255. {
  256. UINT32 startDepth = elemDepth;
  257. UINT32 endDepth = group.depth;
  258. bool foundOverlap = false;
  259. for(auto& material : materialGroups)
  260. {
  261. for(auto& group : material.second)
  262. {
  263. if(group.depth > startDepth && group.depth < endDepth)
  264. {
  265. if(group.bounds.overlaps(tfrmedBounds))
  266. {
  267. foundOverlap = true;
  268. break;
  269. }
  270. }
  271. }
  272. }
  273. if(!foundOverlap)
  274. {
  275. foundGroup = &group;
  276. break;
  277. }
  278. }
  279. }
  280. if(foundGroup == nullptr)
  281. {
  282. allGroups.push_back(GUIMaterialGroup());
  283. foundGroup = &allGroups[allGroups.size() - 1];
  284. foundGroup->depth = elemDepth;
  285. foundGroup->bounds = tfrmedBounds;
  286. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  287. foundGroup->material = mat;
  288. foundGroup->numQuads = guiElem->getNumQuads(renderElemIdx);
  289. }
  290. else
  291. {
  292. foundGroup->bounds.encapsulate(tfrmedBounds);
  293. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  294. foundGroup->numQuads += guiElem->getNumQuads(renderElemIdx);
  295. }
  296. }
  297. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  298. auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
  299. {
  300. return (a->depth > b->depth) || (a->depth == b->depth && a > b);
  301. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  302. // requires all elements to be unique
  303. };
  304. Set<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>>::type sortedGroups(groupComp);
  305. for(auto& material : materialGroups)
  306. {
  307. for(auto& group : material.second)
  308. {
  309. sortedGroups.insert(&group);
  310. }
  311. }
  312. UINT32 numMeshes = (UINT32)sortedGroups.size();
  313. UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
  314. if(numMeshes < oldNumMeshes)
  315. renderData.cachedMeshes.resize(numMeshes);
  316. renderData.cachedMaterials.resize(numMeshes);
  317. if(mSeparateMeshesByWidget)
  318. renderData.cachedWidgetsPerMesh.resize(numMeshes);
  319. // Fill buffers for each group and update their meshes
  320. UINT32 groupIdx = 0;
  321. for(auto& group : sortedGroups)
  322. {
  323. renderData.cachedMaterials[groupIdx] = group->material;
  324. if(mSeparateMeshesByWidget)
  325. {
  326. if(group->elements.size() == 0)
  327. renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
  328. else
  329. {
  330. GUIElement* elem = group->elements.begin()->element;
  331. renderData.cachedWidgetsPerMesh[groupIdx] = &elem->_getParentWidget();
  332. }
  333. }
  334. MeshDataPtr meshData = cm_shared_ptr<MeshData, PoolAlloc>(group->numQuads * 4);
  335. meshData->beginDesc();
  336. meshData->addVertElem(VET_FLOAT2, VES_POSITION);
  337. meshData->addVertElem(VET_FLOAT2, VES_TEXCOORD);
  338. meshData->addSubMesh(group->numQuads * 6);
  339. meshData->endDesc();
  340. UINT8* vertices = meshData->getElementData(VES_POSITION);
  341. UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
  342. UINT32* indices = meshData->getIndices32();
  343. UINT32 vertexStride = meshData->getVertexStride();
  344. UINT32 indexStride = meshData->getIndexElementSize();
  345. UINT32 quadOffset = 0;
  346. for(auto& matElement : group->elements)
  347. {
  348. matElement.element->fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
  349. UINT32 numQuads = matElement.element->getNumQuads(matElement.renderElement);
  350. UINT32 indexStart = quadOffset * 6;
  351. UINT32 indexEnd = indexStart + numQuads * 6;
  352. UINT32 vertOffset = quadOffset * 4;
  353. for(UINT32 i = indexStart; i < indexEnd; i++)
  354. indices[i] += vertOffset;
  355. quadOffset += numQuads;
  356. }
  357. if(groupIdx >= (UINT32)renderData.cachedMeshes.size())
  358. {
  359. renderData.cachedMeshes.push_back(Mesh::create());
  360. }
  361. gMainSyncedCA().writeSubresource(renderData.cachedMeshes[groupIdx].getInternalPtr(), 0, *meshData);
  362. gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
  363. groupIdx++;
  364. }
  365. }
  366. }
  367. void GUIManager::updateCaretTexture()
  368. {
  369. if(mCaretTexture == nullptr)
  370. {
  371. HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
  372. mCaretTexture = cm_shared_ptr<SpriteTexture>(newTex);
  373. }
  374. const HTexture& tex = mCaretTexture->getTexture();
  375. UINT32 subresourceIdx = tex->mapToSubresourceIdx(0, 0);
  376. PixelDataPtr data = tex->allocateSubresourceBuffer(subresourceIdx);
  377. data->setColorAt(Color::Red, 0, 0);
  378. gMainSyncedCA().writeSubresource(tex.getInternalPtr(), tex->mapToSubresourceIdx(0, 0), *data);
  379. gMainSyncedCA().submitToCoreThread(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
  380. }
  381. void GUIManager::onButtonDown(const ButtonEvent& event)
  382. {
  383. if(event.isUsed())
  384. return;
  385. if(event.isKeyboard())
  386. {
  387. if(mKeyboardFocusElement != nullptr)
  388. {
  389. mKeyEvent = GUIButtonEvent();
  390. mKeyEvent.setKeyDownData(event.buttonCode);
  391. mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);
  392. }
  393. }
  394. if(event.isMouse())
  395. {
  396. // TODO - Maybe avoid querying these for every event separately?
  397. bool buttonStates[(int)GUIMouseButton::Count];
  398. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  399. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  400. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  401. mMouseEvent = GUIMouseEvent(buttonStates);
  402. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  403. // HACK: This should never happen, as MouseUp was meant to happen before another MouseDown,
  404. // and MouseUp will clear the active element. HOWEVER Windows doesn't send a MouseUp message when resizing
  405. // a window really fast. My guess is that the cursor gets out of bounds and message is sent to another window.
  406. if(mActiveMouseButton == guiButton && mActiveElement != nullptr)
  407. {
  408. mActiveElement = nullptr;
  409. mActiveWidget = nullptr;
  410. mActiveMouseButton = GUIMouseButton::Left;
  411. }
  412. // We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
  413. bool acceptMouseDown = mActiveElement == nullptr && mMouseOverElement != nullptr;
  414. if(acceptMouseDown)
  415. {
  416. Int2 localPos = getWidgetRelativePos(*mMouseOverWidget, gInput().getMousePosition());
  417. mMouseEvent.setMouseDownData(mMouseOverElement, localPos, guiButton);
  418. mMouseOverWidget->_mouseEvent(mMouseOverElement, mMouseEvent);
  419. // DragStart is for all intents and purposes same as mouse down but since I need a DragEnd event, I feel a separate DragStart
  420. // event was also needed to make things clearer.
  421. mMouseEvent.setMouseDragStartData(mMouseOverElement, localPos);
  422. mMouseOverWidget->_mouseEvent(mMouseOverElement, mMouseEvent);
  423. mActiveElement = mMouseOverElement;
  424. mActiveWidget = mMouseOverWidget;
  425. mActiveMouseButton = guiButton;
  426. }
  427. if(mKeyboardFocusElement != nullptr && mMouseOverElement != mKeyboardFocusElement)
  428. mKeyboardFocusElement->_setFocus(false);
  429. if(mMouseOverElement != nullptr)
  430. mMouseOverElement->_setFocus(true);
  431. mKeyboardFocusElement = mMouseOverElement;
  432. mKeyboardFocusWidget = mMouseOverWidget;
  433. }
  434. event.markAsUsed();
  435. }
  436. void GUIManager::onButtonUp(const ButtonEvent& event)
  437. {
  438. if(event.isUsed())
  439. return;
  440. // TODO - Maybe avoid querying these for every event separately?
  441. if(event.isMouse())
  442. {
  443. bool buttonStates[(int)GUIMouseButton::Count];
  444. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  445. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  446. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  447. mMouseEvent = GUIMouseEvent(buttonStates);
  448. Int2 localPos;
  449. if(mMouseOverWidget != nullptr)
  450. {
  451. localPos = getWidgetRelativePos(*mMouseOverWidget, gInput().getMousePosition());
  452. }
  453. GUIMouseButton guiButton = buttonToMouseButton(event.buttonCode);
  454. // Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
  455. // And only activate when a button that originally caused the active state is released, otherwise ignore it.
  456. bool acceptMouseUp = mActiveMouseButton == guiButton && (mMouseOverElement != nullptr && mActiveElement == mMouseOverElement);
  457. if(acceptMouseUp)
  458. {
  459. mMouseEvent.setMouseUpData(mMouseOverElement, localPos, guiButton);
  460. mMouseOverWidget->_mouseEvent(mMouseOverElement, mMouseEvent);
  461. }
  462. // Send DragEnd event to whichever element is active
  463. bool acceptEndDrag = mActiveMouseButton == guiButton && mActiveElement != nullptr;
  464. if(acceptEndDrag)
  465. {
  466. mMouseEvent.setMouseDragEndData(mMouseOverElement, localPos);
  467. mActiveWidget->_mouseEvent(mActiveElement, mMouseEvent);
  468. mActiveElement = nullptr;
  469. mActiveWidget = nullptr;
  470. mActiveMouseButton = GUIMouseButton::Left;
  471. }
  472. }
  473. event.markAsUsed();
  474. }
  475. void GUIManager::onMouseMoved(const MouseEvent& event)
  476. {
  477. if(event.isUsed())
  478. return;
  479. #if CM_DEBUG_MODE
  480. // Checks if all referenced windows actually exist
  481. Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
  482. for(auto& widget : mWidgets)
  483. {
  484. auto iterFind = std::find(begin(activeWindows), end(activeWindows), widget->getOwnerWindow());
  485. if(iterFind == activeWindows.end())
  486. {
  487. CM_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
  488. Please detach all GUIWidgets from windows before destroying a window.");
  489. }
  490. }
  491. #endif
  492. // TODO - Maybe avoid querying these for every event separately?
  493. bool buttonStates[(int)GUIMouseButton::Count];
  494. buttonStates[0] = gInput().isButtonDown(BC_MOUSE_LEFT);
  495. buttonStates[1] = gInput().isButtonDown(BC_MOUSE_MIDDLE);
  496. buttonStates[2] = gInput().isButtonDown(BC_MOUSE_RIGHT);
  497. mMouseEvent = GUIMouseEvent(buttonStates);
  498. GUIWidget* widgetInFocus = nullptr;
  499. GUIElement* topMostElement = nullptr;
  500. Int2 screenPos;
  501. Int2 localPos;
  502. for(auto& widget : mWidgets)
  503. {
  504. const RenderWindow* window = widget->getOwnerWindow();
  505. if(window->hasFocus())
  506. {
  507. widgetInFocus = widget;
  508. break;
  509. }
  510. }
  511. if(widgetInFocus != nullptr)
  512. {
  513. const RenderWindow* window = widgetInFocus->getOwnerWindow();
  514. Int2 screenPos = window->screenToWindowPos(event.screenPos);
  515. Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
  516. UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
  517. for(auto& widget : mWidgets)
  518. {
  519. if(widget->getOwnerWindow() == window && widget->inBounds(screenPos))
  520. {
  521. const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
  522. Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
  523. localPos = Int2(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  524. Vector<GUIElement*>::type sortedElements = widget->getElements();
  525. std::sort(sortedElements.begin(), sortedElements.end(),
  526. [](GUIElement* a, GUIElement* b)
  527. {
  528. return a->_getDepth() < b->_getDepth();
  529. });
  530. // Elements with lowest depth (most to the front) get handled first
  531. for(auto iter = sortedElements.begin(); iter != sortedElements.end(); ++iter)
  532. {
  533. GUIElement* element = *iter;
  534. if(element->_isInBounds(localPos) && element->_getDepth() < topMostDepth)
  535. {
  536. topMostElement = element;
  537. topMostDepth = element->_getDepth();
  538. widgetInFocus = widget;
  539. break;
  540. }
  541. }
  542. }
  543. }
  544. }
  545. // Send MouseOver/MouseOut events to any elements the mouse passes over, except when
  546. // mouse is being held down, in which we only send them to the active element
  547. if(topMostElement != mMouseOverElement)
  548. {
  549. if(mMouseOverElement != nullptr)
  550. {
  551. // Send MouseOut event
  552. if(mActiveElement == nullptr || mMouseOverElement == mActiveElement)
  553. {
  554. Int2 curLocalPos = getWidgetRelativePos(*mMouseOverWidget, event.screenPos);
  555. mMouseEvent.setMouseOutData(topMostElement, curLocalPos);
  556. mMouseOverWidget->_mouseEvent(mMouseOverElement, mMouseEvent);
  557. }
  558. }
  559. if(topMostElement != nullptr)
  560. {
  561. // Send MouseOver event
  562. if(mActiveElement == nullptr || topMostElement == mActiveElement)
  563. {
  564. mMouseEvent.setMouseOverData(topMostElement, localPos);
  565. widgetInFocus->_mouseEvent(topMostElement, mMouseEvent);
  566. }
  567. }
  568. }
  569. // If mouse is being held down send MouseDrag events
  570. if(mActiveElement != nullptr)
  571. {
  572. Int2 curLocalPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
  573. if(mLastCursorLocalPos != curLocalPos)
  574. {
  575. mMouseEvent.setMouseDragData(topMostElement, curLocalPos, curLocalPos - mLastCursorLocalPos);
  576. mActiveWidget->_mouseEvent(mActiveElement, mMouseEvent);
  577. mLastCursorLocalPos = curLocalPos;
  578. }
  579. }
  580. else // Otherwise, send MouseMove events if we are hovering over any element
  581. {
  582. if(topMostElement != nullptr)
  583. {
  584. // Send MouseMove event
  585. if(mLastCursorLocalPos != localPos)
  586. {
  587. LOGWRN(toString(event.screenPos.x) + " - " + toString(event.screenPos.y) + " - " + toString((UINT32)widgetInFocus));
  588. mMouseEvent.setMouseMoveData(topMostElement, localPos);
  589. widgetInFocus->_mouseEvent(topMostElement, mMouseEvent);
  590. mLastCursorLocalPos = localPos;
  591. }
  592. }
  593. }
  594. mMouseOverElement = topMostElement;
  595. mMouseOverWidget = widgetInFocus;
  596. event.markAsUsed();
  597. }
  598. void GUIManager::onTextInput(const CM::TextInputEvent& event)
  599. {
  600. if(mKeyboardFocusElement != nullptr)
  601. {
  602. mKeyEvent = GUIButtonEvent();
  603. mKeyEvent.setTextInputData(event.textChar);
  604. mKeyboardFocusWidget->_keyEvent(mKeyboardFocusElement, mKeyEvent);
  605. }
  606. }
  607. void GUIManager::onWindowFocusGained(RenderWindow& win)
  608. {
  609. for(auto& widget : mWidgets)
  610. {
  611. if(widget->getOwnerWindow() == &win)
  612. widget->ownerWindowFocusChanged();
  613. }
  614. }
  615. void GUIManager::onWindowFocusLost(RenderWindow& win)
  616. {
  617. for(auto& widget : mWidgets)
  618. {
  619. if(widget->getOwnerWindow() == &win)
  620. widget->ownerWindowFocusChanged();
  621. }
  622. }
  623. void GUIManager::onWindowMovedOrResized(RenderWindow& win)
  624. {
  625. for(auto& widget : mWidgets)
  626. {
  627. if(widget->getOwnerWindow() == &win)
  628. widget->ownerWindowResized();
  629. }
  630. }
  631. GUIMouseButton GUIManager::buttonToMouseButton(ButtonCode code) const
  632. {
  633. if(code == BC_MOUSE_LEFT)
  634. return GUIMouseButton::Left;
  635. else if(code == BC_MOUSE_MIDDLE)
  636. return GUIMouseButton::Middle;
  637. else if(code == BC_MOUSE_RIGHT)
  638. return GUIMouseButton::Right;
  639. CM_EXCEPT(InvalidParametersException, "Provided button code is not a GUI supported mouse button.");
  640. }
  641. Int2 GUIManager::getWidgetRelativePos(const GUIWidget& widget, const Int2& screenPos) const
  642. {
  643. const RenderWindow* window = widget.getOwnerWindow();
  644. Int2 windowPos = window->screenToWindowPos(screenPos);
  645. const Matrix4& worldTfrm = widget.SO()->getWorldTfrm();
  646. Vector4 vecLocalPos = worldTfrm.inverse() * Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
  647. Int2 curLocalPos(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  648. return curLocalPos;
  649. }
  650. }