ScrollView.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. //
  2. // Copyright (c) 2008-2014 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Context.h"
  24. #include "BorderImage.h"
  25. #include "InputEvents.h"
  26. #include "ScrollBar.h"
  27. #include "ScrollView.h"
  28. #include "Slider.h"
  29. #include "UI.h"
  30. #include "UIEvents.h"
  31. #include "DebugNew.h"
  32. namespace Urho3D
  33. {
  34. static const float STEP_FACTOR = 300.0f;
  35. extern const char* UI_CATEGORY;
  36. ScrollView::ScrollView(Context* context) :
  37. UIElement(context),
  38. viewPosition_(IntVector2::ZERO),
  39. viewSize_(IntVector2::ZERO),
  40. viewPositionAttr_(IntVector2::ZERO),
  41. touchScrollSpeed_(Vector2::ZERO),
  42. touchScrollSpeedMax_(Vector2::ZERO),
  43. scrollDeceleration_(30.0),
  44. scrollSnapEpsilon_(M_EPSILON),
  45. scrollFingerDown_(false),
  46. pageStep_(1.0f),
  47. scrollBarsAutoVisible_(true),
  48. ignoreEvents_(false),
  49. resizeContentWidth_(false)
  50. {
  51. clipChildren_ = true;
  52. enabled_ = true;
  53. focusMode_ = FM_FOCUSABLE_DEFOCUSABLE;
  54. horizontalScrollBar_ = CreateChild<ScrollBar>("SV_HorizontalScrollBar");
  55. horizontalScrollBar_->SetInternal(true);
  56. horizontalScrollBar_->SetAlignment(HA_LEFT, VA_BOTTOM);
  57. horizontalScrollBar_->SetOrientation(O_HORIZONTAL);
  58. verticalScrollBar_ = CreateChild<ScrollBar>("SV_VerticalScrollBar");
  59. verticalScrollBar_->SetInternal(true);
  60. verticalScrollBar_->SetAlignment(HA_RIGHT, VA_TOP);
  61. verticalScrollBar_->SetOrientation(O_VERTICAL);
  62. scrollPanel_ = CreateChild<BorderImage>("SV_ScrollPanel");
  63. scrollPanel_->SetInternal(true);
  64. scrollPanel_->SetEnabled(true);
  65. scrollPanel_->SetClipChildren(true);
  66. SubscribeToEvent(horizontalScrollBar_, E_SCROLLBARCHANGED, HANDLER(ScrollView, HandleScrollBarChanged));
  67. SubscribeToEvent(horizontalScrollBar_, E_VISIBLECHANGED, HANDLER(ScrollView, HandleScrollBarVisibleChanged));
  68. SubscribeToEvent(verticalScrollBar_, E_SCROLLBARCHANGED, HANDLER(ScrollView, HandleScrollBarChanged));
  69. SubscribeToEvent(verticalScrollBar_, E_VISIBLECHANGED, HANDLER(ScrollView, HandleScrollBarVisibleChanged));
  70. SubscribeToEvent(E_TOUCHMOVE, HANDLER(ScrollView, HandleTouchMove));
  71. SubscribeToEvent(E_TOUCHBEGIN, HANDLER(ScrollView, HandleTouchMove));
  72. SubscribeToEvent(E_TOUCHEND, HANDLER(ScrollView, HandleTouchMove));
  73. }
  74. ScrollView::~ScrollView()
  75. {
  76. }
  77. void ScrollView::RegisterObject(Context* context)
  78. {
  79. context->RegisterFactory<ScrollView>(UI_CATEGORY);
  80. COPY_BASE_ATTRIBUTES(ScrollView, UIElement);
  81. UPDATE_ATTRIBUTE_DEFAULT_VALUE(ScrollView, "Clip Children", true);
  82. UPDATE_ATTRIBUTE_DEFAULT_VALUE(ScrollView, "Is Enabled", true);
  83. UPDATE_ATTRIBUTE_DEFAULT_VALUE(ScrollView, "Focus Mode", FM_FOCUSABLE_DEFOCUSABLE);
  84. REF_ACCESSOR_ATTRIBUTE(ScrollView, VAR_INTVECTOR2, "View Position", GetViewPosition, SetViewPositionAttr, IntVector2, IntVector2::ZERO, AM_FILE);
  85. ACCESSOR_ATTRIBUTE(ScrollView, VAR_FLOAT, "Scroll Step", GetScrollStep, SetScrollStep, float, 0.1f, AM_FILE);
  86. ACCESSOR_ATTRIBUTE(ScrollView, VAR_FLOAT, "Page Step", GetPageStep, SetPageStep, float, 1.0f, AM_FILE);
  87. ACCESSOR_ATTRIBUTE(ScrollView, VAR_BOOL, "Auto Show/Hide Scrollbars", GetScrollBarsAutoVisible, SetScrollBarsAutoVisible, bool, true, AM_FILE);
  88. ACCESSOR_ATTRIBUTE(ScrollView, VAR_FLOAT, "Scroll Deceleration", GetScrollDeceleration, SetScrollDeceleration, float, 30.0f, AM_FILE);
  89. ACCESSOR_ATTRIBUTE(ScrollView, VAR_FLOAT, "Scroll Snap Epsilon", GetScrollSnapEpsilon, SetScrollSnapEpsilon, float, 1.0f, AM_FILE);
  90. }
  91. void ScrollView::Update(float timeStep)
  92. {
  93. // Update touch scrolling here if necessary
  94. if (touchScrollSpeed_ == Vector2::ZERO && touchScrollSpeedMax_ == Vector2::ZERO)
  95. return;
  96. // Check if we should not scroll:
  97. // - ScrollView is not visible, is not enabled, or doesn't have focus
  98. // - The element being dragged is not a child of the ScrollView, or is one of our scrollbars
  99. if (!IsVisible() || !IsEnabled() || !HasFocus())
  100. {
  101. touchScrollSpeed_ = Vector2::ZERO;
  102. touchScrollSpeedMax_ = Vector2::ZERO;
  103. return;
  104. }
  105. UIElement* dragElement = GetSubsystem<UI>()->GetDragElement();
  106. if (dragElement)
  107. {
  108. UIElement* dragParent = dragElement->GetParent();
  109. bool dragElementIsChild = false;
  110. while (dragParent)
  111. {
  112. if (dragParent == this)
  113. {
  114. dragElementIsChild = true;
  115. break;
  116. }
  117. dragParent = dragParent->GetParent();
  118. }
  119. if (!dragElementIsChild || dragElement == horizontalScrollBar_->GetSlider() || dragElement == verticalScrollBar_->GetSlider())
  120. {
  121. touchScrollSpeed_ = Vector2::ZERO;
  122. touchScrollSpeedMax_ = Vector2::ZERO;
  123. return;
  124. }
  125. }
  126. // Update view position
  127. IntVector2 newPosition = viewPosition_;
  128. newPosition.x_ += touchScrollSpeed_.x_;
  129. newPosition.y_ += touchScrollSpeed_.y_;
  130. SetViewPosition(newPosition);
  131. // Smooth deceleration
  132. ScrollSmooth(timeStep);
  133. }
  134. void ScrollView::ApplyAttributes()
  135. {
  136. UIElement::ApplyAttributes();
  137. // Set the scrollbar orientations again and perform size update now that the style is known
  138. horizontalScrollBar_->SetOrientation(O_HORIZONTAL);
  139. verticalScrollBar_->SetOrientation(O_VERTICAL);
  140. // If the scroll panel has a child, it should be the content element, which has some special handling
  141. if (scrollPanel_->GetNumChildren())
  142. SetContentElement(scrollPanel_->GetChild(0));
  143. OnResize();
  144. // Reapply view position with proper content element and size
  145. SetViewPosition(viewPositionAttr_);
  146. }
  147. void ScrollView::OnWheel(int delta, int buttons, int qualifiers)
  148. {
  149. if (delta > 0)
  150. verticalScrollBar_->StepBack();
  151. if (delta < 0)
  152. verticalScrollBar_->StepForward();
  153. }
  154. void ScrollView::OnKey(int key, int buttons, int qualifiers)
  155. {
  156. switch (key)
  157. {
  158. case KEY_LEFT:
  159. if (horizontalScrollBar_->IsVisible())
  160. {
  161. if (qualifiers & QUAL_CTRL)
  162. horizontalScrollBar_->SetValue(0.0f);
  163. else
  164. horizontalScrollBar_->StepBack();
  165. }
  166. break;
  167. case KEY_RIGHT:
  168. if (horizontalScrollBar_->IsVisible())
  169. {
  170. if (qualifiers & QUAL_CTRL)
  171. horizontalScrollBar_->SetValue(horizontalScrollBar_->GetRange());
  172. else
  173. horizontalScrollBar_->StepForward();
  174. }
  175. break;
  176. case KEY_HOME:
  177. qualifiers |= QUAL_CTRL;
  178. // Fallthru
  179. case KEY_UP:
  180. if (verticalScrollBar_->IsVisible())
  181. {
  182. if (qualifiers & QUAL_CTRL)
  183. verticalScrollBar_->SetValue(0.0f);
  184. else
  185. verticalScrollBar_->StepBack();
  186. }
  187. break;
  188. case KEY_END:
  189. qualifiers |= QUAL_CTRL;
  190. // Fallthru
  191. case KEY_DOWN:
  192. if (verticalScrollBar_->IsVisible())
  193. {
  194. if (qualifiers & QUAL_CTRL)
  195. verticalScrollBar_->SetValue(verticalScrollBar_->GetRange());
  196. else
  197. verticalScrollBar_->StepForward();
  198. }
  199. break;
  200. case KEY_PAGEUP:
  201. if (verticalScrollBar_->IsVisible())
  202. verticalScrollBar_->ChangeValue(-pageStep_);
  203. break;
  204. case KEY_PAGEDOWN:
  205. if (verticalScrollBar_->IsVisible())
  206. verticalScrollBar_->ChangeValue(pageStep_);
  207. break;
  208. }
  209. }
  210. void ScrollView::OnResize()
  211. {
  212. UpdatePanelSize();
  213. UpdateViewSize();
  214. // If scrollbar autovisibility is enabled, check whether scrollbars should be visible.
  215. // This may force another update of the panel size
  216. if (scrollBarsAutoVisible_)
  217. {
  218. ignoreEvents_ = true;
  219. horizontalScrollBar_->SetVisible(horizontalScrollBar_->GetRange() > M_EPSILON);
  220. verticalScrollBar_->SetVisible(verticalScrollBar_->GetRange() > M_EPSILON);
  221. ignoreEvents_ = false;
  222. UpdatePanelSize();
  223. }
  224. }
  225. void ScrollView::SetContentElement(UIElement* element)
  226. {
  227. if (element == contentElement_)
  228. return;
  229. if (contentElement_)
  230. {
  231. scrollPanel_->RemoveChild(contentElement_);
  232. UnsubscribeFromEvent(contentElement_, E_RESIZED);
  233. }
  234. contentElement_ = element;
  235. if (contentElement_)
  236. {
  237. scrollPanel_->AddChild(contentElement_);
  238. SubscribeToEvent(contentElement_, E_RESIZED, HANDLER(ScrollView, HandleElementResized));
  239. }
  240. OnResize();
  241. }
  242. void ScrollView::SetViewPosition(const IntVector2& position)
  243. {
  244. UpdateView(position);
  245. UpdateScrollBars();
  246. }
  247. void ScrollView::SetViewPosition(int x, int y)
  248. {
  249. SetViewPosition(IntVector2(x, y));
  250. }
  251. void ScrollView::SetScrollBarsVisible(bool horizontal, bool vertical)
  252. {
  253. scrollBarsAutoVisible_ = false;
  254. horizontalScrollBar_->SetVisible(horizontal);
  255. verticalScrollBar_->SetVisible(vertical);
  256. }
  257. void ScrollView::SetScrollBarsAutoVisible(bool enable)
  258. {
  259. if (enable != scrollBarsAutoVisible_)
  260. {
  261. scrollBarsAutoVisible_ = enable;
  262. // Check whether scrollbars should be visible now
  263. if (enable)
  264. OnResize();
  265. else
  266. {
  267. horizontalScrollBar_->SetVisible(true);
  268. verticalScrollBar_->SetVisible(true);
  269. }
  270. }
  271. }
  272. void ScrollView::SetScrollStep(float step)
  273. {
  274. horizontalScrollBar_->SetScrollStep(step);
  275. verticalScrollBar_->SetScrollStep(step);
  276. }
  277. void ScrollView::SetPageStep(float step)
  278. {
  279. pageStep_ = Max(step, 0.0f);
  280. }
  281. float ScrollView::GetScrollStep() const
  282. {
  283. return horizontalScrollBar_->GetScrollStep();
  284. }
  285. void ScrollView::SetViewPositionAttr(const IntVector2& value)
  286. {
  287. viewPositionAttr_ = value;
  288. SetViewPosition(value);
  289. }
  290. bool ScrollView::FilterImplicitAttributes(XMLElement& dest) const
  291. {
  292. if (!UIElement::FilterImplicitAttributes(dest))
  293. return false;
  294. XMLElement childElem = dest.GetChild("element");
  295. if (!FilterScrollBarImplicitAttributes(childElem, "SV_HorizontalScrollBar"))
  296. return false;
  297. if (!RemoveChildXML(childElem, "Vert Alignment", "Bottom"))
  298. return false;
  299. childElem = childElem.GetNext("element");
  300. if (!FilterScrollBarImplicitAttributes(childElem, "SV_VerticalScrollBar"))
  301. return false;
  302. if (!RemoveChildXML(childElem, "Horiz Alignment", "Right"))
  303. return false;
  304. childElem = childElem.GetNext("element");
  305. if (!childElem)
  306. return false;
  307. if (!RemoveChildXML(childElem, "Name", "SV_ScrollPanel"))
  308. return false;
  309. if (!RemoveChildXML(childElem, "Is Enabled", "true"))
  310. return false;
  311. if (!RemoveChildXML(childElem, "Clip Children", "true"))
  312. return false;
  313. if (!RemoveChildXML(childElem, "Size"))
  314. return false;
  315. return true;
  316. }
  317. bool ScrollView::FilterScrollBarImplicitAttributes(XMLElement& dest, const String& name) const
  318. {
  319. if (!dest)
  320. return false;
  321. if (!RemoveChildXML(dest, "Name", name))
  322. return false;
  323. if (!RemoveChildXML(dest, "Orientation"))
  324. return false;
  325. if (!RemoveChildXML(dest, "Range"))
  326. return false;
  327. if (!RemoveChildXML(dest, "Step Factor"))
  328. return false;
  329. if (scrollBarsAutoVisible_)
  330. {
  331. if (!RemoveChildXML(dest, "Is Visible"))
  332. return false;
  333. }
  334. return true;
  335. }
  336. void ScrollView::UpdatePanelSize()
  337. {
  338. // Ignore events in case content element resizes itself along with the panel
  339. // (content element resize triggers our OnResize(), so it could lead to infinite recursion)
  340. ignoreEvents_ = true;
  341. IntVector2 panelSize = GetSize();
  342. if (verticalScrollBar_->IsVisible())
  343. panelSize.x_ -= verticalScrollBar_->GetWidth();
  344. if (horizontalScrollBar_->IsVisible())
  345. panelSize.y_ -= horizontalScrollBar_->GetHeight();
  346. scrollPanel_->SetSize(panelSize);
  347. horizontalScrollBar_->SetWidth(scrollPanel_->GetWidth());
  348. verticalScrollBar_->SetHeight(scrollPanel_->GetHeight());
  349. if (resizeContentWidth_ && contentElement_)
  350. {
  351. IntRect panelBorder = scrollPanel_->GetClipBorder();
  352. contentElement_->SetWidth(scrollPanel_->GetWidth() - panelBorder.left_ - panelBorder.right_);
  353. UpdateViewSize();
  354. }
  355. ignoreEvents_ = false;
  356. }
  357. void ScrollView::UpdateViewSize()
  358. {
  359. IntVector2 size(IntVector2::ZERO);
  360. if (contentElement_)
  361. size = contentElement_->GetSize();
  362. IntRect panelBorder = scrollPanel_->GetClipBorder();
  363. viewSize_.x_ = Max(size.x_, scrollPanel_->GetWidth() - panelBorder.left_ - panelBorder.right_);
  364. viewSize_.y_ = Max(size.y_, scrollPanel_->GetHeight() - panelBorder.top_ - panelBorder.bottom_);
  365. UpdateView(viewPosition_);
  366. UpdateScrollBars();
  367. }
  368. void ScrollView::UpdateScrollBars()
  369. {
  370. ignoreEvents_ = true;
  371. IntVector2 size = scrollPanel_->GetSize();
  372. IntRect panelBorder = scrollPanel_->GetClipBorder();
  373. size.x_ -= panelBorder.left_ + panelBorder.right_;
  374. size.y_ -= panelBorder.top_ + panelBorder.bottom_;
  375. if (size.x_ > 0 && viewSize_.x_ > 0)
  376. {
  377. float range = (float)viewSize_.x_ / (float)size.x_ - 1.0f;
  378. horizontalScrollBar_->SetRange(range);
  379. horizontalScrollBar_->SetValue((float)viewPosition_.x_ / (float)size.x_);
  380. horizontalScrollBar_->SetStepFactor(STEP_FACTOR / (float)size.x_);
  381. }
  382. if (size.y_ > 0 && viewSize_.y_ > 0)
  383. {
  384. float range = (float)viewSize_.y_ / (float)size.y_ - 1.0f;
  385. verticalScrollBar_->SetRange(range);
  386. verticalScrollBar_->SetValue((float)viewPosition_.y_ / (float)size.y_);
  387. verticalScrollBar_->SetStepFactor(STEP_FACTOR / (float)size.y_);
  388. }
  389. ignoreEvents_ = false;
  390. }
  391. void ScrollView::UpdateView(const IntVector2& position)
  392. {
  393. IntVector2 oldPosition = viewPosition_;
  394. IntRect panelBorder = scrollPanel_->GetClipBorder();
  395. IntVector2 panelSize(scrollPanel_->GetWidth() - panelBorder.left_ - panelBorder.right_, scrollPanel_->GetHeight() -
  396. panelBorder.top_ - panelBorder.bottom_);
  397. viewPosition_.x_ = Clamp(position.x_, 0, viewSize_.x_ - panelSize.x_);
  398. viewPosition_.y_ = Clamp(position.y_, 0, viewSize_.y_ - panelSize.y_);
  399. scrollPanel_->SetChildOffset(IntVector2(-viewPosition_.x_ + panelBorder.left_, -viewPosition_.y_ + panelBorder.top_));
  400. if (viewPosition_ != oldPosition)
  401. {
  402. using namespace ViewChanged;
  403. VariantMap& eventData = GetEventDataMap();
  404. eventData[P_ELEMENT] = this;
  405. eventData[P_X] = viewPosition_.x_;
  406. eventData[P_Y] = viewPosition_.y_;
  407. SendEvent(E_VIEWCHANGED, eventData);
  408. }
  409. }
  410. void ScrollView::HandleScrollBarChanged(StringHash eventType, VariantMap& eventData)
  411. {
  412. if (!ignoreEvents_)
  413. {
  414. IntVector2 size = scrollPanel_->GetSize();
  415. IntRect panelBorder = scrollPanel_->GetClipBorder();
  416. size.x_ -= panelBorder.left_ + panelBorder.right_;
  417. size.y_ -= panelBorder.top_ + panelBorder.bottom_;
  418. UpdateView(IntVector2(
  419. (int)(horizontalScrollBar_->GetValue() * (float)size.x_),
  420. (int)(verticalScrollBar_->GetValue() * (float)size.y_)
  421. ));
  422. }
  423. }
  424. void ScrollView::HandleScrollBarVisibleChanged(StringHash eventType, VariantMap& eventData)
  425. {
  426. // Need to recalculate panel size when scrollbar visibility changes
  427. if (!ignoreEvents_)
  428. OnResize();
  429. }
  430. void ScrollView::HandleElementResized(StringHash eventType, VariantMap& eventData)
  431. {
  432. if (!ignoreEvents_)
  433. OnResize();
  434. }
  435. void ScrollView::HandleTouchMove(StringHash eventType, VariantMap& eventData)
  436. {
  437. using namespace TouchMove;
  438. if (eventType == E_TOUCHMOVE)
  439. {
  440. scrollFingerDown_ = true;
  441. // Take new scrolling speed if it's faster than the current accumulated value
  442. int dX = -eventData[P_DX].GetInt();
  443. int dY = -eventData[P_DY].GetInt();
  444. if (Abs(dX) > Abs(touchScrollSpeed_.x_))
  445. touchScrollSpeed_.x_ = dX;
  446. if (Abs(dY) > Abs(touchScrollSpeed_.y_))
  447. touchScrollSpeed_.y_ = dY;
  448. if (Abs(dX) > Abs(touchScrollSpeedMax_.x_))
  449. touchScrollSpeedMax_.x_ = dX;
  450. if (Abs(dY) > Abs(touchScrollSpeedMax_.y_))
  451. touchScrollSpeedMax_.y_ = dY;
  452. }
  453. else if (eventType == E_TOUCHBEGIN)
  454. {
  455. // Stop the control if the figer goes back down
  456. touchScrollSpeed_ = Vector2::ZERO;
  457. touchScrollSpeedMax_ = Vector2::ZERO;
  458. }
  459. else
  460. {
  461. // 'Flick' action
  462. scrollFingerDown_ = false;
  463. if (Abs(touchScrollSpeedMax_.x_) > scrollSnapEpsilon_ )
  464. touchScrollSpeed_.x_ = touchScrollSpeedMax_.x_;
  465. else
  466. touchScrollSpeed_.x_ = 0;
  467. if (Abs(touchScrollSpeedMax_.y_) > scrollSnapEpsilon_ )
  468. touchScrollSpeed_.y_ = touchScrollSpeedMax_.y_;
  469. else
  470. touchScrollSpeed_.y_ = 0;
  471. touchScrollSpeedMax_ = Vector2::ZERO;
  472. }
  473. }
  474. void ScrollView::ScrollSmooth(float timeStep)
  475. {
  476. // Decay the momentum
  477. if (touchScrollSpeedMax_.x_ >= scrollSnapEpsilon_)
  478. {
  479. touchScrollSpeedMax_.x_ -= scrollDeceleration_ * timeStep;
  480. touchScrollSpeedMax_.x_ = touchScrollSpeedMax_.x_ > 0 ? touchScrollSpeedMax_.x_ : 0;
  481. }
  482. else if (touchScrollSpeedMax_.x_ <= -scrollSnapEpsilon_)
  483. {
  484. touchScrollSpeedMax_.x_ += scrollDeceleration_ * timeStep;
  485. touchScrollSpeedMax_.x_ = touchScrollSpeedMax_.x_ < 0 ? touchScrollSpeedMax_.x_ : 0;
  486. }
  487. else
  488. touchScrollSpeedMax_.x_ = 0;
  489. if (touchScrollSpeedMax_.y_ >= scrollSnapEpsilon_)
  490. {
  491. touchScrollSpeedMax_.y_ -= scrollDeceleration_ * timeStep;
  492. touchScrollSpeedMax_.y_ = touchScrollSpeedMax_.y_ > 0 ? touchScrollSpeedMax_.y_ : 0;
  493. }
  494. else if (touchScrollSpeedMax_.y_ <= -scrollSnapEpsilon_)
  495. {
  496. touchScrollSpeedMax_.y_ += scrollDeceleration_ * timeStep;
  497. touchScrollSpeedMax_.y_ = touchScrollSpeedMax_.y_ < 0 ? touchScrollSpeedMax_.y_ : 0;
  498. }
  499. else
  500. touchScrollSpeedMax_.y_ = 0;
  501. // Control vs flick
  502. if (scrollFingerDown_)
  503. {
  504. // Finger is held down: control = instant stop
  505. touchScrollSpeed_ = Vector2::ZERO;
  506. }
  507. else
  508. {
  509. // Finger is released: flick = smooth deceleration
  510. if (touchScrollSpeed_.x_ >= scrollSnapEpsilon_)
  511. {
  512. touchScrollSpeed_.x_ -= scrollDeceleration_ * timeStep;
  513. if (touchScrollSpeed_.x_ < 0)
  514. {
  515. touchScrollSpeed_.x_ = 0;
  516. }
  517. if (horizontalScrollBar_->GetValue() >= horizontalScrollBar_->GetRange() - M_EPSILON)
  518. {
  519. // Stop movement when we reach end of scroll
  520. touchScrollSpeed_.x_ = 0;
  521. }
  522. }
  523. else if (touchScrollSpeed_.x_ < -scrollSnapEpsilon_)
  524. {
  525. touchScrollSpeed_.x_ += scrollDeceleration_ * timeStep;
  526. if (touchScrollSpeed_.x_ > 0)
  527. {
  528. touchScrollSpeed_.x_ = 0;
  529. }
  530. if (horizontalScrollBar_->GetValue() <= M_EPSILON)
  531. {
  532. // Stop movement when we reach end of scroll
  533. touchScrollSpeed_.x_ = 0;
  534. }
  535. }
  536. else
  537. touchScrollSpeed_.x_ = 0;
  538. if (touchScrollSpeed_.y_ >= scrollSnapEpsilon_)
  539. {
  540. touchScrollSpeed_.y_ -= scrollDeceleration_ * timeStep;
  541. if (touchScrollSpeed_.y_ < 0)
  542. {
  543. touchScrollSpeed_.y_ = 0;
  544. }
  545. if (verticalScrollBar_->GetValue() >= verticalScrollBar_->GetRange() - M_EPSILON)
  546. {
  547. // Stop movement when we reach end of scroll
  548. touchScrollSpeed_.y_ = 0;
  549. }
  550. }
  551. else if (touchScrollSpeed_.y_ < -scrollSnapEpsilon_)
  552. {
  553. touchScrollSpeed_.y_ += scrollDeceleration_ * timeStep;
  554. if (touchScrollSpeed_.y_ > 0)
  555. {
  556. touchScrollSpeed_.y_ = 0;
  557. }
  558. if (verticalScrollBar_->GetValue() <= M_EPSILON)
  559. {
  560. // Stop movement when we reach end of scroll
  561. touchScrollSpeed_.y_ = 0;
  562. }
  563. }
  564. else
  565. touchScrollSpeed_.y_ = 0;
  566. }
  567. }
  568. }