void ScrollView::startAutoScroll(const Vec2& deltaMove, float timeInSec, bool attenuated) { Vec2 adjustedDeltaMove = flattenVectorByDirection(deltaMove); _autoScrolling = true; _autoScrollTargetDelta = adjustedDeltaMove; _autoScrollAttenuate = attenuated; _autoScrollStartPosition = _innerContainer->getPosition(); _autoScrollTotalTime = timeInSec; _autoScrollAccumulatedTime = 0; _autoScrollBraking = false; _autoScrollBrakingStartPosition = Vec2::ZERO; // If the destination is also out of boundary of same side, start brake from beginning. Vec2 currentOutOfBoundary = getHowMuchOutOfBoundary(); if (!fltEqualZero(currentOutOfBoundary)) { _autoScrollCurrentlyOutOfBoundary = true; Vec2 afterOutOfBoundary = getHowMuchOutOfBoundary(adjustedDeltaMove); if(currentOutOfBoundary.x * afterOutOfBoundary.x > 0 || currentOutOfBoundary.y * afterOutOfBoundary.y > 0) { _autoScrollBraking = true; } } }
void ScrollView::processInertiaScrolling(float dt) { _inertiaScrollElapsedTime += dt; if(isOutOfBoundaryLeftOrRight() || isOutOfBoundaryTopOrBottom()) { // If the inner container is out of boundary, shorten the inertia time. _inertiaScrollElapsedTime += dt * (45000 / INERTIA_DEACCELERATION); } float percentage = _inertiaScrollElapsedTime / _inertiaScrollExpectedTime; if(percentage >= 1) { _inertiaScrolling = false; startBounceBackIfNeeded(); return; } percentage = tweenfunc::quartEaseOut(percentage); Vec2 inertiaVelocity = _inertiaInitiVelocity * (1 - percentage); Vec2 displacement = inertiaVelocity * dt; if(!_bounceEnabled) { Vec2 outOfBoundary = getHowMuchOutOfBoundary(displacement); if(outOfBoundary != Vec2::ZERO) { // Don't allow to go out of boundary displacement += outOfBoundary; _inertiaScrolling = false; } } moveChildren(displacement.x, displacement.y); }
void ScrollView::moveChildrenToPosition(const Vec2& position) { setInnerContainerPosition(position); Vec2 outOfBoundary = getHowMuchOutOfBoundary(Vec2::ZERO); updateScrollBar(outOfBoundary); }
void ScrollView::onSizeChanged() { Layout::onSizeChanged(); _topBoundary = _contentSize.height; _rightBoundary = _contentSize.width; Size innerSize = _innerContainer->getContentSize(); float orginInnerSizeWidth = innerSize.width; float orginInnerSizeHeight = innerSize.height; float innerSizeWidth = MAX(orginInnerSizeWidth, _contentSize.width); float innerSizeHeight = MAX(orginInnerSizeHeight, _contentSize.height); _innerContainer->setContentSize(Size(innerSizeWidth, innerSizeHeight)); setInnerContainerPosition(Vec2(0, _contentSize.height - _innerContainer->getContentSize().height)); if (_verticalScrollBar != nullptr) _verticalScrollBar->onScrolled(getHowMuchOutOfBoundary()); if (_horizontalScrollBar != nullptr) _horizontalScrollBar->onScrolled(getHowMuchOutOfBoundary()); }
bool ScrollView::isOutOfBoundary(MoveDirection dir) { Vec2 outOfBoundary = getHowMuchOutOfBoundary(); switch(dir) { case MoveDirection::TOP: return outOfBoundary.y > 0; case MoveDirection::BOTTOM: return outOfBoundary.y < 0; case MoveDirection::LEFT: return outOfBoundary.x < 0; case MoveDirection::RIGHT: return outOfBoundary.x > 0; } return false; }
void ScrollView::processAutoScrolling(float deltaTime) { // Make auto scroll shorter if it needs to deaccelerate. float brakingFactor = (isNecessaryAutoScrollBrake() ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1); // Elapsed time _autoScrollAccumulatedTime += deltaTime * (1 / brakingFactor); // Calculate the progress percentage float percentage = MIN(1, _autoScrollAccumulatedTime / _autoScrollTotalTime); if(_autoScrollAttenuate) { // Use quintic(5th degree) polynomial percentage = tweenfunc::quintEaseOut(percentage); } // Calculate the new position Vec2 newPosition = _autoScrollStartPosition + (_autoScrollTargetDelta * percentage); bool reachedEnd = std::abs(percentage - 1) <= this->getAutoScrollStopEpsilon(); if (reachedEnd) { newPosition = _autoScrollStartPosition + _autoScrollTargetDelta; } if(_bounceEnabled) { // The new position is adjusted if out of boundary newPosition = _autoScrollBrakingStartPosition + (newPosition - _autoScrollBrakingStartPosition) * brakingFactor; } else { // Don't let go out of boundary Vec2 moveDelta = newPosition - getInnerContainerPosition(); Vec2 outOfBoundary = getHowMuchOutOfBoundary(moveDelta); if (!fltEqualZero(outOfBoundary)) { newPosition += outOfBoundary; reachedEnd = true; } } // Finish auto scroll if it ended if(reachedEnd) { _autoScrolling = false; dispatchEvent(SCROLLVIEW_EVENT_AUTOSCROLL_ENDED, EventType::AUTOSCROLL_ENDED); } moveInnerContainer(newPosition - getInnerContainerPosition(), reachedEnd); }
void ScrollView::moveInnerContainer(const Vec2& deltaMove, bool canStartBounceBack) { Vec2 adjustedMove = flattenVectorByDirection(deltaMove); setInnerContainerPosition(getInnerContainerPosition() + adjustedMove); Vec2 outOfBoundary = getHowMuchOutOfBoundary(); updateScrollBar(outOfBoundary); if(_bounceEnabled && canStartBounceBack) { startBounceBackIfNeeded(); } }
bool ScrollView::startBounceBackIfNeeded() { if (!_bounceEnabled) { return false; } Vec2 bounceBackAmount = getHowMuchOutOfBoundary(); if(fltEqualZero(bounceBackAmount)) { return false; } startAutoScroll(bounceBackAmount, BOUNCE_BACK_DURATION, true); return true; }
bool ScrollView::startBounceBackIfNeeded() { if (!_bounceEnabled) { return false; } Vec2 outOfBoundary = getHowMuchOutOfBoundary(Vec2::ZERO); if(outOfBoundary == Vec2::ZERO) { return false; } _bouncingBack = true; startAutoScroll(outOfBoundary, BOUNCE_BACK_DURATION, true); return true; }
void ListView::jumpToItem(ssize_t itemIndex, const Vec2& positionRatioInView, const Vec2& itemAnchorPoint) { Widget* item = getItem(itemIndex); if (item == nullptr) { return; } doLayout(); Vec2 destination = calculateItemDestination(positionRatioInView, item, itemAnchorPoint); if(!_bounceEnabled) { Vec2 delta = destination - getInnerContainerPosition(); Vec2 outOfBoundary = getHowMuchOutOfBoundary(delta); destination += outOfBoundary; } jumpToDestination(destination); }
void ListView::startAttenuatingAutoScroll(const Vec2& deltaMove, const Vec2& initialVelocity) { Vec2 adjustedDeltaMove = deltaMove; if(!_items.empty() && _magneticType != MagneticType::NONE) { adjustedDeltaMove = flattenVectorByDirection(adjustedDeltaMove); // If the destination is out of boundary, do nothing here. Because it will be handled by bouncing back. if(getHowMuchOutOfBoundary(adjustedDeltaMove) == Vec2::ZERO) { MagneticType magType = _magneticType; if(magType == MagneticType::BOTH_END) { if(_direction == Direction::HORIZONTAL) { magType = (adjustedDeltaMove.x > 0 ? MagneticType::LEFT : MagneticType::RIGHT); } else if(_direction == Direction::VERTICAL) { magType = (adjustedDeltaMove.y > 0 ? MagneticType::BOTTOM : MagneticType::TOP); } } // Adjust the delta move amount according to the magnetic type Vec2 magneticAnchorPoint = getAnchorPointByMagneticType(magType); Vec2 magneticPosition = -_innerContainer->getPosition(); magneticPosition.x += getContentSize().width * magneticAnchorPoint.x; magneticPosition.y += getContentSize().height * magneticAnchorPoint.y; Widget* pTargetItem = getClosestItemToPosition(magneticPosition - adjustedDeltaMove, magneticAnchorPoint); Vec2 itemPosition = calculateItemPositionWithAnchor(pTargetItem, magneticAnchorPoint); adjustedDeltaMove = magneticPosition - itemPosition; } } ScrollView::startAttenuatingAutoScroll(adjustedDeltaMove, initialVelocity); }
void ScrollView::scrollChildren(const Vec2& deltaMove) { Vec2 realMove = deltaMove; if(_bounceEnabled) { // If the position of the inner container is out of the boundary, the offsets should be divided by two. Vec2 outOfBoundary = getHowMuchOutOfBoundary(); realMove.x *= (outOfBoundary.x == 0 ? 1 : 0.5f); realMove.y *= (outOfBoundary.y == 0 ? 1 : 0.5f); } if(!_bounceEnabled) { Vec2 outOfBoundary = getHowMuchOutOfBoundary(realMove); realMove += outOfBoundary; } bool scrolledToLeft = false; bool scrolledToRight = false; bool scrolledToTop = false; bool scrolledToBottom = false; if (realMove.y > 0.0f) // up { float icBottomPos = _innerContainer->getBottomBoundary(); if (icBottomPos + realMove.y >= _bottomBoundary) { scrolledToBottom = true; } } else if (realMove.y < 0.0f) // down { float icTopPos = _innerContainer->getTopBoundary(); if (icTopPos + realMove.y <= _topBoundary) { scrolledToTop = true; } } if (realMove.x < 0.0f) // left { float icRightPos = _innerContainer->getRightBoundary(); if (icRightPos + realMove.x <= _rightBoundary) { scrolledToRight = true; } } else if (realMove.x > 0.0f) // right { float icLeftPos = _innerContainer->getLeftBoundary(); if (icLeftPos + realMove.x >= _leftBoundary) { scrolledToLeft = true; } } moveInnerContainer(realMove, false); if(realMove.x != 0 || realMove.y != 0) { processScrollingEvent(); } if(scrolledToBottom) { processScrollEvent(MoveDirection::BOTTOM, false); } if(scrolledToTop) { processScrollEvent(MoveDirection::TOP, false); } if(scrolledToLeft) { processScrollEvent(MoveDirection::LEFT, false); } if(scrolledToRight) { processScrollEvent(MoveDirection::RIGHT, false); } }
bool ScrollView::isOutOfBoundary() { return !fltEqualZero(getHowMuchOutOfBoundary()); }
bool ScrollView::isOutOfBoundary() { return getHowMuchOutOfBoundary() != Vec2::ZERO; }