UI.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "BaseUIElementFactory.h"
  25. #include "Cursor.h"
  26. #include "Font.h"
  27. #include "InputEvents.h"
  28. #include "Log.h"
  29. #include "Matrix4.h"
  30. #include "PixelShader.h"
  31. #include "Profiler.h"
  32. #include "Renderer.h"
  33. #include "RendererEvents.h"
  34. #include "RendererImpl.h"
  35. #include "ResourceCache.h"
  36. #include "StringUtils.h"
  37. #include "Texture2D.h"
  38. #include "UI.h"
  39. #include "UIEvents.h"
  40. #include "VertexShader.h"
  41. #include <algorithm>
  42. #include "DebugNew.h"
  43. static bool compareUIElements(const UIElement* lhs, const UIElement* rhs)
  44. {
  45. return lhs->getPriority() < rhs->getPriority();
  46. }
  47. UI::UI(Renderer* renderer, ResourceCache* cache) :
  48. mRenderer(renderer),
  49. mCache(cache),
  50. mMouseButtons(0),
  51. mQualifiers(0)
  52. {
  53. if (!mRenderer)
  54. EXCEPTION("Null renderer for UI");
  55. if (!mCache)
  56. EXCEPTION("Null resource cache for UI");
  57. LOGINFO("UI created");
  58. mRootElement = new UIElement();
  59. mRootElement->setSize(mRenderer->getWidth(), mRenderer->getHeight());
  60. subscribeToEvent(mRenderer, EVENT_SCREENMODE, EVENT_HANDLER(UI, handleScreenMode));
  61. subscribeToEvent(EVENT_MOUSEMOVE, EVENT_HANDLER(UI, handleMouseMove));
  62. subscribeToEvent(EVENT_MOUSEBUTTONDOWN, EVENT_HANDLER(UI, handleMouseButtonDown));
  63. subscribeToEvent(EVENT_MOUSEBUTTONUP, EVENT_HANDLER(UI, handleMouseButtonUp));
  64. subscribeToEvent(EVENT_MOUSEWHEEL, EVENT_HANDLER(UI, handleMouseWheel));
  65. subscribeToEvent(EVENT_KEYDOWN, EVENT_HANDLER(UI, handleKeyDown));
  66. subscribeToEvent(EVENT_CHAR, EVENT_HANDLER(UI, handleChar));
  67. mNoTextureVS = mCache->getResource<VertexShader>("Shaders/SM2/Basic_VCol.vs2");
  68. mDiffTextureVS = mCache->getResource<VertexShader>("Shaders/SM2/Basic_DiffVCol.vs2");
  69. mNoTexturePS = mCache->getResource<PixelShader>("Shaders/SM2/Basic_VCol.ps2");
  70. mDiffTexturePS = mCache->getResource<PixelShader>("Shaders/SM2/Basic_DiffVCol.ps2");
  71. mAlphaTexturePS = mCache->getResource<PixelShader>("Shaders/SM2/Basic_AlphaVCol.ps2");
  72. // Add the base element factory
  73. addElementFactory(new BaseUIElementFactory());
  74. }
  75. UI::~UI()
  76. {
  77. LOGINFO("UI shut down");
  78. }
  79. void UI::setCursor(Cursor* cursor)
  80. {
  81. // Remove old cursor (if any) and set new
  82. if (mCursor)
  83. {
  84. mRootElement->removeChild(mCursor);
  85. mCursor.reset();
  86. }
  87. if (cursor)
  88. {
  89. mRootElement->addChild(cursor);
  90. mCursor = cursor;
  91. IntVector2 pos = mCursor->getPosition();
  92. const IntVector2& rootSize = mRootElement->getSize();
  93. pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
  94. pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
  95. mCursor->setPosition(pos);
  96. }
  97. }
  98. void UI::setFocusElement(UIElement* element)
  99. {
  100. using namespace FocusChanged;
  101. VariantMap eventData;
  102. eventData[P_ORIGINALELEMENT] = (void*)element;
  103. if (element)
  104. {
  105. // Return if already has focus
  106. if (element->hasFocus())
  107. return;
  108. // If element can not be focused, and does not reset the focus either, search toward the parent
  109. for (;;)
  110. {
  111. if (element->getFocusMode() != FM_NOTFOCUSABLE)
  112. break;
  113. element = element->getParent();
  114. // Return if did not find any parent element that changes the focus
  115. if (!element)
  116. return;
  117. }
  118. }
  119. std::vector<UIElement*> allChildren = mRootElement->getChildren(true);
  120. // Go through all elements to clear the old focus
  121. for (std::vector<UIElement*>::iterator i = allChildren.begin(); i != allChildren.end(); ++i)
  122. {
  123. UIElement* other = *i;
  124. if ((other != element) && (other->hasFocus()))
  125. other->setFocus(false);
  126. }
  127. if (element)
  128. element->setFocus(true);
  129. eventData[P_ELEMENT] = (void*)element;
  130. sendEvent(EVENT_FOCUSCHANGED, eventData);
  131. }
  132. void UI::clear()
  133. {
  134. mRootElement->removeAllChildren();
  135. if (mCursor)
  136. mRootElement->addChild(mCursor);
  137. }
  138. void UI::update(float timeStep)
  139. {
  140. PROFILE(UI_Update);
  141. if ((mCursor) && (mCursor->isVisible()))
  142. {
  143. IntVector2 pos = mCursor->getPosition();
  144. WeakPtr<UIElement> element(getElementAt(pos));
  145. bool dragSource = (mMouseDragElement) && ((mMouseDragElement->getDragDropMode() & DD_SOURCE) != 0);
  146. bool dragTarget = (element) && ((element->getDragDropMode() & DD_TARGET) != 0);
  147. bool dragDropTest = (dragSource) && (dragTarget) && (element != mMouseDragElement);
  148. // Hover effect
  149. // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
  150. if (element)
  151. {
  152. if ((!mMouseDragElement) || (mMouseDragElement == element) || (dragDropTest))
  153. element->onHover(element->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  154. }
  155. // Drag and drop test
  156. if (dragDropTest)
  157. {
  158. bool accept = element->onDragDropTest(mMouseDragElement);
  159. if (accept)
  160. {
  161. using namespace DragDropTest;
  162. VariantMap eventData;
  163. eventData[P_SOURCE] = (void*)mMouseDragElement.getPtr();
  164. eventData[P_TARGET] = (void*)element.getPtr();
  165. eventData[P_ACCEPT] = accept;
  166. sendEvent(EVENT_DRAGDROPTEST, eventData);
  167. accept = eventData[P_ACCEPT].getBool();
  168. }
  169. mCursor->setShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
  170. }
  171. else if (dragSource)
  172. mCursor->setShape(mMouseDragElement == element ? CS_ACCEPTDROP : CS_REJECTDROP);
  173. }
  174. // Defocus element now if should
  175. if (mDefocusElement)
  176. {
  177. // Do nothing if the focus element changed in the meanwhile
  178. if (mDefocusElement == getFocusElement())
  179. setFocusElement(0);
  180. mDefocusElement.reset();
  181. }
  182. {
  183. PROFILE(UI_UpdateElements);
  184. update(timeStep, mRootElement);
  185. }
  186. }
  187. void UI::renderUpdate()
  188. {
  189. if (mRenderer->isDeviceLost())
  190. return;
  191. {
  192. PROFILE(UI_GetBatches);
  193. mBatches.clear();
  194. mQuads.clear();
  195. const IntVector2& rootSize = mRootElement->getSize();
  196. getBatches(mRootElement, IntRect(0, 0, rootSize.mX, rootSize.mY));
  197. }
  198. // If no drag, reset cursor shape for next frame
  199. if ((mCursor) && (!mMouseDragElement))
  200. mCursor->setShape(CS_NORMAL);
  201. }
  202. void UI::render()
  203. {
  204. PROFILE(UI_Render);
  205. static const Vector2 scale(2.0f, -2.0f);
  206. static const Vector2 offset(-1.0f, 1.0f);
  207. Matrix4 projection;
  208. memset(&projection, 0, sizeof(projection));
  209. projection.m00 = scale.mX;
  210. projection.m03 = offset.mX;
  211. projection.m11 = scale.mY;
  212. projection.m13 = offset.mY;
  213. projection.m22 = 1.0f;
  214. projection.m23 = 0.0f;
  215. projection.m33 = 1.0f;
  216. mRenderer->resetRenderTargets();
  217. mRenderer->setCullMode(CULL_CCW);
  218. mRenderer->setDepthTest(CMP_ALWAYS);
  219. mRenderer->setDepthWrite(false);
  220. mRenderer->setFillMode(FILL_SOLID);
  221. mRenderer->setStencilTest(false);
  222. mRenderer->setVertexShaderConstant(getVSRegister(VSP_MODELVIEWPROJ), projection);
  223. mRenderer->setPixelShaderConstant(getPSRegister(PSP_MATDIFFCOLOR), Color(1.0f, 1.0f, 1.0f, 1.0f));
  224. PixelShader* ps = 0;
  225. VertexShader* vs = 0;
  226. for (unsigned i = 0; i < mBatches.size(); ++i)
  227. {
  228. // Choose shaders here so that UIBatch does not need to look up shaders each time
  229. if (!mBatches[i].mTexture)
  230. {
  231. ps = mNoTexturePS;
  232. vs = mNoTextureVS;
  233. }
  234. else
  235. {
  236. // If texture contains only an alpha channel, use the alpha pixel shader
  237. vs = mDiffTextureVS;
  238. if (mBatches[i].mTexture->getFormat() == D3DFMT_A8)
  239. ps = mAlphaTexturePS;
  240. else
  241. ps = mDiffTexturePS;
  242. }
  243. mBatches[i].draw(mRenderer, vs, ps);
  244. }
  245. }
  246. void UI::addElementFactory(UIElementFactory* factory)
  247. {
  248. if (!factory)
  249. return;
  250. mFactories.push_back(SharedPtr<UIElementFactory>(factory));
  251. }
  252. SharedPtr<UIElement> UI::createElement(ShortStringHash type, const std::string& name)
  253. {
  254. SharedPtr<UIElement> element;
  255. if (!type)
  256. type = UIElement::getTypeStatic();
  257. for (unsigned i = 0; i < mFactories.size(); ++i)
  258. {
  259. element = mFactories[i]->createElement(type, name);
  260. if (element)
  261. return element;
  262. }
  263. EXCEPTION("Could not create unknown UI element type " + toString(type));
  264. }
  265. SharedPtr<UIElement> UI::loadLayout(XMLFile* file, XMLFile* styleFile)
  266. {
  267. PROFILE(UI_LoadLayout);
  268. SharedPtr<UIElement> root;
  269. if (!file)
  270. {
  271. LOGERROR("Null UI layout XML file");
  272. return root;
  273. }
  274. LOGDEBUG("Loading UI layout " + file->getName());
  275. XMLElement rootElem = file->getRootElement("element");
  276. if (!rootElem)
  277. {
  278. LOGERROR("No root UI element in " + file->getName());
  279. return root;
  280. }
  281. root = createElement(ShortStringHash(rootElem.getString("type")), rootElem.getString("name"));
  282. std::string styleName = rootElem.hasAttribute("style") ? rootElem.getString("style") : rootElem.getString("type");
  283. // First set the base style from the style file if exists, then apply UI layout overrides
  284. if (styleFile)
  285. root->setStyle(styleFile, styleName, mCache);
  286. root->setStyle(rootElem, mCache);
  287. // Load rest of the elements recursively
  288. loadLayout(root, rootElem, styleFile);
  289. return root;
  290. }
  291. UIElement* UI::getElementAt(const IntVector2& position, bool enabledOnly)
  292. {
  293. UIElement* result = 0;
  294. getElementAt(result, mRootElement, position, enabledOnly);
  295. return result;
  296. }
  297. UIElement* UI::getElementAt(int x, int y, bool enabledOnly)
  298. {
  299. return getElementAt(IntVector2(x, y), enabledOnly);
  300. }
  301. UIElement* UI::getFocusElement() const
  302. {
  303. std::vector<UIElement*> allChildren = mRootElement->getChildren(true);
  304. for (std::vector<UIElement*>::iterator i = allChildren.begin(); i != allChildren.end(); ++i)
  305. {
  306. if ((*i)->hasFocus())
  307. return *i;
  308. }
  309. return 0;
  310. }
  311. UIElement* UI::getFrontElement() const
  312. {
  313. std::vector<UIElement*> rootChildren = mRootElement->getChildren(false);
  314. int maxPriority = M_MIN_INT;
  315. UIElement* front = 0;
  316. for (unsigned i = 0; i < rootChildren.size(); ++i)
  317. {
  318. // Do not take into account input-disabled elements, hidden elements or those that are always in the front
  319. if ((!rootChildren[i]->isEnabled()) || (!rootChildren[i]->isVisible()) || (!rootChildren[i]->getBringToBack()))
  320. continue;
  321. int priority = rootChildren[i]->getPriority();
  322. if (priority > maxPriority)
  323. {
  324. maxPriority = priority;
  325. front = rootChildren[i];
  326. }
  327. }
  328. return front;
  329. }
  330. IntVector2 UI::getCursorPosition()
  331. {
  332. if (!mCursor)
  333. return IntVector2::sZero;
  334. else
  335. return mCursor->getPosition();
  336. }
  337. void UI::update(float timeStep, UIElement* element)
  338. {
  339. element->update(timeStep);
  340. const std::vector<UIElement*> children = element->getChildren();
  341. for (std::vector<UIElement*>::const_iterator i = children.begin(); i != children.end(); ++i)
  342. update(timeStep, *i);
  343. }
  344. void UI::getBatches(UIElement* element, IntRect currentScissor)
  345. {
  346. // Set clipping scissor for child elements. No need to draw if zero size
  347. element->adjustScissor(currentScissor);
  348. if ((currentScissor.mLeft == currentScissor.mRight) || (currentScissor.mTop == currentScissor.mBottom))
  349. return;
  350. std::vector<UIElement*> children = element->getChildren();
  351. if (children.empty())
  352. return;
  353. std::sort(children.begin(), children.end(), compareUIElements);
  354. // For non-root elements draw all children of same priority before recursing into their children: assumption is that they have
  355. // same renderstate
  356. std::vector<UIElement*>::const_iterator i = children.begin();
  357. if (element != mRootElement)
  358. {
  359. std::vector<UIElement*>::const_iterator j = i;
  360. int currentPriority = children.front()->getPriority();
  361. while (i != children.end())
  362. {
  363. while ((j != children.end()) && ((*j)->getPriority() == currentPriority))
  364. {
  365. if ((*j)->isVisible())
  366. (*j)->getBatches(mBatches, mQuads, currentScissor);
  367. ++j;
  368. }
  369. // Now recurse into the children
  370. while (i != j)
  371. {
  372. if ((*i)->isVisible())
  373. getBatches(*i, currentScissor);
  374. ++i;
  375. }
  376. if (i != children.end())
  377. currentPriority = (*i)->getPriority();
  378. }
  379. }
  380. // On the root level draw each element and its children immediately after to avoid artifacts
  381. else
  382. {
  383. while (i != children.end())
  384. {
  385. if ((*i)->isVisible())
  386. {
  387. (*i)->getBatches(mBatches, mQuads, currentScissor);
  388. getBatches(*i, currentScissor);
  389. }
  390. ++i;
  391. }
  392. }
  393. }
  394. void UI::getElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool enabledOnly)
  395. {
  396. if (!current)
  397. return;
  398. // Get children from lowest priority to highest
  399. std::vector<UIElement*> children = current->getChildren(false);
  400. std::sort(children.begin(), children.end(), compareUIElements);
  401. for (std::vector<UIElement*>::const_iterator i = children.begin(); i != children.end(); ++i)
  402. {
  403. UIElement* element = *i;
  404. if ((element != mCursor.getPtr()) && (element->isVisible()))
  405. {
  406. if (element->isInside(position, true))
  407. {
  408. // Store the current result, then recurse into its children. Because children
  409. // are sorted from lowest to highest priority, we should be left with the topmost match
  410. if ((element->isEnabled()) || (!enabledOnly))
  411. result = element;
  412. }
  413. if (element->isInsideCombined(position, true))
  414. getElementAt(result, element, position, enabledOnly);
  415. }
  416. }
  417. }
  418. void UI::handleScreenMode(StringHash eventType, VariantMap& eventData)
  419. {
  420. using namespace ScreenMode;
  421. mRootElement->setSize(mRenderer->getWidth(), mRenderer->getHeight());
  422. }
  423. void UI::handleMouseMove(StringHash eventType, VariantMap& eventData)
  424. {
  425. using namespace MouseMove;
  426. mMouseButtons = eventData[P_BUTTONS].getInt();
  427. mQualifiers = eventData[P_QUALIFIERS].getInt();
  428. if (mCursor)
  429. {
  430. const IntVector2& rootSize = mRootElement->getSize();
  431. if (eventData[P_CLIPCURSOR].getBool())
  432. {
  433. // When in confined cursor mode, move cursor only when visible
  434. if (mCursor->isVisible())
  435. {
  436. IntVector2 pos = mCursor->getPosition();
  437. pos.mX += eventData[P_DX].getInt();
  438. pos.mY += eventData[P_DY].getInt();
  439. pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
  440. pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
  441. mCursor->setPosition(pos);
  442. }
  443. }
  444. else
  445. {
  446. // When in non-confined mode, move cursor always to ensure accurate position
  447. IntVector2 pos(eventData[P_X].getInt(), eventData[P_Y].getInt());
  448. bool inside = (pos.mX >= 0) && (pos.mX < rootSize.mX) && (pos.mY >= 0) && (pos.mY < rootSize.mY);
  449. // Hide by moving completely outside if outside
  450. // (do not use setVisible(), so that actual visibility remains under application control)
  451. if (pos.mX < 0)
  452. pos.mX = -mCursor->getWidth() * 2;
  453. if (pos.mX >= rootSize.mX)
  454. pos.mX = rootSize.mX + mCursor->getWidth() * 2;
  455. if (pos.mY < 0)
  456. pos.mY = -mCursor->getHeight() * 2;
  457. if (pos.mY >= rootSize.mY)
  458. pos.mY = rootSize.mY + mCursor->getHeight() * 2;
  459. mCursor->setPosition(pos);
  460. // Do not drag when outside
  461. if (!inside)
  462. return;
  463. }
  464. if ((mMouseDragElement) && (mMouseButtons))
  465. {
  466. IntVector2 pos = mCursor->getPosition();
  467. if ((mMouseDragElement->isEnabled()) && (mMouseDragElement->isVisible()))
  468. mMouseDragElement->onDragMove(mMouseDragElement->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  469. else
  470. {
  471. mMouseDragElement->onDragEnd(mMouseDragElement->screenToElement(pos), pos, mCursor);
  472. mMouseDragElement.reset();
  473. }
  474. }
  475. }
  476. }
  477. void UI::handleMouseButtonDown(StringHash eventType, VariantMap& eventData)
  478. {
  479. mMouseButtons = eventData[MouseButtonDown::P_BUTTONS].getInt();
  480. mQualifiers = eventData[MouseButtonDown::P_QUALIFIERS].getInt();
  481. int button = eventData[MouseButtonDown::P_BUTTON].getInt();
  482. if ((mCursor) && (mCursor->isVisible()))
  483. {
  484. IntVector2 pos = mCursor->getPosition();
  485. WeakPtr<UIElement> element(getElementAt(pos));
  486. if (element)
  487. {
  488. // Handle focusing & bringing to front
  489. if (button == MOUSEB_LEFT)
  490. {
  491. setFocusElement(element);
  492. element->bringToFront();
  493. }
  494. // Handle click
  495. element->onClick(element->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  496. // Handle start of drag. onClick() may have caused destruction of the element, so check the pointer again
  497. if ((element) && (!mMouseDragElement) && (mMouseButtons == MOUSEB_LEFT))
  498. {
  499. mMouseDragElement = element;
  500. element->onDragStart(element->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  501. }
  502. }
  503. else
  504. {
  505. // If clicked over no element, or a disabled element, try to lose focus
  506. mDefocusElement = getFocusElement();
  507. }
  508. using namespace UIMouseClick;
  509. VariantMap eventData;
  510. eventData[UIMouseClick::P_ELEMENT] = (void*)element.getPtr();
  511. eventData[UIMouseClick::P_X] = pos.mX;
  512. eventData[UIMouseClick::P_Y] = pos.mY;
  513. eventData[UIMouseClick::P_BUTTON] = button;
  514. eventData[UIMouseClick::P_BUTTONS] = mMouseButtons;
  515. eventData[UIMouseClick::P_QUALIFIERS] = mQualifiers;
  516. sendEvent(EVENT_UIMOUSECLICK, eventData);
  517. }
  518. }
  519. void UI::handleMouseButtonUp(StringHash eventType, VariantMap& eventData)
  520. {
  521. using namespace MouseButtonUp;
  522. mMouseButtons = eventData[P_BUTTONS].getInt();
  523. mQualifiers = eventData[P_QUALIFIERS].getInt();
  524. if ((mCursor) && ((mCursor->isVisible()) || (mMouseDragElement)))
  525. {
  526. IntVector2 pos = mCursor->getPosition();
  527. if ((mMouseDragElement) && (!mMouseButtons))
  528. {
  529. if ((mMouseDragElement->isEnabled()) && (mMouseDragElement->isVisible()))
  530. {
  531. mMouseDragElement->onDragEnd(mMouseDragElement->screenToElement(pos), pos, mCursor);
  532. // Drag and drop finish
  533. bool dragSource = (mMouseDragElement) && ((mMouseDragElement->getDragDropMode() & DD_SOURCE) != 0);
  534. if (dragSource)
  535. {
  536. WeakPtr<UIElement> target(getElementAt(pos));
  537. bool dragTarget = (target) && ((target->getDragDropMode() & DD_TARGET) != 0);
  538. bool dragDropFinish = (dragSource) && (dragTarget) && (target != mMouseDragElement);
  539. if (dragDropFinish)
  540. {
  541. bool accept = target->onDragDropFinish(mMouseDragElement);
  542. // onDragDropFinish() may have caused destruction of the elements, so check the pointers again
  543. if ((accept) && (mMouseDragElement) && (target))
  544. {
  545. using namespace DragDropFinish;
  546. VariantMap eventData;
  547. eventData[P_SOURCE] = (void*)mMouseDragElement.getPtr();
  548. eventData[P_TARGET] = (void*)target.getPtr();
  549. eventData[P_ACCEPT] = accept;
  550. sendEvent(EVENT_DRAGDROPFINISH, eventData);
  551. }
  552. }
  553. }
  554. }
  555. mMouseDragElement.reset();
  556. }
  557. }
  558. }
  559. void UI::handleMouseWheel(StringHash eventType, VariantMap& eventData)
  560. {
  561. using namespace MouseWheel;
  562. mMouseButtons = eventData[P_BUTTONS].getInt();
  563. mQualifiers = eventData[P_QUALIFIERS].getInt();
  564. int delta = eventData[P_WHEEL].getInt();
  565. UIElement* element = getFocusElement();
  566. if (element)
  567. element->onWheel(delta, mMouseButtons, mQualifiers);
  568. }
  569. void UI::handleKeyDown(StringHash eventType, VariantMap& eventData)
  570. {
  571. using namespace KeyDown;
  572. mMouseButtons = eventData[P_BUTTONS].getInt();
  573. mQualifiers = eventData[P_QUALIFIERS].getInt();
  574. int key = eventData[P_KEY].getInt();
  575. UIElement* element = getFocusElement();
  576. if (element)
  577. {
  578. // Switch focus between focusable elements in the same top level window
  579. if (key == KEY_TAB)
  580. {
  581. UIElement* topLevel = element->getParent();
  582. while ((topLevel) && (topLevel->getParent() != mRootElement))
  583. topLevel = topLevel->getParent();
  584. if (topLevel)
  585. {
  586. std::vector<UIElement*> children = topLevel->getChildren(true);
  587. for (std::vector<UIElement*>::iterator i = children.begin(); i != children.end();)
  588. {
  589. if ((*i)->getFocusMode() < FM_FOCUSABLE)
  590. i = children.erase(i);
  591. else
  592. ++i;
  593. }
  594. for (unsigned i = 0; i < children.size(); ++i)
  595. {
  596. if (children[i] == element)
  597. {
  598. UIElement* next = children[(i + 1) % children.size()];
  599. setFocusElement(next);
  600. return;
  601. }
  602. }
  603. }
  604. }
  605. // Defocus the element
  606. else if ((key == KEY_ESC) && (element->getFocusMode() == FM_FOCUSABLE_DEFOCUSABLE))
  607. mDefocusElement = element;
  608. // If none of the special keys, pass the key to the focused element
  609. else
  610. element->onKey(key, mMouseButtons, mQualifiers);
  611. }
  612. }
  613. void UI::handleChar(StringHash eventType, VariantMap& eventData)
  614. {
  615. using namespace Char;
  616. mMouseButtons = eventData[P_BUTTONS].getInt();
  617. mQualifiers = eventData[P_QUALIFIERS].getInt();
  618. UIElement* element = getFocusElement();
  619. if (element)
  620. element->onChar(eventData[P_CHAR].getInt(), mMouseButtons, mQualifiers);
  621. }
  622. void UI::loadLayout(UIElement* current, const XMLElement& elem, XMLFile* styleFile)
  623. {
  624. XMLElement childElem = elem.getChildElement("element");
  625. while (childElem)
  626. {
  627. // Create and add to the hierarchy
  628. SharedPtr<UIElement> child = createElement(ShortStringHash(childElem.getString("type")), childElem.getString("name"));
  629. current->addChild(child);
  630. // First set the base style from the style file if exists, then apply UI layout overrides
  631. std::string styleName = childElem.hasAttribute("style") ? childElem.getString("style") : childElem.getString("type");
  632. if (styleFile)
  633. child->setStyle(styleFile, styleName, mCache);
  634. child->setStyle(childElem, mCache);
  635. // Load the children recursively
  636. loadLayout(child, childElem, styleFile);
  637. childElem = childElem.getNextElement("element");
  638. }
  639. }