Container.cpp 35 KB

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