Container.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  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. Container::Container()
  22. : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
  23. _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
  24. _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
  25. _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
  26. _scrollingFirstX(0), _scrollingFirstY(0),
  27. _scrollingLastX(0), _scrollingLastY(0),
  28. _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
  29. _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
  30. _scrollingRight(false), _scrollingDown(false), _scrollBarOpacityClip(NULL), _zIndexDefault(0)
  31. {
  32. }
  33. Container::Container(const Container& copy)
  34. {
  35. }
  36. Container::~Container()
  37. {
  38. std::vector<Control*>::iterator it;
  39. for (it = _controls.begin(); it < _controls.end(); it++)
  40. {
  41. SAFE_RELEASE((*it));
  42. }
  43. SAFE_RELEASE(_layout);
  44. }
  45. Container* Container::create(Layout::Type type)
  46. {
  47. Layout* layout = NULL;
  48. switch (type)
  49. {
  50. case Layout::LAYOUT_ABSOLUTE:
  51. layout = AbsoluteLayout::create();
  52. break;
  53. case Layout::LAYOUT_FLOW:
  54. layout = FlowLayout::create();
  55. break;
  56. case Layout::LAYOUT_VERTICAL:
  57. layout = VerticalLayout::create();
  58. break;
  59. }
  60. Container* container = new Container();
  61. container->_layout = layout;
  62. return container;
  63. }
  64. Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
  65. {
  66. GP_ASSERT(properties);
  67. const char* layoutString = properties->getString("layout");
  68. Container* container = Container::create(getLayoutType(layoutString));
  69. container->initialize(style, properties);
  70. container->_scroll = getScroll(properties->getString("scroll"));
  71. container->_scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
  72. if (container->_scrollBarsAutoHide)
  73. {
  74. container->_scrollBarOpacity = 0.0f;
  75. }
  76. container->addControls(theme, properties);
  77. container->_layout->update(container, container->_scrollPosition);
  78. return container;
  79. }
  80. void Container::addControls(Theme* theme, Properties* properties)
  81. {
  82. GP_ASSERT(theme);
  83. GP_ASSERT(properties);
  84. // Add all the controls to this container.
  85. Properties* controlSpace = properties->getNextNamespace();
  86. while (controlSpace != NULL)
  87. {
  88. Control* control = NULL;
  89. const char* controlStyleName = controlSpace->getString("style");
  90. Theme::Style* controlStyle = NULL;
  91. if (controlStyleName)
  92. {
  93. controlStyle = theme->getStyle(controlStyleName);
  94. }
  95. else
  96. {
  97. Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
  98. controlStyle = new Theme::Style(theme, "", 1.0f / theme->_texture->getWidth(), 1.0f / theme->_texture->getHeight(),
  99. Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
  100. }
  101. std::string controlName(controlSpace->getNamespace());
  102. std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
  103. if (controlName == "LABEL")
  104. {
  105. control = Label::create(controlStyle, controlSpace);
  106. }
  107. else if (controlName == "BUTTON")
  108. {
  109. control = Button::create(controlStyle, controlSpace);
  110. }
  111. else if (controlName == "CHECKBOX")
  112. {
  113. control = CheckBox::create(controlStyle, controlSpace);
  114. }
  115. else if (controlName == "RADIOBUTTON")
  116. {
  117. control = RadioButton::create(controlStyle, controlSpace);
  118. }
  119. else if (controlName == "CONTAINER")
  120. {
  121. control = Container::create(controlStyle, controlSpace, theme);
  122. }
  123. else if (controlName == "SLIDER")
  124. {
  125. control = Slider::create(controlStyle, controlSpace);
  126. }
  127. else if (controlName == "TEXTBOX")
  128. {
  129. control = TextBox::create(controlStyle, controlSpace);
  130. }
  131. else if (controlName == "JOYSTICK")
  132. {
  133. control = Joystick::create(controlStyle, controlSpace);
  134. }
  135. else
  136. {
  137. GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
  138. }
  139. // Add the new control to the form.
  140. if (control)
  141. {
  142. addControl(control);
  143. if (control->getZIndex() == -1)
  144. {
  145. control->setZIndex(_zIndexDefault++);
  146. }
  147. }
  148. // Get the next control.
  149. controlSpace = properties->getNextNamespace();
  150. }
  151. // Sort controls by Z-Order.
  152. std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
  153. }
  154. Layout* Container::getLayout()
  155. {
  156. return _layout;
  157. }
  158. unsigned int Container::addControl(Control* control)
  159. {
  160. GP_ASSERT(control);
  161. _controls.push_back(control);
  162. return _controls.size() - 1;
  163. }
  164. void Container::insertControl(Control* control, unsigned int index)
  165. {
  166. GP_ASSERT(control);
  167. std::vector<Control*>::iterator it = _controls.begin() + index;
  168. _controls.insert(it, control);
  169. }
  170. void Container::removeControl(unsigned int index)
  171. {
  172. std::vector<Control*>::iterator it = _controls.begin() + index;
  173. _controls.erase(it);
  174. }
  175. void Container::removeControl(const char* id)
  176. {
  177. std::vector<Control*>::iterator it;
  178. for (it = _controls.begin(); it < _controls.end(); it++)
  179. {
  180. Control* c = *it;
  181. if (strcmp(id, c->getID()) == 0)
  182. {
  183. _controls.erase(it);
  184. return;
  185. }
  186. }
  187. }
  188. void Container::removeControl(Control* control)
  189. {
  190. GP_ASSERT(control);
  191. std::vector<Control*>::iterator it;
  192. for (it = _controls.begin(); it < _controls.end(); it++)
  193. {
  194. if (*it == control)
  195. {
  196. _controls.erase(it);
  197. return;
  198. }
  199. }
  200. }
  201. Control* Container::getControl(unsigned int index) const
  202. {
  203. std::vector<Control*>::const_iterator it = _controls.begin() + index;
  204. return *it;
  205. }
  206. Control* Container::getControl(const char* id) const
  207. {
  208. GP_ASSERT(id);
  209. std::vector<Control*>::const_iterator it;
  210. for (it = _controls.begin(); it < _controls.end(); it++)
  211. {
  212. Control* c = *it;
  213. GP_ASSERT(c);
  214. if (strcmp(id, c->getID()) == 0)
  215. {
  216. return c;
  217. }
  218. else if (c->isContainer())
  219. {
  220. Control* cc = ((Container*)c)->getControl(id);
  221. if (cc)
  222. {
  223. return cc;
  224. }
  225. }
  226. }
  227. return NULL;
  228. }
  229. const std::vector<Control*>& Container::getControls() const
  230. {
  231. return _controls;
  232. }
  233. void Container::setScroll(Scroll scroll)
  234. {
  235. if (scroll != _scroll)
  236. {
  237. _scroll = scroll;
  238. _dirty = true;
  239. }
  240. }
  241. Container::Scroll Container::getScroll() const
  242. {
  243. return _scroll;
  244. }
  245. void Container::setScrollBarsAutoHide(bool autoHide)
  246. {
  247. if (autoHide != _scrollBarsAutoHide)
  248. {
  249. _scrollBarsAutoHide = autoHide;
  250. _dirty = true;
  251. }
  252. }
  253. bool Container::isScrollBarsAutoHide() const
  254. {
  255. return _scrollBarsAutoHide;
  256. }
  257. Animation* Container::getAnimation(const char* id) const
  258. {
  259. std::vector<Control*>::const_iterator itr = _controls.begin();
  260. std::vector<Control*>::const_iterator end = _controls.end();
  261. Control* control = NULL;
  262. for (; itr != end; itr++)
  263. {
  264. control = *itr;
  265. GP_ASSERT(control);
  266. Animation* animation = control->getAnimation(id);
  267. if (animation)
  268. return animation;
  269. if (control->isContainer())
  270. {
  271. animation = ((Container*)control)->getAnimation(id);
  272. if (animation)
  273. return animation;
  274. }
  275. }
  276. return NULL;
  277. }
  278. void Container::update(const Control* container, const Vector2& offset)
  279. {
  280. // Update this container's viewport.
  281. Control::update(container, offset);
  282. // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
  283. if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
  284. {
  285. _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
  286. _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
  287. _scrollBarRightCap = getImage("scrollBarRightCap", _state);
  288. GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
  289. _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
  290. }
  291. if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
  292. {
  293. _scrollBarTopCap = getImage("scrollBarTopCap", _state);
  294. _scrollBarVertical = getImage("verticalScrollBar", _state);
  295. _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
  296. GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
  297. _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
  298. }
  299. // Sort controls by Z-Order.
  300. std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
  301. GP_ASSERT(_layout);
  302. if (_scroll != SCROLL_NONE)
  303. updateScroll();
  304. else
  305. _layout->update(this, Vector2::zero());
  306. }
  307. void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
  308. {
  309. if (needsClear)
  310. {
  311. GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
  312. GL_ASSERT( glClearColor(0, 0, 0, 0) );
  313. float clearY = targetHeight - _clearBounds.y - _clearBounds.height;
  314. GL_ASSERT( glScissor(_clearBounds.x, clearY,
  315. _clearBounds.width, _clearBounds.height) );
  316. GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
  317. GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
  318. needsClear = false;
  319. cleared = true;
  320. }
  321. else if (!cleared)
  322. {
  323. needsClear = true;
  324. }
  325. spriteBatch->begin();
  326. Control::drawBorder(spriteBatch, clip);
  327. spriteBatch->end();
  328. std::vector<Control*>::const_iterator it;
  329. Rectangle boundsUnion = Rectangle::empty();
  330. for (it = _controls.begin(); it < _controls.end(); it++)
  331. {
  332. Control* control = *it;
  333. GP_ASSERT(control);
  334. if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
  335. {
  336. control->draw(spriteBatch, _viewportClipBounds, needsClear, cleared, targetHeight);
  337. Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
  338. }
  339. }
  340. if (_scroll != SCROLL_NONE && (_scrollBarOpacity > 0.0f))
  341. {
  342. // Draw scroll bars.
  343. Rectangle clipRegion(_viewportClipBounds);
  344. spriteBatch->begin();
  345. if (_scrollBarBounds.height > 0)
  346. {
  347. const Rectangle& topRegion = _scrollBarTopCap->getRegion();
  348. const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
  349. Vector4 topColor = _scrollBarTopCap->getColor();
  350. topColor.w *= _scrollBarOpacity * _opacity;
  351. const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
  352. const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
  353. Vector4 verticalColor = _scrollBarVertical->getColor();
  354. verticalColor.w *= _scrollBarOpacity * _opacity;
  355. const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
  356. const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
  357. Vector4 bottomColor = _scrollBarBottomCap->getColor();
  358. bottomColor.w *= _scrollBarOpacity * _opacity;
  359. clipRegion.width += verticalRegion.width;
  360. Rectangle bounds(_viewportBounds.x + _viewportBounds.width - verticalRegion.width,
  361. _viewportBounds.y + _scrollBarBounds.y,
  362. topRegion.width, topRegion.height);
  363. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, topUVs.u1, topUVs.v1, topUVs.u2, topUVs.v2, topColor, clipRegion);
  364. bounds.y += topRegion.height;
  365. bounds.height = _scrollBarBounds.height - topRegion.height - bottomRegion.height;
  366. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, verticalUVs.u1, verticalUVs.v1, verticalUVs.u2, verticalUVs.v2, verticalColor, clipRegion);
  367. bounds.y += bounds.height;
  368. bounds.height = bottomRegion.height;
  369. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
  370. }
  371. if (_scrollBarBounds.width > 0)
  372. {
  373. const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
  374. const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
  375. Vector4 leftColor = _scrollBarLeftCap->getColor();
  376. leftColor.w *= _scrollBarOpacity * _opacity;
  377. const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
  378. const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
  379. Vector4 horizontalColor = _scrollBarHorizontal->getColor();
  380. horizontalColor.w *= _scrollBarOpacity * _opacity;
  381. const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
  382. const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
  383. Vector4 rightColor = _scrollBarRightCap->getColor();
  384. rightColor.w *= _scrollBarOpacity * _opacity;
  385. clipRegion.height += horizontalRegion.height;
  386. Rectangle bounds(_viewportBounds.x + _scrollBarBounds.x,
  387. _viewportBounds.y + _viewportBounds.height - horizontalRegion.height,
  388. leftRegion.width, leftRegion.height);
  389. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, leftUVs.u1, leftUVs.v1, leftUVs.u2, leftUVs.v2, leftColor, clipRegion);
  390. bounds.x += leftRegion.width;
  391. bounds.width = _scrollBarBounds.width - leftRegion.width - rightRegion.width;
  392. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, horizontalUVs.u1, horizontalUVs.v1, horizontalUVs.u2, horizontalUVs.v2, horizontalColor, clipRegion);
  393. bounds.x += bounds.width;
  394. bounds.width = rightRegion.width;
  395. spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, rightUVs.u1, rightUVs.v1, rightUVs.u2, rightUVs.v2, rightColor, clipRegion);
  396. }
  397. spriteBatch->end();
  398. if (_scrollingVelocity.isZero())
  399. {
  400. _dirty = false;
  401. }
  402. }
  403. else
  404. {
  405. _dirty = false;
  406. }
  407. }
  408. bool Container::isDirty()
  409. {
  410. if (_dirty)
  411. {
  412. return true;
  413. }
  414. else
  415. {
  416. std::vector<Control*>::const_iterator it;
  417. for (it = _controls.begin(); it < _controls.end(); it++)
  418. {
  419. GP_ASSERT(*it);
  420. if ((*it)->isDirty())
  421. {
  422. return true;
  423. }
  424. }
  425. }
  426. return false;
  427. }
  428. bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  429. {
  430. if (!isEnabled())
  431. {
  432. return false;
  433. }
  434. bool eventConsumed = false;
  435. const Theme::Border& border = getBorder(_state);
  436. const Theme::Padding& padding = getPadding();
  437. float xPos = border.left + padding.left;
  438. float yPos = border.top + padding.top;
  439. Vector2* offset = NULL;
  440. if (_scroll != SCROLL_NONE)
  441. {
  442. offset = &_scrollPosition;
  443. }
  444. std::vector<Control*>::const_iterator it;
  445. for (it = _controls.begin(); it < _controls.end(); it++)
  446. {
  447. Control* control = *it;
  448. GP_ASSERT(control);
  449. if (!control->isEnabled())
  450. {
  451. continue;
  452. }
  453. const Rectangle& bounds = control->getBounds();
  454. float boundsX = bounds.x;
  455. float boundsY = bounds.y;
  456. if (offset)
  457. {
  458. boundsX += offset->x;
  459. boundsY += offset->y;
  460. }
  461. if (control->getState() != Control::NORMAL ||
  462. (evt == Touch::TOUCH_PRESS &&
  463. x >= xPos + boundsX &&
  464. x <= xPos + boundsX + bounds.width &&
  465. y >= yPos + boundsY &&
  466. y <= yPos + boundsY + bounds.height))
  467. {
  468. // Pass on the event's clip relative to the control.
  469. eventConsumed |= control->touchEvent(evt, x - xPos - boundsX, y - yPos - boundsY, contactIndex);
  470. }
  471. }
  472. if (!isEnabled())
  473. {
  474. return (_consumeTouchEvents | eventConsumed);
  475. }
  476. switch (evt)
  477. {
  478. case Touch::TOUCH_PRESS:
  479. setState(Control::FOCUS);
  480. break;
  481. case Touch::TOUCH_RELEASE:
  482. setState(Control::NORMAL);
  483. break;
  484. }
  485. if (!eventConsumed && _scroll != SCROLL_NONE)
  486. {
  487. if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
  488. {
  489. _dirty = true;
  490. }
  491. }
  492. return (_consumeTouchEvents | eventConsumed);
  493. }
  494. void Container::keyEvent(Keyboard::KeyEvent evt, int key)
  495. {
  496. std::vector<Control*>::const_iterator it;
  497. for (it = _controls.begin(); it < _controls.end(); it++)
  498. {
  499. Control* control = *it;
  500. GP_ASSERT(control);
  501. if (!control->isEnabled())
  502. {
  503. continue;
  504. }
  505. if (control->isContainer() || control->getState() == Control::FOCUS)
  506. {
  507. control->keyEvent(evt, key);
  508. }
  509. }
  510. }
  511. bool Container::isContainer()
  512. {
  513. return true;
  514. }
  515. Layout::Type Container::getLayoutType(const char* layoutString)
  516. {
  517. if (!layoutString)
  518. {
  519. return Layout::LAYOUT_ABSOLUTE;
  520. }
  521. std::string layoutName(layoutString);
  522. std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
  523. if (layoutName == "LAYOUT_ABSOLUTE")
  524. {
  525. return Layout::LAYOUT_ABSOLUTE;
  526. }
  527. else if (layoutName == "LAYOUT_VERTICAL")
  528. {
  529. return Layout::LAYOUT_VERTICAL;
  530. }
  531. else if (layoutName == "LAYOUT_FLOW")
  532. {
  533. return Layout::LAYOUT_FLOW;
  534. }
  535. else if (layoutName == "LAYOUT_SCROLL")
  536. {
  537. return Layout::LAYOUT_SCROLL;
  538. }
  539. else
  540. {
  541. // Default.
  542. return Layout::LAYOUT_ABSOLUTE;
  543. }
  544. }
  545. void Container::updateScroll()
  546. {
  547. // Update Time.
  548. static long lastFrameTime = Game::getGameTime();
  549. long frameTime = Game::getGameTime();
  550. long elapsedTime = (frameTime - lastFrameTime);
  551. lastFrameTime = frameTime;
  552. const Theme::Border& containerBorder = getBorder(_state);
  553. const Theme::Padding& containerPadding = getPadding();
  554. // Calculate total width and height.
  555. float totalWidth = 0;
  556. float totalHeight = 0;
  557. std::vector<Control*> controls = getControls();
  558. unsigned int controlsCount = controls.size();
  559. for (unsigned int i = 0; i < controlsCount; i++)
  560. {
  561. Control* control = controls.at(i);
  562. const Rectangle& bounds = control->getBounds();
  563. const Theme::Margin& margin = control->getMargin();
  564. float newWidth = bounds.x + bounds.width;
  565. if (newWidth > totalWidth)
  566. {
  567. totalWidth = newWidth;
  568. }
  569. float newHeight = bounds.y + bounds.height;
  570. if (newHeight > totalHeight)
  571. {
  572. totalHeight = newHeight;
  573. }
  574. }
  575. float vWidth = getImageRegion("verticalScrollBar", _state).width;
  576. float hHeight = getImageRegion("horizontalScrollBar", _state).height;
  577. float clipWidth = _bounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
  578. float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
  579. // Apply and dampen inertia.
  580. if (!_scrolling && !_scrollingVelocity.isZero())
  581. {
  582. // Calculate the time passed since last update.
  583. float elapsedSecs = (float)elapsedTime * 0.001f;
  584. _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
  585. _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
  586. float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
  587. _scrollingVelocity.x *= dampening;
  588. _scrollingVelocity.y *= dampening;
  589. if (abs(_scrollingVelocity.x) < 100.0f)
  590. _scrollingVelocity.x = 0.0f;
  591. if (abs(_scrollingVelocity.y) < 100.0f)
  592. _scrollingVelocity.y = 0.0f;
  593. }
  594. // Stop scrolling when the far edge is reached.
  595. if (-_scrollPosition.x > totalWidth - clipWidth)
  596. {
  597. _scrollPosition.x = -(totalWidth - clipWidth);
  598. _scrollingVelocity.x = 0;
  599. }
  600. if (-_scrollPosition.y > totalHeight - clipHeight)
  601. {
  602. _scrollPosition.y = -(totalHeight - clipHeight);
  603. _scrollingVelocity.y = 0;
  604. }
  605. if (_scrollPosition.x > 0)
  606. {
  607. _scrollPosition.x = 0;
  608. _scrollingVelocity.x = 0;
  609. }
  610. if (_scrollPosition.y > 0)
  611. {
  612. _scrollPosition.y = 0;
  613. _scrollingVelocity.y = 0;
  614. }
  615. float scrollWidth = 0;
  616. if (clipWidth < totalWidth)
  617. scrollWidth = (clipWidth / totalWidth) * clipWidth;
  618. float scrollHeight = 0;
  619. if (clipHeight < totalHeight)
  620. scrollHeight = (clipHeight / totalHeight) * clipHeight;
  621. _scrollBarBounds.set(((-_scrollPosition.x) / totalWidth) * clipWidth,
  622. ((-_scrollPosition.y) / totalHeight) * clipHeight,
  623. scrollWidth, scrollHeight);
  624. // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
  625. if (!_scrolling && _scrollingVelocity.isZero() && _scrollBarsAutoHide && _scrollBarOpacity == 1.0f)
  626. {
  627. float to = 0;
  628. _scrollBarOpacity = 0.99f;
  629. Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
  630. _scrollBarOpacityClip = animation->getClip();
  631. _scrollBarOpacityClip->play();
  632. }
  633. // Position controls within scroll area.
  634. _layout->update(this, _scrollPosition);
  635. }
  636. bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  637. {
  638. switch(evt)
  639. {
  640. case Touch::TOUCH_PRESS:
  641. _scrollingLastX = _scrollingFirstX = x;
  642. _scrollingLastY = _scrollingFirstY = y;
  643. _scrollingVelocity.set(0, 0);
  644. _scrolling = true;
  645. _scrollingStartTimeX = _scrollingStartTimeY = 0;
  646. if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
  647. {
  648. _scrollBarOpacityClip->stop();
  649. _scrollBarOpacityClip = NULL;
  650. }
  651. _scrollBarOpacity = 1.0f;
  652. return true;
  653. case Touch::TOUCH_MOVE:
  654. if (_scrolling)
  655. {
  656. // Calculate the latest movement delta for the next update to use.
  657. int vx = x - _scrollingLastX;
  658. int vy = y - _scrollingLastY;
  659. _scrollingVelocity.set(vx, vy);
  660. _scrollPosition.x += vx;
  661. _scrollPosition.y += vy;
  662. _scrollingLastX = x;
  663. _scrollingLastY = y;
  664. // If the user changes direction, reset the start time and position.
  665. bool goingRight = (vx > 0);
  666. if (goingRight != _scrollingRight)
  667. {
  668. _scrollingFirstX = x;
  669. _scrollingRight = goingRight;
  670. _scrollingStartTimeX = Game::getAbsoluteTime();
  671. }
  672. bool goingDown = (vy > 0);
  673. if (goingDown != _scrollingDown)
  674. {
  675. _scrollingFirstY = y;
  676. _scrollingDown = goingDown;
  677. _scrollingStartTimeY = Game::getAbsoluteTime();
  678. }
  679. if (!_scrollingStartTimeX)
  680. _scrollingStartTimeX = Game::getAbsoluteTime();
  681. if (!_scrollingStartTimeY)
  682. _scrollingStartTimeY = Game::getAbsoluteTime();
  683. _scrollingLastTime = Game::getAbsoluteTime();
  684. return true;
  685. }
  686. break;
  687. case Touch::TOUCH_RELEASE:
  688. _scrolling = false;
  689. long timeSinceLastMove = Game::getAbsoluteTime() - _scrollingLastTime;
  690. if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
  691. {
  692. _scrollingVelocity.set(0, 0);
  693. return true;
  694. }
  695. int dx = _scrollingLastX - _scrollingFirstX;
  696. int dy = _scrollingLastY - _scrollingFirstY;
  697. long timeTakenX = Game::getAbsoluteTime() - _scrollingStartTimeX;
  698. float elapsedSecsX = (float)timeTakenX * 0.001f;
  699. long timeTakenY = Game::getAbsoluteTime() - _scrollingStartTimeY;
  700. float elapsedSecsY = (float)timeTakenY * 0.001f;
  701. float vx = dx;
  702. float vy = dy;
  703. if (elapsedSecsX > 0)
  704. vx = (float)dx / elapsedSecsX;
  705. if (elapsedSecsY > 0)
  706. vy = (float)dy / elapsedSecsY;
  707. _scrollingVelocity.set(vx, vy);
  708. return true;
  709. }
  710. return false;
  711. }
  712. Container::Scroll Container::getScroll(const char* scroll)
  713. {
  714. if (!scroll)
  715. return Container::SCROLL_NONE;
  716. if (strcmp(scroll, "SCROLL_NONE") == 0)
  717. {
  718. return Container::SCROLL_NONE;
  719. }
  720. else if (strcmp(scroll, "SCROLL_HORIZONTAL") == 0)
  721. {
  722. return Container::SCROLL_HORIZONTAL;
  723. }
  724. else if (strcmp(scroll, "SCROLL_VERTICAL") == 0)
  725. {
  726. return Container::SCROLL_VERTICAL;
  727. }
  728. else if (strcmp(scroll, "SCROLL_BOTH") == 0)
  729. {
  730. return Container::SCROLL_BOTH;
  731. }
  732. else
  733. {
  734. GP_ERROR("Failed to get corresponding scroll state for unsupported value '%s'.", scroll);
  735. }
  736. return Container::SCROLL_NONE;
  737. }
  738. bool sortControlsByZOrder(Control* c1, Control* c2)
  739. {
  740. if (c1->getZIndex() < c2->getZIndex())
  741. return true;
  742. return false;
  743. }
  744. unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
  745. {
  746. switch(propertyId)
  747. {
  748. case ANIMATE_OPACITY:
  749. return 1;
  750. default:
  751. return Control::getAnimationPropertyComponentCount(propertyId);
  752. }
  753. }
  754. void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
  755. {
  756. GP_ASSERT(value);
  757. switch(propertyId)
  758. {
  759. case ANIMATE_OPACITY:
  760. value->setFloat(0, _scrollBarOpacity);
  761. break;
  762. default:
  763. Control::getAnimationPropertyValue(propertyId, value);
  764. break;
  765. }
  766. }
  767. void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
  768. {
  769. GP_ASSERT(value);
  770. switch(propertyId)
  771. {
  772. case ANIMATE_OPACITY:
  773. _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
  774. _dirty = true;
  775. break;
  776. default:
  777. Control::setAnimationPropertyValue(propertyId, value, blendWeight);
  778. break;
  779. }
  780. }
  781. }