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