Container.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572
  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 "JoystickControl.h"
  14. #include "ImageControl.h"
  15. #include "Form.h"
  16. #include "Game.h"
  17. #include "ControlFactory.h"
  18. namespace gameplay
  19. {
  20. // If the user stops scrolling for this amount of time (in millis) before touch/click release, don't apply inertia.
  21. static const long SCROLL_INERTIA_DELAY = 100L;
  22. // Factor to multiply friction by before applying to velocity.
  23. static const float SCROLL_FRICTION_FACTOR = 5.0f;
  24. // Distance that must be scrolled before isScrolling() will return true, used e.g. to cancel button-click events.
  25. static const float SCROLL_THRESHOLD = 10.0f;
  26. // Number of milliseconds to fade auto-hide scrollbars out for
  27. static const long SCROLLBAR_FADE_TIME = 1500L;
  28. // If the DPad or joystick is held down, this is the initial delay in milliseconds between focus change events.
  29. static const float FOCUS_CHANGE_REPEAT_DELAY = 300.0f;
  30. /**
  31. * Sort function for use with _controls.sort(), based on Z-Order.
  32. *
  33. * @param c1 The first control
  34. * @param c2 The second control
  35. * return true if the first controls z index is less than the second.
  36. */
  37. static bool sortControlsByZOrder(Control* c1, Control* c2);
  38. void Container::clearContacts()
  39. {
  40. for (int i = 0; i < MAX_CONTACT_INDICES; ++i)
  41. _contactIndices[i] = false;
  42. }
  43. Container::Container()
  44. : _layout(NULL), _activeControl(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
  45. _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
  46. _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
  47. _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
  48. _scrollingVeryFirstX(0), _scrollingVeryFirstY(0), _scrollingFirstX(0), _scrollingFirstY(0), _scrollingLastX(0), _scrollingLastY(0),
  49. _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
  50. _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f), _scrollWheelSpeed(400.0f),
  51. _scrollingRight(false), _scrollingDown(false),
  52. _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
  53. _scrollBarOpacityClip(NULL), _zIndexDefault(0),
  54. _selectButtonDown(false), _lastFrameTime(0), _totalWidth(0), _totalHeight(0),
  55. _initializedWithScroll(false), _scrollWheelRequiresFocus(false)
  56. {
  57. clearContacts();
  58. }
  59. Container::~Container()
  60. {
  61. std::vector<Control*>::iterator it;
  62. for (it = _controls.begin(); it < _controls.end(); it++)
  63. {
  64. (*it)->_parent = nullptr;
  65. SAFE_RELEASE((*it));
  66. }
  67. SAFE_RELEASE(_layout);
  68. }
  69. Container* Container::create(const char* id, Theme::Style* style, Layout::Type layout)
  70. {
  71. Container* container = new Container();
  72. container->_id = id ? id : "";
  73. container->_layout = createLayout(layout);
  74. container->initialize("Container", style, NULL);
  75. return container;
  76. }
  77. Control* Container::create(Theme::Style* style, Properties* properties)
  78. {
  79. Container* container = new Container();
  80. container->initialize("Container", style, properties);
  81. return container;
  82. }
  83. void Container::initialize(const char* typeName, Theme::Style* style, Properties* properties)
  84. {
  85. Control::initialize(typeName, style, properties);
  86. if (properties)
  87. {
  88. // Parse layout
  89. Properties* layoutNS = properties->getNamespace("layout", true, false);
  90. if (layoutNS)
  91. {
  92. _layout = createLayout(getLayoutType(layoutNS->getString("type")));
  93. switch (_layout->getType())
  94. {
  95. case Layout::LAYOUT_FLOW:
  96. static_cast<FlowLayout*>(_layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
  97. break;
  98. case Layout::LAYOUT_VERTICAL:
  99. static_cast<VerticalLayout*>(_layout)->setSpacing(layoutNS->getInt("spacing"));
  100. break;
  101. }
  102. }
  103. else
  104. {
  105. _layout = createLayout(getLayoutType(properties->getString("layout")));
  106. }
  107. setScroll(getScroll(properties->getString("scroll")));
  108. _scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
  109. if (_scrollBarsAutoHide)
  110. {
  111. _scrollBarOpacity = 0.0f;
  112. }
  113. _scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
  114. if (properties->exists("scrollingFriction"))
  115. _scrollingFriction = properties->getFloat("scrollingFriction");
  116. if (properties->exists("scrollWheelSpeed"))
  117. _scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
  118. addControls(properties);
  119. const char* activeControl = properties->getString("activeControl");
  120. if (activeControl)
  121. {
  122. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  123. {
  124. if (_controls[i]->_id == activeControl)
  125. {
  126. _activeControl = _controls[i];
  127. break;
  128. }
  129. }
  130. }
  131. }
  132. // Create a default layout if one does not yet exist
  133. if (_layout == NULL)
  134. _layout = createLayout(Layout::LAYOUT_ABSOLUTE);
  135. }
  136. void Container::addControls(Properties* properties)
  137. {
  138. GP_ASSERT(properties);
  139. // Add all the controls to this container.
  140. Properties* controlSpace = properties->getNextNamespace();
  141. while (controlSpace != NULL)
  142. {
  143. const char* controlName = controlSpace->getNamespace();
  144. // Pass our own style into the creation of the child control.
  145. // The child control's style will be looked up using the passed in style's theme.
  146. Control* control = ControlFactory::getInstance()->createControl(controlName, _style, controlSpace);
  147. // Add the new control to the form.
  148. if (control)
  149. {
  150. addControl(control);
  151. control->release();
  152. }
  153. // Get the next control.
  154. controlSpace = properties->getNextNamespace();
  155. }
  156. // Sort controls by Z-Order.
  157. sortControls();
  158. }
  159. const char* Container::getTypeName() const
  160. {
  161. return "Container";
  162. }
  163. Layout* Container::getLayout()
  164. {
  165. return _layout;
  166. }
  167. void Container::setLayout(Layout::Type type)
  168. {
  169. if (_layout == NULL || _layout->getType() != type)
  170. {
  171. SAFE_RELEASE(_layout);
  172. _layout = createLayout(type);
  173. setDirty(Control::DIRTY_BOUNDS);
  174. }
  175. }
  176. unsigned int Container::addControl(Control* control)
  177. {
  178. GP_ASSERT(control);
  179. if( control->_parent == this )
  180. {
  181. // Control is already in this container.
  182. // Do nothing but determine and return control's index.
  183. const size_t size = _controls.size();
  184. for( size_t i = 0; i < size; ++i ) {
  185. Control* c = _controls[ i ];
  186. if( c == control ) {
  187. return (unsigned int)i;
  188. }
  189. }
  190. // Should never reach this.
  191. GP_ASSERT( false );
  192. return 0;
  193. }
  194. if( control->getZIndex() == -1 ) {
  195. control->setZIndex( _zIndexDefault++ );
  196. }
  197. if( control->getFocusIndex() == -1 ) {
  198. // Find the current largest focus index
  199. int maxFocusIndex = 0;
  200. for( size_t i = 0, count = _controls.size(); i < count; ++i ) {
  201. if( _controls[ i ]->_focusIndex > maxFocusIndex )
  202. maxFocusIndex = _controls[ i ]->_focusIndex;
  203. }
  204. control->setFocusIndex( maxFocusIndex + 1 );
  205. }
  206. _controls.push_back( control );
  207. control->addRef();
  208. // Remove the control from its current parent
  209. if( control->_parent )
  210. {
  211. control->_parent->removeControl( control );
  212. }
  213. control->_parent = this;
  214. sortControls();
  215. setDirty(Control::DIRTY_BOUNDS);
  216. return (unsigned int)( _controls.size() - 1 );
  217. }
  218. void Container::insertControl(Control* control, unsigned int index)
  219. {
  220. GP_ASSERT(control);
  221. if (control->_parent && control->_parent != this)
  222. {
  223. control->_parent->removeControl(control);
  224. }
  225. if (control->_parent != this)
  226. {
  227. std::vector<Control*>::iterator it = _controls.begin() + index;
  228. _controls.insert(it, control);
  229. control->addRef();
  230. control->_parent = this;
  231. setDirty(Control::DIRTY_BOUNDS);
  232. }
  233. }
  234. void Container::removeControl(unsigned int index)
  235. {
  236. GP_ASSERT(index < _controls.size());
  237. std::vector<Control*>::iterator it = _controls.begin() + index;
  238. Control* control = *it;
  239. _controls.erase(it);
  240. control->_parent = NULL;
  241. setDirty(Control::DIRTY_BOUNDS);
  242. if (_activeControl == control)
  243. _activeControl = NULL;
  244. Form::verifyRemovedControlState(control);
  245. SAFE_RELEASE(control);
  246. }
  247. void Container::removeControl(const char* id)
  248. {
  249. GP_ASSERT(id);
  250. for (size_t i = 0, size = _controls.size(); i < size; ++i)
  251. {
  252. Control* c = _controls[i];
  253. if (strcmp(id, c->getId()) == 0)
  254. {
  255. removeControl((unsigned int)i);
  256. return;
  257. }
  258. }
  259. }
  260. void Container::removeControl(Control* control)
  261. {
  262. GP_ASSERT(control);
  263. for (size_t i = 0, size = _controls.size(); i < size; ++i)
  264. {
  265. Control* c = _controls[i];
  266. if (c == control)
  267. {
  268. removeControl((unsigned int)i);
  269. return;
  270. }
  271. }
  272. }
  273. Control* Container::getControl(unsigned int index) const
  274. {
  275. GP_ASSERT(index < _controls.size());
  276. return _controls[index];
  277. }
  278. Control* Container::getControl(const char* id) const
  279. {
  280. GP_ASSERT(id);
  281. std::vector<Control*>::const_iterator it;
  282. for (it = _controls.begin(); it < _controls.end(); it++)
  283. {
  284. Control* c = *it;
  285. GP_ASSERT(c);
  286. if (strcmp(id, c->getId()) == 0)
  287. {
  288. return c;
  289. }
  290. else if (c->isContainer())
  291. {
  292. Control* cc = ((Container*)c)->getControl(id);
  293. if (cc)
  294. {
  295. return cc;
  296. }
  297. }
  298. }
  299. return NULL;
  300. }
  301. unsigned int Container::getControlCount() const
  302. {
  303. return (unsigned int)_controls.size();
  304. }
  305. const std::vector<Control*>& Container::getControls() const
  306. {
  307. return _controls;
  308. }
  309. bool Container::isForm() const
  310. {
  311. return false;
  312. }
  313. void Container::setScroll(Scroll scroll)
  314. {
  315. if (scroll != _scroll)
  316. {
  317. _scroll = scroll;
  318. if (_scroll == SCROLL_NONE)
  319. {
  320. _scrollPosition.set(0, 0);
  321. }
  322. else
  323. {
  324. // Scrollable containers can be focused (to allow scrolling)
  325. _canFocus = true;
  326. }
  327. setDirty(DIRTY_BOUNDS | DIRTY_STATE);
  328. }
  329. }
  330. Container::Scroll Container::getScroll() const
  331. {
  332. return _scroll;
  333. }
  334. void Container::setScrollBarsAutoHide(bool autoHide)
  335. {
  336. if (autoHide != _scrollBarsAutoHide)
  337. {
  338. _scrollBarsAutoHide = autoHide;
  339. setDirty(DIRTY_BOUNDS | DIRTY_STATE);
  340. }
  341. }
  342. bool Container::isScrollBarsAutoHide() const
  343. {
  344. return _scrollBarsAutoHide;
  345. }
  346. bool Container::isScrolling() const
  347. {
  348. if (_scrolling &&
  349. (abs(_scrollingLastX - _scrollingVeryFirstX) > SCROLL_THRESHOLD ||
  350. abs(_scrollingLastY - _scrollingVeryFirstY) > SCROLL_THRESHOLD))
  351. {
  352. return true;
  353. }
  354. if (_parent && _parent->isScrolling())
  355. return true;
  356. return false;
  357. }
  358. const Vector2& Container::getScrollPosition() const
  359. {
  360. return _scrollPosition;
  361. }
  362. void Container::setScrollPosition(const Vector2& scrollPosition)
  363. {
  364. _scrollPosition = scrollPosition;
  365. setChildrenDirty(DIRTY_BOUNDS, true);
  366. }
  367. Animation* Container::getAnimation(const char* id) const
  368. {
  369. std::vector<Control*>::const_iterator itr = _controls.begin();
  370. std::vector<Control*>::const_iterator end = _controls.end();
  371. Control* control = NULL;
  372. for (; itr != end; itr++)
  373. {
  374. control = *itr;
  375. GP_ASSERT(control);
  376. Animation* animation = control->getAnimation(id);
  377. if (animation)
  378. return animation;
  379. if (control->isContainer())
  380. {
  381. animation = ((Container*)control)->getAnimation(id);
  382. if (animation)
  383. return animation;
  384. }
  385. }
  386. return NULL;
  387. }
  388. bool Container::getScrollWheelRequiresFocus() const
  389. {
  390. return _scrollWheelRequiresFocus;
  391. }
  392. void Container::setScrollWheelRequiresFocus(bool required)
  393. {
  394. _scrollWheelRequiresFocus = required;
  395. }
  396. bool Container::setFocus()
  397. {
  398. // If this container (or one of its children) already has focus, do nothing
  399. if (Form::getFocusControl() && (Form::getFocusControl() == this || Form::getFocusControl()->isChild(this)))
  400. return true;
  401. // First try to set focus to our active control
  402. if (_activeControl)
  403. {
  404. if (_activeControl->setFocus())
  405. return true;
  406. }
  407. // Try to set focus to one of our children
  408. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  409. {
  410. if (_controls[i]->setFocus())
  411. return true;
  412. }
  413. // Lastly, try to set focus to ourself if none of our children will accept it
  414. return Control::setFocus();
  415. }
  416. Control* Container::getActiveControl() const
  417. {
  418. return _activeControl;
  419. }
  420. void Container::setActiveControl(Control* control)
  421. {
  422. if (std::find(_controls.begin(), _controls.end(), control) != _controls.end())
  423. {
  424. _activeControl = control;
  425. // If a control within this container currently has focus, switch focus to the new active control
  426. if (Form::getFocusControl() && Form::getFocusControl() != control && Form::getFocusControl()->isChild(this))
  427. Form::setFocusControl(control);
  428. }
  429. }
  430. void Container::setChildrenDirty(int bits, bool recursive)
  431. {
  432. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  433. {
  434. Control* ctrl = _controls[i];
  435. ctrl->setDirty(bits);
  436. if (recursive && ctrl->isContainer())
  437. static_cast<Container*>(ctrl)->setChildrenDirty(bits, true);
  438. }
  439. }
  440. void Container::update(float elapsedTime)
  441. {
  442. Control::update(elapsedTime);
  443. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  444. _controls[i]->update(elapsedTime);
  445. }
  446. void Container::updateState(State state)
  447. {
  448. Control::updateState(state);
  449. // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
  450. if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
  451. {
  452. _scrollBarLeftCap = getImage("scrollBarLeftCap", state);
  453. _scrollBarHorizontal = getImage("horizontalScrollBar", state);
  454. _scrollBarRightCap = getImage("scrollBarRightCap", state);
  455. }
  456. if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
  457. {
  458. _scrollBarTopCap = getImage("scrollBarTopCap", state);
  459. _scrollBarVertical = getImage("verticalScrollBar", state);
  460. _scrollBarBottomCap = getImage("scrollBarBottomCap", state);
  461. }
  462. }
  463. void Container::updateBounds()
  464. {
  465. // Handle automatically sizing based on our children
  466. if (_autoSize != AUTO_SIZE_NONE)
  467. {
  468. if (_autoSize & AUTO_SIZE_WIDTH)
  469. {
  470. // Size ourself to tightly fit the width of our children
  471. float width = 0;
  472. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  473. {
  474. Control* ctrl = _controls[i];
  475. if (ctrl->isVisible() && !ctrl->isXPercentage() && !ctrl->isWidthPercentage())
  476. {
  477. float w = ctrl->getWidth();
  478. if (!ctrl->isXPercentage())
  479. w += ctrl->getX();
  480. if (width < w)
  481. width = w;
  482. }
  483. }
  484. width += getBorder(NORMAL).left + getBorder(NORMAL).right + getPadding().left + getPadding().right;
  485. setWidthInternal(width);
  486. }
  487. if (_autoSize & AUTO_SIZE_HEIGHT)
  488. {
  489. // Size ourself to tightly fit the height of our children
  490. float height = 0;
  491. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  492. {
  493. Control* ctrl = _controls[i];
  494. if (ctrl->isVisible() && !ctrl->isYPercentage() && !ctrl->isHeightPercentage())
  495. {
  496. float h = ctrl->getHeight();
  497. if (!ctrl->isYPercentage())
  498. h += ctrl->getY();
  499. if (height < h)
  500. height = h;
  501. }
  502. }
  503. height += getBorder(NORMAL).top + getBorder(NORMAL).bottom + getPadding().top + getPadding().bottom;
  504. setHeightInternal(height);
  505. }
  506. }
  507. // Compute total bounds of container
  508. Control::updateBounds();
  509. // Update layout to position children correctly within us
  510. GP_ASSERT(_layout);
  511. _layout->update(this);
  512. }
  513. void Container::updateAbsoluteBounds(const Vector2& offset)
  514. {
  515. Control::updateAbsoluteBounds(offset);
  516. // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
  517. if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
  518. {
  519. GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
  520. _viewportBounds.height -= _scrollBarHorizontal->getRegion().height;
  521. _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
  522. }
  523. if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
  524. {
  525. GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
  526. _viewportBounds.width -= _scrollBarVertical->getRegion().width;
  527. _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
  528. }
  529. // Update scroll position and scrollbars after updating absolute bounds since
  530. // computation relies on up-to-date absolute bounds information.
  531. updateScroll();
  532. }
  533. bool Container::updateChildBounds()
  534. {
  535. bool result = false;
  536. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  537. {
  538. Control* ctrl = _controls[i];
  539. GP_ASSERT(ctrl);
  540. if (ctrl->isVisible())
  541. {
  542. bool changed = ctrl->updateBoundsInternal(_scrollPosition);
  543. // If the child bounds have changed, dirty our bounds and all of our
  544. // parent bounds so that our layout and/or bounds are recomputed.
  545. if (changed)
  546. {
  547. Control* parent = this;
  548. while (parent && (parent->_autoSize != AUTO_SIZE_NONE || static_cast<Container *>(parent)->getLayout()->getType() != Layout::LAYOUT_ABSOLUTE))
  549. {
  550. parent->setDirty(DIRTY_BOUNDS);
  551. parent = parent->_parent;
  552. }
  553. }
  554. result = result || changed;
  555. }
  556. }
  557. return result;
  558. }
  559. unsigned int Container::draw(Form* form, const Rectangle& clip)
  560. {
  561. if (!_visible)
  562. return 0;
  563. // Draw container skin
  564. unsigned int drawCalls = Control::draw(form, clip);
  565. // Draw child controls
  566. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  567. {
  568. Control* control = _controls[i];
  569. if (control && control->_absoluteClipBounds.intersects(_absoluteClipBounds))
  570. {
  571. drawCalls += control->draw(form, _viewportClipBounds);
  572. }
  573. }
  574. // Draw scrollbars
  575. if (_scroll != SCROLL_NONE && (_scrollBarOpacity > 0.0f))
  576. {
  577. // Draw scroll bars.
  578. Rectangle clipRegion(_absoluteClipBounds);
  579. SpriteBatch* batch = _style->getTheme()->getSpriteBatch();
  580. startBatch(form, batch);
  581. if (_scrollBarBounds.height > 0 && ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL))
  582. {
  583. const Rectangle& topRegion = _scrollBarTopCap->getRegion();
  584. const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
  585. Vector4 topColor = _scrollBarTopCap->getColor();
  586. topColor.w *= _scrollBarOpacity * _opacity;
  587. const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
  588. const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
  589. Vector4 verticalColor = _scrollBarVertical->getColor();
  590. verticalColor.w *= _scrollBarOpacity * _opacity;
  591. const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
  592. const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
  593. Vector4 bottomColor = _scrollBarBottomCap->getColor();
  594. bottomColor.w *= _scrollBarOpacity * _opacity;
  595. clipRegion.width += verticalRegion.width;
  596. Rectangle bounds(_viewportBounds.right() + (_absoluteBounds.right() - _viewportBounds.right())*0.5f - topRegion.width*0.5f, _viewportBounds.y + _scrollBarBounds.y, topRegion.width, topRegion.height);
  597. batch->draw(bounds.x, bounds.y, bounds.width, bounds.height, topUVs.u1, topUVs.v1, topUVs.u2, topUVs.v2, topColor, clipRegion);
  598. bounds.y += topRegion.height;
  599. bounds.height = _scrollBarBounds.height - topRegion.height - bottomRegion.height;
  600. batch->draw(bounds.x, bounds.y, bounds.width, bounds.height, verticalUVs.u1, verticalUVs.v1, verticalUVs.u2, verticalUVs.v2, verticalColor, clipRegion);
  601. bounds.y += bounds.height;
  602. bounds.height = bottomRegion.height;
  603. batch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
  604. drawCalls += 3;
  605. }
  606. if (_scrollBarBounds.width > 0 && ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL))
  607. {
  608. const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
  609. const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
  610. Vector4 leftColor = _scrollBarLeftCap->getColor();
  611. leftColor.w *= _scrollBarOpacity * _opacity;
  612. const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
  613. const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
  614. Vector4 horizontalColor = _scrollBarHorizontal->getColor();
  615. horizontalColor.w *= _scrollBarOpacity * _opacity;
  616. const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
  617. const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
  618. Vector4 rightColor = _scrollBarRightCap->getColor();
  619. rightColor.w *= _scrollBarOpacity * _opacity;
  620. clipRegion.height += horizontalRegion.height;
  621. Rectangle bounds(_viewportBounds.x + _scrollBarBounds.x, _viewportBounds.bottom() + (_absoluteBounds.bottom() - _viewportBounds.bottom())*0.5f - leftRegion.height*0.5f, leftRegion.width, leftRegion.height);
  622. batch->draw(bounds.x, bounds.y, bounds.width, bounds.height, leftUVs.u1, leftUVs.v1, leftUVs.u2, leftUVs.v2, leftColor, clipRegion);
  623. bounds.x += leftRegion.width;
  624. bounds.width = _scrollBarBounds.width - leftRegion.width - rightRegion.width;
  625. batch->draw(bounds.x, bounds.y, bounds.width, bounds.height, horizontalUVs.u1, horizontalUVs.v1, horizontalUVs.u2, horizontalUVs.v2, horizontalColor, clipRegion);
  626. bounds.x += bounds.width;
  627. bounds.width = rightRegion.width;
  628. batch->draw(bounds.x, bounds.y, bounds.width, bounds.height, rightUVs.u1, rightUVs.v1, rightUVs.u2, rightUVs.v2, rightColor, clipRegion);
  629. drawCalls += 3;
  630. }
  631. finishBatch(form, batch);
  632. }
  633. return drawCalls;
  634. }
  635. static bool canReceiveFocus(Control* control)
  636. {
  637. if (control->getFocusIndex() < 0 || !(control->isEnabled() && control->isVisible()))
  638. return false;
  639. if (control->canFocus())
  640. return true;
  641. if (control->isContainer())
  642. {
  643. Container* container = static_cast<Container*>(control);
  644. for (unsigned int i = 0, count = (unsigned int)container->getControlCount(); i < count; ++i)
  645. {
  646. if (canReceiveFocus(container->getControl(i)))
  647. return true;
  648. }
  649. }
  650. return false;
  651. }
  652. bool Container::moveFocus(Direction direction)
  653. {
  654. switch (direction)
  655. {
  656. case NEXT:
  657. case PREVIOUS:
  658. return moveFocusNextPrevious(direction);
  659. case UP:
  660. case DOWN:
  661. case LEFT:
  662. case RIGHT:
  663. return moveFocusDirectional(direction);
  664. default:
  665. return false;
  666. }
  667. }
  668. bool Container::moveFocusNextPrevious(Direction direction)
  669. {
  670. // Get the current control that has focus (either directly or indirectly) within this container
  671. Control* currentFocus = Form::getFocusControl();
  672. Control* current = NULL;
  673. if (currentFocus && currentFocus->isChild(this))
  674. {
  675. if (currentFocus->_parent == this)
  676. {
  677. // Currently focused control is a direct child of us
  678. current = currentFocus;
  679. }
  680. else
  681. {
  682. // Currently focused control is a child of one of our child containers
  683. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  684. {
  685. if (currentFocus->isChild(_controls[i]))
  686. {
  687. current = _controls[i];
  688. break;
  689. }
  690. }
  691. }
  692. }
  693. Control* nextCtrl = NULL;
  694. int nextIndex = direction == NEXT ? INT_MAX : INT_MIN;
  695. bool moveFirst = false;
  696. if (current)
  697. {
  698. // There is a control inside us that currently has focus, so find the next control that
  699. // should receive focus.
  700. int focusableControlCount = 0; // track the number of valid focusable controls in this container
  701. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  702. {
  703. Control* ctrl = _controls[i];
  704. if (!canReceiveFocus(ctrl))
  705. continue;
  706. if ((direction == NEXT && ctrl->_focusIndex > current->_focusIndex && ctrl->_focusIndex < nextIndex) ||
  707. (direction == PREVIOUS && ctrl->_focusIndex < current->_focusIndex && ctrl->_focusIndex > nextIndex))
  708. {
  709. nextCtrl = ctrl;
  710. nextIndex = ctrl->_focusIndex;
  711. }
  712. ++focusableControlCount;
  713. }
  714. if (nextCtrl)
  715. {
  716. if (nextCtrl->isContainer() && static_cast<Container*>(nextCtrl)->moveFocus(direction))
  717. return true;
  718. if (nextCtrl->setFocus())
  719. return true;
  720. }
  721. // Search up into our parent container for a focus move
  722. if (_parent && _parent->moveFocus(direction))
  723. return true;
  724. // We didn't find a control to move to, so we must be the first or last focusable control in our parent.
  725. // Wrap focus to the other side of the container.
  726. if (focusableControlCount > 1)
  727. {
  728. moveFirst = true;
  729. }
  730. }
  731. else
  732. {
  733. moveFirst = true;
  734. }
  735. if (moveFirst)
  736. {
  737. nextIndex = direction == NEXT ? INT_MAX : INT_MIN;
  738. nextCtrl = NULL;
  739. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  740. {
  741. Control* ctrl = _controls[i];
  742. if (!canReceiveFocus(ctrl))
  743. continue;
  744. if ((direction == NEXT && ctrl->_focusIndex < nextIndex) ||
  745. (direction == PREVIOUS && ctrl->_focusIndex > nextIndex))
  746. {
  747. nextCtrl = ctrl;
  748. nextIndex = ctrl->_focusIndex;
  749. }
  750. }
  751. if (nextCtrl)
  752. {
  753. if (nextCtrl->isContainer() && static_cast<Container*>(nextCtrl)->moveFocus(direction))
  754. return true;
  755. if (nextCtrl->setFocus())
  756. return true;
  757. }
  758. }
  759. return false;
  760. }
  761. bool Container::moveFocusDirectional(Direction direction)
  762. {
  763. Control* startControl = Form::getFocusControl();
  764. if (startControl == NULL)
  765. return false;
  766. const Rectangle& startBounds = startControl->_absoluteBounds;
  767. Control* next = NULL;
  768. Vector2 vStart, vNext;
  769. float distance = FLT_MAX;
  770. switch (direction)
  771. {
  772. case UP:
  773. vStart.set(startBounds.x + startBounds.width * 0.5f, startBounds.y);
  774. break;
  775. case DOWN:
  776. vStart.set(startBounds.x + startBounds.width * 0.5f, startBounds.bottom());
  777. break;
  778. case LEFT:
  779. vStart.set(startBounds.x, startBounds.y + startBounds.height * 0.5f);
  780. break;
  781. case RIGHT:
  782. vStart.set(startBounds.right(), startBounds.y + startBounds.height * 0.5f);
  783. break;
  784. }
  785. for (size_t i = 0, count = _controls.size(); i < count; ++i)
  786. {
  787. Control* ctrl = _controls[i];
  788. if (!canReceiveFocus(ctrl))
  789. continue;
  790. const Rectangle& nextBounds = ctrl->getAbsoluteBounds();
  791. switch (direction)
  792. {
  793. case UP:
  794. vNext.set(nextBounds.x + nextBounds.width * 0.5f, nextBounds.bottom());
  795. if (vNext.y > vStart.y)
  796. continue;
  797. break;
  798. case DOWN:
  799. vNext.set(nextBounds.x + nextBounds.width * 0.5f, nextBounds.y);
  800. if (vNext.y < vStart.y)
  801. continue;
  802. break;
  803. case LEFT:
  804. vNext.set(nextBounds.right(), nextBounds.y + nextBounds.height * 0.5f);
  805. if (vNext.x > vStart.x)
  806. continue;
  807. break;
  808. case RIGHT:
  809. vNext.set(nextBounds.x, nextBounds.y + nextBounds.height * 0.5f);
  810. if (vNext.x < vStart.x)
  811. continue;
  812. break;
  813. }
  814. float nextDistance = vStart.distance(vNext);
  815. if (std::fabs(nextDistance) < distance)
  816. {
  817. distance = nextDistance;
  818. next = ctrl;
  819. }
  820. }
  821. if (next)
  822. {
  823. // If this control is a container, try to move focus to the first control within it
  824. if (next->isContainer())
  825. {
  826. if (static_cast<Container*>(next)->moveFocusDirectional(direction))
  827. return true;
  828. }
  829. if (next->setFocus())
  830. return true;
  831. }
  832. else
  833. {
  834. // If no control was found, try searching in our parent container
  835. if (_parent && _parent->moveFocusDirectional(direction))
  836. return true;
  837. }
  838. return false;
  839. }
  840. void Container::startScrolling(float x, float y, bool resetTime)
  841. {
  842. _scrollingVelocity.set(-x, y);
  843. _scrolling = true;
  844. _scrollBarOpacity = 1.0f;
  845. setDirty(DIRTY_BOUNDS);
  846. if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
  847. {
  848. _scrollBarOpacityClip->stop();
  849. _scrollBarOpacityClip = NULL;
  850. }
  851. if (resetTime)
  852. {
  853. _lastFrameTime = Game::getAbsoluteTime();
  854. }
  855. }
  856. void Container::stopScrolling()
  857. {
  858. _scrollingVelocity.set(0, 0);
  859. _scrolling = false;
  860. setDirty(DIRTY_BOUNDS);
  861. if (_parent)
  862. _parent->stopScrolling();
  863. }
  864. bool Container::isContainer() const
  865. {
  866. return true;
  867. }
  868. Layout::Type Container::getLayoutType(const char* layoutString)
  869. {
  870. if (!layoutString)
  871. {
  872. return Layout::LAYOUT_ABSOLUTE;
  873. }
  874. std::string layoutName(layoutString);
  875. std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
  876. if (layoutName == "LAYOUT_ABSOLUTE")
  877. {
  878. return Layout::LAYOUT_ABSOLUTE;
  879. }
  880. else if (layoutName == "LAYOUT_VERTICAL")
  881. {
  882. return Layout::LAYOUT_VERTICAL;
  883. }
  884. else if (layoutName == "LAYOUT_FLOW")
  885. {
  886. return Layout::LAYOUT_FLOW;
  887. }
  888. else
  889. {
  890. // Default.
  891. return Layout::LAYOUT_ABSOLUTE;
  892. }
  893. }
  894. Layout* Container::createLayout(Layout::Type type)
  895. {
  896. switch (type)
  897. {
  898. case Layout::LAYOUT_ABSOLUTE:
  899. return AbsoluteLayout::create();
  900. case Layout::LAYOUT_FLOW:
  901. return FlowLayout::create();
  902. case Layout::LAYOUT_VERTICAL:
  903. return VerticalLayout::create();
  904. default:
  905. return AbsoluteLayout::create();
  906. }
  907. }
  908. void Container::updateScroll()
  909. {
  910. if (_scroll == SCROLL_NONE)
  911. return;
  912. Control::State state = getState();
  913. // Update time.
  914. if (!_lastFrameTime)
  915. {
  916. _lastFrameTime = Game::getAbsoluteTime();
  917. }
  918. double frameTime = Game::getAbsoluteTime();
  919. float elapsedTime = (float)(frameTime - _lastFrameTime);
  920. _lastFrameTime = frameTime;
  921. const Theme::Border& containerBorder = getBorder(state);
  922. const Theme::Padding& containerPadding = getPadding();
  923. // Calculate total width and height.
  924. _totalWidth = _totalHeight = 0.0f;
  925. std::vector<Control*> controls = getControls();
  926. for (size_t i = 0, count = controls.size(); i < count; ++i)
  927. {
  928. Control* control = _controls[i];
  929. const Rectangle& bounds = control->getBounds();
  930. const Theme::Margin& margin = control->getMargin();
  931. float newWidth = bounds.x + bounds.width + margin.right;
  932. if (newWidth > _totalWidth)
  933. {
  934. _totalWidth = newWidth;
  935. }
  936. float newHeight = bounds.y + bounds.height + margin.bottom;
  937. if (newHeight > _totalHeight)
  938. {
  939. _totalHeight = newHeight;
  940. }
  941. }
  942. float vWidth = getImageRegion("verticalScrollBar", state).width;
  943. float hHeight = getImageRegion("horizontalScrollBar", state).height;
  944. float clipWidth = _absoluteBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
  945. float clipHeight = _absoluteBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
  946. bool dirty = false;
  947. // Apply and dampen inertia.
  948. if (!_scrollingVelocity.isZero())
  949. {
  950. // Calculate the time passed since last update.
  951. float elapsedSecs = (float)elapsedTime * 0.001f;
  952. _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
  953. _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
  954. if (!_scrolling)
  955. {
  956. float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
  957. _scrollingVelocity.x *= dampening;
  958. _scrollingVelocity.y *= dampening;
  959. if (fabs(_scrollingVelocity.x) < 100.0f)
  960. _scrollingVelocity.x = 0.0f;
  961. if (fabs(_scrollingVelocity.y) < 100.0f)
  962. _scrollingVelocity.y = 0.0f;
  963. }
  964. dirty = true;
  965. }
  966. // Stop scrolling when the far edge is reached.
  967. if (-_scrollPosition.x > _totalWidth - clipWidth)
  968. {
  969. _scrollPosition.x = -(_totalWidth - clipWidth);
  970. _scrollingVelocity.x = 0;
  971. dirty = true;
  972. }
  973. if (-_scrollPosition.y > _totalHeight - clipHeight)
  974. {
  975. _scrollPosition.y = -(_totalHeight - clipHeight);
  976. _scrollingVelocity.y = 0;
  977. dirty = true;
  978. }
  979. if (_scrollPosition.x > 0)
  980. {
  981. _scrollPosition.x = 0;
  982. _scrollingVelocity.x = 0;
  983. dirty = true;
  984. }
  985. if (_scrollPosition.y > 0)
  986. {
  987. _scrollPosition.y = 0;
  988. _scrollingVelocity.y = 0;
  989. dirty = true;
  990. }
  991. float scrollWidth = 0;
  992. if (clipWidth < _totalWidth)
  993. scrollWidth = (clipWidth / _totalWidth) * clipWidth;
  994. float scrollHeight = 0;
  995. if (clipHeight < _totalHeight)
  996. scrollHeight = (clipHeight / _totalHeight) * clipHeight;
  997. _scrollBarBounds.set(((-_scrollPosition.x) / _totalWidth) * clipWidth,
  998. ((-_scrollPosition.y) / _totalHeight) * clipHeight,
  999. scrollWidth, scrollHeight);
  1000. // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
  1001. if (!_scrolling && _scrollingVelocity.isZero() && _scrollBarsAutoHide && _scrollBarOpacity == 1.0f)
  1002. {
  1003. float to = 0;
  1004. _scrollBarOpacity = 0.99f;
  1005. if (!_scrollBarOpacityClip)
  1006. {
  1007. Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_SCROLLBAR_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, SCROLLBAR_FADE_TIME);
  1008. _scrollBarOpacityClip = animation->getClip();
  1009. }
  1010. _scrollBarOpacityClip->play();
  1011. }
  1012. // When scroll position is updated, we need to recompute bounds since children
  1013. // absolute bounds offset will need to be updated.
  1014. if (dirty)
  1015. {
  1016. setDirty(DIRTY_BOUNDS);
  1017. setChildrenDirty(DIRTY_BOUNDS, true);
  1018. }
  1019. }
  1020. void Container::sortControls()
  1021. {
  1022. if (_layout->getType() == Layout::LAYOUT_ABSOLUTE)
  1023. {
  1024. std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
  1025. }
  1026. }
  1027. bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  1028. {
  1029. switch (evt)
  1030. {
  1031. case Touch::TOUCH_PRESS:
  1032. if (_contactIndex == INVALID_CONTACT_INDEX)
  1033. {
  1034. bool dirty = !_scrollingVelocity.isZero();
  1035. _contactIndex = (int)contactIndex;
  1036. _scrollingLastX = _scrollingFirstX = _scrollingVeryFirstX = x;
  1037. _scrollingLastY = _scrollingFirstY = _scrollingVeryFirstY = y;
  1038. _scrollingVelocity.set(0, 0);
  1039. _scrolling = true;
  1040. _scrollingStartTimeX = _scrollingStartTimeY = 0;
  1041. if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
  1042. {
  1043. _scrollBarOpacityClip->stop();
  1044. _scrollBarOpacityClip = NULL;
  1045. }
  1046. _scrollBarOpacity = 1.0f;
  1047. if (dirty)
  1048. setDirty(DIRTY_BOUNDS);
  1049. return false;
  1050. }
  1051. break;
  1052. case Touch::TOUCH_MOVE:
  1053. if (_scrolling && _contactIndex == (int)contactIndex)
  1054. {
  1055. double gameTime = Game::getAbsoluteTime();
  1056. // Calculate the latest movement delta for the next update to use.
  1057. int vx = x - _scrollingLastX;
  1058. int vy = y - _scrollingLastY;
  1059. if (_scrollingMouseVertically)
  1060. {
  1061. float yRatio = _totalHeight / _absoluteBounds.height;
  1062. vy *= yRatio;
  1063. _scrollingVelocity.set(0, -vy);
  1064. _scrollPosition.y -= vy;
  1065. }
  1066. else if (_scrollingMouseHorizontally)
  1067. {
  1068. float xRatio = _totalWidth / _absoluteBounds.width;
  1069. vx *= xRatio;
  1070. _scrollingVelocity.set(-vx, 0);
  1071. _scrollPosition.x -= vx;
  1072. }
  1073. else
  1074. {
  1075. _scrollingVelocity.set(vx, vy);
  1076. _scrollPosition.x += vx;
  1077. _scrollPosition.y += vy;
  1078. }
  1079. _scrollingLastX = x;
  1080. _scrollingLastY = y;
  1081. // If the user changes direction, reset the start time and position.
  1082. bool goingRight = (vx > 0);
  1083. if (goingRight != _scrollingRight)
  1084. {
  1085. _scrollingFirstX = x;
  1086. _scrollingRight = goingRight;
  1087. _scrollingStartTimeX = gameTime;
  1088. }
  1089. bool goingDown = (vy > 0);
  1090. if (goingDown != _scrollingDown)
  1091. {
  1092. _scrollingFirstY = y;
  1093. _scrollingDown = goingDown;
  1094. _scrollingStartTimeY = gameTime;
  1095. }
  1096. if (!_scrollingStartTimeX)
  1097. _scrollingStartTimeX = gameTime;
  1098. if (!_scrollingStartTimeY)
  1099. _scrollingStartTimeY = gameTime;
  1100. _scrollingLastTime = gameTime;
  1101. setDirty(DIRTY_BOUNDS);
  1102. setChildrenDirty(DIRTY_BOUNDS, true);
  1103. return false;
  1104. }
  1105. break;
  1106. case Touch::TOUCH_RELEASE:
  1107. if (_contactIndex == (int) contactIndex)
  1108. {
  1109. _contactIndex = INVALID_CONTACT_INDEX;
  1110. _scrolling = false;
  1111. double gameTime = Game::getAbsoluteTime();
  1112. float timeSinceLastMove = (float)(gameTime - _scrollingLastTime);
  1113. if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
  1114. {
  1115. _scrollingVelocity.set(0, 0);
  1116. _scrollingMouseVertically = _scrollingMouseHorizontally = false;
  1117. return false;
  1118. }
  1119. int dx = _scrollingLastX - _scrollingFirstX;
  1120. int dy = _scrollingLastY - _scrollingFirstY;
  1121. float timeTakenX = (float)(gameTime - _scrollingStartTimeX);
  1122. float elapsedSecsX = timeTakenX * 0.001f;
  1123. float timeTakenY = (float)(gameTime - _scrollingStartTimeY);
  1124. float elapsedSecsY = timeTakenY * 0.001f;
  1125. float vx = dx;
  1126. float vy = dy;
  1127. if (elapsedSecsX > 0)
  1128. vx = (float)dx / elapsedSecsX;
  1129. if (elapsedSecsY > 0)
  1130. vy = (float)dy / elapsedSecsY;
  1131. if (_scrollingMouseVertically)
  1132. {
  1133. float yRatio = _totalHeight / _absoluteBounds.height;
  1134. vy *= yRatio;
  1135. _scrollingVelocity.set(0, -vy);
  1136. }
  1137. else if (_scrollingMouseHorizontally)
  1138. {
  1139. float xRatio = _totalWidth / _absoluteBounds.width;
  1140. vx *= xRatio;
  1141. _scrollingVelocity.set(-vx, 0);
  1142. }
  1143. else
  1144. {
  1145. _scrollingVelocity.set(vx, vy);
  1146. }
  1147. _scrollingMouseVertically = _scrollingMouseHorizontally = false;
  1148. setDirty(DIRTY_BOUNDS);
  1149. return false;
  1150. }
  1151. break;
  1152. }
  1153. return false;
  1154. }
  1155. bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
  1156. {
  1157. switch (evt)
  1158. {
  1159. case Mouse::MOUSE_PRESS_LEFT_BUTTON:
  1160. {
  1161. bool dirty = false;
  1162. if (_scrollBarVertical)
  1163. {
  1164. float vWidth = _scrollBarVertical->getRegion().width;
  1165. float rightPadding = _absoluteBounds.right() - _viewportBounds.right();
  1166. float topPadding = _viewportBounds.y - _absoluteBounds.y;
  1167. float localVpRight = _bounds.width - rightPadding;
  1168. Rectangle vBounds(
  1169. localVpRight + rightPadding*0.5f - vWidth*0.5f,
  1170. topPadding + _scrollBarBounds.y,
  1171. vWidth, _scrollBarBounds.height);
  1172. if (x >= vBounds.x && x <= vBounds.right())
  1173. {
  1174. // Then we're within the horizontal bounds of the vertical scrollbar.
  1175. // We want to either jump up or down, or drag the scrollbar itself.
  1176. if (y < vBounds.y)
  1177. {
  1178. _scrollPosition.y += _totalHeight / 5.0f;
  1179. dirty = true;
  1180. }
  1181. else if (y > vBounds.bottom())
  1182. {
  1183. _scrollPosition.y -= _totalHeight / 5.0f;
  1184. dirty = true;
  1185. }
  1186. else
  1187. {
  1188. _scrollingMouseVertically = true;
  1189. }
  1190. }
  1191. }
  1192. if (_scrollBarHorizontal)
  1193. {
  1194. float hHeight = _scrollBarHorizontal->getRegion().height;
  1195. float bottomPadding = _absoluteBounds.bottom() - _viewportBounds.bottom();
  1196. float leftPadding = _viewportBounds.x - _absoluteBounds.x;
  1197. float localVpBottom = _bounds.height - bottomPadding;
  1198. Rectangle hBounds(
  1199. leftPadding + _scrollBarBounds.x,
  1200. localVpBottom + bottomPadding*0.5f - hHeight*0.5f,
  1201. _scrollBarBounds.width, hHeight);
  1202. if (y >= hBounds.y && y <= hBounds.bottom())
  1203. {
  1204. // We're within the vertical bounds of the horizontal scrollbar.
  1205. if (x < hBounds.x)
  1206. {
  1207. _scrollPosition.x += _totalWidth / 5.0f;
  1208. dirty = true;
  1209. }
  1210. else if (x > hBounds.x + hBounds.width)
  1211. {
  1212. _scrollPosition.x -= _totalWidth / 5.0f;
  1213. dirty = true;
  1214. }
  1215. else
  1216. {
  1217. _scrollingMouseHorizontally = true;
  1218. }
  1219. }
  1220. }
  1221. if (dirty)
  1222. {
  1223. setDirty(DIRTY_BOUNDS);
  1224. setChildrenDirty(DIRTY_BOUNDS, true);
  1225. }
  1226. return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
  1227. }
  1228. case Mouse::MOUSE_MOVE:
  1229. return touchEventScroll(Touch::TOUCH_MOVE, x, y, 0);
  1230. case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
  1231. return touchEventScroll(Touch::TOUCH_RELEASE, x, y, 0);
  1232. case Mouse::MOUSE_WHEEL:
  1233. {
  1234. if (_scrollingVelocity.isZero())
  1235. {
  1236. _lastFrameTime = Game::getAbsoluteTime();
  1237. }
  1238. _scrolling = _scrollingMouseVertically = _scrollingMouseHorizontally = false;
  1239. _scrollingVelocity.y += _scrollWheelSpeed * wheelDelta;
  1240. if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
  1241. {
  1242. _scrollBarOpacityClip->stop();
  1243. _scrollBarOpacityClip = NULL;
  1244. }
  1245. _scrollBarOpacity = 1.0f;
  1246. setDirty(DIRTY_BOUNDS);
  1247. return false;
  1248. }
  1249. }
  1250. return false;
  1251. }
  1252. bool Container::inContact()
  1253. {
  1254. for (int i = 0; i < MAX_CONTACT_INDICES; ++i)
  1255. {
  1256. if (_contactIndices[i])
  1257. return true;
  1258. }
  1259. return false;
  1260. }
  1261. Container::Scroll Container::getScroll(const char* scroll)
  1262. {
  1263. if (!scroll)
  1264. return Container::SCROLL_NONE;
  1265. if (strcmp(scroll, "SCROLL_NONE") == 0)
  1266. {
  1267. return Container::SCROLL_NONE;
  1268. }
  1269. else if (strcmp(scroll, "SCROLL_HORIZONTAL") == 0)
  1270. {
  1271. return Container::SCROLL_HORIZONTAL;
  1272. }
  1273. else if (strcmp(scroll, "SCROLL_VERTICAL") == 0)
  1274. {
  1275. return Container::SCROLL_VERTICAL;
  1276. }
  1277. else if (strcmp(scroll, "SCROLL_BOTH") == 0)
  1278. {
  1279. return Container::SCROLL_BOTH;
  1280. }
  1281. else
  1282. {
  1283. GP_ERROR("Failed to get corresponding scroll state for unsupported value '%s'.", scroll);
  1284. }
  1285. return Container::SCROLL_NONE;
  1286. }
  1287. float Container::getScrollingFriction() const
  1288. {
  1289. return _scrollingFriction;
  1290. }
  1291. void Container::setScrollingFriction(float friction)
  1292. {
  1293. _scrollingFriction = friction;
  1294. }
  1295. float Container::getScrollWheelSpeed() const
  1296. {
  1297. return _scrollWheelSpeed;
  1298. }
  1299. void Container::setScrollWheelSpeed(float speed)
  1300. {
  1301. _scrollWheelSpeed = speed;
  1302. }
  1303. static bool sortControlsByZOrder(Control* c1, Control* c2)
  1304. {
  1305. if (c1->getZIndex() < c2->getZIndex())
  1306. return true;
  1307. return false;
  1308. }
  1309. unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
  1310. {
  1311. switch(propertyId)
  1312. {
  1313. case ANIMATE_SCROLLBAR_OPACITY:
  1314. return 1;
  1315. default:
  1316. return Control::getAnimationPropertyComponentCount(propertyId);
  1317. }
  1318. }
  1319. void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
  1320. {
  1321. GP_ASSERT(value);
  1322. switch(propertyId)
  1323. {
  1324. case ANIMATE_SCROLLBAR_OPACITY:
  1325. value->setFloat(0, _scrollBarOpacity);
  1326. break;
  1327. default:
  1328. Control::getAnimationPropertyValue(propertyId, value);
  1329. break;
  1330. }
  1331. }
  1332. void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
  1333. {
  1334. GP_ASSERT(value);
  1335. switch(propertyId)
  1336. {
  1337. case ANIMATE_SCROLLBAR_OPACITY:
  1338. _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
  1339. break;
  1340. default:
  1341. Control::setAnimationPropertyValue(propertyId, value, blendWeight);
  1342. break;
  1343. }
  1344. }
  1345. }