Container.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. #include "Base.h"
  2. #include "Container.h"
  3. #include "Layout.h"
  4. #include "AbsoluteLayout.h"
  5. #include "FlowLayout.h"
  6. #include "VerticalLayout.h"
  7. #include "Label.h"
  8. #include "Button.h"
  9. #include "CheckBox.h"
  10. #include "RadioButton.h"
  11. #include "Slider.h"
  12. #include "TextBox.h"
  13. #include "Joystick.h"
  14. #include "Game.h"
  15. namespace gameplay
  16. {
  17. // If the user stops scrolling for this amount of time (in millis) before touch/click release, don't apply inertia.
  18. static const long SCROLL_INERTIA_DELAY = 100L;
  19. // Factor to multiply friction by before applying to velocity.
  20. static const float SCROLL_FRICTION_FACTOR = 5.0f;
  21. /**
  22. * Sort function for use with _controls.sort(), based on Z-Order.
  23. *
  24. * @param c1 The first control
  25. * @param c2 The second control
  26. * return true if the first controls z index is less than the second.
  27. */
  28. static bool sortControlsByZOrder(Control* c1, Control* c2);
  29. Container::Container()
  30. : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
  31. _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
  32. _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
  33. _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
  34. _scrollingFirstX(0), _scrollingFirstY(0), _scrollingLastX(0), _scrollingLastY(0),
  35. _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
  36. _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
  37. _scrollingRight(false), _scrollingDown(false),
  38. _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
  39. _scrollBarOpacityClip(NULL), _zIndexDefault(0), _focusIndexDefault(0), _focusIndexMax(0), _totalWidth(0), _totalHeight(0),
  40. _contactIndices(0), _initializedWithScroll(false)
  41. {
  42. }
  43. Container::~Container()
  44. {
  45. std::vector<Control*>::iterator it;
  46. for (it = _controls.begin(); it < _controls.end(); it++)
  47. {
  48. SAFE_RELEASE((*it));
  49. }
  50. SAFE_RELEASE(_layout);
  51. }
  52. Container* Container::create(const char* id, Theme::Style* style, Layout::Type layoutType)
  53. {
  54. GP_ASSERT(style);
  55. Container* container = Container::create(layoutType);
  56. if (id)
  57. container->_id = id;
  58. container->_style = style;
  59. return container;
  60. }
  61. Container* Container::create(Layout::Type type)
  62. {
  63. Layout* layout = NULL;
  64. switch (type)
  65. {
  66. case Layout::LAYOUT_ABSOLUTE:
  67. layout = AbsoluteLayout::create();
  68. break;
  69. case Layout::LAYOUT_FLOW:
  70. layout = FlowLayout::create();
  71. break;
  72. case Layout::LAYOUT_VERTICAL:
  73. layout = VerticalLayout::create();
  74. break;
  75. }
  76. Container* container = new Container();
  77. container->_layout = layout;
  78. return container;
  79. }
  80. Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
  81. {
  82. GP_ASSERT(properties);
  83. const char* layoutString = properties->getString("layout");
  84. Container* container = Container::create(getLayoutType(layoutString));
  85. container->initialize(style, properties);
  86. container->_scroll = getScroll(properties->getString("scroll"));
  87. container->_scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
  88. if (container->_scrollBarsAutoHide)
  89. {
  90. container->_scrollBarOpacity = 0.0f;
  91. }
  92. container->addControls(theme, properties);
  93. container->_layout->update(container, container->_scrollPosition);
  94. return container;
  95. }
  96. void Container::addControls(Theme* theme, Properties* properties)
  97. {
  98. GP_ASSERT(theme);
  99. GP_ASSERT(properties);
  100. // Add all the controls to this container.
  101. Properties* controlSpace = properties->getNextNamespace();
  102. while (controlSpace != NULL)
  103. {
  104. Control* control = NULL;
  105. const char* controlStyleName = controlSpace->getString("style");
  106. Theme::Style* controlStyle = NULL;
  107. if (controlStyleName)
  108. {
  109. controlStyle = theme->getStyle(controlStyleName);
  110. }
  111. else
  112. {
  113. controlStyle = theme->getEmptyStyle();
  114. }
  115. std::string controlName(controlSpace->getNamespace());
  116. std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
  117. if (controlName == "LABEL")
  118. {
  119. control = Label::create(controlStyle, controlSpace);
  120. }
  121. else if (controlName == "BUTTON")
  122. {
  123. control = Button::create(controlStyle, controlSpace);
  124. }
  125. else if (controlName == "CHECKBOX")
  126. {
  127. control = CheckBox::create(controlStyle, controlSpace);
  128. }
  129. else if (controlName == "RADIOBUTTON")
  130. {
  131. control = RadioButton::create(controlStyle, controlSpace);
  132. }
  133. else if (controlName == "CONTAINER")
  134. {
  135. control = Container::create(controlStyle, controlSpace, theme);
  136. }
  137. else if (controlName == "SLIDER")
  138. {
  139. control = Slider::create(controlStyle, controlSpace);
  140. }
  141. else if (controlName == "TEXTBOX")
  142. {
  143. control = TextBox::create(controlStyle, controlSpace);
  144. }
  145. else if (controlName == "JOYSTICK")
  146. {
  147. control = Joystick::create(controlStyle, controlSpace);
  148. }
  149. else
  150. {
  151. GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
  152. }
  153. // Add the new control to the form.
  154. if (control)
  155. {
  156. addControl(control);
  157. control->release();
  158. if (control->getZIndex() == -1)
  159. {
  160. control->setZIndex(_zIndexDefault++);
  161. }
  162. if (control->getFocusIndex() == -1)
  163. {
  164. control->setFocusIndex(_focusIndexDefault++);
  165. }
  166. int focusIndex = control->getFocusIndex();
  167. if (focusIndex > _focusIndexMax)
  168. _focusIndexMax = focusIndex;
  169. }
  170. // Get the next control.
  171. controlSpace = properties->getNextNamespace();
  172. }
  173. // Sort controls by Z-Order.
  174. std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
  175. }
  176. Layout* Container::getLayout()
  177. {
  178. return _layout;
  179. }
  180. unsigned int Container::addControl(Control* control)
  181. {
  182. GP_ASSERT(control);
  183. if (control->_parent && control->_parent != this)
  184. {
  185. control->_parent->removeControl(control);
  186. }
  187. if (control->_parent != this)
  188. {
  189. _controls.push_back(control);
  190. control->addRef();
  191. control->_parent = this;
  192. return _controls.size() - 1;
  193. }
  194. else
  195. {
  196. // Control is already in this container.
  197. // Do nothing but determine and return control's index.
  198. const size_t size = _controls.size();
  199. for (size_t i = 0; i < size; ++i)
  200. {
  201. Control* c = _controls[i];
  202. if (c == control)
  203. {
  204. return i;
  205. }
  206. }
  207. // Should never reach this.
  208. GP_ASSERT(false);
  209. return 0;
  210. }
  211. }
  212. void Container::insertControl(Control* control, unsigned int index)
  213. {
  214. GP_ASSERT(control);
  215. if (control->_parent && control->_parent != this)
  216. {
  217. control->_parent->removeControl(control);
  218. }
  219. if (control->_parent != this)
  220. {
  221. std::vector<Control*>::iterator it = _controls.begin() + index;
  222. _controls.insert(it, control);
  223. control->addRef();
  224. control->_parent = this;
  225. }
  226. }
  227. void Container::removeControl(unsigned int index)
  228. {
  229. GP_ASSERT(index < _controls.size());
  230. std::vector<Control*>::iterator it = _controls.begin() + index;
  231. _controls.erase(it);
  232. Control* control = *it;
  233. control->_parent = NULL;
  234. SAFE_RELEASE(control);
  235. }
  236. void Container::removeControl(const char* id)
  237. {
  238. GP_ASSERT(id);
  239. std::vector<Control*>::iterator it;
  240. for (it = _controls.begin(); it < _controls.end(); it++)
  241. {
  242. Control* c = *it;
  243. if (strcmp(id, c->getId()) == 0)
  244. {
  245. SAFE_RELEASE(c);
  246. _controls.erase(it);
  247. return;
  248. }
  249. }
  250. }
  251. void Container::removeControl(Control* control)
  252. {
  253. GP_ASSERT(control);
  254. std::vector<Control*>::iterator it;
  255. for (it = _controls.begin(); it < _controls.end(); it++)
  256. {
  257. if (*it == control)
  258. {
  259. SAFE_RELEASE(control);
  260. _controls.erase(it);
  261. return;
  262. }
  263. }
  264. }
  265. Control* Container::getControl(unsigned int index) const
  266. {
  267. std::vector<Control*>::const_iterator it = _controls.begin() + index;
  268. return *it;
  269. }
  270. Control* Container::getControl(const char* id) const
  271. {
  272. GP_ASSERT(id);
  273. std::vector<Control*>::const_iterator it;
  274. for (it = _controls.begin(); it < _controls.end(); it++)
  275. {
  276. Control* c = *it;
  277. GP_ASSERT(c);
  278. if (strcmp(id, c->getId()) == 0)
  279. {
  280. return c;
  281. }
  282. else if (c->isContainer())
  283. {
  284. Control* cc = ((Container*)c)->getControl(id);
  285. if (cc)
  286. {
  287. return cc;
  288. }
  289. }
  290. }
  291. return NULL;
  292. }
  293. const std::vector<Control*>& Container::getControls() const
  294. {
  295. return _controls;
  296. }
  297. void Container::setScroll(Scroll scroll)
  298. {
  299. if (scroll != _scroll)
  300. {
  301. _scroll = scroll;
  302. _dirty = true;
  303. }
  304. }
  305. Container::Scroll Container::getScroll() const
  306. {
  307. return _scroll;
  308. }
  309. void Container::setScrollBarsAutoHide(bool autoHide)
  310. {
  311. if (autoHide != _scrollBarsAutoHide)
  312. {
  313. _scrollBarsAutoHide = autoHide;
  314. _dirty = true;
  315. }
  316. }
  317. bool Container::isScrollBarsAutoHide() const
  318. {
  319. return _scrollBarsAutoHide;
  320. }
  321. Animation* Container::getAnimation(const char* id) const
  322. {
  323. std::vector<Control*>::const_iterator itr = _controls.begin();
  324. std::vector<Control*>::const_iterator end = _controls.end();
  325. Control* control = NULL;
  326. for (; itr != end; itr++)
  327. {
  328. control = *itr;
  329. GP_ASSERT(control);
  330. Animation* animation = control->getAnimation(id);
  331. if (animation)
  332. return animation;
  333. if (control->isContainer())
  334. {
  335. animation = ((Container*)control)->getAnimation(id);
  336. if (animation)
  337. return animation;
  338. }
  339. }
  340. return NULL;
  341. }
  342. const char* Container::getType() const
  343. {
  344. return "container";
  345. }
  346. void Container::update(const Control* container, const Vector2& offset)
  347. {
  348. // Update this container's viewport.
  349. Control::update(container, offset);
  350. // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
  351. if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
  352. {
  353. _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
  354. _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
  355. _scrollBarRightCap = getImage("scrollBarRightCap", _state);
  356. GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
  357. _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
  358. }
  359. if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
  360. {
  361. _scrollBarTopCap = getImage("scrollBarTopCap", _state);
  362. _scrollBarVertical = getImage("verticalScrollBar", _state);
  363. _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
  364. GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
  365. _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
  366. }
  367. // Sort controls by Z-Order.
  368. std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
  369. GP_ASSERT(_layout);
  370. if (_scroll != SCROLL_NONE)
  371. updateScroll();
  372. else
  373. _layout->update(this, Vector2::zero());
  374. }
  375. void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
  376. {
  377. if (needsClear)
  378. {
  379. GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
  380. float clearY = targetHeight - _clearBounds.y - _clearBounds.height;
  381. GL_ASSERT( glScissor(_clearBounds.x, clearY, _clearBounds.width, _clearBounds.height) );
  382. Game::getInstance()->clear(Game::CLEAR_COLOR, Vector4::zero(), 1.0f, 0);
  383. GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
  384. needsClear = false;
  385. cleared = true;
  386. }
  387. else if (!cleared)
  388. {
  389. needsClear = true;
  390. }
  391. spriteBatch->start();
  392. Control::drawBorder(spriteBatch, clip);
  393. spriteBatch->finish();
  394. std::vector<Control*>::const_iterator it;
  395. Rectangle boundsUnion = Rectangle::empty();
  396. for (it = _controls.begin(); it < _controls.end(); it++)
  397. {
  398. Control* control = *it;
  399. GP_ASSERT(control);
  400. if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
  401. {
  402. control->draw(spriteBatch, _viewportClipBounds, needsClear, cleared, targetHeight);
  403. Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
  404. }
  405. }
  406. if (_scroll != SCROLL_NONE && (_scrollBarOpacity > 0.0f))
  407. {
  408. // Draw scroll bars.
  409. Rectangle clipRegion(_viewportClipBounds);
  410. spriteBatch->start();
  411. if (_scrollBarBounds.height > 0 &&
  412. ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL))
  413. {
  414. const Rectangle& topRegion = _scrollBarTopCap->getRegion();
  415. const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
  416. Vector4 topColor = _scrollBarTopCap->getColor();
  417. topColor.w *= _scrollBarOpacity * _opacity;
  418. const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
  419. const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
  420. Vector4 verticalColor = _scrollBarVertical->getColor();
  421. verticalColor.w *= _scrollBarOpacity * _opacity;
  422. const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
  423. const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
  424. Vector4 bottomColor = _scrollBarBottomCap->getColor();
  425. bottomColor.w *= _scrollBarOpacity * _opacity;
  426. clipRegion.width += verticalRegion.width;
  427. Rectangle bounds(_viewportBounds.x + _viewportBounds.width - verticalRegion.width, _viewportBounds.y + _scrollBarBounds.y, topRegion.width, topRegion.height);
  428. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, topUVs.u1, topUVs.v1, topUVs.u2, topUVs.v2, topColor, clipRegion);
  429. bounds.y += topRegion.height;
  430. bounds.height = _scrollBarBounds.height - topRegion.height - bottomRegion.height;
  431. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, verticalUVs.u1, verticalUVs.v1, verticalUVs.u2, verticalUVs.v2, verticalColor, clipRegion);
  432. bounds.y += bounds.height;
  433. bounds.height = bottomRegion.height;
  434. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
  435. }
  436. if (_scrollBarBounds.width > 0 &&
  437. ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL))
  438. {
  439. const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
  440. const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
  441. Vector4 leftColor = _scrollBarLeftCap->getColor();
  442. leftColor.w *= _scrollBarOpacity * _opacity;
  443. const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
  444. const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
  445. Vector4 horizontalColor = _scrollBarHorizontal->getColor();
  446. horizontalColor.w *= _scrollBarOpacity * _opacity;
  447. const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
  448. const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
  449. Vector4 rightColor = _scrollBarRightCap->getColor();
  450. rightColor.w *= _scrollBarOpacity * _opacity;
  451. clipRegion.height += horizontalRegion.height;
  452. Rectangle bounds(_viewportBounds.x + _scrollBarBounds.x, _viewportBounds.y + _viewportBounds.height - horizontalRegion.height, leftRegion.width, leftRegion.height);
  453. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, leftUVs.u1, leftUVs.v1, leftUVs.u2, leftUVs.v2, leftColor, clipRegion);
  454. bounds.x += leftRegion.width;
  455. bounds.width = _scrollBarBounds.width - leftRegion.width - rightRegion.width;
  456. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, horizontalUVs.u1, horizontalUVs.v1, horizontalUVs.u2, horizontalUVs.v2, horizontalColor, clipRegion);
  457. bounds.x += bounds.width;
  458. bounds.width = rightRegion.width;
  459. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, rightUVs.u1, rightUVs.v1, rightUVs.u2, rightUVs.v2, rightColor, clipRegion);
  460. }
  461. spriteBatch->finish();
  462. if (_scrollingVelocity.isZero())
  463. {
  464. _dirty = false;
  465. }
  466. }
  467. else
  468. {
  469. _dirty = false;
  470. }
  471. }
  472. bool Container::isDirty()
  473. {
  474. if (_dirty)
  475. {
  476. return true;
  477. }
  478. else
  479. {
  480. std::vector<Control*>::const_iterator it;
  481. for (it = _controls.begin(); it < _controls.end(); it++)
  482. {
  483. GP_ASSERT(*it);
  484. if ((*it)->isDirty())
  485. {
  486. return true;
  487. }
  488. }
  489. }
  490. return false;
  491. }
  492. bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  493. {
  494. return pointerEvent(false, evt, x, y, (int)contactIndex);
  495. }
  496. bool Container::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
  497. {
  498. return pointerEvent(true, evt, x, y, wheelDelta);
  499. }
  500. bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
  501. {
  502. // This event may run untrusted code by notifying listeners of events.
  503. // If the user calls exit() or otherwise releases this container, we
  504. // need to keep it alive until the method returns.
  505. addRef();
  506. std::vector<Control*>::const_iterator it;
  507. for (it = _controls.begin(); it < _controls.end(); it++)
  508. {
  509. Control* control = *it;
  510. GP_ASSERT(control);
  511. if (!control->isEnabled())
  512. {
  513. continue;
  514. }
  515. if (control->getState() == Control::FOCUS)
  516. {
  517. if (control->keyEvent(evt, key))
  518. {
  519. release();
  520. return _consumeInputEvents;
  521. }
  522. else if (evt == Keyboard::KEY_CHAR && key == Keyboard::KEY_TAB)
  523. {
  524. // Shift focus to next control.
  525. int focusIndex = control->getFocusIndex() + 1; // Index to search for.
  526. if (focusIndex > _focusIndexMax)
  527. {
  528. focusIndex = 0;
  529. }
  530. control->setState(Control::NORMAL);
  531. std::vector<Control*>::const_iterator itt;
  532. for (itt = _controls.begin(); itt < _controls.end(); itt++)
  533. {
  534. Control* nextControl = *itt;
  535. if (nextControl->getFocusIndex() == focusIndex)
  536. {
  537. nextControl->setState(Control::FOCUS);
  538. release();
  539. return _consumeInputEvents;
  540. }
  541. }
  542. }
  543. }
  544. }
  545. release();
  546. return false;
  547. }
  548. bool Container::isContainer() const
  549. {
  550. return true;
  551. }
  552. Layout::Type Container::getLayoutType(const char* layoutString)
  553. {
  554. if (!layoutString)
  555. {
  556. return Layout::LAYOUT_ABSOLUTE;
  557. }
  558. std::string layoutName(layoutString);
  559. std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
  560. if (layoutName == "LAYOUT_ABSOLUTE")
  561. {
  562. return Layout::LAYOUT_ABSOLUTE;
  563. }
  564. else if (layoutName == "LAYOUT_VERTICAL")
  565. {
  566. return Layout::LAYOUT_VERTICAL;
  567. }
  568. else if (layoutName == "LAYOUT_FLOW")
  569. {
  570. return Layout::LAYOUT_FLOW;
  571. }
  572. else
  573. {
  574. // Default.
  575. return Layout::LAYOUT_ABSOLUTE;
  576. }
  577. }
  578. void Container::updateScroll()
  579. {
  580. if (!_initializedWithScroll)
  581. {
  582. _initializedWithScroll = true;
  583. _layout->update(this, _scrollPosition);
  584. }
  585. // Update Time.
  586. static double lastFrameTime = Game::getGameTime();
  587. double frameTime = Game::getGameTime();
  588. float elapsedTime = (float)(frameTime - lastFrameTime);
  589. lastFrameTime = frameTime;
  590. const Theme::Border& containerBorder = getBorder(_state);
  591. const Theme::Padding& containerPadding = getPadding();
  592. // Calculate total width and height.
  593. std::vector<Control*> controls = getControls();
  594. unsigned int controlsCount = controls.size();
  595. for (unsigned int i = 0; i < controlsCount; i++)
  596. {
  597. Control* control = controls.at(i);
  598. const Rectangle& bounds = control->getBounds();
  599. const Theme::Margin& margin = control->getMargin();
  600. float newWidth = bounds.x + bounds.width;
  601. if (newWidth > _totalWidth)
  602. {
  603. _totalWidth = newWidth;
  604. }
  605. float newHeight = bounds.y + bounds.height;
  606. if (newHeight > _totalHeight)
  607. {
  608. _totalHeight = newHeight;
  609. }
  610. }
  611. float vWidth = getImageRegion("verticalScrollBar", _state).width;
  612. float hHeight = getImageRegion("horizontalScrollBar", _state).height;
  613. float clipWidth = _bounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
  614. float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
  615. // Apply and dampen inertia.
  616. if (!_scrolling && !_scrollingVelocity.isZero())
  617. {
  618. // Calculate the time passed since last update.
  619. float elapsedSecs = (float)elapsedTime * 0.001f;
  620. _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
  621. _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
  622. float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
  623. _scrollingVelocity.x *= dampening;
  624. _scrollingVelocity.y *= dampening;
  625. if (fabs(_scrollingVelocity.x) < 100.0f)
  626. _scrollingVelocity.x = 0.0f;
  627. if (fabs(_scrollingVelocity.y) < 100.0f)
  628. _scrollingVelocity.y = 0.0f;
  629. }
  630. // Stop scrolling when the far edge is reached.
  631. if (-_scrollPosition.x > _totalWidth - clipWidth)
  632. {
  633. _scrollPosition.x = -(_totalWidth - clipWidth);
  634. _scrollingVelocity.x = 0;
  635. }
  636. if (-_scrollPosition.y > _totalHeight - clipHeight)
  637. {
  638. _scrollPosition.y = -(_totalHeight - clipHeight);
  639. _scrollingVelocity.y = 0;
  640. }
  641. if (_scrollPosition.x > 0)
  642. {
  643. _scrollPosition.x = 0;
  644. _scrollingVelocity.x = 0;
  645. }
  646. if (_scrollPosition.y > 0)
  647. {
  648. _scrollPosition.y = 0;
  649. _scrollingVelocity.y = 0;
  650. }
  651. float scrollWidth = 0;
  652. if (clipWidth < _totalWidth)
  653. scrollWidth = (clipWidth / _totalWidth) * clipWidth;
  654. float scrollHeight = 0;
  655. if (clipHeight < _totalHeight)
  656. scrollHeight = (clipHeight / _totalHeight) * clipHeight;
  657. _scrollBarBounds.set(((-_scrollPosition.x) / _totalWidth) * clipWidth,
  658. ((-_scrollPosition.y) / _totalHeight) * clipHeight,
  659. scrollWidth, scrollHeight);
  660. // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
  661. if (!_scrolling && _scrollingVelocity.isZero() && _scrollBarsAutoHide && _scrollBarOpacity == 1.0f)
  662. {
  663. float to = 0;
  664. _scrollBarOpacity = 0.99f;
  665. if (!_scrollBarOpacityClip)
  666. {
  667. Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
  668. _scrollBarOpacityClip = animation->getClip();
  669. }
  670. _scrollBarOpacityClip->play();
  671. }
  672. // Position controls within scroll area.
  673. _layout->update(this, _scrollPosition);
  674. }
  675. bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  676. {
  677. switch(evt)
  678. {
  679. case Touch::TOUCH_PRESS:
  680. if (_contactIndex == INVALID_CONTACT_INDEX)
  681. {
  682. _contactIndex = (int) contactIndex;
  683. _contactIndices++;
  684. _scrollingLastX = _scrollingFirstX = x;
  685. _scrollingLastY = _scrollingFirstY = y;
  686. _scrollingVelocity.set(0, 0);
  687. _scrolling = true;
  688. _scrollingStartTimeX = _scrollingStartTimeY = 0;
  689. if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
  690. {
  691. _scrollBarOpacityClip->stop();
  692. _scrollBarOpacityClip = NULL;
  693. }
  694. _scrollBarOpacity = 1.0f;
  695. _dirty = true;
  696. return _consumeInputEvents;
  697. }
  698. break;
  699. case Touch::TOUCH_MOVE:
  700. if (_scrolling && _contactIndex == (int) contactIndex)
  701. {
  702. double gameTime = Game::getAbsoluteTime();
  703. // Calculate the latest movement delta for the next update to use.
  704. int vx = x - _scrollingLastX;
  705. int vy = y - _scrollingLastY;
  706. if (_scrollingMouseVertically)
  707. {
  708. float yRatio = _totalHeight / _absoluteBounds.height;
  709. vy *= yRatio;
  710. _scrollingVelocity.set(0, -vy);
  711. _scrollPosition.y -= vy;
  712. }
  713. else if (_scrollingMouseHorizontally)
  714. {
  715. float xRatio = _totalWidth / _absoluteBounds.width;
  716. vx *= xRatio;
  717. _scrollingVelocity.set(-vx, 0);
  718. _scrollPosition.x -= vx;
  719. }
  720. else
  721. {
  722. _scrollingVelocity.set(vx, vy);
  723. _scrollPosition.x += vx;
  724. _scrollPosition.y += vy;
  725. }
  726. _scrollingLastX = x;
  727. _scrollingLastY = y;
  728. // If the user changes direction, reset the start time and position.
  729. bool goingRight = (vx > 0);
  730. if (goingRight != _scrollingRight)
  731. {
  732. _scrollingFirstX = x;
  733. _scrollingRight = goingRight;
  734. _scrollingStartTimeX = gameTime;
  735. }
  736. bool goingDown = (vy > 0);
  737. if (goingDown != _scrollingDown)
  738. {
  739. _scrollingFirstY = y;
  740. _scrollingDown = goingDown;
  741. _scrollingStartTimeY = gameTime;
  742. }
  743. if (!_scrollingStartTimeX)
  744. _scrollingStartTimeX = gameTime;
  745. if (!_scrollingStartTimeY)
  746. _scrollingStartTimeY = gameTime;
  747. _scrollingLastTime = gameTime;
  748. _dirty = true;
  749. return _consumeInputEvents;
  750. }
  751. break;
  752. case Touch::TOUCH_RELEASE:
  753. if (_contactIndex == (int) contactIndex)
  754. {
  755. _contactIndex = INVALID_CONTACT_INDEX;
  756. _contactIndices--;
  757. _scrolling = false;
  758. double gameTime = Game::getAbsoluteTime();
  759. float timeSinceLastMove = (float)(gameTime - _scrollingLastTime);
  760. if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
  761. {
  762. _scrollingVelocity.set(0, 0);
  763. _scrollingMouseVertically = _scrollingMouseHorizontally = false;
  764. _dirty = true;
  765. return _consumeInputEvents;
  766. }
  767. int dx = _scrollingLastX - _scrollingFirstX;
  768. int dy = _scrollingLastY - _scrollingFirstY;
  769. float timeTakenX = (float)(gameTime - _scrollingStartTimeX);
  770. float elapsedSecsX = timeTakenX * 0.001f;
  771. float timeTakenY = (float)(gameTime - _scrollingStartTimeY);
  772. float elapsedSecsY = timeTakenY * 0.001f;
  773. float vx = dx;
  774. float vy = dy;
  775. if (elapsedSecsX > 0)
  776. vx = (float)dx / elapsedSecsX;
  777. if (elapsedSecsY > 0)
  778. vy = (float)dy / elapsedSecsY;
  779. if (_scrollingMouseVertically)
  780. {
  781. float yRatio = _totalHeight / _absoluteBounds.height;
  782. vy *= yRatio;
  783. _scrollingVelocity.set(0, -vy);
  784. }
  785. else if (_scrollingMouseHorizontally)
  786. {
  787. float xRatio = _totalWidth / _absoluteBounds.width;
  788. vx *= xRatio;
  789. _scrollingVelocity.set(-vx, 0);
  790. }
  791. else
  792. {
  793. _scrollingVelocity.set(vx, vy);
  794. }
  795. _scrollingMouseVertically = _scrollingMouseHorizontally = false;
  796. _dirty = true;
  797. return _consumeInputEvents;
  798. }
  799. break;
  800. }
  801. return false;
  802. }
  803. bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
  804. {
  805. switch(evt)
  806. {
  807. case Mouse::MOUSE_PRESS_LEFT_BUTTON:
  808. {
  809. if (_scrollBarVertical)
  810. {
  811. float vWidth = _scrollBarVertical->getRegion().width;
  812. Rectangle vBounds(_viewportBounds.x + _viewportBounds.width - vWidth,
  813. _scrollBarBounds.y,
  814. vWidth, _scrollBarBounds.height);
  815. if (x + _viewportBounds.x >= vBounds.x &&
  816. x + _viewportBounds.x <= vBounds.x + vBounds.width)
  817. {
  818. // Then we're within the horizontal bounds of the verticle scrollbar.
  819. // We want to either jump up or down, or drag the scrollbar itself.
  820. if (y < vBounds.y)
  821. {
  822. _scrollPosition.y += _totalHeight / 5.0f;
  823. }
  824. else if (y > vBounds.y + vBounds.height)
  825. {
  826. _scrollPosition.y -= _totalHeight / 5.0f;
  827. }
  828. else
  829. {
  830. _scrollingMouseVertically = true;
  831. }
  832. }
  833. }
  834. if (_scrollBarHorizontal)
  835. {
  836. float hHeight = _scrollBarHorizontal->getRegion().height;
  837. Rectangle hBounds(_scrollBarBounds.x,
  838. _viewportBounds.y + _viewportBounds.height - hHeight,
  839. _scrollBarBounds.width, hHeight);
  840. if (y + _viewportBounds.y >= hBounds.y &&
  841. y + _viewportBounds.y <= hBounds.y + hBounds.height)
  842. {
  843. // We're within the vertical bounds of the horizontal scrollbar.
  844. if (x < hBounds.x)
  845. _scrollPosition.x += _totalWidth / 5.0f;
  846. else if (x > hBounds.x + hBounds.width)
  847. _scrollPosition.x -= _totalWidth / 5.0f;
  848. else
  849. _scrollingMouseHorizontally = true;
  850. }
  851. }
  852. return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
  853. }
  854. case Mouse::MOUSE_MOVE:
  855. return touchEventScroll(Touch::TOUCH_MOVE, x, y, 0);
  856. case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
  857. return touchEventScroll(Touch::TOUCH_RELEASE, x, y, 0);
  858. case Mouse::MOUSE_WHEEL:
  859. _scrollPosition.y += (_totalHeight / 10.0f) * wheelDelta;
  860. if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
  861. {
  862. _scrollBarOpacityClip->stop();
  863. _scrollBarOpacityClip = NULL;
  864. }
  865. _scrollBarOpacity = 1.0f;
  866. _dirty = true;
  867. return _consumeInputEvents;
  868. }
  869. return false;
  870. }
  871. bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
  872. {
  873. if (!isEnabled())
  874. {
  875. return false;
  876. }
  877. // This event may run untrusted code by notifying listeners of events.
  878. // If the user calls exit() or otherwise releases this container, we
  879. // need to keep it alive until the method returns.
  880. addRef();
  881. bool eventConsumed = false;
  882. const Theme::Border& border = getBorder(_state);
  883. const Theme::Padding& padding = getPadding();
  884. float xPos = border.left + padding.left;
  885. float yPos = border.top + padding.top;
  886. Vector2* offset = NULL;
  887. if (_scroll != SCROLL_NONE)
  888. {
  889. offset = &_scrollPosition;
  890. }
  891. std::vector<Control*>::const_iterator it;
  892. for (it = _controls.begin(); it < _controls.end(); it++)
  893. {
  894. Control* control = *it;
  895. GP_ASSERT(control);
  896. if (!control->isEnabled())
  897. {
  898. continue;
  899. }
  900. const Rectangle& bounds = control->getBounds();
  901. float boundsX = bounds.x;
  902. float boundsY = bounds.y;
  903. if (offset)
  904. {
  905. boundsX += offset->x;
  906. boundsY += offset->y;
  907. }
  908. Control::State currentState = control->getState();
  909. if ((control->isContainer() && currentState == Control::FOCUS) ||
  910. (currentState != Control::NORMAL) ||
  911. ((evt == Touch::TOUCH_PRESS ||
  912. evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
  913. evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
  914. evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON) &&
  915. x >= xPos + boundsX &&
  916. x <= xPos + boundsX + bounds.width &&
  917. y >= yPos + boundsY &&
  918. y <= yPos + boundsY + bounds.height))
  919. {
  920. // Pass on the event's clip relative to the control.
  921. if (mouse)
  922. eventConsumed |= control->mouseEvent((Mouse::MouseEvent)evt, x - xPos - boundsX, y - yPos - boundsY, data);
  923. else
  924. eventConsumed |= control->touchEvent((Touch::TouchEvent)evt, x - xPos - boundsX, y - yPos - boundsY, (unsigned int)data);
  925. }
  926. }
  927. if (!isEnabled())
  928. {
  929. release();
  930. return (_consumeInputEvents | eventConsumed);
  931. }
  932. bool withinClipBounds = false;
  933. switch (evt)
  934. {
  935. case Touch::TOUCH_PRESS:
  936. if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
  937. y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
  938. {
  939. setState(Control::FOCUS);
  940. withinClipBounds = true;
  941. if (eventConsumed)
  942. _contactIndices++;
  943. }
  944. else if (_contactIndices == 0)
  945. {
  946. setState(Control::NORMAL);
  947. _contactIndex = INVALID_CONTACT_INDEX;
  948. release();
  949. return false;
  950. }
  951. break;
  952. case Touch::TOUCH_RELEASE:
  953. {
  954. if (eventConsumed)
  955. {
  956. if (_contactIndices > 0)
  957. _contactIndices--;
  958. }
  959. }
  960. break;
  961. }
  962. if (!eventConsumed && _scroll != SCROLL_NONE && getState() == Control::FOCUS && (evt != Touch::TOUCH_PRESS || withinClipBounds))
  963. {
  964. if ((mouse && mouseEventScroll((Mouse::MouseEvent)evt, x - xPos, y - yPos, data)) ||
  965. (!mouse && touchEventScroll((Touch::TouchEvent)evt, x - xPos, y - yPos, (unsigned int)data)))
  966. {
  967. eventConsumed = true;
  968. }
  969. }
  970. release();
  971. return (_consumeInputEvents | eventConsumed);
  972. }
  973. Container::Scroll Container::getScroll(const char* scroll)
  974. {
  975. if (!scroll)
  976. return Container::SCROLL_NONE;
  977. if (strcmp(scroll, "SCROLL_NONE") == 0)
  978. {
  979. return Container::SCROLL_NONE;
  980. }
  981. else if (strcmp(scroll, "SCROLL_HORIZONTAL") == 0)
  982. {
  983. return Container::SCROLL_HORIZONTAL;
  984. }
  985. else if (strcmp(scroll, "SCROLL_VERTICAL") == 0)
  986. {
  987. return Container::SCROLL_VERTICAL;
  988. }
  989. else if (strcmp(scroll, "SCROLL_BOTH") == 0)
  990. {
  991. return Container::SCROLL_BOTH;
  992. }
  993. else
  994. {
  995. GP_ERROR("Failed to get corresponding scroll state for unsupported value '%s'.", scroll);
  996. }
  997. return Container::SCROLL_NONE;
  998. }
  999. static bool sortControlsByZOrder(Control* c1, Control* c2)
  1000. {
  1001. if (c1->getZIndex() < c2->getZIndex())
  1002. return true;
  1003. return false;
  1004. }
  1005. unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
  1006. {
  1007. switch(propertyId)
  1008. {
  1009. case ANIMATE_OPACITY:
  1010. return 1;
  1011. default:
  1012. return Control::getAnimationPropertyComponentCount(propertyId);
  1013. }
  1014. }
  1015. void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
  1016. {
  1017. GP_ASSERT(value);
  1018. switch(propertyId)
  1019. {
  1020. case ANIMATE_OPACITY:
  1021. value->setFloat(0, _scrollBarOpacity);
  1022. break;
  1023. default:
  1024. Control::getAnimationPropertyValue(propertyId, value);
  1025. break;
  1026. }
  1027. }
  1028. void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
  1029. {
  1030. GP_ASSERT(value);
  1031. switch(propertyId)
  1032. {
  1033. case ANIMATE_OPACITY:
  1034. _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
  1035. _dirty = true;
  1036. break;
  1037. default:
  1038. Control::setAnimationPropertyValue(propertyId, value, blendWeight);
  1039. break;
  1040. }
  1041. }
  1042. }