Container.cpp 39 KB

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