LCOV - code coverage report
Current view: top level - xenolith/renderer/basic2d - XL2dScrollViewBase.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 352 564 62.4 %
Date: 2024-05-12 00:16:13 Functions: 52 79 65.8 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       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             : 
      23             : #include "XL2dScrollViewBase.h"
      24             : #include "XLInputListener.h"
      25             : #include "XL2dScrollController.h"
      26             : 
      27             : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
      28             : 
      29          46 : bool ScrollViewBase::init(Layout layout) {
      30          46 :         if (!DynamicStateNode::init()) {
      31           0 :                 return false;
      32             :         }
      33             : 
      34          46 :         _layout = layout;
      35             : 
      36          46 :         _inputListener = addInputListener(Rc<InputListener>::create());
      37          46 :         _inputListener->addTapRecognizer([this] (const GestureTap &tap) {
      38           0 :                 if (tap.event == GestureEvent::Activated) {
      39           0 :                         onTap(tap.count, tap.pos);
      40             :                 }
      41           0 :                 return false;
      42          46 :         }, InputListener::makeButtonMask({InputMouseButton::Touch}));
      43             : 
      44          46 :         _inputListener->addPressRecognizer([this] (const GesturePress &s) -> bool {
      45          24 :                 switch (s.event) {
      46          24 :                 case GestureEvent::Began: return onPressBegin(s.pos); break;
      47           0 :                 case GestureEvent::Activated: return onLongPress(s.pos, s.time, s.tickCount); break;
      48           0 :                 case GestureEvent::Ended: return onPressEnd(s.pos, s.time); break;
      49           0 :                 case GestureEvent::Cancelled: return onPressCancel(s.pos, s.time); break;
      50             :                 }
      51           0 :                 return false;
      52          46 :         }, TimeInterval::milliseconds(425), true);
      53             : 
      54          46 :         _inputListener->addSwipeRecognizer([this] (const GestureSwipe &s) -> bool {
      55         168 :                 switch (s.event) {
      56          14 :                 case GestureEvent::Began: return onSwipeEventBegin(s.getId(), s.midpoint, s.delta, s.velocity); break;
      57         140 :                 case GestureEvent::Activated: return onSwipeEvent(s.getId(), s.midpoint, s.delta, s.velocity); break;
      58          14 :                 case GestureEvent::Ended:
      59             :                 case GestureEvent::Cancelled:
      60          14 :                         return onSwipeEventEnd(s.getId(), s.midpoint, s.delta, s.velocity);
      61             :                         break;
      62             :                 }
      63           0 :                 return false;
      64             :         }, TapDistanceAllowed, true);
      65             : 
      66          46 :         _inputListener->addScrollRecognizer([this] (const GestureScroll &w) -> bool {
      67           0 :                 auto pos = getScrollPosition();
      68           0 :                 onSwipeBegin();
      69           0 :                 if (_layout == Vertical) {
      70           0 :                         onDelta(- w.amount.y * 5.0f / _globalScale.y);
      71             :                 } else {
      72           0 :                         onDelta(- w.amount.x * 5.0f / _globalScale.x);
      73             :                 }
      74           0 :                 onScroll(getScrollPosition() - pos, false);
      75           0 :                 return true;
      76             :         });
      77             : 
      78          46 :         setCascadeOpacityEnabled(true);
      79             : 
      80          46 :         _root = addChild(Rc<Node>::create());
      81          46 :         _root->setPosition(Vec2(0, 0));
      82          46 :         _root->setAnchorPoint((_layout == Vertical)?Vec2(0, 1):Vec2(0, 0));
      83          46 :         _root->setCascadeOpacityEnabled(true);
      84          46 :         _root->setOnContentSizeDirtyCallback(std::bind(&ScrollViewBase::onPosition, this));
      85          46 :         _root->setOnTransformDirtyCallback(std::bind(&ScrollViewBase::onPosition, this));
      86             : 
      87          46 :         return true;
      88             : }
      89             : 
      90           0 : void ScrollViewBase::setLayout(Layout l) {
      91           0 :         _layout = l;
      92           0 :         _contentSizeDirty = true;
      93           0 : }
      94             : 
      95         832 : bool ScrollViewBase::visitDraw(FrameInfo &info, NodeFlags parentFlags) {
      96         832 :         if (_scrollDirty) {
      97          12 :                 updateScrollBounds();
      98             :         }
      99         832 :         if (_animationDirty) {
     100           0 :                 fixPosition();
     101             :         }
     102         832 :         auto ret = DynamicStateNode::visitDraw(info, parentFlags);
     103         832 :         if (_animationDirty) {
     104           0 :                 onPosition();
     105           0 :                 onScroll(0, true);
     106           0 :                 _animationDirty = false;
     107             :         }
     108         832 :         return ret;
     109             : }
     110             : 
     111          46 : void ScrollViewBase::onEnter(Scene *scene) {
     112          46 :         DynamicStateNode::onEnter(scene);
     113          46 :         onPosition();
     114          46 : }
     115             : 
     116          58 : void ScrollViewBase::onContentSizeDirty() {
     117          58 :         if (!isnan(_scrollSpaceLimit)) {
     118           0 :                 auto padding = _paddingGlobal;
     119           0 :                 if (isVertical()) {
     120           0 :                         if (_contentSize.width > _scrollSpaceLimit + _scrollSpacePadding * 2.0f) {
     121           0 :                                 padding.left = padding.right = (_contentSize.width - _scrollSpaceLimit) / 2.0f;
     122             :                         } else {
     123           0 :                                 padding.left = padding.right = _scrollSpacePadding;
     124             :                         }
     125             :                 } else {
     126           0 :                         if (_contentSize.height > _scrollSpaceLimit + _scrollSpacePadding * 2.0f) {
     127           0 :                                 padding.top = padding.bottom = (_contentSize.height - _scrollSpaceLimit) / 2.0f;
     128             :                         } else {
     129           0 :                                 padding.top = padding.bottom = _scrollSpacePadding;
     130             :                         }
     131             :                 }
     132           0 :                 _paddingGlobal = padding;
     133             :         }
     134          58 :         DynamicStateNode::onContentSizeDirty();
     135          58 :         updateScrollBounds();
     136          58 :         fixPosition();
     137          58 : }
     138             : 
     139          38 : void ScrollViewBase::onTransformDirty(const Mat4 &parentTransform) {
     140          38 :         DynamicStateNode::onTransformDirty(parentTransform);
     141             : 
     142          38 :         Vec3 scale;
     143          38 :         parentTransform.decompose(&scale, nullptr, nullptr);
     144             : 
     145          38 :         if (_scale.x != 1.f) { scale.x *= _scale.x; }
     146          38 :         if (_scale.y != 1.f) { scale.y *= _scale.y; }
     147          38 :         if (_scale.z != 1.f) { scale.z *= _scale.z; }
     148             : 
     149          38 :         _globalScale = Vec2(scale.x, scale.y);
     150          38 : }
     151             : 
     152          20 : void ScrollViewBase::setEnabled(bool value) {
     153          20 :         _inputListener->setEnabled(value);
     154          20 : }
     155           0 : bool ScrollViewBase::isEnabled() const {
     156           0 :         return _inputListener->isEnabled();
     157             : }
     158           0 : bool ScrollViewBase::isInMotion() const {
     159           0 :         return _movement == Movement::Manual;
     160             : }
     161           0 : bool ScrollViewBase::isMoved() const {
     162           0 :         return _movement != Movement::None;
     163             : }
     164             : 
     165          12 : void ScrollViewBase::setScrollCallback(const ScrollCallback & cb) {
     166          12 :         _scrollCallback = cb;
     167          12 : }
     168           0 : const ScrollViewBase::ScrollCallback &ScrollViewBase::getScrollCallback() const {
     169           0 :         return _scrollCallback;
     170             : }
     171             : 
     172           0 : void ScrollViewBase::setOverscrollCallback(const OverscrollCallback & cb) {
     173           0 :         _overscrollCallback = cb;
     174           0 : }
     175           0 : const ScrollViewBase::OverscrollCallback &ScrollViewBase::getOverscrollCallback() const {
     176           0 :         return _overscrollCallback;
     177             : }
     178             : 
     179          10 : bool ScrollViewBase::addComponentItem(Component *cmp) {
     180          10 :         if (auto c = dynamic_cast<ScrollController *>(cmp)) {
     181           0 :                 setController(c);
     182           0 :                 return true;
     183             :         } else {
     184          10 :                 return DynamicStateNode::addComponentItem(cmp);
     185             :         }
     186             : }
     187             : 
     188          46 : void ScrollViewBase::setController(ScrollController *c) {
     189          46 :         if (c != _controller) {
     190          46 :                 if (_controller) {
     191           0 :                         DynamicStateNode::removeComponent(_controller);
     192           0 :                         _controller = nullptr;
     193             :                 }
     194          46 :                 _controller = c;
     195          46 :                 if (_controller) {
     196          46 :                         DynamicStateNode::addComponentItem(_controller);
     197             :                 }
     198             :         }
     199          46 : }
     200           4 : ScrollController *ScrollViewBase::getController() {
     201           4 :         return _controller;
     202             : }
     203             : 
     204          10 : void ScrollViewBase::setPadding(const Padding &p) {
     205          10 :         if (p != _paddingGlobal) {
     206          10 :                 _paddingGlobal = p;
     207          10 :                 _contentSizeDirty = true;
     208             :         }
     209          10 : }
     210         298 : const Padding &ScrollViewBase::getPadding() const {
     211         298 :         return _paddingGlobal;
     212             : }
     213             : 
     214           0 : void ScrollViewBase::setSpaceLimit(float value, float padding) {
     215           0 :         if (_scrollSpaceLimit != value || _scrollSpacePadding != padding) {
     216           0 :                 _scrollSpaceLimit = value;
     217           0 :                 _scrollSpacePadding = padding;
     218           0 :                 _contentSizeDirty = true;
     219             :         }
     220           0 : }
     221           0 : float ScrollViewBase::getSpaceLimit() const {
     222           0 :         return _scrollSpaceLimit;
     223             : }
     224           0 : float ScrollViewBase::getSpacePadding() const {
     225           0 :         return _scrollSpacePadding;
     226             : }
     227             : 
     228        3778 : float ScrollViewBase::getScrollableAreaOffset() const {
     229        3778 :         if (_controller) {
     230        3778 :                 return _controller->getScrollableAreaOffset();
     231             :         }
     232           0 :         return std::numeric_limits<float>::quiet_NaN();
     233             : }
     234             : 
     235        3409 : float ScrollViewBase::getScrollableAreaSize() const {
     236        3409 :         if (_controller) {
     237        3409 :                 return _controller->getScrollableAreaSize();
     238             :         }
     239           0 :         return std::numeric_limits<float>::quiet_NaN();
     240             : }
     241             : 
     242       28710 : Vec2 ScrollViewBase::getPositionForNode(float scrollPos) const {
     243       28710 :         if (isVertical()) {
     244       28704 :                 return Vec2(0.0f, scrollPos);
     245             :         } else {
     246           6 :                 return Vec2(scrollPos, 0.0f);
     247             :         }
     248             : }
     249       28710 : Size2 ScrollViewBase::getContentSizeForNode(float size) const {
     250       28710 :         if (isVertical()) {
     251       28704 :                 return Size2(nan(), size);
     252             :         } else {
     253           6 :                 return Size2(size, nan());
     254             :         }
     255             : }
     256       26929 : Vec2 ScrollViewBase::getAnchorPointForNode() const {
     257       26929 :         if (isVertical()) {
     258       26917 :                 return Vec2(0, 1.0f);
     259             :         } else {
     260          12 :                 return Vec2(0, 0);
     261             :         }
     262             : }
     263             : 
     264      809172 : float ScrollViewBase::getNodeScrollSize(Size2 size) const {
     265      809172 :         return (isVertical())?(size.height):(size.width);
     266             : }
     267             : 
     268      809474 : float ScrollViewBase::getNodeScrollPosition(Vec2 pos) const {
     269      809474 :         return (isVertical())?(pos.y):(pos.x);
     270             : }
     271             : 
     272       16639 : bool ScrollViewBase::addScrollNode(Node *node, Vec2 pos, Size2 size, ZOrder z, StringView name) {
     273       16639 :         updateScrollNode(node, pos, size, z, name);
     274       16639 :         if (z != ZOrder(0)) {
     275         145 :                 _root->addChild(node, z);
     276             :         } else {
     277       16494 :                 _root->addChild(node);
     278             :         }
     279       16639 :         return true;
     280             : }
     281             : 
     282       26929 : void ScrollViewBase::updateScrollNode(Node *node, Vec2 pos, Size2 size, ZOrder z, StringView name) {
     283       26929 :         auto p = node->getParent();
     284       26929 :         if (p == _root || p == nullptr) {
     285       28000 :                 auto cs = Size2(isnan(size.width)?_root->getContentSize().width:size.width,
     286       54929 :                                 isnan(size.height)?_root->getContentSize().height:size.height);
     287             : 
     288       26929 :                 node->setContentSize(cs);
     289       26929 :                 node->setPosition((isVertical()?Vec2(pos.x,-pos.y):pos));
     290       26929 :                 node->setAnchorPoint(getAnchorPointForNode());
     291       26929 :                 if (z != ZOrder(0)) {
     292         887 :                         node->setLocalZOrder(z);
     293             :                 }
     294             :         }
     295       26929 : }
     296             : 
     297       14709 : bool ScrollViewBase::removeScrollNode(Node *node) {
     298       14709 :         if (node && node->getParent() == _root) {
     299       14709 :                 node->removeFromParent();
     300       14709 :                 return true;
     301             :         }
     302           0 :         return false;
     303             : }
     304             : 
     305         220 : float ScrollViewBase::getDistanceFromStart() const {
     306         220 :         auto min = getScrollMinPosition();
     307         220 :         if (!isnan(min)) {
     308         220 :                 return fabsf(getScrollPosition() - min);
     309             :         } else {
     310           0 :                 return std::numeric_limits<float>::quiet_NaN();
     311             :         }
     312             : }
     313             : 
     314          10 : void ScrollViewBase::setScrollMaxVelocity(float value) {
     315          10 :         _maxVelocity = value;
     316          10 : }
     317           0 : float ScrollViewBase::getScrollMaxVelocity() const {
     318           0 :         return _maxVelocity;
     319             : }
     320             : 
     321             : 
     322           0 : Node * ScrollViewBase::getFrontNode() const {
     323           0 :         if (_controller) {
     324           0 :                 return _controller->getFrontNode();
     325             :         }
     326           0 :         return nullptr;
     327             : }
     328           0 : Node * ScrollViewBase::getBackNode() const {
     329           0 :         if (_controller) {
     330           0 :                 return _controller->getBackNode();
     331             :         }
     332           0 :         return nullptr;
     333             : }
     334             : 
     335        2056 : float ScrollViewBase::getScrollMinPosition() const {
     336        2056 :         auto pos = getScrollableAreaOffset();
     337        2056 :         if (!isnan(pos)) {
     338        2056 :                 return pos - (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
     339             :         }
     340           0 :         if (_controller) {
     341           0 :                 float min = _controller->getScrollMin();
     342           0 :                 if (!isnan(min)) {
     343           0 :                         return min - (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
     344             :                 }
     345             :         }
     346           0 :         return std::numeric_limits<float>::quiet_NaN();
     347             : }
     348             : 
     349        1512 : float ScrollViewBase::getScrollMaxPosition() const {
     350        1512 :         auto pos = getScrollableAreaOffset();
     351        1512 :         auto size = getScrollableAreaSize();
     352        1512 :         if (!isnan(pos) && !isnan(size)) {
     353        1230 :                 pos -= (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
     354        1230 :                 size += (isVertical()?(_paddingGlobal.top + _paddingGlobal.bottom):(_paddingGlobal.left + _paddingGlobal.right));
     355        1230 :                 if (size > _scrollSize) {
     356        1046 :                         return pos + size - _scrollSize;
     357             :                 } else {
     358         184 :                         return pos;
     359             :                 }
     360             :         }
     361         282 :         if (_controller) {
     362         282 :                 float min = _controller->getScrollMin();
     363         282 :                 float max = _controller->getScrollMax();
     364         282 :                 if (!isnan(max) && !isnan(min)) {
     365          20 :                         return std::max(min, max - _scrollSize + (isVertical()?_paddingGlobal.bottom:_paddingGlobal.right));
     366         262 :                 } else if (!isnan(max)) {
     367           0 :                         return max - _scrollSize + (isVertical()?_paddingGlobal.bottom:_paddingGlobal.right);
     368             :                 }
     369             :         }
     370             : 
     371         262 :         return std::numeric_limits<float>::quiet_NaN();
     372             : }
     373             : 
     374        1467 : float ScrollViewBase::getScrollLength() const {
     375        1467 :         float size = getScrollableAreaSize();
     376        1467 :         if (!isnan(size)) {
     377        1311 :                 return size + (isVertical()?(_paddingGlobal.top + _paddingGlobal.bottom):(_paddingGlobal.left + _paddingGlobal.right));
     378             :         }
     379             : 
     380         156 :         float min = getScrollMinPosition();
     381         156 :         float max = getScrollMaxPosition();
     382             : 
     383         156 :         if (!isnan(min) && !isnan(max)) {
     384           0 :                 float trueMax = max - (isVertical()?_paddingGlobal.bottom:_paddingGlobal.right);
     385           0 :                 float trueMin = min + (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
     386           0 :                 if (trueMax > trueMin) {
     387           0 :                         return max  - min + _scrollSize;
     388             :                 } else {
     389           0 :                         return _scrollSize;
     390             :                 }
     391             :         } else {
     392         156 :                 return std::numeric_limits<float>::quiet_NaN();
     393             :         }
     394             : }
     395             : 
     396         862 : float ScrollViewBase::getScrollSize() const {
     397         862 :         return _scrollSize;
     398             : }
     399             : 
     400         145 : void ScrollViewBase::setScrollRelativePosition(float value) {
     401         145 :         if (!isnan(value)) {
     402         145 :                 if (value < 0.0f) {
     403           0 :                         value = 0.0f;
     404         145 :                 } else if (value > 1.0f) {
     405           0 :                         value = 1.0f;
     406             :                 }
     407             :         } else {
     408           0 :                 value = 0.0f;
     409             :         }
     410             : 
     411         145 :         float areaSize = getScrollableAreaSize();
     412         145 :         float areaOffset = getScrollableAreaOffset();
     413         145 :         float size = getScrollSize();
     414             : 
     415         145 :         if (areaSize < size) {
     416           0 :                 value = 0.0f;
     417             :         }
     418             : 
     419         145 :         auto &padding = getPadding();
     420         145 :         auto paddingFront = (isVertical())?padding.top:padding.left;
     421         145 :         auto paddingBack = (isVertical())?padding.bottom:padding.right;
     422             : 
     423         145 :         if (!isnan(areaSize) && !isnan(areaOffset) && areaSize > 0) {
     424         145 :                 float liveSize = areaSize + paddingFront + paddingBack - size;
     425         145 :                 float pos = (value * liveSize) - paddingFront + areaOffset;
     426             : 
     427         145 :                 doSetScrollPosition(pos);
     428             :         } else {
     429           0 :                 _savedRelativePosition = value;
     430             :         }
     431         145 : }
     432             : 
     433          61 : float ScrollViewBase::getScrollRelativePosition() const {
     434          61 :         if (!isnan(_savedRelativePosition)) {
     435           0 :                 return _savedRelativePosition;
     436             :         }
     437             : 
     438          61 :         return getScrollRelativePosition(getScrollPosition());
     439             : }
     440             : 
     441          61 : float ScrollViewBase::getScrollRelativePosition(float pos) const {
     442          61 :         float areaSize = getScrollableAreaSize();
     443          61 :         float areaOffset = getScrollableAreaOffset();
     444          61 :         float size = getScrollSize();
     445             : 
     446          61 :         auto &padding = getPadding();
     447          61 :         auto paddingFront = (isVertical())?padding.top:padding.left;
     448          61 :         auto paddingBack = (isVertical())?padding.bottom:padding.right;
     449             : 
     450          61 :         if (!isnan(areaSize) && !isnan(areaOffset)) {
     451          61 :                 float liveSize = areaSize + paddingFront + paddingBack - size;
     452          61 :                 return (pos - areaOffset + paddingFront) / liveSize;
     453             :         }
     454             : 
     455           0 :         return 0.0f;
     456             : }
     457             : 
     458         156 : void ScrollViewBase::setScrollPosition(float pos) {
     459         156 :         if (pos != _scrollPosition) {
     460         154 :                 doSetScrollPosition(pos);
     461             :         }
     462         156 : }
     463             : 
     464         299 : void ScrollViewBase::doSetScrollPosition(float pos) {
     465         299 :         if (isVertical()) {
     466         299 :                 _root->setPositionY(pos + _scrollSize);
     467             :         } else {
     468           0 :                 _root->setPositionX(-pos);
     469             :         }
     470         299 : }
     471             : 
     472        1343 : float ScrollViewBase::getScrollPosition() const {
     473        1343 :         if (isVertical()) {
     474        1315 :                 return _root->getPosition().y - _scrollSize;
     475             :         } else {
     476          28 :                 return -_root->getPosition().x;
     477             :         }
     478             : }
     479             : 
     480          14 : Vec2 ScrollViewBase::getPointForScrollPosition(float pos) {
     481          14 :         return (isVertical())?Vec2(_root->getPosition().x, pos + _scrollSize):Vec2(-pos, _root->getPosition().y);
     482             : }
     483             : 
     484         140 : void ScrollViewBase::onDelta(float delta) {
     485         140 :         auto pos = getScrollPosition();
     486         140 :         if (delta < 0) {
     487           0 :                 if (!isnan(_scrollMin) && pos + delta < _scrollMin) {
     488           0 :                         if (_bounce) {
     489           0 :                                 float mod = 1.0f / (1.0f + (_scrollMin - (pos + delta)) / 5.0f);
     490           0 :                                 setScrollPosition(pos + delta * mod);
     491           0 :                                 return;
     492             :                         } else {
     493           0 :                                 onOverscroll(delta);
     494           0 :                                 setScrollPosition(_scrollMin);
     495           0 :                                 return;
     496             :                         }
     497             :                 }
     498         140 :         } else if (delta > 0) {
     499         140 :                 if (!isnan(_scrollMax) && pos + delta > _scrollMax) {
     500           0 :                         if (_bounce) {
     501           0 :                                 float mod = 1.0f / (1.0f + ((pos + delta) - _scrollMax) / 5.0f);
     502           0 :                                 setScrollPosition(pos + delta * mod);
     503           0 :                                 return;
     504             :                         } else {
     505           0 :                                 onOverscroll(delta);
     506           0 :                                 setScrollPosition(_scrollMax);
     507           0 :                                 return;
     508             :                         }
     509             :                 }
     510             :         }
     511         140 :         setScrollPosition(pos + delta);
     512             : }
     513             : 
     514           0 : void ScrollViewBase::onOverscrollPerformed(float velocity, float pos, float boundary) {
     515           0 :         if (_movement == Movement::Auto) {
     516           0 :                 if (_movementAction) {
     517           0 :                         auto n = (pos < boundary)?1.0f:-1.0f;
     518             : 
     519           0 :                         auto vel = _movementAction->getCurrentVelocity();
     520           0 :                         auto normal = _movementAction->getNormal();
     521           0 :                         if (n * (isVertical()?normal.y:-normal.x) > 0) {
     522           0 :                                 velocity = vel;
     523             :                         } else {
     524           0 :                                 velocity = -vel;
     525             :                         }
     526             :                 }
     527             :         }
     528             : 
     529           0 :         if (_animationAction) {
     530           0 :                 _root->stopAction(_animationAction);
     531           0 :                 _animationAction = nullptr;
     532           0 :                 _movementAction = nullptr;
     533             :         }
     534             : 
     535           0 :         if (_movement == Movement::Manual || _movement == Movement::None) {
     536           0 :                 if (!_bounce && pos == boundary) {
     537           0 :                         return;
     538             :                 }
     539           0 :                 if ((pos < boundary && velocity < 0) || (pos > boundary && velocity > 0)) {
     540           0 :                         velocity = - fabs(velocity);
     541             :                 } else {
     542           0 :                         velocity = fabs(velocity);
     543             :                 }
     544             :         }
     545             : 
     546           0 :         if (_movement != Movement::Overscroll) {
     547           0 :                 Vec2 boundaryPos = getPointForScrollPosition(boundary);
     548           0 :                 Vec2 currentPos = getPointForScrollPosition(pos);
     549             : 
     550           0 :                 auto a = ActionAcceleratedMove::createBounce(5000, currentPos, boundaryPos, velocity, std::max(25000.0f, fabsf(velocity) * 50));
     551           0 :                 if (a) {
     552           0 :                         _controller->dropAnimationPadding();
     553           0 :                         _movement = Movement::Overscroll;
     554           0 :                         _animationAction = Rc<Sequence>::create(a, [this] {
     555           0 :                                 onAnimationFinished();
     556           0 :                         });
     557           0 :                         _root->runAction(_animationAction);
     558             :                 }
     559           0 :         }
     560             : }
     561             : 
     562          14 : bool ScrollViewBase::onSwipeEventBegin(uint32_t id, const Vec2 &loc, const Vec2 &delta, const Vec2 &velocity) {
     563          14 :         if (_layout == Layout::Vertical) {
     564          14 :                 if (std::abs(delta.x) < std::abs(delta.y)) {
     565          14 :                         _inputListener->setExclusiveForTouch(id);
     566             :                 } else {
     567           0 :                         return false;
     568             :                 }
     569             :         } else {
     570           0 :                 if (std::abs(delta.x) > std::abs(delta.y)) {
     571           0 :                         _inputListener->setExclusiveForTouch(id);
     572             :                 } else {
     573           0 :                         return false;
     574             :                 }
     575             :         }
     576             : 
     577          14 :         auto cs = (_layout == Vertical)?(_contentSize.height):(_contentSize.width);
     578          14 :         auto length = getScrollLength();
     579          14 :         if (!isnan(length) && cs >= length) {
     580           0 :                 return false;
     581             :         }
     582             : 
     583          14 :         onSwipeBegin();
     584             : 
     585             :         /*if (_layout == Vertical) {
     586             :                 return onSwipe(delta.y / _globalScale.y, velocity.y / _globalScale.y, false);
     587             :         } else {
     588             :                 return onSwipe(- delta.x / _globalScale.x, - velocity.x / _globalScale.x, false);
     589             :         }*/
     590             : 
     591          14 :         return true;
     592             : }
     593             : 
     594         140 : bool ScrollViewBase::onSwipeEvent(uint32_t id, const Vec2 &loc, const Vec2 &delta, const Vec2 &velocity) {
     595         140 :         if (_layout == Vertical) {
     596         140 :                 return onSwipe(delta.y / _globalScale.y, velocity.y / _globalScale.y, false);
     597             :         } else {
     598           0 :                 return onSwipe(- delta.x / _globalScale.x, - velocity.x / _globalScale.x, false);
     599             :         }
     600             : }
     601             : 
     602          14 : bool ScrollViewBase::onSwipeEventEnd(uint32_t id, const Vec2 &loc, const Vec2 &d, const Vec2 &velocity) {
     603          14 :         _movement = Movement::None;
     604          14 :         if (_layout == Vertical) {
     605          14 :                 return onSwipe(0, velocity.y / _globalScale.y, true);
     606             :         } else {
     607           0 :                 return onSwipe(0, - velocity.x / _globalScale.x, true);
     608             :         }
     609             : }
     610             : 
     611          14 : void ScrollViewBase::onSwipeBegin() {
     612          14 :         if (_controller) {
     613          14 :                 _controller->dropAnimationPadding();
     614             :         }
     615          14 :         _root->stopAllActions();
     616          14 :         _movementAction = nullptr;
     617          14 :         _animationAction = nullptr;
     618          14 :         _movement = Movement::Manual;
     619          14 : }
     620             : 
     621         154 : bool ScrollViewBase::onSwipe(float delta, float velocity, bool ended) {
     622         154 :         if (!ended) {
     623         140 :                 if (_scrollFilter) {
     624           0 :                         delta = _scrollFilter(delta);
     625             :                 }
     626         140 :                 onDelta(delta);
     627             :         } else {
     628          14 :                 float pos = getScrollPosition();
     629             : 
     630          14 :                 float acceleration = (velocity > 0)?-5000.0f:5000.0f;
     631          14 :                 if (!isnan(_maxVelocity)) {
     632          10 :                         if (velocity > fabs(_maxVelocity)) {
     633           0 :                                 velocity = fabs(_maxVelocity);
     634          10 :                         } else if (velocity < -fabs(_maxVelocity)) {
     635           0 :                                 velocity = -fabs(_maxVelocity);
     636             :                         }
     637             :                 }
     638             : 
     639          14 :                 float duration = fabsf(velocity / acceleration);
     640          14 :                 float path = velocity * duration + acceleration * duration * duration * 0.5f;
     641             : 
     642          14 :                 if (_controller) {
     643          14 :                         _controller->setAnimationPadding(path);
     644          14 :                         _controller->onScrollPosition();
     645             :                 }
     646             : 
     647          14 :                 if (!isnan(_scrollMin)) {
     648          14 :                         if (pos < _scrollMin) {
     649           0 :                                 float mod = 1.0f / (1.0f + fabsf(_scrollMin - pos) / 5.0f);
     650           0 :                                 onOverscrollPerformed(velocity * mod, pos, _scrollMin);
     651           0 :                                 return true;
     652             :                         }
     653             :                 }
     654             : 
     655          14 :                 if (!isnan(_scrollMax)) {
     656          14 :                         if (pos > _scrollMax) {
     657           0 :                                 float mod = 1.0f / (1.0f + fabsf(_scrollMax - pos) / 5.0f);
     658           0 :                                 onOverscrollPerformed(velocity * mod, pos, _scrollMax);
     659           0 :                                 return true;
     660             :                         }
     661             :                 }
     662             : 
     663          14 :                 if (auto a = onSwipeFinalizeAction(velocity)) {
     664          14 :                         _movement = Movement::Auto;
     665          28 :                         _animationAction = Rc<Sequence>::create(a, [this] {
     666          14 :                                 onAnimationFinished();
     667          14 :                         });
     668          14 :                         _root->runAction(_animationAction);
     669             :                 } else {
     670           0 :                         onScroll(0, true);
     671          14 :                 }
     672             :         }
     673         154 :         return true;
     674             : }
     675             : 
     676          14 : Rc<ActionInterval> ScrollViewBase::onSwipeFinalizeAction(float velocity) {
     677          14 :         if (velocity == 0) {
     678           0 :                 return nullptr;
     679             :         }
     680             : 
     681          14 :         float acceleration = (velocity > 0)?-5000.0f:5000.0f;
     682          14 :         float boundary = (velocity > 0)?_scrollMax:_scrollMin;
     683             : 
     684          14 :         Vec2 normal = (isVertical())
     685             :                         ?(Vec2(0.0f, (velocity > 0)?1.0f:-1.0f))
     686          14 :                         :(Vec2((velocity > 0)?-1.0f:1.0f, 0.0f));
     687             : 
     688          14 :         Rc<ActionInterval> a;
     689             : 
     690          14 :         if (!isnan(_maxVelocity)) {
     691          10 :                 if (velocity > fabs(_maxVelocity)) {
     692           0 :                         velocity = fabs(_maxVelocity);
     693          10 :                 } else if (velocity < -fabs(_maxVelocity)) {
     694           0 :                         velocity = -fabs(_maxVelocity);
     695             :                 }
     696             :         }
     697             : 
     698          14 :         if (!isnan(boundary)) {
     699          14 :                 float pos = getScrollPosition();
     700          14 :                 float duration = fabsf(velocity / acceleration);
     701          14 :                 float path = velocity * duration + acceleration * duration * duration * 0.5f;
     702             : 
     703          14 :                 auto from = _root->getPosition().xy();
     704          14 :                 auto to = getPointForScrollPosition(boundary);
     705             : 
     706          14 :                 float distance = from.distance(to);
     707          14 :                 if (distance < 2.0f) {
     708           0 :                         setScrollPosition(boundary);
     709           0 :                         return nullptr;
     710             :                 }
     711             : 
     712          14 :                 if ((velocity > 0 && pos + path > boundary) || (velocity < 0 && pos + path < boundary)) {
     713           0 :                         _movementAction = ActionAcceleratedMove::createAccelerationTo(from, to, fabsf(velocity), -fabsf(acceleration));
     714             : 
     715           0 :                         auto overscrollPath = path + ((velocity < 0)?(distance):(-distance));
     716           0 :                         if (overscrollPath) {
     717           0 :                                 a = Rc<Sequence>::create(_movementAction, [this, overscrollPath] {
     718           0 :                                         onOverscroll(overscrollPath);
     719           0 :                                 });
     720             :                         }
     721             :                 }
     722             :         }
     723             : 
     724          14 :         if (!_movementAction) {
     725          14 :                 _movementAction = ActionAcceleratedMove::createDecceleration(normal, _root->getPosition().xy(), fabs(velocity), fabsf(acceleration));
     726             :         }
     727             : 
     728          14 :         if (!a) {
     729          14 :                 a = _movementAction;
     730             :         }
     731             : 
     732          14 :         return a;
     733          14 : }
     734             : 
     735          38 : void ScrollViewBase::onAnimationFinished() {
     736          38 :         if (_movement != Movement::None) {
     737          14 :                 _animationDirty = true;
     738             :         }
     739          38 :         if (_controller) {
     740          38 :                 _controller->dropAnimationPadding();
     741             :         }
     742          38 :         _movement = Movement::None;
     743          38 :         _movementAction = nullptr;
     744          38 :         _animationAction = nullptr;
     745          38 :         onPosition();
     746          38 : }
     747             : 
     748         374 : void ScrollViewBase::fixPosition() {
     749         374 :         if (_movement == Movement::None) {
     750         196 :                 auto pos = getScrollPosition();
     751         196 :                 if (!isnan(_scrollMin)) {
     752         194 :                         if (pos < _scrollMin) {
     753           0 :                                 setScrollPosition(_scrollMin);
     754           0 :                                 return;
     755             :                         }
     756             :                 }
     757             : 
     758         196 :                 if (!isnan(_scrollMax)) {
     759         184 :                         if (pos > _scrollMax) {
     760           0 :                                 setScrollPosition(_scrollMax);
     761           0 :                                 return;
     762             :                         }
     763             :                 }
     764             :         }
     765             : }
     766             : 
     767         676 : void ScrollViewBase::onPosition() {
     768         676 :         float oldPos = _scrollPosition;
     769             :         float newPos;
     770         676 :         if (isVertical()) {
     771         666 :                 newPos = _root->getPosition().y - _scrollSize;
     772             :         } else {
     773          10 :                 newPos = -_root->getPosition().x;
     774             :         }
     775             : 
     776         676 :         _scrollPosition = newPos;
     777             : 
     778         676 :         if (_controller) {
     779         676 :                 if (_movement == Movement::Auto) {
     780         204 :                         _controller->updateAnimationPadding(newPos - oldPos);
     781             :                 }
     782         676 :                 _controller->onScrollPosition();
     783             :         }
     784             : 
     785         676 :         if (_movement == Movement::Auto) {
     786         204 :                 if (!isnan(_scrollMin)) {
     787         204 :                         if (newPos < _scrollMin) {
     788           0 :                                 onOverscrollPerformed(0, newPos, _scrollMin);
     789           0 :                                 return;
     790             :                         }
     791             :                 }
     792             : 
     793         204 :                 if (!isnan(_scrollMax)) {
     794         130 :                         if (newPos > _scrollMax) {
     795           0 :                                 onOverscrollPerformed(0, newPos, _scrollMax);
     796           0 :                                 return;
     797             :                         }
     798             :                 }
     799             :         }
     800             : 
     801         676 :         if (_movement != Movement::None && _movement != Movement::Overscroll && newPos - oldPos != 0) {
     802         333 :                 onScroll(newPos - oldPos, false);
     803         343 :         } else if (_movement == Movement::Overscroll) {
     804           0 :                 if (!isnan(_scrollMin)) {
     805           0 :                         if (_scrollPosition < _scrollMin) {
     806           0 :                                 if (newPos - oldPos < 0) {
     807           0 :                                         onOverscroll(newPos - oldPos);
     808             :                                 }
     809           0 :                                 return;
     810             :                         }
     811             :                 }
     812             : 
     813           0 :                 if (!isnan(_scrollMax)) {
     814           0 :                         if (newPos > _scrollMax) {
     815           0 :                                 if (newPos - oldPos > 0) {
     816           0 :                                         onOverscroll(newPos - oldPos);
     817             :                                 }
     818           0 :                                 return;
     819             :                         }
     820             :                 }
     821             :         }
     822             : }
     823             : 
     824         320 : void ScrollViewBase::updateScrollBounds() {
     825         320 :         if ((isVertical() && _contentSize.width == 0) || (isHorizontal() && _contentSize.height == 0)) {
     826           4 :                 return;
     827             :         }
     828             : 
     829         316 :         if (_contentSizeDirty) {
     830          58 :                 if (isVertical()) {
     831          54 :                         auto pos = _root->getPosition().y - _scrollSize;
     832          54 :                         _scrollSize = _contentSize.height;
     833          54 :                         _root->setAnchorPoint(Vec2(0, 1));
     834          54 :                         _root->setContentSize(Size2(_contentSize.width - _paddingGlobal.left - _paddingGlobal.right, 0));
     835          54 :                         _root->setPositionY(pos + _scrollSize);
     836          54 :                         _root->setPositionX(_paddingGlobal.left);
     837             :                 } else {
     838           4 :                         _scrollSize = _contentSize.width;
     839           4 :                         _root->setAnchorPoint(Vec2(0, 0));
     840           4 :                         _root->setContentSize(Size2(0, _contentSize.height - _paddingGlobal.top - _paddingGlobal.bottom));
     841           4 :                         _root->setPositionY(_paddingGlobal.bottom);
     842             :                 }
     843             :         }
     844             : 
     845         316 :         _scrollMin = getScrollMinPosition();
     846         316 :         _scrollMax = getScrollMaxPosition();
     847             : 
     848         316 :         _scrollDirty = false;
     849             : 
     850         316 :         fixPosition();
     851             : 
     852         316 :         _scrollMin = getScrollMinPosition();
     853         316 :         _scrollMax = getScrollMaxPosition();
     854             : 
     855         316 :         if (!isnan(_savedRelativePosition)) {
     856           0 :                 float value = _savedRelativePosition;
     857           0 :                 _savedRelativePosition = nan();
     858           0 :                 setScrollRelativePosition(value);
     859             :         }
     860             : 
     861         316 :         _root->setPositionZ(1.0f);
     862         316 :         _root->setPositionZ(0.0f);
     863             : }
     864             : 
     865         333 : void ScrollViewBase::onScroll(float delta, bool finished) {
     866         333 :         if (_controller) {
     867         333 :                 _controller->onScroll(delta, finished);
     868             :         }
     869         333 :         if (_scrollCallback) {
     870         220 :                 _scrollCallback(delta, finished);
     871             :         }
     872         333 : }
     873             : 
     874           0 : void ScrollViewBase::onOverscroll(float delta) {
     875           0 :         if (_controller) {
     876           0 :                 _controller->onOverscroll(delta);
     877             :         }
     878           0 :         if (_overscrollCallback) {
     879           0 :                 _overscrollCallback(delta);
     880             :         }
     881           0 : }
     882             : 
     883         130 : void ScrollViewBase::setScrollDirty(bool value) {
     884         130 :         _scrollDirty = value;
     885         130 : }
     886             : 
     887          24 : bool ScrollViewBase::onPressBegin(const Vec2 &) {
     888          24 :         _root->stopAllActions();
     889          24 :         onAnimationFinished();
     890          24 :         return false;
     891             : }
     892             : 
     893           0 : bool ScrollViewBase::onLongPress(const Vec2 &, const TimeInterval &time, int count) {
     894           0 :         return true;
     895             : }
     896           0 : bool ScrollViewBase::onPressEnd(const Vec2 &, const TimeInterval &) {
     897           0 :         return true;
     898             : }
     899           0 : bool ScrollViewBase::onPressCancel(const Vec2 &, const TimeInterval &) {
     900           0 :         return true;
     901             : }
     902             : 
     903           0 : void ScrollViewBase::onTap(int count, const Vec2 &loc) {
     904             : 
     905           0 : }
     906             : 
     907           0 : Vec2 ScrollViewBase::convertFromScrollableSpace(const Vec2 &pos) {
     908           0 :         return _root->getNodeToParentTransform().transformPoint(pos);
     909             : }
     910             : 
     911           0 : Vec2 ScrollViewBase::convertToScrollableSpace(const Vec2 &pos) {
     912           0 :         return _root->getParentToNodeTransform().transformPoint(pos);
     913             : }
     914             : 
     915           0 : Vec2 ScrollViewBase::convertFromScrollableSpace(Node *node, Vec2 pos) {
     916           0 :         auto tmp = node->getNodeToParentTransform() * _root->getNodeToParentTransform();
     917           0 :         return tmp.transformPoint(pos);
     918             : }
     919             : 
     920           0 : Vec2 ScrollViewBase::convertToScrollableSpace(Node *node, Vec2 pos) {
     921           0 :         auto tmp = _root->getParentToNodeTransform() * node->getParentToNodeTransform();
     922           0 :         return tmp.transformPoint(pos);
     923             : }
     924             : 
     925             : }

Generated by: LCOV version 1.14