BsGUIManager.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284
  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 "CmVertexDataDesc.h"
  11. #include "CmMesh.h"
  12. #include "CmUtil.h"
  13. #include "CmRenderWindowManager.h"
  14. #include "CmPlatform.h"
  15. #include "CmRectI.h"
  16. #include "CmApplication.h"
  17. #include "CmException.h"
  18. #include "CmInput.h"
  19. #include "CmPass.h"
  20. #include "CmDebug.h"
  21. #include "CmRenderQueue.h"
  22. #include "BsGUIInputCaret.h"
  23. #include "BsGUIInputSelection.h"
  24. #include "BsGUIListBox.h"
  25. #include "BsGUIButton.h"
  26. #include "BsGUIDropDownBox.h"
  27. #include "BsGUIContextMenu.h"
  28. #include "BsDragAndDropManager.h"
  29. #include "BsGUIDropDownBoxManager.h"
  30. #include "BsGUIContextMenu.h"
  31. #include "CmProfiler.h"
  32. #include "CmMeshHeap.h"
  33. #include "CmTransientMesh.h"
  34. using namespace CamelotFramework;
  35. namespace BansheeEngine
  36. {
  37. struct GUIGroupElement
  38. {
  39. GUIGroupElement()
  40. { }
  41. GUIGroupElement(GUIElement* _element, UINT32 _renderElement)
  42. :element(_element), renderElement(_renderElement)
  43. { }
  44. GUIElement* element;
  45. UINT32 renderElement;
  46. };
  47. struct GUIMaterialGroup
  48. {
  49. GUIMaterialInfo matInfo;
  50. UINT32 numQuads;
  51. UINT32 depth;
  52. RectI bounds;
  53. Vector<GUIGroupElement>::type elements;
  54. };
  55. const UINT32 GUIManager::DRAG_DISTANCE = 3;
  56. const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_VERTS = 16384;
  57. const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_INDICES = 49152;
  58. GUIManager::GUIManager()
  59. :mElementUnderCursor(nullptr), mWidgetUnderCursor(nullptr), mSeparateMeshesByWidget(true), mActiveElement(nullptr),
  60. mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
  61. mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(1.0f, 0.6588f, 0.0f), mIsCaretOn(false),
  62. mTextSelectionColor(1.0f, 0.6588f, 0.0f), mInputCaret(nullptr), mInputSelection(nullptr), mSelectiveInputActive(false), mDragState(DragState::NoDrag)
  63. {
  64. mOnCursorMovedConn = gInput().onCursorMoved.connect(boost::bind(&GUIManager::onCursorMoved, this, _1));
  65. mOnCursorPressedConn = gInput().onCursorPressed.connect(boost::bind(&GUIManager::onCursorPressed, this, _1));
  66. mOnCursorReleasedConn = gInput().onCursorReleased.connect(boost::bind(&GUIManager::onCursorReleased, this, _1));
  67. mOnCursorDoubleClick = gInput().onDoubleClick.connect(boost::bind(&GUIManager::onCursorDoubleClick, this, _1));
  68. mOnTextInputConn = gInput().onCharInput.connect(boost::bind(&GUIManager::onTextInput, this, _1));
  69. mOnInputCommandConn = gInput().onInputCommand.connect(boost::bind(&GUIManager::onInputCommandEntered, this, _1));
  70. mWindowGainedFocusConn = RenderWindowManager::instance().onFocusGained.connect(boost::bind(&GUIManager::onWindowFocusGained, this, _1));
  71. mWindowLostFocusConn = RenderWindowManager::instance().onFocusLost.connect(boost::bind(&GUIManager::onWindowFocusLost, this, _1));
  72. mMouseLeftWindowConn = Platform::onMouseLeftWindow.connect(boost::bind(&GUIManager::onMouseLeftWindow, this, _1));
  73. mInputCaret = cm_new<GUIInputCaret, PoolAlloc>();
  74. mInputSelection = cm_new<GUIInputSelection, PoolAlloc>();
  75. DragAndDropManager::startUp(cm_new<DragAndDropManager>());
  76. mDragEndedConn = DragAndDropManager::instance().onDragEnded.connect(boost::bind(&GUIManager::onMouseDragEnded, this, _1));
  77. GUIDropDownBoxManager::startUp(cm_new<GUIDropDownBoxManager>());
  78. mVertexDesc = cm_shared_ptr<VertexDataDesc>();
  79. mVertexDesc->addVertElem(VET_FLOAT2, VES_POSITION);
  80. mVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
  81. mMeshHeap = MeshHeap::create(MESH_HEAP_INITIAL_NUM_VERTS, MESH_HEAP_INITIAL_NUM_INDICES, mVertexDesc);
  82. // Need to defer this call because I want to make sure all managers are initialized first
  83. deferredCall(std::bind(&GUIManager::updateCaretTexture, this));
  84. deferredCall(std::bind(&GUIManager::updateTextSelectionTexture, this));
  85. }
  86. GUIManager::~GUIManager()
  87. {
  88. GUIDropDownBoxManager::shutDown();
  89. DragAndDropManager::shutDown();
  90. // Make a copy of widgets, since destroying them will remove them from mWidgets and
  91. // we can't iterate over an array thats getting modified
  92. Vector<WidgetInfo>::type widgetCopy = mWidgets;
  93. for(auto& widget : widgetCopy)
  94. widget.widget->destroy();
  95. mOnCursorPressedConn.disconnect();
  96. mOnCursorReleasedConn.disconnect();
  97. mOnCursorMovedConn.disconnect();
  98. mOnCursorDoubleClick.disconnect();
  99. mOnTextInputConn.disconnect();
  100. mOnInputCommandConn.disconnect();
  101. mDragEndedConn.disconnect();
  102. mWindowGainedFocusConn.disconnect();
  103. mWindowLostFocusConn.disconnect();
  104. mMouseLeftWindowConn.disconnect();
  105. cm_delete<PoolAlloc>(mInputCaret);
  106. cm_delete<PoolAlloc>(mInputSelection);
  107. }
  108. void GUIManager::registerWidget(GUIWidget* widget)
  109. {
  110. mWidgets.push_back(WidgetInfo(widget));
  111. const Viewport* renderTarget = widget->getTarget();
  112. auto findIter = mCachedGUIData.find(renderTarget);
  113. if(findIter == end(mCachedGUIData))
  114. mCachedGUIData[renderTarget] = GUIRenderData();
  115. GUIRenderData& windowData = mCachedGUIData[renderTarget];
  116. windowData.widgets.push_back(widget);
  117. windowData.isDirty = true;
  118. }
  119. void GUIManager::unregisterWidget(GUIWidget* widget)
  120. {
  121. auto findIter = std::find_if(begin(mWidgets), end(mWidgets), [=] (const WidgetInfo& x) { return x.widget == widget; } );
  122. if(findIter != end(mWidgets))
  123. {
  124. mWidgets.erase(findIter);
  125. }
  126. if(mWidgetUnderCursor == widget)
  127. {
  128. mWidgetUnderCursor = nullptr;
  129. mElementUnderCursor = nullptr;
  130. }
  131. if(mKeyboardFocusWidget == widget)
  132. {
  133. mKeyboardFocusWidget = nullptr;
  134. mKeyboardFocusElement = nullptr;
  135. }
  136. if(mActiveWidget == widget)
  137. {
  138. mActiveWidget = nullptr;
  139. mActiveElement = nullptr;
  140. }
  141. const Viewport* renderTarget = widget->getTarget();
  142. GUIRenderData& renderData = mCachedGUIData[renderTarget];
  143. auto findIter2 = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
  144. if(findIter2 != end(renderData.widgets))
  145. renderData.widgets.erase(findIter2);
  146. if(renderData.widgets.size() == 0)
  147. {
  148. mCachedGUIData.erase(renderTarget);
  149. }
  150. else
  151. renderData.isDirty = true;
  152. }
  153. void GUIManager::update()
  154. {
  155. DragAndDropManager::instance().update();
  156. // Update layouts
  157. gProfiler().beginSample("UpdateLayout");
  158. for(auto& widgetInfo : mWidgets)
  159. {
  160. widgetInfo.widget->_updateLayout();
  161. }
  162. gProfiler().endSample("UpdateLayout");
  163. // Blink caret
  164. if(mKeyboardFocusElement != nullptr)
  165. {
  166. float curTime = gTime().getTime();
  167. if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
  168. {
  169. mCaretLastBlinkTime = curTime;
  170. mIsCaretOn = !mIsCaretOn;
  171. mCommandEvent = GUICommandEvent();
  172. mCommandEvent.setType(GUICommandEventType::Redraw);
  173. sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
  174. }
  175. }
  176. PROFILE_CALL(updateMeshes(), "UpdateMeshes");
  177. if(mElementUnderCursor != nullptr && mElementUnderCursor->_isDestroyed())
  178. {
  179. mElementUnderCursor = nullptr;
  180. mWidgetUnderCursor = nullptr;
  181. }
  182. if(mActiveElement != nullptr && mActiveElement->_isDestroyed())
  183. {
  184. mActiveElement = nullptr;
  185. mActiveWidget = nullptr;
  186. }
  187. if(mKeyboardFocusElement != nullptr && mKeyboardFocusElement->_isDestroyed())
  188. {
  189. mKeyboardFocusElement = nullptr;
  190. mKeyboardFocusWidget = nullptr;
  191. }
  192. processDestroyQueue();
  193. }
  194. void GUIManager::render(ViewportPtr& target, CM::RenderQueue& renderQueue) const
  195. {
  196. auto findIter = mCachedGUIData.find(target.get());
  197. if(findIter == mCachedGUIData.end())
  198. return;
  199. const GUIRenderData& renderData = findIter->second;
  200. // Render the meshes
  201. if(mSeparateMeshesByWidget)
  202. {
  203. // TODO - Possible optimization. I currently divide by width/height inside the shader, while it
  204. // might be more optimal to just scale the mesh as the resolution changes?
  205. float invViewportWidth = 1.0f / (target->getWidth() * 0.5f);
  206. float invViewportHeight = 1.0f / (target->getHeight() * 0.5f);
  207. UINT32 meshIdx = 0;
  208. for(auto& mesh : renderData.cachedMeshes)
  209. {
  210. GUIMaterialInfo materialInfo = renderData.cachedMaterials[meshIdx];
  211. GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
  212. if(materialInfo.material == nullptr || !materialInfo.material.isLoaded())
  213. {
  214. meshIdx++;
  215. continue;
  216. }
  217. if(mesh == nullptr)
  218. {
  219. meshIdx++;
  220. continue;
  221. }
  222. materialInfo.invViewportWidth.set(invViewportWidth);
  223. materialInfo.invViewportHeight.set(invViewportHeight);
  224. materialInfo.worldTransform.set(widget->SO()->getWorldTfrm());
  225. renderQueue.add(materialInfo.material.getInternalPtr(), mesh, 0, Vector3::ZERO);
  226. meshIdx++;
  227. }
  228. }
  229. else
  230. {
  231. // TODO: I want to avoid separating meshes by widget in the future. On DX11 and GL I can set up a shader
  232. // that accepts multiple world transforms (one for each widget). Then I can add some instance information to vertices
  233. // and render elements using multiple different transforms with a single call.
  234. // Separating meshes can then be used as a compatibility mode for DX9
  235. CM_EXCEPT(NotImplementedException, "Not implemented");
  236. }
  237. }
  238. void GUIManager::updateMeshes()
  239. {
  240. for(auto& cachedMeshData : mCachedGUIData)
  241. {
  242. GUIRenderData& renderData = cachedMeshData.second;
  243. gProfiler().beginSample("UM_A");
  244. // Check if anything is dirty. If nothing is we can skip the update
  245. bool isDirty = renderData.isDirty;
  246. renderData.isDirty = false;
  247. for(auto& widget : renderData.widgets)
  248. {
  249. if(widget->isDirty(true))
  250. {
  251. isDirty = true;
  252. }
  253. }
  254. gProfiler().endSample("UM_A");
  255. if(!isDirty)
  256. continue;
  257. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  258. auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
  259. {
  260. UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
  261. UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
  262. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  263. // requires all elements to be unique
  264. return (aDepth > bDepth) ||
  265. (aDepth == bDepth && a.element > b.element) ||
  266. (aDepth == bDepth && a.element == b.element && a.renderElement > b.renderElement);
  267. };
  268. Set<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>>::type allElements(elemComp);
  269. for(auto& widget : renderData.widgets)
  270. {
  271. const Vector<GUIElement*>::type& elements = widget->getElements();
  272. for(auto& element : elements)
  273. {
  274. if(element->_isDisabled())
  275. continue;
  276. UINT32 numRenderElems = element->getNumRenderElements();
  277. for(UINT32 i = 0; i < numRenderElems; i++)
  278. {
  279. allElements.insert(GUIGroupElement(element, i));
  280. }
  281. }
  282. }
  283. // Group the elements in such a way so that we end up with a smallest amount of
  284. // meshes, without breaking back to front rendering order
  285. UnorderedMap<UINT64, Vector<GUIMaterialGroup>::type>::type materialGroups;
  286. for(auto& elem : allElements)
  287. {
  288. GUIElement* guiElem = elem.element;
  289. UINT32 renderElemIdx = elem.renderElement;
  290. UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
  291. RectI tfrmedBounds = guiElem->_getClippedBounds();
  292. tfrmedBounds.transform(guiElem->_getParentWidget().SO()->getWorldTfrm());
  293. const GUIMaterialInfo& matInfo = guiElem->getMaterial(renderElemIdx);
  294. UINT64 materialId = matInfo.material->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
  295. // this system won't detect it. Find a better way of determining material similarity?
  296. // If this is a new material, add a new list of groups
  297. auto findIterMaterial = materialGroups.find(materialId);
  298. if(findIterMaterial == end(materialGroups))
  299. materialGroups[materialId] = Vector<GUIMaterialGroup>::type();
  300. // Try to find a group this material will fit in:
  301. // - Group that has a depth value same or one below elements depth will always be a match
  302. // - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
  303. // overlap the current elements bounds.
  304. Vector<GUIMaterialGroup>::type& allGroups = materialGroups[materialId];
  305. GUIMaterialGroup* foundGroup = nullptr;
  306. for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
  307. {
  308. // If we separate meshes by widget, ignore any groups with widget parents other than mine
  309. if(mSeparateMeshesByWidget)
  310. {
  311. if(groupIter->elements.size() > 0)
  312. {
  313. GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
  314. if(&otherElem->_getParentWidget() != &guiElem->_getParentWidget())
  315. continue;
  316. }
  317. }
  318. GUIMaterialGroup& group = *groupIter;
  319. if(group.depth == elemDepth || group.depth == (elemDepth - 1))
  320. {
  321. foundGroup = &group;
  322. break;
  323. }
  324. else
  325. {
  326. UINT32 startDepth = elemDepth;
  327. UINT32 endDepth = group.depth;
  328. bool foundOverlap = false;
  329. for(auto& material : materialGroups)
  330. {
  331. for(auto& group : material.second)
  332. {
  333. if(group.depth > startDepth && group.depth < endDepth)
  334. {
  335. if(group.bounds.overlaps(tfrmedBounds))
  336. {
  337. foundOverlap = true;
  338. break;
  339. }
  340. }
  341. }
  342. }
  343. if(!foundOverlap)
  344. {
  345. foundGroup = &group;
  346. break;
  347. }
  348. }
  349. }
  350. if(foundGroup == nullptr)
  351. {
  352. allGroups.push_back(GUIMaterialGroup());
  353. foundGroup = &allGroups[allGroups.size() - 1];
  354. foundGroup->depth = elemDepth;
  355. foundGroup->bounds = tfrmedBounds;
  356. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  357. foundGroup->matInfo = matInfo;
  358. foundGroup->numQuads = guiElem->getNumQuads(renderElemIdx);
  359. }
  360. else
  361. {
  362. foundGroup->bounds.encapsulate(tfrmedBounds);
  363. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  364. foundGroup->depth = std::min(foundGroup->depth, elemDepth);
  365. foundGroup->numQuads += guiElem->getNumQuads(renderElemIdx);
  366. }
  367. }
  368. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  369. auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
  370. {
  371. return (a->depth > b->depth) || (a->depth == b->depth && a > b);
  372. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  373. // requires all elements to be unique
  374. };
  375. Set<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>>::type sortedGroups(groupComp);
  376. for(auto& material : materialGroups)
  377. {
  378. for(auto& group : material.second)
  379. {
  380. sortedGroups.insert(&group);
  381. }
  382. }
  383. UINT32 numMeshes = (UINT32)sortedGroups.size();
  384. UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
  385. if(numMeshes < oldNumMeshes)
  386. {
  387. renderData.cachedMeshes.resize(numMeshes);
  388. }
  389. renderData.cachedMaterials.resize(numMeshes);
  390. if(mSeparateMeshesByWidget)
  391. renderData.cachedWidgetsPerMesh.resize(numMeshes);
  392. // Fill buffers for each group and update their meshes
  393. UINT32 groupIdx = 0;
  394. for(auto& group : sortedGroups)
  395. {
  396. renderData.cachedMaterials[groupIdx] = group->matInfo;
  397. if(mSeparateMeshesByWidget)
  398. {
  399. if(group->elements.size() == 0)
  400. renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
  401. else
  402. {
  403. GUIElement* elem = group->elements.begin()->element;
  404. renderData.cachedWidgetsPerMesh[groupIdx] = &elem->_getParentWidget();
  405. }
  406. }
  407. MeshDataPtr meshData = cm_shared_ptr<MeshData, PoolAlloc>(group->numQuads * 4, group->numQuads * 6, mVertexDesc);
  408. UINT8* vertices = meshData->getElementData(VES_POSITION);
  409. UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
  410. UINT32* indices = meshData->getIndices32();
  411. UINT32 vertexStride = meshData->getVertexDesc()->getVertexStride();
  412. UINT32 indexStride = meshData->getIndexElementSize();
  413. UINT32 quadOffset = 0;
  414. for(auto& matElement : group->elements)
  415. {
  416. matElement.element->fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
  417. UINT32 numQuads = matElement.element->getNumQuads(matElement.renderElement);
  418. UINT32 indexStart = quadOffset * 6;
  419. UINT32 indexEnd = indexStart + numQuads * 6;
  420. UINT32 vertOffset = quadOffset * 4;
  421. for(UINT32 i = indexStart; i < indexEnd; i++)
  422. indices[i] += vertOffset;
  423. quadOffset += numQuads;
  424. }
  425. if(groupIdx < (UINT32)renderData.cachedMeshes.size())
  426. {
  427. mMeshHeap->dealloc(renderData.cachedMeshes[groupIdx]);
  428. renderData.cachedMeshes[groupIdx] = mMeshHeap->alloc(meshData);
  429. }
  430. else
  431. {
  432. renderData.cachedMeshes.push_back(mMeshHeap->alloc(meshData));
  433. }
  434. groupIdx++;
  435. }
  436. }
  437. }
  438. void GUIManager::updateCaretTexture()
  439. {
  440. if(mCaretTexture == nullptr)
  441. {
  442. HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
  443. newTex->synchonize(); // TODO - Required due to a bug in allocateSubresourceBuffer
  444. mCaretTexture = SpriteTexture::create(newTex);
  445. }
  446. const HTexture& tex = mCaretTexture->getTexture();
  447. UINT32 subresourceIdx = tex->mapToSubresourceIdx(0, 0);
  448. PixelDataPtr data = tex->allocateSubresourceBuffer(subresourceIdx);
  449. data->setColorAt(mCaretColor, 0, 0);
  450. gMainCA().writeSubresource(tex.getInternalPtr(), tex->mapToSubresourceIdx(0, 0), data);
  451. }
  452. void GUIManager::updateTextSelectionTexture()
  453. {
  454. if(mTextSelectionTexture == nullptr)
  455. {
  456. HTexture newTex = Texture::create(TEX_TYPE_2D, 1, 1, 0, PF_R8G8B8A8);
  457. newTex->synchonize(); // TODO - Required due to a bug in allocateSubresourceBuffer
  458. mTextSelectionTexture = SpriteTexture::create(newTex);
  459. }
  460. const HTexture& tex = mTextSelectionTexture->getTexture();
  461. UINT32 subresourceIdx = tex->mapToSubresourceIdx(0, 0);
  462. PixelDataPtr data = tex->allocateSubresourceBuffer(subresourceIdx);
  463. data->setColorAt(mTextSelectionColor, 0, 0);
  464. gMainCA().writeSubresource(tex.getInternalPtr(), tex->mapToSubresourceIdx(0, 0), data);
  465. }
  466. bool GUIManager::onMouseDragEnded(const CM::PositionalInputEvent& event)
  467. {
  468. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  469. if(DragAndDropManager::instance().isDragInProgress() && guiButton == GUIMouseButton::Left)
  470. {
  471. if(mElementUnderCursor != nullptr)
  472. {
  473. Vector2I localPos;
  474. if(mWidgetUnderCursor != nullptr)
  475. localPos = getWidgetRelativePos(*mWidgetUnderCursor, event.screenPos);
  476. mMouseEvent.setDragAndDropDroppedData(mElementUnderCursor, localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  477. bool processed = sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent);
  478. return processed;
  479. }
  480. }
  481. return false;
  482. }
  483. void GUIManager::onCursorMoved(const PositionalInputEvent& event)
  484. {
  485. if(event.isUsed())
  486. return;
  487. bool buttonStates[(int)GUIMouseButton::Count];
  488. buttonStates[0] = event.buttonStates[0];
  489. buttonStates[1] = event.buttonStates[1];
  490. buttonStates[2] = event.buttonStates[2];
  491. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  492. event.markAsUsed();
  493. Vector2I localPos;
  494. if(mWidgetUnderCursor != nullptr)
  495. localPos = getWidgetRelativePos(*mWidgetUnderCursor, event.screenPos);
  496. if(mActiveElement != nullptr && mDragState == DragState::HeldWithoutDrag)
  497. {
  498. UINT32 dist = mLastCursorClickPos.manhattanDist(event.screenPos);
  499. if(dist > DRAG_DISTANCE)
  500. {
  501. mMouseEvent.setMouseDragStartData(mElementUnderCursor, localPos);
  502. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  503. event.markAsUsed();
  504. mDragState = DragState::Dragging;
  505. }
  506. }
  507. // If mouse is being held down send MouseDrag events
  508. if(mActiveElement != nullptr && mDragState == DragState::Dragging)
  509. {
  510. Vector2I curLocalPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
  511. if(mLastCursorLocalPos != curLocalPos)
  512. {
  513. mMouseEvent.setMouseDragData(mElementUnderCursor, curLocalPos, curLocalPos - mLastCursorLocalPos);
  514. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  515. event.markAsUsed();
  516. mLastCursorLocalPos = curLocalPos;
  517. }
  518. }
  519. else // Otherwise, send MouseMove events if we are hovering over any element
  520. {
  521. if(mElementUnderCursor != nullptr)
  522. {
  523. // Send MouseMove event
  524. if(mLastCursorLocalPos != localPos)
  525. {
  526. mMouseEvent.setMouseMoveData(mElementUnderCursor, localPos);
  527. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  528. event.markAsUsed();
  529. mLastCursorLocalPos = localPos;
  530. }
  531. // Also if drag is in progress send DragAndDrop events
  532. if(DragAndDropManager::instance().isDragInProgress())
  533. {
  534. mMouseEvent.setDragAndDropDraggedData(mElementUnderCursor, localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  535. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  536. event.markAsUsed();
  537. }
  538. if(Math::abs(event.mouseWheelScrollAmount) > 0.00001f)
  539. {
  540. mMouseEvent.setMouseWheelScrollData(mElementUnderCursor, event.mouseWheelScrollAmount);
  541. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  542. event.markAsUsed();
  543. }
  544. }
  545. }
  546. }
  547. void GUIManager::onCursorReleased(const CM::PositionalInputEvent& event)
  548. {
  549. if(event.isUsed())
  550. return;
  551. bool buttonStates[(int)GUIMouseButton::Count];
  552. buttonStates[0] = event.buttonStates[0];
  553. buttonStates[1] = event.buttonStates[1];
  554. buttonStates[2] = event.buttonStates[2];
  555. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  556. event.markAsUsed();
  557. mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
  558. Vector2I localPos;
  559. if(mWidgetUnderCursor != nullptr)
  560. {
  561. localPos = getWidgetRelativePos(*mWidgetUnderCursor, event.screenPos);
  562. }
  563. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  564. // Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
  565. // And only activate when a button that originally caused the active state is released, otherwise ignore it.
  566. bool acceptMouseUp = mActiveMouseButton == guiButton && (mElementUnderCursor != nullptr && mActiveElement == mElementUnderCursor);
  567. if(acceptMouseUp)
  568. {
  569. mMouseEvent.setMouseUpData(mElementUnderCursor, localPos, guiButton);
  570. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  571. event.markAsUsed();
  572. }
  573. // Send DragEnd event to whichever element is active
  574. bool acceptEndDrag = mDragState == DragState::Dragging && mActiveMouseButton == guiButton &&
  575. mActiveElement != nullptr && (guiButton == GUIMouseButton::Left);
  576. if(acceptEndDrag)
  577. {
  578. mMouseEvent.setMouseDragEndData(mElementUnderCursor, localPos);
  579. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  580. event.markAsUsed();
  581. mDragState = DragState::NoDrag;
  582. }
  583. if(mActiveMouseButton == guiButton)
  584. {
  585. mActiveElement = nullptr;
  586. mActiveWidget = nullptr;
  587. mActiveMouseButton = GUIMouseButton::Left;
  588. }
  589. }
  590. void GUIManager::onCursorPressed(const CM::PositionalInputEvent& event)
  591. {
  592. if(event.isUsed())
  593. return;
  594. bool buttonStates[(int)GUIMouseButton::Count];
  595. buttonStates[0] = event.buttonStates[0];
  596. buttonStates[1] = event.buttonStates[1];
  597. buttonStates[2] = event.buttonStates[2];
  598. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  599. event.markAsUsed();
  600. mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
  601. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  602. // Send out selective input callback when user clicks on non-selectable element
  603. if(mSelectiveInputActive && mElementUnderCursor == nullptr)
  604. {
  605. if(mOnOutsideClickCallback != nullptr)
  606. mOnOutsideClickCallback();
  607. }
  608. // We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
  609. bool acceptMouseDown = mActiveElement == nullptr && mElementUnderCursor != nullptr;
  610. if(acceptMouseDown)
  611. {
  612. Vector2I localPos = getWidgetRelativePos(*mWidgetUnderCursor, event.screenPos);
  613. mMouseEvent.setMouseDownData(mElementUnderCursor, localPos, guiButton);
  614. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  615. event.markAsUsed();
  616. if(guiButton == GUIMouseButton::Left)
  617. {
  618. mDragState = DragState::HeldWithoutDrag;
  619. mLastCursorClickPos = event.screenPos;
  620. }
  621. mActiveElement = mElementUnderCursor;
  622. mActiveWidget = mWidgetUnderCursor;
  623. mActiveMouseButton = guiButton;
  624. }
  625. if(mElementUnderCursor != nullptr && mElementUnderCursor->_acceptsKeyboardFocus())
  626. {
  627. if(mKeyboardFocusElement != nullptr && mElementUnderCursor != mKeyboardFocusElement)
  628. mKeyboardFocusElement->_setFocus(false);
  629. mElementUnderCursor->_setFocus(true);
  630. mKeyboardFocusElement = mElementUnderCursor;
  631. mKeyboardFocusWidget = mWidgetUnderCursor;
  632. event.markAsUsed();
  633. }
  634. // If right click try to open context menu
  635. if(mElementUnderCursor != nullptr && buttonStates[2] == true)
  636. {
  637. GUIContextMenu* menu = mElementUnderCursor->getContextMenu();
  638. if(menu != nullptr)
  639. {
  640. const RenderWindow* window = getWidgetWindow(*mWidgetUnderCursor);
  641. Vector2I windowPos = window->screenToWindowPos(event.screenPos);
  642. menu->open(windowPos, *mWidgetUnderCursor);
  643. event.markAsUsed();
  644. }
  645. }
  646. }
  647. void GUIManager::onCursorDoubleClick(const CM::PositionalInputEvent& event)
  648. {
  649. if(event.isUsed())
  650. return;
  651. bool buttonStates[(int)GUIMouseButton::Count];
  652. buttonStates[0] = event.buttonStates[0];
  653. buttonStates[1] = event.buttonStates[1];
  654. buttonStates[2] = event.buttonStates[2];
  655. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  656. event.markAsUsed();
  657. mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
  658. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  659. // We only check for mouse down if we are hovering over an element
  660. bool acceptMouseDown = mElementUnderCursor != nullptr;
  661. if(acceptMouseDown)
  662. {
  663. Vector2I localPos = getWidgetRelativePos(*mWidgetUnderCursor, event.screenPos);
  664. mMouseEvent.setMouseDoubleClickData(mElementUnderCursor, localPos, guiButton);
  665. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  666. event.markAsUsed();
  667. }
  668. }
  669. void GUIManager::onInputCommandEntered(CM::InputCommandType commandType)
  670. {
  671. if(mKeyboardFocusElement == nullptr)
  672. return;
  673. mCommandEvent = GUICommandEvent();
  674. switch(commandType)
  675. {
  676. case InputCommandType::Backspace:
  677. mCommandEvent.setType(GUICommandEventType::Backspace);
  678. break;
  679. case InputCommandType::Delete:
  680. mCommandEvent.setType(GUICommandEventType::Delete);
  681. break;
  682. case InputCommandType::Copy:
  683. mCommandEvent.setType(GUICommandEventType::Copy);
  684. break;
  685. case InputCommandType::Cut:
  686. mCommandEvent.setType(GUICommandEventType::Cut);
  687. break;
  688. case InputCommandType::Paste:
  689. mCommandEvent.setType(GUICommandEventType::Paste);
  690. break;
  691. case InputCommandType::Undo:
  692. mCommandEvent.setType(GUICommandEventType::Undo);
  693. break;
  694. case InputCommandType::Redo:
  695. mCommandEvent.setType(GUICommandEventType::Redo);
  696. break;
  697. case InputCommandType::Return:
  698. mCommandEvent.setType(GUICommandEventType::Return);
  699. break;
  700. case InputCommandType::Escape:
  701. mCommandEvent.setType(GUICommandEventType::Escape);
  702. break;
  703. case InputCommandType::Tab:
  704. mCommandEvent.setType(GUICommandEventType::Tab);
  705. break;
  706. case InputCommandType::SelectAll:
  707. mCommandEvent.setType(GUICommandEventType::SelectAll);
  708. break;
  709. case InputCommandType::CursorMoveLeft:
  710. mCommandEvent.setType(GUICommandEventType::CursorMoveLeft);
  711. break;
  712. case InputCommandType::CursorMoveRight:
  713. mCommandEvent.setType(GUICommandEventType::CursorMoveRight);
  714. break;
  715. case InputCommandType::CursorMoveUp:
  716. mCommandEvent.setType(GUICommandEventType::CursorMoveUp);
  717. break;
  718. case InputCommandType::CursorMoveDown:
  719. mCommandEvent.setType(GUICommandEventType::CursorMoveDown);
  720. break;
  721. case InputCommandType::SelectLeft:
  722. mCommandEvent.setType(GUICommandEventType::SelectLeft);
  723. break;
  724. case InputCommandType::SelectRight:
  725. mCommandEvent.setType(GUICommandEventType::SelectRight);
  726. break;
  727. case InputCommandType::SelectUp:
  728. mCommandEvent.setType(GUICommandEventType::SelectUp);
  729. break;
  730. case InputCommandType::SelectDown:
  731. mCommandEvent.setType(GUICommandEventType::SelectDown);
  732. break;
  733. }
  734. sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
  735. }
  736. bool GUIManager::findElementUnderCursor(const CM::Vector2I& cursorScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
  737. {
  738. Vector<const RenderWindow*>::type widgetWindows;
  739. for(auto& widgetInfo : mWidgets)
  740. widgetWindows.push_back(getWidgetWindow(*widgetInfo.widget));
  741. #if CM_DEBUG_MODE
  742. // Checks if all referenced windows actually exist
  743. Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
  744. for(auto& window : widgetWindows)
  745. {
  746. if(window == nullptr)
  747. continue;
  748. auto iterFind = std::find(begin(activeWindows), end(activeWindows), window);
  749. if(iterFind == activeWindows.end())
  750. {
  751. CM_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
  752. Please detach all GUIWidgets from windows before destroying a window.");
  753. }
  754. }
  755. #endif
  756. GUIWidget* cursorOverWidget = nullptr;
  757. GUIElement* cursorOverElement = nullptr;
  758. const RenderWindow* windowUnderCursor = nullptr;
  759. UnorderedSet<const RenderWindow*>::type uniqueWindows;
  760. for(auto& window : widgetWindows)
  761. {
  762. if(window == nullptr)
  763. continue;
  764. uniqueWindows.insert(window);
  765. }
  766. for(auto& window : uniqueWindows)
  767. {
  768. if(Platform::isPointOverWindow(*window, cursorScreenPos))
  769. {
  770. windowUnderCursor = window;
  771. break;
  772. }
  773. }
  774. if(windowUnderCursor != nullptr)
  775. {
  776. Vector2I windowPos = windowUnderCursor->screenToWindowPos(cursorScreenPos);
  777. Vector4 vecWindowPos((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
  778. UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
  779. UINT32 widgetIdx = 0;
  780. for(auto& widgetInfo : mWidgets)
  781. {
  782. if(widgetWindows[widgetIdx] == nullptr)
  783. {
  784. widgetIdx++;
  785. continue;
  786. }
  787. GUIWidget* widget = widgetInfo.widget;
  788. if(widgetWindows[widgetIdx] == windowUnderCursor && widget->inBounds(windowToBridgedCoords(*widget, windowPos)))
  789. {
  790. SelectiveInputData* selectiveInputData = nullptr;
  791. if(mSelectiveInputActive)
  792. {
  793. auto selectionIterFind = mSelectiveInputData.find(widget);
  794. if(selectionIterFind == mSelectiveInputData.end())
  795. {
  796. widgetIdx++;
  797. continue;
  798. }
  799. else
  800. selectiveInputData = &selectionIterFind->second;
  801. }
  802. Vector2I localPos = getWidgetRelativePos(*widget, cursorScreenPos);
  803. Vector<GUIElement*>::type sortedElements = widget->getElements();
  804. std::sort(sortedElements.begin(), sortedElements.end(),
  805. [](GUIElement* a, GUIElement* b)
  806. {
  807. return a->_getDepth() < b->_getDepth();
  808. });
  809. // Elements with lowest depth (most to the front) get handled first
  810. for(auto iter = sortedElements.begin(); iter != sortedElements.end(); ++iter)
  811. {
  812. GUIElement* element = *iter;
  813. if(!element->_isDisabled() && element->_isInBounds(localPos) && element->_getDepth() < topMostDepth)
  814. {
  815. if(mSelectiveInputActive && !selectiveInputData->acceptAllElements)
  816. {
  817. if(selectiveInputData->elements.find(element) == selectiveInputData->elements.end())
  818. continue;
  819. }
  820. cursorOverElement = element;
  821. topMostDepth = element->_getDepth();
  822. cursorOverWidget = widget;
  823. break;
  824. }
  825. }
  826. }
  827. widgetIdx++;
  828. }
  829. }
  830. return handleCursorOver(cursorOverWidget, cursorOverElement, cursorScreenPos, buttonStates, shift, control, alt);
  831. }
  832. bool GUIManager::handleCursorOver(GUIWidget* widget, GUIElement* element, const CM::Vector2I& screenPos,
  833. bool buttonStates[3], bool shift, bool control, bool alt)
  834. {
  835. bool eventProcessed = false;
  836. Vector2I localPos;
  837. if(widget != nullptr)
  838. localPos = getWidgetRelativePos(*widget, screenPos);
  839. mMouseEvent = GUIMouseEvent(buttonStates, shift, control, alt);
  840. // Send MouseOver/MouseOut events to any elements the mouse passes over, except when
  841. // mouse is being held down, in which we only send them to the active element
  842. if(element != mElementUnderCursor)
  843. {
  844. if(mElementUnderCursor != nullptr)
  845. {
  846. // Send MouseOut event
  847. if(mActiveElement == nullptr || mElementUnderCursor == mActiveElement)
  848. {
  849. Vector2I curLocalPos = getWidgetRelativePos(*mWidgetUnderCursor, screenPos);
  850. mMouseEvent.setMouseOutData(element, curLocalPos);
  851. if(sendMouseEvent(mWidgetUnderCursor, mElementUnderCursor, mMouseEvent))
  852. eventProcessed = true;
  853. }
  854. }
  855. if(element != nullptr)
  856. {
  857. // Send MouseOver event
  858. if(mActiveElement == nullptr || element == mActiveElement)
  859. {
  860. mMouseEvent.setMouseOverData(element, localPos);
  861. if(sendMouseEvent(widget, element, mMouseEvent))
  862. eventProcessed = true;
  863. }
  864. }
  865. }
  866. mElementUnderCursor = element;
  867. mWidgetUnderCursor = widget;
  868. return eventProcessed;
  869. }
  870. void GUIManager::onTextInput(const CM::TextInputEvent& event)
  871. {
  872. if(mKeyboardFocusElement != nullptr)
  873. {
  874. mTextInputEvent = GUITextInputEvent();
  875. mTextInputEvent.setData(event.textChar);
  876. if(sendTextInputEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mTextInputEvent))
  877. event.markAsUsed();
  878. }
  879. }
  880. void GUIManager::onWindowFocusGained(RenderWindow& win)
  881. {
  882. for(auto& widgetInfo : mWidgets)
  883. {
  884. GUIWidget* widget = widgetInfo.widget;
  885. if(getWidgetWindow(*widget) == &win)
  886. widget->ownerWindowFocusChanged();
  887. }
  888. }
  889. void GUIManager::onWindowFocusLost(RenderWindow& win)
  890. {
  891. for(auto& widgetInfo : mWidgets)
  892. {
  893. GUIWidget* widget = widgetInfo.widget;
  894. if(getWidgetWindow(*widget) == &win)
  895. widget->ownerWindowFocusChanged();
  896. }
  897. }
  898. // We stop getting mouse move events once it leaves the window, so make sure
  899. // nothing stays in hover state
  900. void GUIManager::onMouseLeftWindow(CM::RenderWindow* win)
  901. {
  902. bool buttonStates[3];
  903. buttonStates[0] = false;
  904. buttonStates[1] = false;
  905. buttonStates[2] = false;
  906. handleCursorOver(nullptr, nullptr, Vector2I(), buttonStates, false, false, false);
  907. }
  908. void GUIManager::queueForDestroy(GUIElement* element)
  909. {
  910. mScheduledForDestruction.push(element);
  911. }
  912. void GUIManager::processDestroyQueue()
  913. {
  914. // Need two loops and a temporary since element destructors may themselves
  915. // queue other elements for destruction
  916. while(!mScheduledForDestruction.empty())
  917. {
  918. CM::Stack<GUIElement*>::type toDestroy = mScheduledForDestruction;
  919. mScheduledForDestruction = CM::Stack<GUIElement*>::type();
  920. while(!toDestroy.empty())
  921. {
  922. cm_delete<PoolAlloc>(toDestroy.top());
  923. toDestroy.pop();
  924. }
  925. }
  926. }
  927. // HACK - This callback is very hackish and very specific. Attempt to replace it with a more
  928. // general purpose solution
  929. void GUIManager::enableSelectiveInput(std::function<void()> onOutsideClickCallback)
  930. {
  931. mSelectiveInputActive = true;
  932. mOnOutsideClickCallback = onOutsideClickCallback;
  933. }
  934. void GUIManager::disableSelectiveInput()
  935. {
  936. mSelectiveInputData.clear();
  937. mSelectiveInputActive = false;
  938. mOnOutsideClickCallback = nullptr;
  939. }
  940. void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)
  941. {
  942. auto findIter = mSelectiveInputData.find(widget);
  943. if(findIter == mSelectiveInputData.end())
  944. {
  945. SelectiveInputData& data = mSelectiveInputData[widget];
  946. data.acceptAllElements = true;
  947. }
  948. else
  949. {
  950. mSelectiveInputData[widget].acceptAllElements = true;
  951. }
  952. }
  953. void GUIManager::addSelectiveInputElement(const GUIElement* element)
  954. {
  955. mSelectiveInputData[&element->_getParentWidget()].elements.insert(element);
  956. }
  957. void GUIManager::setInputBridge(const CM::RenderTexture* renderTex, const GUIElement* element)
  958. {
  959. if(element == nullptr)
  960. mInputBridge.erase(renderTex);
  961. else
  962. mInputBridge[renderTex] = element;
  963. }
  964. GUIMouseButton GUIManager::buttonToGUIButton(PositionalInputEventButton cursorButton) const
  965. {
  966. if(cursorButton == PositionalInputEventButton::Left)
  967. return GUIMouseButton::Left;
  968. else if(cursorButton == PositionalInputEventButton::Middle)
  969. return GUIMouseButton::Middle;
  970. else if(cursorButton == PositionalInputEventButton::Right)
  971. return GUIMouseButton::Right;
  972. CM_EXCEPT(InvalidParametersException, "Provided button is not a GUI supported mouse button.");
  973. }
  974. Vector2I GUIManager::getWidgetRelativePos(const GUIWidget& widget, const Vector2I& screenPos) const
  975. {
  976. const RenderWindow* window = getWidgetWindow(widget);
  977. Vector2I windowPos = window->screenToWindowPos(screenPos);
  978. windowPos = windowToBridgedCoords(widget, windowPos);
  979. const Matrix4& worldTfrm = widget.SO()->getWorldTfrm();
  980. Vector4 vecLocalPos = worldTfrm.inverse().multiply3x4(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
  981. Vector2I curLocalPos(Math::roundToInt(vecLocalPos.x), Math::roundToInt(vecLocalPos.y));
  982. return curLocalPos;
  983. }
  984. Vector2I GUIManager::windowToBridgedCoords(const GUIWidget& widget, const Vector2I& windowPos) const
  985. {
  986. // This cast might not be valid (the render target could be a window), but we only really need to cast
  987. // so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
  988. // (in which case we know it is a RenderTexture)
  989. const RenderTexture* renderTexture = static_cast<const RenderTexture*>(widget.getTarget()->getTarget().get());
  990. auto iterFind = mInputBridge.find(renderTexture);
  991. if(iterFind != mInputBridge.end()) // Widget input is bridged, which means we need to transform the coordinates
  992. {
  993. const GUIElement* bridgeElement = iterFind->second;
  994. const Matrix4& worldTfrm = bridgeElement->_getParentWidget().SO()->getWorldTfrm();
  995. Vector4 vecLocalPos = worldTfrm.inverse().multiply3x4(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
  996. RectI bridgeBounds = bridgeElement->getBounds();
  997. // Find coordinates relative to the bridge element
  998. float x = vecLocalPos.x - (float)bridgeBounds.x;
  999. float y = vecLocalPos.y - (float)bridgeBounds.y;
  1000. float scaleX = renderTexture->getWidth() / (float)bridgeBounds.width;
  1001. float scaleY = renderTexture->getHeight() / (float)bridgeBounds.height;
  1002. return Vector2I(Math::roundToInt(x * scaleX), Math::roundToInt(y * scaleY));
  1003. }
  1004. return windowPos;
  1005. }
  1006. const CM::RenderWindow* GUIManager::getWidgetWindow(const GUIWidget& widget) const
  1007. {
  1008. // This cast might not be valid (the render target could be a window), but we only really need to cast
  1009. // so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
  1010. // (in which case we know it is a RenderTexture)
  1011. const RenderTexture* renderTexture = static_cast<const RenderTexture*>(widget.getTarget()->getTarget().get());
  1012. auto iterFind = mInputBridge.find(renderTexture);
  1013. if(iterFind != mInputBridge.end())
  1014. {
  1015. GUIWidget& parentWidget = iterFind->second->_getParentWidget();
  1016. if(&parentWidget != &widget)
  1017. {
  1018. return getWidgetWindow(parentWidget);
  1019. }
  1020. }
  1021. RenderTargetPtr renderTarget = widget.getTarget()->getTarget();
  1022. Vector<RenderWindow*>::type renderWindows = RenderWindowManager::instance().getRenderWindows();
  1023. auto iterFindWin = std::find(renderWindows.begin(), renderWindows.end(), renderTarget.get());
  1024. if(iterFindWin != renderWindows.end())
  1025. return static_cast<RenderWindow*>(renderTarget.get());
  1026. return nullptr;
  1027. }
  1028. bool GUIManager::sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event)
  1029. {
  1030. if(!mouseEventFilter.empty())
  1031. {
  1032. if(mouseEventFilter(widget, element, event)) // TODO - If multiple filters and processed at once I should only call the first one
  1033. return true;
  1034. }
  1035. return widget->_mouseEvent(element, event);
  1036. }
  1037. bool GUIManager::sendTextInputEvent(GUIWidget* widget, GUIElement* element, const GUITextInputEvent& event)
  1038. {
  1039. if(!textInputEventFilter.empty())
  1040. textInputEventFilter(widget, element, event);
  1041. return widget->_textInputEvent(element, event);
  1042. }
  1043. bool GUIManager::sendCommandEvent(GUIWidget* widget, GUIElement* element, const GUICommandEvent& event)
  1044. {
  1045. return widget->_commandEvent(element, event);
  1046. }
  1047. GUIManager& gGUIManager()
  1048. {
  1049. return GUIManager::instance();
  1050. }
  1051. }