Container.cpp 32 KB

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