UI.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  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(EVENT_WINDOWRESIZED, EVENT_HANDLER(UI, handleWindowResized));
  61. subscribeToEvent(EVENT_MOUSEMOVE, EVENT_HANDLER(UI, handleMouseMove));
  62. subscribeToEvent(EVENT_MOUSEPOS, EVENT_HANDLER(UI, handleMouseMove));
  63. subscribeToEvent(EVENT_MOUSEBUTTONDOWN, EVENT_HANDLER(UI, handleMouseButtonDown));
  64. subscribeToEvent(EVENT_MOUSEBUTTONUP, EVENT_HANDLER(UI, handleMouseButtonUp));
  65. subscribeToEvent(EVENT_MOUSEWHEEL, EVENT_HANDLER(UI, handleMouseWheel));
  66. subscribeToEvent(EVENT_KEYDOWN, EVENT_HANDLER(UI, handleKeyDown));
  67. subscribeToEvent(EVENT_CHAR, EVENT_HANDLER(UI, handleChar));
  68. mNoTextureVS = mCache->getResource<VertexShader>("Shaders/SM2/Basic_VCol.vs2");
  69. mDiffTextureVS = mCache->getResource<VertexShader>("Shaders/SM2/Basic_DiffVCol.vs2");
  70. mNoTexturePS = mCache->getResource<PixelShader>("Shaders/SM2/Basic_VCol.ps2");
  71. mDiffTexturePS = mCache->getResource<PixelShader>("Shaders/SM2/Basic_DiffVCol.ps2");
  72. mAlphaTexturePS = mCache->getResource<PixelShader>("Shaders/SM2/Basic_AlphaVCol.ps2");
  73. // Add the base element factory
  74. addElementFactory(new BaseUIElementFactory());
  75. }
  76. UI::~UI()
  77. {
  78. LOGINFO("UI shut down");
  79. }
  80. void UI::setCursor(Cursor* cursor)
  81. {
  82. // Remove old cursor (if any) and set new
  83. if (mCursor)
  84. {
  85. mRootElement->removeChild(mCursor);
  86. mCursor.reset();
  87. }
  88. if (cursor)
  89. {
  90. mRootElement->addChild(cursor);
  91. mCursor = cursor;
  92. IntVector2 pos = mCursor->getPosition();
  93. const IntVector2& rootSize = mRootElement->getSize();
  94. pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
  95. pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
  96. mCursor->setPosition(pos);
  97. }
  98. }
  99. void UI::setFocusElement(UIElement* element)
  100. {
  101. if (element)
  102. {
  103. // Return if already has focus
  104. if (element->hasFocus())
  105. return;
  106. // If element can not be focused, and does not reset the focus either, search toward the parent
  107. for (;;)
  108. {
  109. if (element->getFocusMode() != FM_NOTFOCUSABLE)
  110. break;
  111. element = element->getParent();
  112. // Return if did not find any parent element that changes the focus
  113. if (!element)
  114. return;
  115. }
  116. }
  117. std::vector<UIElement*> allChildren = mRootElement->getChildren(true);
  118. // Go through all elements to clear the old focus
  119. for (std::vector<UIElement*>::iterator i = allChildren.begin(); i != allChildren.end(); ++i)
  120. {
  121. UIElement* other = *i;
  122. if ((other != element) && (other->hasFocus()))
  123. other->setFocus(false);
  124. }
  125. if (element)
  126. element->setFocus(true);
  127. }
  128. void UI::clear()
  129. {
  130. mRootElement->removeAllChildren();
  131. if (mCursor)
  132. mRootElement->addChild(mCursor);
  133. }
  134. void UI::update(float timeStep)
  135. {
  136. PROFILE(UI_Update);
  137. if (mRenderer->isDeviceLost())
  138. return;
  139. if ((mCursor) && (mCursor->isVisible()))
  140. {
  141. IntVector2 pos = mCursor->getPosition();
  142. WeakPtr<UIElement> element(getElementAt(pos));
  143. bool dragSource = (mMouseDragElement) && ((mMouseDragElement->getDragDropMode() & DD_SOURCE) != 0);
  144. bool dragTarget = (element) && ((element->getDragDropMode() & DD_TARGET) != 0);
  145. bool dragDropTest = (dragSource) && (dragTarget) && (element != mMouseDragElement);
  146. // Hover effect
  147. // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
  148. if (element)
  149. {
  150. if ((!mMouseDragElement) || (mMouseDragElement == element) || (dragDropTest))
  151. element->onHover(element->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  152. }
  153. // Drag and drop test
  154. if (dragDropTest)
  155. {
  156. bool accept = element->onDragDropTest(mMouseDragElement);
  157. if (accept)
  158. {
  159. using namespace UIDragDropTest;
  160. VariantMap eventData;
  161. eventData[P_SOURCE] = (void*)mMouseDragElement.getPtr();
  162. eventData[P_TARGET] = (void*)element.getPtr();
  163. eventData[P_ACCEPT] = accept;
  164. sendEvent(EVENT_UIDRAGDROPTEST, eventData);
  165. accept = eventData[P_ACCEPT].getBool();
  166. }
  167. mCursor->setShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
  168. }
  169. else if (dragSource)
  170. mCursor->setShape(mMouseDragElement == element ? CS_ACCEPTDROP : CS_REJECTDROP);
  171. }
  172. // Defocus element now if should
  173. if (mDefocusElement)
  174. {
  175. // Do nothing if the focus element changed in the meanwhile
  176. if (mDefocusElement == getFocusElement())
  177. setFocusElement(0);
  178. mDefocusElement.reset();
  179. }
  180. {
  181. PROFILE(UI_UpdateElements);
  182. update(timeStep, mRootElement);
  183. }
  184. {
  185. PROFILE(UI_GetBatches);
  186. mBatches.clear();
  187. mQuads.clear();
  188. const IntVector2& rootSize = mRootElement->getSize();
  189. getBatches(mRootElement, IntRect(0, 0, rootSize.mX, rootSize.mY));
  190. }
  191. // If no drag, reset cursor shape for next frame
  192. if ((mCursor) && (!mMouseDragElement))
  193. mCursor->setShape(CS_NORMAL);
  194. }
  195. void UI::render()
  196. {
  197. PROFILE(UI_Render);
  198. static const Vector2 scale(2.0f, -2.0f);
  199. static const Vector2 offset(-1.0f, 1.0f);
  200. Matrix4 projection;
  201. memset(&projection, 0, sizeof(projection));
  202. projection.m00 = scale.mX;
  203. projection.m03 = offset.mX;
  204. projection.m11 = scale.mY;
  205. projection.m13 = offset.mY;
  206. projection.m22 = 1.0f;
  207. projection.m23 = 0.0f;
  208. projection.m33 = 1.0f;
  209. mRenderer->resetRenderTargets();
  210. mRenderer->setCullMode(CULL_CCW);
  211. mRenderer->setDepthTest(CMP_ALWAYS);
  212. mRenderer->setDepthWrite(false);
  213. mRenderer->setFillMode(FILL_SOLID);
  214. mRenderer->setStencilTest(false);
  215. mRenderer->setVertexShaderConstant(getVSRegister(VSP_MODELVIEWPROJ), projection);
  216. mRenderer->setPixelShaderConstant(getPSRegister(PSP_MATDIFFCOLOR), Color(1.0f, 1.0f, 1.0f, 1.0f));
  217. PixelShader* ps = 0;
  218. VertexShader* vs = 0;
  219. for (unsigned i = 0; i < mBatches.size(); ++i)
  220. {
  221. // Choose shaders here so that UIBatch does not need to look up shaders each time
  222. if (!mBatches[i].mTexture)
  223. {
  224. ps = mNoTexturePS;
  225. vs = mNoTextureVS;
  226. }
  227. else
  228. {
  229. // If texture contains only an alpha channel, use the alpha pixel shader
  230. vs = mDiffTextureVS;
  231. if (mBatches[i].mTexture->getFormat() == D3DFMT_A8)
  232. ps = mAlphaTexturePS;
  233. else
  234. ps = mDiffTexturePS;
  235. }
  236. mBatches[i].draw(mRenderer, vs, ps);
  237. }
  238. }
  239. void UI::addElementFactory(UIElementFactory* factory)
  240. {
  241. if (!factory)
  242. return;
  243. mFactories.push_back(SharedPtr<UIElementFactory>(factory));
  244. }
  245. SharedPtr<UIElement> UI::createElement(ShortStringHash type, const std::string& name)
  246. {
  247. SharedPtr<UIElement> element;
  248. for (unsigned i = 0; i < mFactories.size(); ++i)
  249. {
  250. element = mFactories[i]->createElement(type, name);
  251. if (element)
  252. return element;
  253. }
  254. EXCEPTION("Could not create unknown UI element type " + toString(type));
  255. }
  256. SharedPtr<UIElement> UI::loadLayout(XMLFile* file, XMLFile* styleFile)
  257. {
  258. PROFILE(UI_LoadLayout);
  259. SharedPtr<UIElement> root;
  260. if (!file)
  261. {
  262. LOGERROR("Null UI layout XML file");
  263. return root;
  264. }
  265. LOGDEBUG("Loading UI layout " + file->getName());
  266. XMLElement rootElem = file->getRootElement("element");
  267. if (!rootElem)
  268. {
  269. LOGERROR("No root UI element in " + file->getName());
  270. return root;
  271. }
  272. root = createElement(ShortStringHash(rootElem.getString("type")), rootElem.getString("name"));
  273. // First set the base style from the style file if exists, then apply UI layout overrides
  274. if (styleFile)
  275. root->setStyleAuto(styleFile, mCache);
  276. root->setStyle(rootElem, mCache);
  277. // Load rest of the elements recursively
  278. loadLayout(root, rootElem, styleFile);
  279. return root;
  280. }
  281. UIElement* UI::getElementAt(const IntVector2& position, bool enabledOnly)
  282. {
  283. UIElement* result = 0;
  284. getElementAt(result, mRootElement, position, enabledOnly);
  285. return result;
  286. }
  287. UIElement* UI::getElementAt(int x, int y, bool enabledOnly)
  288. {
  289. return getElementAt(IntVector2(x, y), enabledOnly);
  290. }
  291. UIElement* UI::getFocusElement()
  292. {
  293. std::vector<UIElement*> allChildren = mRootElement->getChildren(true);
  294. for (std::vector<UIElement*>::iterator i = allChildren.begin(); i != allChildren.end(); ++i)
  295. {
  296. if ((*i)->hasFocus())
  297. return *i;
  298. }
  299. return 0;
  300. }
  301. IntVector2 UI::getCursorPosition()
  302. {
  303. if (!mCursor)
  304. return IntVector2::sZero;
  305. else
  306. return mCursor->getPosition();
  307. }
  308. void UI::update(float timeStep, UIElement* element)
  309. {
  310. element->update(timeStep);
  311. const std::vector<UIElement*> children = element->getChildren();
  312. for (std::vector<UIElement*>::const_iterator i = children.begin(); i != children.end(); ++i)
  313. update(timeStep, *i);
  314. }
  315. void UI::getBatches(UIElement* element, IntRect currentScissor)
  316. {
  317. // Set clipping scissor for child elements. No need to draw if zero size
  318. element->adjustScissor(currentScissor);
  319. if ((currentScissor.mLeft == currentScissor.mRight) || (currentScissor.mTop == currentScissor.mBottom))
  320. return;
  321. std::vector<UIElement*> children = element->getChildren();
  322. if (children.empty())
  323. return;
  324. std::sort(children.begin(), children.end(), compareUIElements);
  325. // For non-root elements draw all children of same priority before recursing into their children: assumption is that they have
  326. // same renderstate
  327. std::vector<UIElement*>::const_iterator i = children.begin();
  328. if (element != mRootElement)
  329. {
  330. std::vector<UIElement*>::const_iterator j = i;
  331. int currentPriority = children.front()->getPriority();
  332. while (i != children.end())
  333. {
  334. while ((j != children.end()) && ((*j)->getPriority() == currentPriority))
  335. {
  336. if ((*j)->isVisible())
  337. (*j)->getBatches(mBatches, mQuads, currentScissor);
  338. ++j;
  339. }
  340. // Now recurse into the children
  341. while (i != j)
  342. {
  343. if ((*i)->isVisible())
  344. getBatches(*i, currentScissor);
  345. ++i;
  346. }
  347. if (i != children.end())
  348. currentPriority = (*i)->getPriority();
  349. }
  350. }
  351. // On the root level draw each element and its children immediately after to avoid artifacts
  352. else
  353. {
  354. while (i != children.end())
  355. {
  356. if ((*i)->isVisible())
  357. {
  358. (*i)->getBatches(mBatches, mQuads, currentScissor);
  359. getBatches(*i, currentScissor);
  360. }
  361. ++i;
  362. }
  363. }
  364. }
  365. void UI::getElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool enabledOnly)
  366. {
  367. if (!current)
  368. return;
  369. // Get children from lowest priority to highest
  370. std::vector<UIElement*> children = current->getChildren(false);
  371. std::sort(children.begin(), children.end(), compareUIElements);
  372. for (std::vector<UIElement*>::const_iterator i = children.begin(); i != children.end(); ++i)
  373. {
  374. UIElement* element = *i;
  375. if ((element != mCursor.getPtr()) && (element->isVisible()))
  376. {
  377. if (element->isInside(position, true))
  378. {
  379. // Store the current result, then recurse into its children. Because children
  380. // are sorted from lowest to highest priority, we should be left with the topmost match
  381. if ((element->isEnabled()) || (!enabledOnly))
  382. result = element;
  383. }
  384. if (element->isInsideCombined(position, true))
  385. getElementAt(result, element, position, enabledOnly);
  386. }
  387. }
  388. }
  389. void UI::handleWindowResized(StringHash eventType, VariantMap& eventData)
  390. {
  391. using namespace WindowResized;
  392. mRootElement->setSize(eventData[P_WIDTH].getInt(), eventData[P_HEIGHT].getInt());
  393. }
  394. void UI::handleMouseMove(StringHash eventType, VariantMap& eventData)
  395. {
  396. using namespace MouseMove;
  397. mMouseButtons = eventData[P_BUTTONS].getInt();
  398. mQualifiers = eventData[P_QUALIFIERS].getInt();
  399. if (mCursor)
  400. {
  401. if (eventType == EVENT_MOUSEMOVE)
  402. {
  403. // When deltas are sent, move the cursor only when visible
  404. if (mCursor->isVisible())
  405. {
  406. IntVector2 pos = mCursor->getPosition();
  407. pos.mX += eventData[P_X].getInt();
  408. pos.mY += eventData[P_Y].getInt();
  409. const IntVector2& rootSize = mRootElement->getSize();
  410. pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
  411. pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
  412. mCursor->setPosition(pos);
  413. }
  414. }
  415. else
  416. {
  417. // When absolute positions are sent, the cursor is not confined, so do not clamp the on-screen cursor's position
  418. mCursor->setPosition(eventData[P_X].getInt(), eventData[P_Y].getInt());
  419. }
  420. if ((mMouseDragElement) && (mMouseButtons))
  421. {
  422. IntVector2 pos = mCursor->getPosition();
  423. if ((mMouseDragElement->isEnabled()) && (mMouseDragElement->isVisible()))
  424. mMouseDragElement->onDragMove(mMouseDragElement->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  425. else
  426. mMouseDragElement.reset();
  427. }
  428. }
  429. }
  430. void UI::handleMouseButtonDown(StringHash eventType, VariantMap& eventData)
  431. {
  432. mMouseButtons = eventData[MouseButtonDown::P_BUTTONS].getInt();
  433. mQualifiers = eventData[MouseButtonDown::P_QUALIFIERS].getInt();
  434. int button = eventData[MouseButtonDown::P_BUTTON].getInt();
  435. if ((mCursor) && (mCursor->isVisible()))
  436. {
  437. IntVector2 pos = mCursor->getPosition();
  438. WeakPtr<UIElement> element(getElementAt(pos));
  439. if (element)
  440. {
  441. // Handle focusing & bringing to front
  442. if (button == MOUSEB_LEFT)
  443. {
  444. setFocusElement(element);
  445. element->bringToFront();
  446. }
  447. // Handle click
  448. element->onClick(element->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  449. // Handle start of drag. onClick() may have caused destruction of the element, so check the pointer again
  450. if ((element) && (!mMouseDragElement))
  451. {
  452. mMouseDragElement = element;
  453. element->onDragStart(element->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
  454. }
  455. }
  456. else
  457. {
  458. // If clicked over no element, or a disabled element, try to lose focus
  459. mDefocusElement = getFocusElement();
  460. }
  461. using namespace UIMouseClick;
  462. VariantMap eventData;
  463. eventData[UIMouseClick::P_ELEMENT] = (void*)element.getPtr();
  464. eventData[UIMouseClick::P_X] = pos.mX;
  465. eventData[UIMouseClick::P_Y] = pos.mY;
  466. eventData[UIMouseClick::P_BUTTON] = button;
  467. eventData[UIMouseClick::P_BUTTONS] = mMouseButtons;
  468. eventData[UIMouseClick::P_QUALIFIERS] = mQualifiers;
  469. sendEvent(EVENT_UIMOUSECLICK, eventData);
  470. }
  471. }
  472. void UI::handleMouseButtonUp(StringHash eventType, VariantMap& eventData)
  473. {
  474. using namespace MouseButtonUp;
  475. mMouseButtons = eventData[P_BUTTONS].getInt();
  476. mQualifiers = eventData[P_QUALIFIERS].getInt();
  477. if ((mCursor) && (mCursor->isVisible()))
  478. {
  479. IntVector2 pos = mCursor->getPosition();
  480. if ((mMouseDragElement) && (!mMouseButtons))
  481. {
  482. if ((mMouseDragElement->isEnabled()) && (mMouseDragElement->isVisible()))
  483. {
  484. mMouseDragElement->onDragEnd(mMouseDragElement->screenToElement(pos), pos, mCursor);
  485. // Drag and drop finish
  486. bool dragSource = (mMouseDragElement) && ((mMouseDragElement->getDragDropMode() & DD_SOURCE) != 0);
  487. if (dragSource)
  488. {
  489. WeakPtr<UIElement> target(getElementAt(pos));
  490. bool dragTarget = (target) && ((target->getDragDropMode() & DD_TARGET) != 0);
  491. bool dragDropFinish = (dragSource) && (dragTarget) && (target != mMouseDragElement);
  492. if (dragDropFinish)
  493. {
  494. bool accept = target->onDragDropFinish(mMouseDragElement);
  495. // onDragDropFinish() may have caused destruction of the elements, so check the pointers again
  496. if ((accept) && (mMouseDragElement) && (target))
  497. {
  498. using namespace UIDragDropFinish;
  499. VariantMap eventData;
  500. eventData[P_SOURCE] = (void*)mMouseDragElement.getPtr();
  501. eventData[P_TARGET] = (void*)target.getPtr();
  502. eventData[P_ACCEPT] = accept;
  503. sendEvent(EVENT_UIDRAGDROPFINISH, eventData);
  504. }
  505. }
  506. }
  507. }
  508. mMouseDragElement.reset();
  509. }
  510. }
  511. }
  512. void UI::handleMouseWheel(StringHash eventType, VariantMap& eventData)
  513. {
  514. using namespace MouseWheel;
  515. mMouseButtons = eventData[P_BUTTONS].getInt();
  516. mQualifiers = eventData[P_QUALIFIERS].getInt();
  517. int delta = eventData[P_WHEEL].getInt();
  518. UIElement* element = getFocusElement();
  519. if (element)
  520. element->onWheel(delta, mMouseButtons, mQualifiers);
  521. }
  522. void UI::handleKeyDown(StringHash eventType, VariantMap& eventData)
  523. {
  524. using namespace KeyDown;
  525. mMouseButtons = eventData[P_BUTTONS].getInt();
  526. mQualifiers = eventData[P_QUALIFIERS].getInt();
  527. int key = eventData[P_KEY].getInt();
  528. UIElement* element = getFocusElement();
  529. if (element)
  530. {
  531. // Switch focus between focusable elements in the same top level window
  532. if (key == KEY_TAB)
  533. {
  534. UIElement* topLevel = element->getParent();
  535. while ((topLevel) && (topLevel->getParent() != mRootElement))
  536. topLevel = topLevel->getParent();
  537. if (topLevel)
  538. {
  539. std::vector<UIElement*> children = topLevel->getChildren(true);
  540. for (std::vector<UIElement*>::iterator i = children.begin(); i != children.end();)
  541. {
  542. if ((*i)->getFocusMode() < FM_FOCUSABLE)
  543. i = children.erase(i);
  544. else
  545. ++i;
  546. }
  547. for (unsigned i = 0; i < children.size(); ++i)
  548. {
  549. if (children[i] == element)
  550. {
  551. UIElement* next = children[(i + 1) % children.size()];
  552. setFocusElement(next);
  553. return;
  554. }
  555. }
  556. }
  557. }
  558. // Defocus the element
  559. else if ((key == KEY_ESC) && (element->getFocusMode() == FM_FOCUSABLE_DEFOCUSABLE))
  560. mDefocusElement = element;
  561. // If none of the special keys, pass the key to the focused element
  562. else
  563. element->onKey(key, mMouseButtons, mQualifiers);
  564. }
  565. }
  566. void UI::handleChar(StringHash eventType, VariantMap& eventData)
  567. {
  568. using namespace Char;
  569. mMouseButtons = eventData[P_BUTTONS].getInt();
  570. mQualifiers = eventData[P_QUALIFIERS].getInt();
  571. UIElement* element = getFocusElement();
  572. if (element)
  573. element->onChar(eventData[P_CHAR].getInt(), mMouseButtons, mQualifiers);
  574. }
  575. void UI::loadLayout(UIElement* current, const XMLElement& elem, XMLFile* styleFile)
  576. {
  577. XMLElement childElem = elem.getChildElement("element");
  578. while (childElem)
  579. {
  580. // Create and add to the hierarchy
  581. SharedPtr<UIElement> child = createElement(ShortStringHash(childElem.getString("type")), childElem.getString("name"));
  582. current->addChild(child);
  583. // First set the base style from the style file if exists, then apply UI layout overrides
  584. if (styleFile)
  585. child->setStyleAuto(styleFile, mCache);
  586. child->setStyle(childElem, mCache);
  587. // Load the children recursively
  588. loadLayout(child, childElem, styleFile);
  589. childElem = childElem.getNextElement("element");
  590. }
  591. }