Container.cpp 34 KB

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