BsGUIManager.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  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. :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. auto findIter2 = std::find_if(begin(mElementsUnderCursor), end(mElementsUnderCursor), [=] (const ElementInfo& x) { return x.widget == widget; } );
  127. if(findIter2 != mElementsUnderCursor.end())
  128. mElementsUnderCursor.erase(findIter2);
  129. if(mKeyboardFocusWidget == widget)
  130. {
  131. mKeyboardFocusWidget = nullptr;
  132. mKeyboardFocusElement = nullptr;
  133. }
  134. if(mActiveWidget == widget)
  135. {
  136. mActiveWidget = nullptr;
  137. mActiveElement = nullptr;
  138. }
  139. const Viewport* renderTarget = widget->getTarget();
  140. GUIRenderData& renderData = mCachedGUIData[renderTarget];
  141. auto findIter3 = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
  142. if(findIter3 != end(renderData.widgets))
  143. renderData.widgets.erase(findIter3);
  144. if(renderData.widgets.size() == 0)
  145. {
  146. mCachedGUIData.erase(renderTarget);
  147. }
  148. else
  149. renderData.isDirty = true;
  150. }
  151. void GUIManager::update()
  152. {
  153. DragAndDropManager::instance().update();
  154. // Update layouts
  155. gProfiler().beginSample("UpdateLayout");
  156. for(auto& widgetInfo : mWidgets)
  157. {
  158. widgetInfo.widget->_updateLayout();
  159. }
  160. gProfiler().endSample("UpdateLayout");
  161. // Blink caret
  162. if(mKeyboardFocusElement != nullptr)
  163. {
  164. float curTime = gTime().getTime();
  165. if((curTime - mCaretLastBlinkTime) >= mCaretBlinkInterval)
  166. {
  167. mCaretLastBlinkTime = curTime;
  168. mIsCaretOn = !mIsCaretOn;
  169. mCommandEvent = GUICommandEvent();
  170. mCommandEvent.setType(GUICommandEventType::Redraw);
  171. sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
  172. }
  173. }
  174. PROFILE_CALL(updateMeshes(), "UpdateMeshes");
  175. mNewElementsUnderCursor.clear();
  176. for(auto& elementInfo : mElementsUnderCursor)
  177. {
  178. if(!elementInfo.element->_isDestroyed())
  179. mNewElementsUnderCursor.push_back(elementInfo);
  180. }
  181. mElementsUnderCursor.swap(mNewElementsUnderCursor);
  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. for(auto& elementInfo : mElementsUnderCursor)
  472. {
  473. Vector2I localPos;
  474. if(elementInfo.widget != nullptr)
  475. localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
  476. mMouseEvent.setDragAndDropDroppedData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  477. bool processed = sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent);
  478. if(processed)
  479. return true;
  480. }
  481. }
  482. return false;
  483. }
  484. void GUIManager::onCursorMoved(const PositionalInputEvent& event)
  485. {
  486. if(event.isUsed())
  487. return;
  488. bool buttonStates[(int)GUIMouseButton::Count];
  489. buttonStates[0] = event.buttonStates[0];
  490. buttonStates[1] = event.buttonStates[1];
  491. buttonStates[2] = event.buttonStates[2];
  492. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  493. event.markAsUsed();
  494. if(mActiveElement != nullptr && mDragState == DragState::HeldWithoutDrag)
  495. {
  496. UINT32 dist = mLastCursorClickPos.manhattanDist(event.screenPos);
  497. if(dist > DRAG_DISTANCE)
  498. {
  499. // NOTE: I am always using the first widget to calculate the position,
  500. // which technically could be wrong if there are multiple widgets overlapping
  501. Vector2I localPos;
  502. if(mElementsUnderCursor.size() > 0)
  503. localPos = getWidgetRelativePos(*mElementsUnderCursor[0].widget, event.screenPos);
  504. mMouseEvent.setMouseDragStartData(localPos);
  505. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  506. event.markAsUsed();
  507. mDragState = DragState::Dragging;
  508. }
  509. }
  510. // If mouse is being held down send MouseDrag events
  511. if(mActiveElement != nullptr && mDragState == DragState::Dragging)
  512. {
  513. Vector2I curLocalPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
  514. if(mLastCursorLocalPos != curLocalPos)
  515. {
  516. mMouseEvent.setMouseDragData(curLocalPos, curLocalPos - mLastCursorLocalPos);
  517. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  518. event.markAsUsed();
  519. mLastCursorLocalPos = curLocalPos;
  520. }
  521. }
  522. else // Otherwise, send MouseMove events if we are hovering over any element
  523. {
  524. for(auto& elementInfo : mElementsUnderCursor)
  525. {
  526. Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
  527. // Send MouseMove event
  528. if(mLastCursorLocalPos != localPos)
  529. {
  530. mMouseEvent.setMouseMoveData(localPos);
  531. bool processed = sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent);
  532. mLastCursorLocalPos = localPos;
  533. if(processed)
  534. {
  535. event.markAsUsed();
  536. break;
  537. }
  538. }
  539. }
  540. // Also if drag is in progress send DragAndDrop events
  541. if(DragAndDropManager::instance().isDragInProgress())
  542. {
  543. for(auto& elementInfo : mElementsUnderCursor)
  544. {
  545. Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
  546. mMouseEvent.setDragAndDropDraggedData(localPos, DragAndDropManager::instance().getDragTypeId(), DragAndDropManager::instance().getDragData());
  547. if(sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent))
  548. {
  549. event.markAsUsed();
  550. break;
  551. }
  552. }
  553. }
  554. if(Math::abs(event.mouseWheelScrollAmount) > 0.00001f)
  555. {
  556. for(auto& elementInfo : mElementsUnderCursor)
  557. {
  558. mMouseEvent.setMouseWheelScrollData(event.mouseWheelScrollAmount);
  559. if(sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent))
  560. {
  561. event.markAsUsed();
  562. break;
  563. }
  564. }
  565. }
  566. }
  567. }
  568. void GUIManager::onCursorReleased(const CM::PositionalInputEvent& event)
  569. {
  570. if(event.isUsed())
  571. return;
  572. bool buttonStates[(int)GUIMouseButton::Count];
  573. buttonStates[0] = event.buttonStates[0];
  574. buttonStates[1] = event.buttonStates[1];
  575. buttonStates[2] = event.buttonStates[2];
  576. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  577. event.markAsUsed();
  578. mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
  579. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  580. // Send MouseUp event only if we are over the active element (we don't want to accidentally trigger other elements).
  581. // And only activate when a button that originally caused the active state is released, otherwise ignore it.
  582. if(mActiveMouseButton == guiButton)
  583. {
  584. for(auto& elementInfo : mElementsUnderCursor)
  585. {
  586. if(mActiveElement == elementInfo.element)
  587. {
  588. Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
  589. mMouseEvent.setMouseUpData(localPos, guiButton);
  590. if(sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent))
  591. event.markAsUsed();
  592. break;
  593. }
  594. }
  595. }
  596. // Send DragEnd event to whichever element is active
  597. bool acceptEndDrag = mDragState == DragState::Dragging && mActiveMouseButton == guiButton &&
  598. mActiveElement != nullptr && (guiButton == GUIMouseButton::Left);
  599. if(acceptEndDrag)
  600. {
  601. // NOTE: I am always using the first widget to calculate the position,
  602. // which technically could be wrong if there are multiple widgets overlapping
  603. Vector2I localPos;
  604. if(mElementsUnderCursor.size() > 0)
  605. localPos = getWidgetRelativePos(*mElementsUnderCursor[0].widget, event.screenPos);
  606. mMouseEvent.setMouseDragEndData(localPos);
  607. if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
  608. event.markAsUsed();
  609. mDragState = DragState::NoDrag;
  610. }
  611. if(mActiveMouseButton == guiButton)
  612. {
  613. mActiveElement = nullptr;
  614. mActiveWidget = nullptr;
  615. mActiveMouseButton = GUIMouseButton::Left;
  616. }
  617. }
  618. void GUIManager::onCursorPressed(const CM::PositionalInputEvent& event)
  619. {
  620. if(event.isUsed())
  621. return;
  622. bool buttonStates[(int)GUIMouseButton::Count];
  623. buttonStates[0] = event.buttonStates[0];
  624. buttonStates[1] = event.buttonStates[1];
  625. buttonStates[2] = event.buttonStates[2];
  626. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  627. event.markAsUsed();
  628. mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
  629. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  630. // Send out selective input callback when user clicks on non-selectable element
  631. if(mSelectiveInputActive && mElementsUnderCursor.size() == 0)
  632. {
  633. if(mOnOutsideClickCallback != nullptr)
  634. mOnOutsideClickCallback();
  635. }
  636. // We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
  637. if(mActiveElement == nullptr)
  638. {
  639. for(auto& elementInfo : mElementsUnderCursor)
  640. {
  641. Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
  642. mMouseEvent.setMouseDownData(localPos, guiButton);
  643. bool processed = sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent);
  644. if(guiButton == GUIMouseButton::Left)
  645. {
  646. mDragState = DragState::HeldWithoutDrag;
  647. mLastCursorClickPos = event.screenPos;
  648. }
  649. mActiveElement = elementInfo.element;
  650. mActiveWidget = elementInfo.widget;
  651. mActiveMouseButton = guiButton;
  652. if(processed)
  653. {
  654. event.markAsUsed();
  655. break;
  656. }
  657. }
  658. }
  659. for(auto& elementInfo : mElementsUnderCursor)
  660. {
  661. if(elementInfo.element->_acceptsKeyboardFocus())
  662. {
  663. if(mKeyboardFocusElement != nullptr && elementInfo.element != mKeyboardFocusElement)
  664. mKeyboardFocusElement->_setFocus(false);
  665. elementInfo.element->_setFocus(true);
  666. mKeyboardFocusElement = elementInfo.element;
  667. mKeyboardFocusWidget = elementInfo.widget;
  668. event.markAsUsed();
  669. break;
  670. }
  671. }
  672. // If right click try to open context menu
  673. if(buttonStates[2] == true)
  674. {
  675. for(auto& elementInfo : mElementsUnderCursor)
  676. {
  677. GUIContextMenu* menu = elementInfo.element->getContextMenu();
  678. if(menu != nullptr)
  679. {
  680. const RenderWindow* window = getWidgetWindow(*elementInfo.widget);
  681. Vector2I windowPos = window->screenToWindowPos(event.screenPos);
  682. menu->open(windowPos, *elementInfo.widget);
  683. event.markAsUsed();
  684. break;
  685. }
  686. }
  687. }
  688. }
  689. void GUIManager::onCursorDoubleClick(const CM::PositionalInputEvent& event)
  690. {
  691. if(event.isUsed())
  692. return;
  693. bool buttonStates[(int)GUIMouseButton::Count];
  694. buttonStates[0] = event.buttonStates[0];
  695. buttonStates[1] = event.buttonStates[1];
  696. buttonStates[2] = event.buttonStates[2];
  697. if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
  698. event.markAsUsed();
  699. mMouseEvent = GUIMouseEvent(buttonStates, event.shift, event.control, event.alt);
  700. GUIMouseButton guiButton = buttonToGUIButton(event.button);
  701. // We only check for mouse down if we are hovering over an element
  702. for(auto& elementInfo : mElementsUnderCursor)
  703. {
  704. Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
  705. mMouseEvent.setMouseDoubleClickData(localPos, guiButton);
  706. if(sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent))
  707. {
  708. event.markAsUsed();
  709. break;
  710. }
  711. }
  712. }
  713. void GUIManager::onInputCommandEntered(CM::InputCommandType commandType)
  714. {
  715. if(mKeyboardFocusElement == nullptr)
  716. return;
  717. mCommandEvent = GUICommandEvent();
  718. switch(commandType)
  719. {
  720. case InputCommandType::Backspace:
  721. mCommandEvent.setType(GUICommandEventType::Backspace);
  722. break;
  723. case InputCommandType::Delete:
  724. mCommandEvent.setType(GUICommandEventType::Delete);
  725. break;
  726. case InputCommandType::Copy:
  727. mCommandEvent.setType(GUICommandEventType::Copy);
  728. break;
  729. case InputCommandType::Cut:
  730. mCommandEvent.setType(GUICommandEventType::Cut);
  731. break;
  732. case InputCommandType::Paste:
  733. mCommandEvent.setType(GUICommandEventType::Paste);
  734. break;
  735. case InputCommandType::Undo:
  736. mCommandEvent.setType(GUICommandEventType::Undo);
  737. break;
  738. case InputCommandType::Redo:
  739. mCommandEvent.setType(GUICommandEventType::Redo);
  740. break;
  741. case InputCommandType::Return:
  742. mCommandEvent.setType(GUICommandEventType::Return);
  743. break;
  744. case InputCommandType::Escape:
  745. mCommandEvent.setType(GUICommandEventType::Escape);
  746. break;
  747. case InputCommandType::Tab:
  748. mCommandEvent.setType(GUICommandEventType::Tab);
  749. break;
  750. case InputCommandType::SelectAll:
  751. mCommandEvent.setType(GUICommandEventType::SelectAll);
  752. break;
  753. case InputCommandType::CursorMoveLeft:
  754. mCommandEvent.setType(GUICommandEventType::CursorMoveLeft);
  755. break;
  756. case InputCommandType::CursorMoveRight:
  757. mCommandEvent.setType(GUICommandEventType::CursorMoveRight);
  758. break;
  759. case InputCommandType::CursorMoveUp:
  760. mCommandEvent.setType(GUICommandEventType::CursorMoveUp);
  761. break;
  762. case InputCommandType::CursorMoveDown:
  763. mCommandEvent.setType(GUICommandEventType::CursorMoveDown);
  764. break;
  765. case InputCommandType::SelectLeft:
  766. mCommandEvent.setType(GUICommandEventType::SelectLeft);
  767. break;
  768. case InputCommandType::SelectRight:
  769. mCommandEvent.setType(GUICommandEventType::SelectRight);
  770. break;
  771. case InputCommandType::SelectUp:
  772. mCommandEvent.setType(GUICommandEventType::SelectUp);
  773. break;
  774. case InputCommandType::SelectDown:
  775. mCommandEvent.setType(GUICommandEventType::SelectDown);
  776. break;
  777. }
  778. sendCommandEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mCommandEvent);
  779. }
  780. bool GUIManager::findElementUnderCursor(const CM::Vector2I& cursorScreenPos, bool buttonStates[3], bool shift, bool control, bool alt)
  781. {
  782. Vector<const RenderWindow*>::type widgetWindows;
  783. for(auto& widgetInfo : mWidgets)
  784. widgetWindows.push_back(getWidgetWindow(*widgetInfo.widget));
  785. #if CM_DEBUG_MODE
  786. // Checks if all referenced windows actually exist
  787. Vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
  788. for(auto& window : widgetWindows)
  789. {
  790. if(window == nullptr)
  791. continue;
  792. auto iterFind = std::find(begin(activeWindows), end(activeWindows), window);
  793. if(iterFind == activeWindows.end())
  794. {
  795. CM_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
  796. Please detach all GUIWidgets from windows before destroying a window.");
  797. }
  798. }
  799. #endif
  800. mNewElementsUnderCursor.clear();
  801. const RenderWindow* windowUnderCursor = nullptr;
  802. UnorderedSet<const RenderWindow*>::type uniqueWindows;
  803. for(auto& window : widgetWindows)
  804. {
  805. if(window == nullptr)
  806. continue;
  807. uniqueWindows.insert(window);
  808. }
  809. for(auto& window : uniqueWindows)
  810. {
  811. if(Platform::isPointOverWindow(*window, cursorScreenPos))
  812. {
  813. windowUnderCursor = window;
  814. break;
  815. }
  816. }
  817. if(windowUnderCursor != nullptr)
  818. {
  819. Vector2I windowPos = windowUnderCursor->screenToWindowPos(cursorScreenPos);
  820. Vector4 vecWindowPos((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f);
  821. UINT32 widgetIdx = 0;
  822. for(auto& widgetInfo : mWidgets)
  823. {
  824. if(widgetWindows[widgetIdx] == nullptr)
  825. {
  826. widgetIdx++;
  827. continue;
  828. }
  829. GUIWidget* widget = widgetInfo.widget;
  830. if(widgetWindows[widgetIdx] == windowUnderCursor && widget->inBounds(windowToBridgedCoords(*widget, windowPos)))
  831. {
  832. SelectiveInputData* selectiveInputData = nullptr;
  833. if(mSelectiveInputActive)
  834. {
  835. auto selectionIterFind = mSelectiveInputData.find(widget);
  836. if(selectionIterFind == mSelectiveInputData.end())
  837. {
  838. widgetIdx++;
  839. continue;
  840. }
  841. else
  842. selectiveInputData = &selectionIterFind->second;
  843. }
  844. const Vector<GUIElement*>::type& elements = widget->getElements();
  845. Vector2I localPos = getWidgetRelativePos(*widget, cursorScreenPos);
  846. // Elements with lowest depth (most to the front) get handled first
  847. for(auto iter = elements.begin(); iter != elements.end(); ++iter)
  848. {
  849. GUIElement* element = *iter;
  850. if(!element->_isDisabled() && element->_isInBounds(localPos))
  851. {
  852. if(mSelectiveInputActive && !selectiveInputData->acceptAllElements)
  853. {
  854. if(selectiveInputData->elements.find(element) == selectiveInputData->elements.end())
  855. continue;
  856. }
  857. mNewElementsUnderCursor.push_back(ElementInfo(element, widget));
  858. }
  859. }
  860. }
  861. widgetIdx++;
  862. }
  863. }
  864. std::sort(mNewElementsUnderCursor.begin(), mNewElementsUnderCursor.end(),
  865. [](const ElementInfo& a, const ElementInfo& b)
  866. {
  867. return a.element->_getDepth() < b.element->_getDepth();
  868. });
  869. return handleCursorOver(cursorScreenPos, buttonStates, shift, control, alt);
  870. }
  871. bool GUIManager::handleCursorOver(const CM::Vector2I& screenPos, bool buttonStates[3], bool shift, bool control, bool alt)
  872. {
  873. bool eventProcessed = false;
  874. for(auto& elementInfo : mNewElementsUnderCursor)
  875. {
  876. GUIElement* element = elementInfo.element;
  877. GUIWidget* widget = elementInfo.widget;
  878. auto findIter = std::find_if(begin(mElementsUnderCursor), end(mElementsUnderCursor),
  879. [=] (const ElementInfo& x) { return x.element == element; });
  880. if(findIter == mElementsUnderCursor.end())
  881. {
  882. // Send MouseOver event
  883. if(mActiveElement == nullptr || element == mActiveElement)
  884. {
  885. Vector2I localPos;
  886. if(widget != nullptr)
  887. localPos = getWidgetRelativePos(*widget, screenPos);
  888. mMouseEvent = GUIMouseEvent(buttonStates, shift, control, alt);
  889. mMouseEvent.setMouseOverData(localPos);
  890. if(sendMouseEvent(widget, element, mMouseEvent))
  891. eventProcessed = true;
  892. }
  893. }
  894. }
  895. for(auto& elementInfo : mElementsUnderCursor)
  896. {
  897. GUIElement* element = elementInfo.element;
  898. GUIWidget* widget = elementInfo.widget;
  899. auto findIter = std::find_if(mNewElementsUnderCursor.begin(), mNewElementsUnderCursor.end(),
  900. [=] (const ElementInfo& x) { return x.element == element; });
  901. if(findIter == mNewElementsUnderCursor.end())
  902. {
  903. // Send MouseOut event
  904. if(mActiveElement == nullptr || element == mActiveElement)
  905. {
  906. Vector2I curLocalPos = getWidgetRelativePos(*widget, screenPos);
  907. mMouseEvent.setMouseOutData(curLocalPos);
  908. if(sendMouseEvent(widget, element, mMouseEvent))
  909. eventProcessed = true;
  910. }
  911. }
  912. }
  913. mElementsUnderCursor.swap(mNewElementsUnderCursor);
  914. return eventProcessed;
  915. }
  916. void GUIManager::onTextInput(const CM::TextInputEvent& event)
  917. {
  918. if(mKeyboardFocusElement != nullptr)
  919. {
  920. mTextInputEvent = GUITextInputEvent();
  921. mTextInputEvent.setData(event.textChar);
  922. if(sendTextInputEvent(mKeyboardFocusWidget, mKeyboardFocusElement, mTextInputEvent))
  923. event.markAsUsed();
  924. }
  925. }
  926. void GUIManager::onWindowFocusGained(RenderWindow& win)
  927. {
  928. for(auto& widgetInfo : mWidgets)
  929. {
  930. GUIWidget* widget = widgetInfo.widget;
  931. if(getWidgetWindow(*widget) == &win)
  932. widget->ownerWindowFocusChanged();
  933. }
  934. }
  935. void GUIManager::onWindowFocusLost(RenderWindow& win)
  936. {
  937. for(auto& widgetInfo : mWidgets)
  938. {
  939. GUIWidget* widget = widgetInfo.widget;
  940. if(getWidgetWindow(*widget) == &win)
  941. widget->ownerWindowFocusChanged();
  942. }
  943. }
  944. // We stop getting mouse move events once it leaves the window, so make sure
  945. // nothing stays in hover state
  946. void GUIManager::onMouseLeftWindow(CM::RenderWindow* win)
  947. {
  948. bool buttonStates[3];
  949. buttonStates[0] = false;
  950. buttonStates[1] = false;
  951. buttonStates[2] = false;
  952. mNewElementsUnderCursor.clear();
  953. handleCursorOver(Vector2I(), buttonStates, false, false, false);
  954. }
  955. void GUIManager::queueForDestroy(GUIElement* element)
  956. {
  957. mScheduledForDestruction.push(element);
  958. }
  959. void GUIManager::processDestroyQueue()
  960. {
  961. // Need two loops and a temporary since element destructors may themselves
  962. // queue other elements for destruction
  963. while(!mScheduledForDestruction.empty())
  964. {
  965. CM::Stack<GUIElement*>::type toDestroy = mScheduledForDestruction;
  966. mScheduledForDestruction = CM::Stack<GUIElement*>::type();
  967. while(!toDestroy.empty())
  968. {
  969. cm_delete<PoolAlloc>(toDestroy.top());
  970. toDestroy.pop();
  971. }
  972. }
  973. }
  974. // HACK - This callback is very hackish and very specific. Attempt to replace it with a more
  975. // general purpose solution
  976. void GUIManager::enableSelectiveInput(std::function<void()> onOutsideClickCallback)
  977. {
  978. mSelectiveInputActive = true;
  979. mOnOutsideClickCallback = onOutsideClickCallback;
  980. }
  981. void GUIManager::disableSelectiveInput()
  982. {
  983. mSelectiveInputData.clear();
  984. mSelectiveInputActive = false;
  985. mOnOutsideClickCallback = nullptr;
  986. }
  987. void GUIManager::addSelectiveInputWidget(const GUIWidget* widget)
  988. {
  989. auto findIter = mSelectiveInputData.find(widget);
  990. if(findIter == mSelectiveInputData.end())
  991. {
  992. SelectiveInputData& data = mSelectiveInputData[widget];
  993. data.acceptAllElements = true;
  994. }
  995. else
  996. {
  997. mSelectiveInputData[widget].acceptAllElements = true;
  998. }
  999. }
  1000. void GUIManager::addSelectiveInputElement(const GUIElement* element)
  1001. {
  1002. mSelectiveInputData[&element->_getParentWidget()].elements.insert(element);
  1003. }
  1004. void GUIManager::setInputBridge(const CM::RenderTexture* renderTex, const GUIElement* element)
  1005. {
  1006. if(element == nullptr)
  1007. mInputBridge.erase(renderTex);
  1008. else
  1009. mInputBridge[renderTex] = element;
  1010. }
  1011. GUIMouseButton GUIManager::buttonToGUIButton(PositionalInputEventButton cursorButton) const
  1012. {
  1013. if(cursorButton == PositionalInputEventButton::Left)
  1014. return GUIMouseButton::Left;
  1015. else if(cursorButton == PositionalInputEventButton::Middle)
  1016. return GUIMouseButton::Middle;
  1017. else if(cursorButton == PositionalInputEventButton::Right)
  1018. return GUIMouseButton::Right;
  1019. CM_EXCEPT(InvalidParametersException, "Provided button is not a GUI supported mouse button.");
  1020. }
  1021. Vector2I GUIManager::getWidgetRelativePos(const GUIWidget& widget, const Vector2I& screenPos) const
  1022. {
  1023. const RenderWindow* window = getWidgetWindow(widget);
  1024. Vector2I windowPos = window->screenToWindowPos(screenPos);
  1025. windowPos = windowToBridgedCoords(widget, windowPos);
  1026. const Matrix4& worldTfrm = widget.SO()->getWorldTfrm();
  1027. Vector4 vecLocalPos = worldTfrm.inverse().multiply3x4(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
  1028. Vector2I curLocalPos(Math::roundToInt(vecLocalPos.x), Math::roundToInt(vecLocalPos.y));
  1029. return curLocalPos;
  1030. }
  1031. Vector2I GUIManager::windowToBridgedCoords(const GUIWidget& widget, const Vector2I& windowPos) const
  1032. {
  1033. // This cast might not be valid (the render target could be a window), but we only really need to cast
  1034. // so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
  1035. // (in which case we know it is a RenderTexture)
  1036. const RenderTexture* renderTexture = static_cast<const RenderTexture*>(widget.getTarget()->getTarget().get());
  1037. auto iterFind = mInputBridge.find(renderTexture);
  1038. if(iterFind != mInputBridge.end()) // Widget input is bridged, which means we need to transform the coordinates
  1039. {
  1040. const GUIElement* bridgeElement = iterFind->second;
  1041. const Matrix4& worldTfrm = bridgeElement->_getParentWidget().SO()->getWorldTfrm();
  1042. Vector4 vecLocalPos = worldTfrm.inverse().multiply3x4(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
  1043. RectI bridgeBounds = bridgeElement->getBounds();
  1044. // Find coordinates relative to the bridge element
  1045. float x = vecLocalPos.x - (float)bridgeBounds.x;
  1046. float y = vecLocalPos.y - (float)bridgeBounds.y;
  1047. float scaleX = renderTexture->getWidth() / (float)bridgeBounds.width;
  1048. float scaleY = renderTexture->getHeight() / (float)bridgeBounds.height;
  1049. return Vector2I(Math::roundToInt(x * scaleX), Math::roundToInt(y * scaleY));
  1050. }
  1051. return windowPos;
  1052. }
  1053. const CM::RenderWindow* GUIManager::getWidgetWindow(const GUIWidget& widget) const
  1054. {
  1055. // This cast might not be valid (the render target could be a window), but we only really need to cast
  1056. // so that mInputBridge map allows us to search through it - we don't access anything unless the target is bridged
  1057. // (in which case we know it is a RenderTexture)
  1058. const RenderTexture* renderTexture = static_cast<const RenderTexture*>(widget.getTarget()->getTarget().get());
  1059. auto iterFind = mInputBridge.find(renderTexture);
  1060. if(iterFind != mInputBridge.end())
  1061. {
  1062. GUIWidget& parentWidget = iterFind->second->_getParentWidget();
  1063. if(&parentWidget != &widget)
  1064. {
  1065. return getWidgetWindow(parentWidget);
  1066. }
  1067. }
  1068. RenderTargetPtr renderTarget = widget.getTarget()->getTarget();
  1069. Vector<RenderWindow*>::type renderWindows = RenderWindowManager::instance().getRenderWindows();
  1070. auto iterFindWin = std::find(renderWindows.begin(), renderWindows.end(), renderTarget.get());
  1071. if(iterFindWin != renderWindows.end())
  1072. return static_cast<RenderWindow*>(renderTarget.get());
  1073. return nullptr;
  1074. }
  1075. bool GUIManager::sendMouseEvent(GUIWidget* widget, GUIElement* element, const GUIMouseEvent& event)
  1076. {
  1077. return widget->_mouseEvent(element, event);
  1078. }
  1079. bool GUIManager::sendTextInputEvent(GUIWidget* widget, GUIElement* element, const GUITextInputEvent& event)
  1080. {
  1081. return widget->_textInputEvent(element, event);
  1082. }
  1083. bool GUIManager::sendCommandEvent(GUIWidget* widget, GUIElement* element, const GUICommandEvent& event)
  1084. {
  1085. return widget->_commandEvent(element, event);
  1086. }
  1087. GUIManager& gGUIManager()
  1088. {
  1089. return GUIManager::instance();
  1090. }
  1091. }