X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fscrollable%2Fscroll-view%2Fscroll-view-impl.cpp;h=712cb17b53360ff58971299ef42dc7f2f9632e66;hp=c9ed83051326ac0f316a697b4f3496d6e953452a;hb=3ce558ca09f928cb18cacfa80f52eb0c591181b9;hpb=7cfada64ad44d4a47785d49993b298f1c2dbd4a0 diff --git a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp old mode 100755 new mode 100644 index c9ed830..712cb17 --- a/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/scroll-view/scroll-view-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,29 +19,32 @@ #include // EXTERNAL INCLUDES -#include // for strcmp +#include +#include +#include +#include +#include #include -#include +#include #include -#include -#include -#include #include -#include -#include +#include +#include +#include // for strcmp // INTERNAL INCLUDES -#include -#include -#include -#include +#include #include #include +#include +#include +#include +#include //#define ENABLED_SCROLL_STATE_LOGGING #ifdef ENABLED_SCROLL_STATE_LOGGING -#define DALI_LOG_SCROLL_STATE(format, ...) Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugInfo, "%s:%d " format "\n", __PRETTY_FUNCTION__, __LINE__, ## __VA_ARGS__) +#define DALI_LOG_SCROLL_STATE(format, ...) Dali::Integration::Log::LogMessageWithFunctionLine(Dali::Integration::Log::DebugInfo, "%s:%d " format "\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) #else #define DALI_LOG_SCROLL_STATE(format, ...) #endif @@ -53,31 +56,34 @@ // TODO: Orientation. // TODO: upgrade Vector2/3 to support returning Unit vectors, normals, & cross product (dot product is already provided) -using namespace Dali; - namespace { -const float DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f); ///< Default Drag-Release animation time. -const float DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f); ///< Default Drag-Flick animation time. -const float DEFAULT_SNAP_OVERSHOOT_DURATION(0.5f); ///< Default Overshoot snapping animation time. -const float DEFAULT_MAX_OVERSHOOT(100.0f); ///< Default maximum allowed overshoot in pixels +using namespace Dali; + +constexpr float DEFAULT_SLOW_SNAP_ANIMATION_DURATION(0.5f); ///< Default Drag-Release animation time. +constexpr float DEFAULT_FAST_SNAP_ANIMATION_DURATION(0.25f); ///< Default Drag-Flick animation time. +constexpr float DEFAULT_SNAP_OVERSHOOT_DURATION(0.5f); ///< Default Overshoot snapping animation time. +constexpr float DEFAULT_MAX_OVERSHOOT(100.0f); ///< Default maximum allowed overshoot in pixels -const float DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f); ///< Default Axis-AutoLock gradient threshold. default is 0.36:1 (20 degrees) -const float DEFAULT_FRICTION_COEFFICIENT(1.0f); ///< Default Friction Co-efficient. (in stage diagonals per second) -const float DEFAULT_FLICK_SPEED_COEFFICIENT(1.0f); ///< Default Flick speed coefficient (multiples input touch velocity) -const float DEFAULT_MAX_FLICK_SPEED(3.0f); ///< Default Maximum flick speed. (in stage diagonals per second) +constexpr float DEFAULT_AXIS_AUTO_LOCK_GRADIENT(0.36f); ///< Default Axis-AutoLock gradient threshold. default is 0.36:1 (20 degrees) +constexpr float DEFAULT_FRICTION_COEFFICIENT(1.0f); ///< Default Friction Co-efficient. (in stage diagonals per second) +constexpr float DEFAULT_FLICK_SPEED_COEFFICIENT(1.0f); ///< Default Flick speed coefficient (multiples input touch velocity) +constexpr float DEFAULT_MAX_FLICK_SPEED(3.0f); ///< Default Maximum flick speed. (in stage diagonals per second) -const Vector2 DEFAULT_MIN_FLICK_DISTANCE(30.0f, 30.0f); ///< minimum distance for pan before flick allowed -const float DEFAULT_MIN_FLICK_SPEED_THRESHOLD(500.0f); ///< Minimum pan speed required for flick in pixels/s -const float FREE_FLICK_SPEED_THRESHOLD = 200.0f; ///< Free-Flick threshold in pixels/ms -const float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f; ///< Auto-lock axis after minimum distance squared. -const float FLICK_ORTHO_ANGLE_RANGE = 75.0f; ///< degrees. (if >45, then supports diagonal flicking) -const Vector2 DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = Vector2(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each wheel event received. -const unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET( 150u ); -const float TOUCH_DOWN_TIMER_INTERVAL = 100.0f; -const float DEFAULT_SCROLL_UPDATE_DISTANCE( 30.0f ); ///< Default distance to travel in pixels for scroll update signal +constexpr Dali::Vector2 DEFAULT_MIN_FLICK_DISTANCE(30.0f, 30.0f); ///< minimum distance for pan before flick allowed +constexpr float DEFAULT_MIN_FLICK_SPEED_THRESHOLD(500.0f); ///< Minimum pan speed required for flick in pixels/s -const std::string INTERNAL_MAX_POSITION_PROPERTY_NAME( "internalMaxPosition" ); +constexpr float FREE_FLICK_SPEED_THRESHOLD = 200.0f; ///< Free-Flick threshold in pixels/ms +constexpr float AUTOLOCK_AXIS_MINIMUM_DISTANCE2 = 100.0f; ///< Auto-lock axis after minimum distance squared. +constexpr float FLICK_ORTHO_ANGLE_RANGE = 75.0f; ///< degrees. (if >45, then supports diagonal flicking) + +constexpr Dali::Vector2 DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION(0.17f, 0.1f); ///< The step of horizontal scroll distance in the proportion of stage size for each wheel event received. + +constexpr unsigned long MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET(150u); +constexpr float TOUCH_DOWN_TIMER_INTERVAL = 100.0f; +constexpr float DEFAULT_SCROLL_UPDATE_DISTANCE(30.0f); ///< Default distance to travel in pixels for scroll update signal + +const std::string INTERNAL_MAX_POSITION_PROPERTY_NAME("internalMaxPosition"); // Helpers //////////////////////////////////////////////////////////////////////////////////////// @@ -100,38 +106,38 @@ const std::string INTERNAL_MAX_POSITION_PROPERTY_NAME( "internalMaxPosition" ); */ float VectorInDomain(float a, float b, float start, float end, Dali::Toolkit::DirectionBias bias) { - if(bias == Dali::Toolkit::DirectionBiasNone) + if(bias == Dali::Toolkit::DIRECTION_BIAS_NONE) { - return ShortestDistanceInDomain( a, b, start, end ); + return Dali::ShortestDistanceInDomain(a, b, start, end); } // (a-start + end-b) - float size = end-start; - float vect = b-a; + float size = end - start; + float vect = b - a; if(vect > 0) { // +ve vector - if(bias == Dali::Toolkit::DirectionBiasRight) // going right, take the vector. + if(bias == Dali::Toolkit::DIRECTION_BIAS_RIGHT) // going right, take the vector. { return vect; } else { - float aRight = a+size; - return b-aRight; + float aRight = a + size; + return b - aRight; } } else { // -ve vector - if(bias == Dali::Toolkit::DirectionBiasLeft) // going left, take the vector. + if(bias == Dali::Toolkit::DIRECTION_BIAS_LEFT) // going left, take the vector. { return vect; } else { - float aLeft = a-size; - return b-aLeft; + float aLeft = a - size; + return b - aLeft; } } } @@ -143,22 +149,118 @@ float VectorInDomain(float a, float b, float start, float end, Dali::Toolkit::Di * @param anchor The Anchor point of interest. * @return The position of the Anchor */ -Vector3 GetPositionOfAnchor(Actor &actor, const Vector3 &anchor) +Dali::Vector3 GetPositionOfAnchor(Dali::Actor& actor, const Dali::Vector3& anchor) { - Vector3 childPosition = actor.GetCurrentProperty< Vector3 >( Actor::Property::POSITION ); - Vector3 childAnchor = - actor.GetCurrentProperty< Vector3 >( Actor::Property::ANCHOR_POINT ) + anchor; - Vector3 childSize = actor.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); + Dali::Vector3 childPosition = actor.GetCurrentProperty(Dali::Actor::Property::POSITION); + Dali::Vector3 childAnchor = -actor.GetCurrentProperty(Dali::Actor::Property::ANCHOR_POINT) + anchor; + Dali::Vector3 childSize = actor.GetCurrentProperty(Dali::Actor::Property::SIZE); return childPosition + childAnchor * childSize; } -// AlphaFunctions ///////////////////////////////////////////////////////////////////////////////// +/** + * Returns the closest actor to the given position + * @param[in] actor The scrollview actor + * @param[in] internalActor The internal actor (to ignore) + * @param[in] position The given position + * @param[in] dirX Direction to search in + * @param[in] dirY Direction to search in + * @param[in] dirZ Direction to search in + * @return the closest child actor + */ +using FindDirection = Dali::Toolkit::Internal::ScrollView::FindDirection; -float FinalDefaultAlphaFunction(float offset) +Actor FindClosestActorToPosition( + CustomActor actor, Actor internalActor, const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ) { - return offset * 0.5f; + Actor closestChild; + float closestDistance2 = 0.0f; + Vector3 actualPosition = position; + + unsigned int numChildren = actor.GetChildCount(); + + for(unsigned int i = 0; i < numChildren; ++i) + { + Actor child = actor.GetChildAt(i); + + if(internalActor == child) // ignore internal actor. + { + continue; + } + + Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER); + + Vector3 delta = childPosition - actualPosition; + + // X-axis checking (only find Actors to the [dirX] of actualPosition) + if(dirX > FindDirection::All) // != All,None + { + FindDirection deltaH = delta.x > 0 ? FindDirection::Right : FindDirection::Left; + if(dirX != deltaH) + { + continue; + } + } + + // Y-axis checking (only find Actors to the [dirY] of actualPosition) + if(dirY > FindDirection::All) // != All,None + { + FindDirection deltaV = delta.y > 0 ? FindDirection::Down : FindDirection::Up; + if(dirY != deltaV) + { + continue; + } + } + + // Z-axis checking (only find Actors to the [dirZ] of actualPosition) + if(dirZ > FindDirection::All) // != All,None + { + FindDirection deltaV = delta.y > 0 ? FindDirection::In : FindDirection::Out; + if(dirZ != deltaV) + { + continue; + } + } + + // compare child to closest child in terms of distance. + float distance2 = 0.0f; + + // distance2 = the Square of the relevant dimensions of delta + if(dirX != FindDirection::None) + { + distance2 += delta.x * delta.x; + } + + if(dirY != FindDirection::None) + { + distance2 += delta.y * delta.y; + } + + if(dirZ != FindDirection::None) + { + distance2 += delta.z * delta.z; + } + + if(closestChild) // Next time. + { + if(distance2 < closestDistance2) + { + closestChild = child; + closestDistance2 = distance2; + } + } + else // First time. + { + closestChild = child; + closestDistance2 = distance2; + } + } + + return closestChild; } +// AlphaFunctions ///////////////////////////////////////////////////////////////////////////////// + /** * ConstantDecelerationAlphaFunction * Newtoninan distance for constant deceleration @@ -175,438 +277,310 @@ float ConstantDecelerationAlphaFunction(float progress) return progress * 2.0f - progress * progress; } -// Internal Constraints /////////////////////////////////////////////////////////////////////////// - -/** - * Internal Relative position Constraint - * Generates the relative position value of the scroll view - * based on the absolute position, and it's relation to the - * scroll domain. This is a value from 0.0f to 1.0f in each - * scroll position axis. - */ -void InternalRelativePositionConstraint( Vector2& relativePosition, const PropertyInputContainer& inputs) -{ - Vector2 position = -inputs[0]->GetVector2(); - const Vector2& min = inputs[1]->GetVector2(); - const Vector2& max = inputs[2]->GetVector2(); - const Vector3& size = inputs[3]->GetVector3(); - - position.x = WrapInDomain(position.x, min.x, max.x); - position.y = WrapInDomain(position.y, min.y, max.y); - - Vector2 domainSize = (max - min) - size.GetVectorXY(); - - relativePosition.x = domainSize.x > Math::MACHINE_EPSILON_1 ? fabsf((position.x - min.x) / domainSize.x) : 0.0f; - relativePosition.y = domainSize.y > Math::MACHINE_EPSILON_1 ? fabsf((position.y - min.y) / domainSize.y) : 0.0f; -} - /** - * Internal scroll domain Constraint - * Generates the scroll domain of the scroll view. + * Clamp a position + * @param[in] size The size to clamp to + * @param[in] rulerX The horizontal ruler + * @param[in] rulerY The vertical ruler + * @param[in,out] position The position to clamp + * @param[out] clamped the clamped state */ -void InternalScrollDomainConstraint( Vector2& scrollDomain, const PropertyInputContainer& inputs) +void ClampPosition(const Vector3& size, Dali::Toolkit::RulerPtr rulerX, Dali::Toolkit::RulerPtr rulerY, Vector2& position, Dali::Toolkit::ClampState2D& clamped) { - const Vector2& min = inputs[0]->GetVector2(); - const Vector2& max = inputs[1]->GetVector2(); - const Vector3& size = inputs[2]->GetVector3(); - - scrollDomain = (max - min) - size.GetVectorXY(); + position.x = -rulerX->Clamp(-position.x, size.width, 1.0f, clamped.x); // NOTE: X & Y rulers think in -ve coordinate system. + position.y = -rulerY->Clamp(-position.y, size.height, 1.0f, clamped.y); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT. } /** - * Internal maximum scroll position Constraint - * Generates the maximum scroll position of the scroll view. + * TODO: In situations where axes are different (X snap, Y free) + * Each axis should really have their own independent animation (time and equation) + * Consider, X axis snapping to nearest grid point (EaseOut over fixed time) + * Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time) + * Currently, the axes have been split however, they both use the same EaseOut equation. + * + * @param[in] scrollView The main scrollview + * @param[in] rulerX The X ruler + * @param[in] rulerY The Y ruler + * @param[in] lockAxis Which axis (if any) is locked. + * @param[in] velocity Current pan velocity + * @param[in] maxOvershoot Maximum overshoot + * @param[in] inAcessibilityPan True if we are currently panning with accessibility + * @param[out] positionSnap The target position of snap animation + * @param[out] positionDuration The duration of the snap animation + * @param[out] alphaFunction The snap animation alpha function + * @param[out] isFlick if we are flicking or not + * @param[out] isFreeFlick if we are free flicking or not */ -void InternalPrePositionMaxConstraint( Vector2& scrollMax, const PropertyInputContainer& inputs) -{ - const Vector2& max = inputs[0]->GetVector2(); - const Vector3& size = inputs[1]->GetVector3(); - - scrollMax = max - size.GetVectorXY(); -} - -} // unnamed namespace - -namespace Dali -{ - -namespace Toolkit +void SnapWithVelocity( + Dali::Toolkit::Internal::ScrollView& scrollView, + Dali::Toolkit::RulerPtr rulerX, + Dali::Toolkit::RulerPtr rulerY, + Dali::Toolkit::Internal::ScrollView::LockAxis lockAxis, + Vector2 velocity, + Vector2 maxOvershoot, + Vector2& positionSnap, + Vector2& positionDuration, + AlphaFunction& alphaFunction, + bool inAccessibilityPan, + bool& isFlick, + bool& isFreeFlick) { + // Animator takes over now, touches are assumed not to interfere. + // And if touches do interfere, then we'll stop animation, update PrePosition + // to current mScroll's properties, and then resume. + // Note: For Flicking this may work a bit different... -namespace Internal -{ + float angle = atan2(velocity.y, velocity.x); + float speed2 = velocity.LengthSquared(); + float biasX = 0.5f; + float biasY = 0.5f; + FindDirection horizontal = FindDirection::None; + FindDirection vertical = FindDirection::None; -namespace -{ + using LockAxis = Dali::Toolkit::Internal::ScrollView::LockAxis; -BaseHandle Create() -{ - return Toolkit::ScrollView::New(); -} + // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction + // that will be accepted as a general N,E,S,W flick direction. -// Setup properties, signals and actions using the type-registry. -DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollView, Toolkit::Scrollable, Create ) - -DALI_PROPERTY_REGISTRATION( Toolkit, ScrollView, "wrapEnabled", BOOLEAN, WRAP_ENABLED ) -DALI_PROPERTY_REGISTRATION( Toolkit, ScrollView, "panningEnabled", BOOLEAN, PANNING_ENABLED ) -DALI_PROPERTY_REGISTRATION( Toolkit, ScrollView, "axisAutoLockEnabled", BOOLEAN, AXIS_AUTO_LOCK_ENABLED ) -DALI_PROPERTY_REGISTRATION( Toolkit, ScrollView, "wheelScrollDistanceStep", VECTOR2, WHEEL_SCROLL_DISTANCE_STEP ) -DALI_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollMode", MAP, SCROLL_MODE ) - -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPosition", VECTOR2, SCROLL_POSITION) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPrePosition", VECTOR2, SCROLL_PRE_POSITION) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionX", SCROLL_PRE_POSITION_X, SCROLL_PRE_POSITION, 0) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionY", SCROLL_PRE_POSITION_Y, SCROLL_PRE_POSITION, 1) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionMax", VECTOR2, SCROLL_PRE_POSITION_MAX) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionMaxX", SCROLL_PRE_POSITION_MAX_X, SCROLL_PRE_POSITION_MAX, 0) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollPrePositionMaxY", SCROLL_PRE_POSITION_MAX_Y, SCROLL_PRE_POSITION_MAX, 1) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "overshootX", FLOAT, OVERSHOOT_X) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "overshootY", FLOAT, OVERSHOOT_Y) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollFinal", VECTOR2, SCROLL_FINAL) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollFinalX", SCROLL_FINAL_X, SCROLL_FINAL,0) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollFinalY", SCROLL_FINAL_Y, SCROLL_FINAL,1) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "wrap", BOOLEAN, WRAP) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "panning", BOOLEAN, PANNING) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrolling", BOOLEAN, SCROLLING) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollDomainSize", VECTOR2, SCROLL_DOMAIN_SIZE) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollDomainSizeX", SCROLL_DOMAIN_SIZE_X, SCROLL_DOMAIN_SIZE, 0) -DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit, ScrollView, "scrollDomainSizeY", SCROLL_DOMAIN_SIZE_Y, SCROLL_DOMAIN_SIZE, 1) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollDomainOffset", VECTOR2, SCROLL_DOMAIN_OFFSET) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "scrollPositionDelta", VECTOR2, SCROLL_POSITION_DELTA) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ScrollView, "startPagePosition", VECTOR3, START_PAGE_POSITION) - -DALI_SIGNAL_REGISTRATION( Toolkit, ScrollView, "valueChanged", SIGNAL_SNAP_STARTED ) + const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f; + const float flickSpeedThreshold2 = scrollView.GetMinimumSpeedForFlick() * scrollView.GetMinimumSpeedForFlick(); -DALI_TYPE_REGISTRATION_END() + // Flick logic X Axis -/** - * Returns whether to lock scrolling to a particular axis - * - * @param[in] panDelta Distance panned since gesture started - * @param[in] currentLockAxis The current lock axis value - * @param[in] lockGradient How quickly to lock to a particular axis - * - * @return The new axis lock state - */ -ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient) -{ - if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 && - currentLockAxis == ScrollView::LockPossible) + if(rulerX->IsEnabled() && lockAxis != LockAxis::LockHorizontal) { - float dx = fabsf(panDelta.x); - float dy = fabsf(panDelta.y); - if(dx * lockGradient >= dy) - { - // 0.36:1 gradient to the horizontal (deviate < 20 degrees) - currentLockAxis = ScrollView::LockVertical; - } - else if(dy * lockGradient > dx) - { - // 0.36:1 gradient to the vertical (deviate < 20 degrees) - currentLockAxis = ScrollView::LockHorizontal; - } - else - { - currentLockAxis = ScrollView::LockNone; - } - } - return currentLockAxis; -} + horizontal = FindDirection::All; -/** - * Internal Pre-Position Property Constraint. - * - * Generates position property based on current position + gesture displacement. - * Or generates position property based on positionX/Y. - * Note: This is the position prior to any clamping at scroll boundaries. - */ -struct InternalPrePositionConstraint -{ - InternalPrePositionConstraint( const Vector2& initialPanPosition, - const Vector2& initialPanMask, - bool axisAutoLock, - float axisAutoLockGradient, - ScrollView::LockAxis initialLockAxis, - const Vector2& maxOvershoot, - const RulerPtr& rulerX, const RulerPtr& rulerY ) - : mLocalStart( initialPanPosition ), - mInitialPanMask( initialPanMask ), - mMaxOvershoot( maxOvershoot ), - mAxisAutoLockGradient( axisAutoLockGradient ), - mLockAxis( initialLockAxis ), - mAxisAutoLock( axisAutoLock ), - mWasPanning( false ) - { - const RulerDomain& rulerDomainX = rulerX->GetDomain(); - const RulerDomain& rulerDomainY = rulerY->GetDomain(); - mDomainMin = Vector2( rulerDomainX.min, -rulerDomainY.min ); - mDomainMax = Vector2( -rulerDomainX.max, -rulerDomainY.max ); - mClampX = rulerDomainX.enabled; - mClampY = rulerDomainY.enabled; - mFixedRulerX = rulerX->GetType() == Ruler::Fixed; - mFixedRulerY = rulerY->GetType() == Ruler::Fixed; - } - - void operator()( Vector2& scrollPostPosition, const PropertyInputContainer& inputs ) - { - const Vector2& panPosition = inputs[0]->GetVector2(); - const bool& inGesture = inputs[1]->GetBoolean(); - - // First check if we are within a gesture. - // The ScrollView may have received a start gesture from ::OnPan() - // while the finish gesture is received now in this constraint. - // This gesture must then be rejected as the value will be "old". - // Typically the last value from the end of the last gesture. - // If we are rejecting the gesture, we simply don't modify the constraint target. - if( inGesture ) - { - if( !mWasPanning ) + if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold + inAccessibilityPan) // With AccessibilityPan its easier to move between snap positions + { + if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East { - mPrePosition = scrollPostPosition; - mStartPosition = mPrePosition; - mCurrentPanMask = mInitialPanMask; - mWasPanning = true; - } - - // Calculate Deltas... - const Vector2& currentPosition = panPosition; - Vector2 panDelta( currentPosition - mLocalStart ); + biasX = 0.0f, horizontal = FindDirection::Left; - // Axis Auto Lock - locks the panning to the horizontal or vertical axis if the pan - // appears mostly horizontal or mostly vertical respectively... - if( mAxisAutoLock ) + // This guards against an error where no movement occurs, due to the flick finishing + // before the update-thread has advanced mScrollPostPosition past the the previous snap point. + positionSnap.x += 1.0f; + } + else if((angle >= M_PI - orthoAngleRange) || (angle < -M_PI + orthoAngleRange)) // Swiping West { - mLockAxis = GetLockAxis( panDelta, mLockAxis, mAxisAutoLockGradient ); - if( mLockAxis == ScrollView::LockVertical ) - { - mCurrentPanMask.y = 0.0f; - } - else if( mLockAxis == ScrollView::LockHorizontal ) - { - mCurrentPanMask.x = 0.0f; - } + biasX = 1.0f, horizontal = FindDirection::Right; + + // This guards against an error where no movement occurs, due to the flick finishing + // before the update-thread has advanced mScrollPostPosition past the the previous snap point. + positionSnap.x -= 1.0f; } + } + } - // Restrict deltas based on ruler enable/disable and axis-lock state... - panDelta *= mCurrentPanMask; + // Flick logic Y Axis - // Perform Position transform based on input deltas... - scrollPostPosition = mPrePosition; - scrollPostPosition += panDelta; + if(rulerY->IsEnabled() && lockAxis != LockAxis::LockVertical) + { + vertical = FindDirection::All; - // if no wrapping then clamp preposition to maximum overshoot amount - const Vector3& size = inputs[2]->GetVector3(); - if( mClampX ) - { - float newXPosition = Clamp( scrollPostPosition.x, ( mDomainMax.x + size.x ) - mMaxOvershoot.x, mDomainMin.x + mMaxOvershoot.x ); - if( (newXPosition < scrollPostPosition.x - Math::MACHINE_EPSILON_1) - || (newXPosition > scrollPostPosition.x + Math::MACHINE_EPSILON_1) ) - { - mPrePosition.x = newXPosition; - mLocalStart.x = panPosition.x; - } - scrollPostPosition.x = newXPosition; - } - if( mClampY ) + if(speed2 > flickSpeedThreshold2 || // exceeds flick threshold + inAccessibilityPan) // With AccessibilityPan its easier to move between snap positions + { + if((angle >= M_PI_2 - orthoAngleRange) && (angle < M_PI_2 + orthoAngleRange)) // Swiping South { - float newYPosition = Clamp( scrollPostPosition.y, ( mDomainMax.y + size.y ) - mMaxOvershoot.y, mDomainMin.y + mMaxOvershoot.y ); - if( ( newYPosition < scrollPostPosition.y - Math::MACHINE_EPSILON_1 ) - || ( newYPosition > scrollPostPosition.y + Math::MACHINE_EPSILON_1 ) ) - { - mPrePosition.y = newYPosition; - mLocalStart.y = panPosition.y; - } - scrollPostPosition.y = newYPosition; + biasY = 0.0f, vertical = FindDirection::Up; } - - // If we are using a fixed ruler in a particular axis, limit the maximum pages scrolled on that axis. - if( mFixedRulerX || mFixedRulerY ) + else if((angle >= -M_PI_2 - orthoAngleRange) && (angle < -M_PI_2 + orthoAngleRange)) // Swiping North { - // Here we limit the maximum amount that can be moved from the starting position of the gesture to one page. - // We do this only if we have a fixed ruler (on that axis) and the mode is enabled. - // Note: 1.0f is subtracted to keep the value within one page size (otherwise we stray on to the page after). - // Note: A further 1.0f is subtracted to handle a compensation that happens later within the flick handling code in SnapWithVelocity(). - // When a flick is completed, an adjustment of 1.0f is sometimes made to allow for the scenario where: - // A flick finishes before the update thread has advanced the scroll position past the previous snap point. - Vector2 viewPageSizeLimit( size.x - ( 1.0f + 1.0f ), size.y - ( 1.0f - 1.0f ) ); - Vector2 minPosition( mStartPosition.x - viewPageSizeLimit.x, mStartPosition.y - viewPageSizeLimit.y ); - Vector2 maxPosition( mStartPosition.x + viewPageSizeLimit.x, mStartPosition.y + viewPageSizeLimit.y ); - - if( mFixedRulerX ) - { - scrollPostPosition.x = Clamp( scrollPostPosition.x, minPosition.x, maxPosition.x ); - } - if( mFixedRulerY ) - { - scrollPostPosition.y = Clamp( scrollPostPosition.y, minPosition.y, maxPosition.y ); - } + biasY = 1.0f, vertical = FindDirection::Down; } } } - Vector2 mPrePosition; - Vector2 mLocalStart; - Vector2 mStartPosition; ///< The start position of the gesture - used to limit scroll amount (not modified by clamping). - Vector2 mInitialPanMask; ///< Initial pan mask (based on ruler settings). - Vector2 mCurrentPanMask; ///< Current pan mask that can be altered by axis lock mode. - Vector2 mDomainMin; - Vector2 mDomainMax; - Vector2 mMaxOvershoot; - - float mAxisAutoLockGradient; ///< Set by ScrollView - ScrollView::LockAxis mLockAxis; - - bool mAxisAutoLock:1; ///< Set by ScrollView - bool mWasPanning:1; - bool mClampX:1; - bool mClampY:1; - bool mFixedRulerX:1; - bool mFixedRulerY:1; -}; + // isFlick: Whether this gesture is a flick or not. + isFlick = (horizontal != FindDirection::All || vertical != FindDirection::All); + // isFreeFlick: Whether this gesture is a flick under free panning criteria. + isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD * FREE_FLICK_SPEED_THRESHOLD); -/** - * Internal Position Property Constraint. - * - * Generates position property based on pre-position - * Note: This is the position after clamping. - * (uses result of InternalPrePositionConstraint) - */ -struct InternalPositionConstraint -{ - InternalPositionConstraint(const RulerDomain& domainX, const RulerDomain& domainY, bool wrap) - : mDomainMin( -domainX.min, -domainY.min ), - mDomainMax( -domainX.max, -domainY.max ), - mClampX( domainX.enabled ), - mClampY( domainY.enabled ), - mWrap( wrap ) + if(isFlick || isFreeFlick) { + positionDuration = Vector2::ONE * scrollView.GetScrollFlickDuration(); + alphaFunction = scrollView.GetScrollFlickAlphaFunction(); } - void operator()( Vector2& position, const PropertyInputContainer& inputs ) + // Calculate next positionSnap //////////////////////////////////////////////////////////// + + if(scrollView.GetActorAutoSnap()) { - position = inputs[0]->GetVector2(); - const Vector2& size = inputs[3]->GetVector3().GetVectorXY(); - const Vector2& min = inputs[1]->GetVector2(); - const Vector2& max = inputs[2]->GetVector2(); + Vector3 size = scrollView.Self().GetCurrentProperty(Actor::Property::SIZE); + + Actor child = scrollView.FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f), horizontal, vertical); - if( mWrap ) + if(!child && isFlick) { - position.x = -WrapInDomain(-position.x, min.x, max.x); - position.y = -WrapInDomain(-position.y, min.y, max.y); + // If we conducted a direction limited search and found no actor, then just snap to the closest actor. + child = scrollView.FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f)); } - else + + if(child) { - // clamp post position to domain - position.x = mClampX ? Clamp(position.x, mDomainMax.x + size.x, mDomainMin.x ) : position.x; - position.y = mClampY ? Clamp(position.y, mDomainMax.y + size.y, mDomainMin.y ) : position.y; + Vector2 position = scrollView.Self().GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION); + + // Get center-point of the Actor. + Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER); + + if(rulerX->IsEnabled()) + { + positionSnap.x = position.x - childPosition.x + size.width * 0.5f; + } + if(rulerY->IsEnabled()) + { + positionSnap.y = position.y - childPosition.y + size.height * 0.5f; + } } } - Vector2 mDomainMin; - Vector2 mDomainMax; - bool mClampX; - bool mClampY; - bool mWrap; - -}; + Vector2 startPosition = positionSnap; + positionSnap.x = -rulerX->Snap(-positionSnap.x, biasX); // NOTE: X & Y rulers think in -ve coordinate system. + positionSnap.y = -rulerY->Snap(-positionSnap.y, biasY); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT. -/** - * This constraint updates the X overshoot property using the difference - * SCROLL_PRE_POSITION.x and SCROLL_POSITION.x, returning a relative value between 0.0f and 1.0f - */ -struct OvershootXConstraint -{ - OvershootXConstraint(float maxOvershoot) : mMaxOvershoot(maxOvershoot) {} + Dali::Toolkit::ClampState2D clamped; + Vector3 size = scrollView.Self().GetCurrentProperty(Actor::Property::SIZE); + Vector2 clampDelta(Vector2::ZERO); + ClampPosition(size, rulerX, rulerY, positionSnap, clamped); - void operator()( float& current, const PropertyInputContainer& inputs ) + if((rulerX->GetType() == Dali::Toolkit::Ruler::FREE || rulerY->GetType() == Dali::Toolkit::Ruler::FREE) && + isFreeFlick && !scrollView.GetActorAutoSnap()) { - if( inputs[2]->GetBoolean() ) + // Calculate target position based on velocity of flick. + + // a = Deceleration (Set to diagonal stage length * friction coefficient) + // u = Initial Velocity (Flick velocity) + // v = 0 (Final Velocity) + // t = Time (Velocity / Deceleration) + Vector2 stageSize = Stage::GetCurrent().GetSize(); + float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length(); + float a = (stageLength * scrollView.GetFrictionCoefficient()); + Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * scrollView.GetFlickSpeedCoefficient(); + float speed = u.Length(); + u /= speed; + + // TODO: Change this to a decay function. (faster you flick, the slower it should be) + speed = std::min(speed, stageLength * scrollView.GetMaxFlickSpeed()); + u *= speed; + alphaFunction = ConstantDecelerationAlphaFunction; + + float t = speed / a; + + if(rulerX->IsEnabled() && rulerX->GetType() == Dali::Toolkit::Ruler::FREE) { - const Vector2& scrollPrePosition = inputs[0]->GetVector2(); - const Vector2& scrollPostPosition = inputs[1]->GetVector2(); - float newOvershoot = scrollPrePosition.x - scrollPostPosition.x; - current = (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot; + positionSnap.x += t * u.x * 0.5f; } - else + + if(rulerY->IsEnabled() && rulerY->GetType() == Dali::Toolkit::Ruler::FREE) { - current = 0.0f; + positionSnap.y += t * u.y * 0.5f; } - } - float mMaxOvershoot; -}; - -/** - * This constraint updates the Y overshoot property using the difference - * SCROLL_PRE_POSITION.y and SCROLL_POSITION.y, returning a relative value between 0.0f and 1.0f - */ -struct OvershootYConstraint -{ - OvershootYConstraint(float maxOvershoot) : mMaxOvershoot(maxOvershoot) {} + clampDelta = positionSnap; + ClampPosition(size, rulerX, rulerY, positionSnap, clamped); - void operator()( float& current, const PropertyInputContainer& inputs ) - { - if( inputs[2]->GetBoolean() ) + if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0) { - const Vector2& scrollPrePosition = inputs[0]->GetVector2(); - const Vector2& scrollPostPosition = inputs[1]->GetVector2(); - float newOvershoot = scrollPrePosition.y - scrollPostPosition.y; - current = (newOvershoot > 0.0f ? std::min(newOvershoot, mMaxOvershoot) : std::max(newOvershoot, -mMaxOvershoot)) / mMaxOvershoot; + clampDelta -= positionSnap; + clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, maxOvershoot.x) : std::max(clampDelta.x, -maxOvershoot.x); + clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, maxOvershoot.y) : std::max(clampDelta.y, -maxOvershoot.y); } else { - current = 0.0f; + clampDelta = Vector2::ZERO; } - } - float mMaxOvershoot; -}; + // If Axis is Free and has velocity, then calculate time taken + // to reach target based on velocity in axis. + if(rulerX->IsEnabled() && rulerX->GetType() == Dali::Toolkit::Ruler::FREE) + { + float deltaX = fabsf(startPosition.x - positionSnap.x); -/** - * Internal Position-Delta Property Constraint. - * - * Generates position-delta property based on scroll-position + scroll-offset properties. - */ -void InternalPositionDeltaConstraint( Vector2& current, const PropertyInputContainer& inputs ) -{ - const Vector2& scrollPosition = inputs[0]->GetVector2(); - const Vector2& scrollOffset = inputs[1]->GetVector2(); + if(fabsf(u.x) > Math::MACHINE_EPSILON_1) + { + positionDuration.x = fabsf(deltaX / u.x); + } + else + { + positionDuration.x = 0; + } + } - current = scrollPosition + scrollOffset; -} + if(rulerY->IsEnabled() && rulerY->GetType() == Dali::Toolkit::Ruler::FREE) + { + float deltaY = fabsf(startPosition.y - positionSnap.y); -/** - * Internal Final Position Constraint - * The position of content is: - * of scroll-position + f(scroll-overshoot) - * where f(...) function defines how overshoot - * should affect final-position. - */ -struct InternalFinalConstraint -{ - InternalFinalConstraint(AlphaFunctionPrototype functionX, - AlphaFunctionPrototype functionY) - : mFunctionX(functionX), - mFunctionY(functionY) - { + if(fabsf(u.y) > Math::MACHINE_EPSILON_1) + { + positionDuration.y = fabsf(deltaY / u.y); + } + else + { + positionDuration.y = 0; + } + } } - void operator()( Vector2& current, const PropertyInputContainer& inputs ) + if(scrollView.IsOvershootEnabled()) { - const float& overshootx = inputs[1]->GetFloat(); - const float& overshooty = inputs[2]->GetFloat(); - Vector2 offset( mFunctionX(overshootx), - mFunctionY(overshooty) ); - - current = inputs[0]->GetVector2() - offset; + // Scroll to the end of the overshoot only when overshoot is enabled. + positionSnap += clampDelta; } +} - AlphaFunctionPrototype mFunctionX; - AlphaFunctionPrototype mFunctionY; -}; +} // unnamed namespace +namespace Dali +{ +namespace Toolkit +{ +namespace Internal +{ +namespace +{ +BaseHandle Create() +{ + return Toolkit::ScrollView::New(); } +// Setup properties, signals and actions using the type-registry. +DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ScrollView, Toolkit::Scrollable, Create) + +DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "wrapEnabled", BOOLEAN, WRAP_ENABLED) +DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "panningEnabled", BOOLEAN, PANNING_ENABLED) +DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "axisAutoLockEnabled", BOOLEAN, AXIS_AUTO_LOCK_ENABLED) +DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "wheelScrollDistanceStep", VECTOR2, WHEEL_SCROLL_DISTANCE_STEP) +DALI_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollMode", MAP, SCROLL_MODE) + +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPosition", VECTOR2, SCROLL_POSITION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPrePosition", VECTOR2, SCROLL_PRE_POSITION) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionX", SCROLL_PRE_POSITION_X, SCROLL_PRE_POSITION, 0) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionY", SCROLL_PRE_POSITION_Y, SCROLL_PRE_POSITION, 1) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionMax", VECTOR2, SCROLL_PRE_POSITION_MAX) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionMaxX", SCROLL_PRE_POSITION_MAX_X, SCROLL_PRE_POSITION_MAX, 0) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollPrePositionMaxY", SCROLL_PRE_POSITION_MAX_Y, SCROLL_PRE_POSITION_MAX, 1) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "overshootX", FLOAT, OVERSHOOT_X) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "overshootY", FLOAT, OVERSHOOT_Y) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollFinal", VECTOR2, SCROLL_FINAL) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollFinalX", SCROLL_FINAL_X, SCROLL_FINAL, 0) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollFinalY", SCROLL_FINAL_Y, SCROLL_FINAL, 1) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "wrap", BOOLEAN, WRAP) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "panning", BOOLEAN, PANNING) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrolling", BOOLEAN, SCROLLING) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollDomainSize", VECTOR2, SCROLL_DOMAIN_SIZE) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollDomainSizeX", SCROLL_DOMAIN_SIZE_X, SCROLL_DOMAIN_SIZE, 0) +DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, ScrollView, "scrollDomainSizeY", SCROLL_DOMAIN_SIZE_Y, SCROLL_DOMAIN_SIZE, 1) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollDomainOffset", VECTOR2, SCROLL_DOMAIN_OFFSET) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "scrollPositionDelta", VECTOR2, SCROLL_POSITION_DELTA) +DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, ScrollView, "startPagePosition", VECTOR3, START_PAGE_POSITION) + +DALI_SIGNAL_REGISTRATION(Toolkit, ScrollView, "valueChanged", SIGNAL_SNAP_STARTED) + +DALI_TYPE_REGISTRATION_END() + +} // namespace /////////////////////////////////////////////////////////////////////////////////////////////////// // ScrollView @@ -628,7 +602,7 @@ Dali::Toolkit::ScrollView ScrollView::New() } ScrollView::ScrollView() -: ScrollBase( ControlBehaviour( REQUIRES_WHEEL_EVENTS | DISABLE_STYLE_CHANGE_SIGNALS ) ), // Enable size negotiation +: ScrollBase(ControlBehaviour(DISABLE_STYLE_CHANGE_SIGNALS)), // Enable size negotiation mTouchDownTime(0u), mGestureStackDepth(0), mScrollStateFlags(0), @@ -676,9 +650,9 @@ void ScrollView::OnInitialize() mInternalActor = Actor::New(); self.Add(mInternalActor); - mInternalActor.SetProperty( Actor::Property::PARENT_ORIGIN,ParentOrigin::CENTER ); - mInternalActor.SetProperty( Actor::Property::ANCHOR_POINT,AnchorPoint::CENTER); - mInternalActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS ); + mInternalActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + mInternalActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + mInternalActor.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS); mAlterChild = true; @@ -688,30 +662,38 @@ void ScrollView::OnInitialize() mGestureStackDepth = 0; - self.TouchSignal().Connect( this, &ScrollView::OnTouch ); - EnableGestureDetection( Gesture::Type( Gesture::Pan ) ); + self.TouchedSignal().Connect(this, &ScrollView::OnTouch); + EnableGestureDetection(GestureType::Value(GestureType::PAN)); // By default we'll allow the user to freely drag the scroll view, // while disabling the other rulers. RulerPtr ruler = new DefaultRuler(); - mRulerX = ruler; - mRulerY = ruler; + mRulerX = ruler; + mRulerY = ruler; self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, mCanScrollVertical); self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, mCanScrollHorizontal); - UpdatePropertyDomain(); - SetInternalConstraints(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); + mConstraints.SetInternalConstraints(*this); + + // Connect wheel event + self.WheelEventSignal().Connect(this, &ScrollView::OnWheelEvent); + + DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) { + return std::unique_ptr( + new AccessibleImpl(actor, Dali::Accessibility::Role::SCROLL_PANE)); + }); } -void ScrollView::OnStageConnection( int depth ) +void ScrollView::OnSceneConnection(int depth) { DALI_LOG_SCROLL_STATE("[0x%X]", this); - if ( mSensitive ) + if(mSensitive) { - SetScrollSensitive( false ); - SetScrollSensitive( true ); + SetScrollSensitive(false); + SetScrollSensitive(true); } if(IsOvershootEnabled()) @@ -720,16 +702,16 @@ void ScrollView::OnStageConnection( int depth ) EnableScrollOvershoot(true); } - ScrollBase::OnStageConnection( depth ); + ScrollBase::OnSceneConnection(depth); } -void ScrollView::OnStageDisconnection() +void ScrollView::OnSceneDisconnection() { DALI_LOG_SCROLL_STATE("[0x%X]", this); StopAnimation(); - ScrollBase::OnStageDisconnection(); + ScrollBase::OnSceneDisconnection(); } ScrollView::~ScrollView() @@ -737,55 +719,15 @@ ScrollView::~ScrollView() DALI_LOG_SCROLL_STATE("[0x%X]", this); } -AlphaFunction ScrollView::GetScrollSnapAlphaFunction() const -{ - return mSnapAlphaFunction; -} - -void ScrollView::SetScrollSnapAlphaFunction(AlphaFunction alpha) -{ - mSnapAlphaFunction = alpha; -} - -AlphaFunction ScrollView::GetScrollFlickAlphaFunction() const -{ - return mFlickAlphaFunction; -} - -void ScrollView::SetScrollFlickAlphaFunction(AlphaFunction alpha) -{ - mFlickAlphaFunction = alpha; -} - -float ScrollView::GetScrollSnapDuration() const -{ - return mSnapDuration; -} - -void ScrollView::SetScrollSnapDuration(float time) -{ - mSnapDuration = time; -} - -float ScrollView::GetScrollFlickDuration() const -{ - return mFlickDuration; -} - -void ScrollView::SetScrollFlickDuration(float time) -{ - mFlickDuration = time; -} - void ScrollView::ApplyEffect(Toolkit::ScrollViewEffect effect) { Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self()); // Assertion check to ensure effect doesn't already exist in this scrollview bool effectAlreadyExistsInScrollView(false); - for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter) + for(ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter) { - if(*iter==effect) + if(*iter == effect) { effectAlreadyExistsInScrollView = true; break; @@ -807,9 +749,9 @@ void ScrollView::RemoveEffect(Toolkit::ScrollViewEffect effect) // remove effect from effects list bool effectExistedInScrollView(false); - for (ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter) + for(ScrollViewEffectIter iter = mEffects.begin(); iter != mEffects.end(); ++iter) { - if(*iter==effect) + if(*iter == effect) { mEffects.erase(iter); effectExistedInScrollView = true; @@ -828,7 +770,7 @@ void ScrollView::RemoveAllEffects() { Dali::Toolkit::ScrollView self = Dali::Toolkit::ScrollView::DownCast(Self()); - for (ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter) + for(ScrollViewEffectIter effectIter = mEffects.begin(); effectIter != mEffects.end(); ++effectIter) { Toolkit::ScrollViewEffect effect = *effectIter; @@ -849,144 +791,26 @@ void ScrollView::RemoveConstraintsFromChildren() RemoveConstraintsFromBoundActors(); } -const RulerPtr ScrollView::GetRulerX() const -{ - return mRulerX; -} - -const RulerPtr ScrollView::GetRulerY() const -{ - return mRulerY; -} - void ScrollView::SetRulerX(RulerPtr ruler) { mRulerX = ruler; - UpdatePropertyDomain(); - UpdateMainInternalConstraint(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); + mConstraints.UpdateMainInternalConstraint(*this); } void ScrollView::SetRulerY(RulerPtr ruler) { mRulerY = ruler; - UpdatePropertyDomain(); - UpdateMainInternalConstraint(); -} - -void ScrollView::UpdatePropertyDomain() -{ - Actor self = Self(); - Vector3 size = self.GetTargetSize(); - Vector2 min = mMinScroll; - Vector2 max = mMaxScroll; - bool scrollPositionChanged = false; - bool domainChanged = false; - - bool canScrollVertical = false; - bool canScrollHorizontal = false; - UpdateLocalScrollProperties(); - if(mRulerX->IsEnabled()) - { - const Toolkit::RulerDomain& rulerDomain = mRulerX->GetDomain(); - if( fabsf(min.x - rulerDomain.min) > Math::MACHINE_EPSILON_100 - || fabsf(max.x - rulerDomain.max) > Math::MACHINE_EPSILON_100 ) - { - domainChanged = true; - min.x = rulerDomain.min; - max.x = rulerDomain.max; - - // make sure new scroll value is within new domain - if( mScrollPrePosition.x < min.x - || mScrollPrePosition.x > max.x ) - { - scrollPositionChanged = true; - mScrollPrePosition.x = Clamp(mScrollPrePosition.x, -(max.x - size.x), -min.x); - } - } - if( (fabsf(rulerDomain.max - rulerDomain.min) - size.x) > Math::MACHINE_EPSILON_100 ) - { - canScrollHorizontal = true; - } - } - else if( fabs(min.x) > Math::MACHINE_EPSILON_100 - || fabs(max.x) > Math::MACHINE_EPSILON_100 ) - { - // need to reset to 0 - domainChanged = true; - min.x = 0.0f; - max.x = 0.0f; - canScrollHorizontal = false; - } - - if(mRulerY->IsEnabled()) - { - const Toolkit::RulerDomain& rulerDomain = mRulerY->GetDomain(); - if( fabsf(min.y - rulerDomain.min) > Math::MACHINE_EPSILON_100 - || fabsf(max.y - rulerDomain.max) > Math::MACHINE_EPSILON_100 ) - { - domainChanged = true; - min.y = rulerDomain.min; - max.y = rulerDomain.max; - - // make sure new scroll value is within new domain - if( mScrollPrePosition.y < min.y - || mScrollPrePosition.y > max.y ) - { - scrollPositionChanged = true; - mScrollPrePosition.y = Clamp(mScrollPrePosition.y, -(max.y - size.y), -min.y); - } - } - if( (fabsf(rulerDomain.max - rulerDomain.min) - size.y) > Math::MACHINE_EPSILON_100 ) - { - canScrollVertical = true; - } - } - else if( fabs(min.y) > Math::MACHINE_EPSILON_100 - || fabs(max.y) > Math::MACHINE_EPSILON_100 ) - { - // need to reset to 0 - domainChanged = true; - min.y = 0.0f; - max.y = 0.0f; - canScrollVertical = false; - } - - // avoid setting properties if possible, otherwise this will cause an entire update as well as triggering constraints using each property we update - if( mCanScrollVertical != canScrollVertical ) - { - mCanScrollVertical = canScrollVertical; - self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, canScrollVertical); - } - if( mCanScrollHorizontal != canScrollHorizontal ) - { - mCanScrollHorizontal = canScrollHorizontal; - self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, canScrollHorizontal); - } - if( scrollPositionChanged ) - { - DALI_LOG_SCROLL_STATE("[0x%X] Domain Changed, setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y ); - self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition); - } - if( domainChanged ) - { - mMinScroll = min; - mMaxScroll = max; - self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, mMinScroll ); - self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, mMaxScroll ); - } -} - -bool ScrollView::GetScrollSensitive() -{ - return mSensitive; + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); + mConstraints.UpdateMainInternalConstraint(*this); } void ScrollView::SetScrollSensitive(bool sensitive) { - Actor self = Self(); - PanGestureDetector panGesture( GetPanGestureDetector() ); + Actor self = Self(); + PanGestureDetector panGesture(GetPanGestureDetector()); DALI_LOG_SCROLL_STATE("[0x%X] sensitive: before:[%d] setting[%d]", this, int(mSensitive), int(sensitive)); @@ -1000,10 +824,10 @@ void ScrollView::SetScrollSensitive(bool sensitive) DALI_LOG_SCROLL_STATE("[0x%X] BEFORE: panning:[%d]", this, int(mPanning)); // while the scroll view is panning, the state needs to be reset. - if ( mPanning ) + if(mPanning) { - PanGesture cancelGesture( Gesture::Cancelled ); - OnPan( cancelGesture ); + PanGesture cancelGesture = DevelPanGesture::New(GestureState::CANCELLED); + OnPan(cancelGesture); } panGesture.Detach(self); @@ -1016,26 +840,11 @@ void ScrollView::SetScrollSensitive(bool sensitive) void ScrollView::SetMaxOvershoot(float overshootX, float overshootY) { - mMaxOvershoot.x = overshootX; - mMaxOvershoot.y = overshootY; - mUserMaxOvershoot = mMaxOvershoot; + mMaxOvershoot.x = overshootX; + mMaxOvershoot.y = overshootY; + mUserMaxOvershoot = mMaxOvershoot; mDefaultMaxOvershoot = false; - UpdateMainInternalConstraint(); -} - -void ScrollView::SetSnapOvershootAlphaFunction(AlphaFunction alpha) -{ - mSnapOvershootAlphaFunction = alpha; -} - -float ScrollView::GetSnapOvershootDuration() -{ - return mSnapOvershootDuration; -} - -void ScrollView::SetSnapOvershootDuration(float duration) -{ - mSnapOvershootDuration = duration; + mConstraints.UpdateMainInternalConstraint(*this); } bool ScrollView::GetActorAutoSnap() @@ -1043,135 +852,50 @@ bool ScrollView::GetActorAutoSnap() return mActorAutoSnapEnabled; } -void ScrollView::SetActorAutoSnap(bool enable) -{ - mActorAutoSnapEnabled = enable; -} - void ScrollView::SetAutoResize(bool enable) { mAutoResizeContainerEnabled = enable; // TODO: This needs a lot of issues to be addressed before working. } -bool ScrollView::GetWrapMode() const -{ - return mWrapMode; -} - void ScrollView::SetWrapMode(bool enable) { mWrapMode = enable; Self().SetProperty(Toolkit::ScrollView::Property::WRAP, enable); } -int ScrollView::GetScrollUpdateDistance() const -{ - return mScrollUpdateDistance; -} - -void ScrollView::SetScrollUpdateDistance(int distance) -{ - mScrollUpdateDistance = distance; -} - -bool ScrollView::GetAxisAutoLock() const -{ - return mAxisAutoLock; -} - void ScrollView::SetAxisAutoLock(bool enable) { mAxisAutoLock = enable; - UpdateMainInternalConstraint(); -} - -float ScrollView::GetAxisAutoLockGradient() const -{ - return mAxisAutoLockGradient; + mConstraints.UpdateMainInternalConstraint(*this); } void ScrollView::SetAxisAutoLockGradient(float gradient) { - DALI_ASSERT_DEBUG( gradient >= 0.0f && gradient <= 1.0f ); + DALI_ASSERT_DEBUG(gradient >= 0.0f && gradient <= 1.0f); mAxisAutoLockGradient = gradient; - UpdateMainInternalConstraint(); -} - -float ScrollView::GetFrictionCoefficient() const -{ - return mFrictionCoefficient; + mConstraints.UpdateMainInternalConstraint(*this); } void ScrollView::SetFrictionCoefficient(float friction) { - DALI_ASSERT_DEBUG( friction > 0.0f ); + DALI_ASSERT_DEBUG(friction > 0.0f); mFrictionCoefficient = friction; } -float ScrollView::GetFlickSpeedCoefficient() const -{ - return mFlickSpeedCoefficient; -} - -void ScrollView::SetFlickSpeedCoefficient(float speed) -{ - mFlickSpeedCoefficient = speed; -} - -Vector2 ScrollView::GetMinimumDistanceForFlick() const -{ - return mMinFlickDistance; -} - -void ScrollView::SetMinimumDistanceForFlick( const Vector2& distance ) -{ - mMinFlickDistance = distance; -} - -float ScrollView::GetMinimumSpeedForFlick() const -{ - return mFlickSpeedThreshold; -} - -void ScrollView::SetMinimumSpeedForFlick( float speed ) -{ - mFlickSpeedThreshold = speed; -} - -float ScrollView::GetMaxFlickSpeed() const -{ - return mMaxFlickSpeed; -} - -void ScrollView::SetMaxFlickSpeed(float speed) -{ - mMaxFlickSpeed = speed; -} - -void ScrollView::SetWheelScrollDistanceStep(Vector2 step) -{ - mWheelScrollDistanceStep = step; -} - -Vector2 ScrollView::GetWheelScrollDistanceStep() const -{ - return mWheelScrollDistanceStep; -} - unsigned int ScrollView::GetCurrentPage() const { // in case animation is currently taking place. Vector2 position = GetPropertyPosition(); - Actor self = Self(); - unsigned int page = 0; + Actor self = Self(); + unsigned int page = 0; unsigned int pagesPerVolume = 1; - unsigned int volume = 0; + unsigned int volume = 0; // if rulerX is enabled, then get page count (columns) - page = mRulerX->GetPageFromPosition(-position.x, mWrapMode); - volume = mRulerY->GetPageFromPosition(-position.y, mWrapMode); + page = mRulerX->GetPageFromPosition(-position.x, mWrapMode); + volume = mRulerY->GetPageFromPosition(-position.y, mWrapMode); pagesPerVolume = mRulerX->GetTotalPages(); return volume * pagesPerVolume + page; @@ -1183,47 +907,52 @@ Vector2 ScrollView::GetCurrentScrollPosition() const } void ScrollView::TransformTo(const Vector2& position, - DirectionBias horizontalBias, DirectionBias verticalBias) + DirectionBias horizontalBias, + DirectionBias verticalBias) { TransformTo(position, mSnapDuration, mSnapAlphaFunction, horizontalBias, verticalBias); } -void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunction alpha, - DirectionBias horizontalBias, DirectionBias verticalBias) +void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunction alpha, DirectionBias horizontalBias, DirectionBias verticalBias) { // If this is called while the timer is running, then cancel it StopTouchDownTimer(); - Actor self( Self() ); + Actor self(Self()); // Guard against destruction during signal emission // Note that Emit() methods are called indirectly e.g. from within ScrollView::AnimateTo() - Toolkit::ScrollView handle( GetOwner() ); + Toolkit::ScrollView handle(GetOwner()); DALI_LOG_SCROLL_STATE("[0x%X] pos[%.2f,%.2f], duration[%.2f] bias[%d, %d]", - this, position.x, position.y, duration, int(horizontalBias), int(verticalBias)); + this, + position.x, + position.y, + duration, + int(horizontalBias), + int(verticalBias)); Vector2 currentScrollPosition = GetCurrentScrollPosition(); - self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(currentScrollPosition) ); + self.SetProperty(Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(currentScrollPosition)); - if( mScrolling ) // are we interrupting a current scroll? + if(mScrolling) // are we interrupting a current scroll? { // set mScrolling to false, in case user has code that interrogates mScrolling Getter() in complete. mScrolling = false; DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y); - mScrollCompletedSignal.Emit( currentScrollPosition ); + mScrollCompletedSignal.Emit(currentScrollPosition); } - if( mPanning ) // are we interrupting a current pan? + if(mPanning) // are we interrupting a current pan? { - DALI_LOG_SCROLL_STATE("[0x%X] Interrupting Pan, set to false", this ); - mPanning = false; + DALI_LOG_SCROLL_STATE("[0x%X] Interrupting Pan, set to false", this); + mPanning = false; mGestureStackDepth = 0; - self.SetProperty( Toolkit::ScrollView::Property::PANNING, false ); + self.SetProperty(Toolkit::ScrollView::Property::PANNING, false); - if( mScrollMainInternalPrePositionConstraint ) + if(mConstraints.mScrollMainInternalPrePositionConstraint) { - mScrollMainInternalPrePositionConstraint.Remove(); + mConstraints.mScrollMainInternalPrePositionConstraint.Remove(); } } @@ -1231,14 +960,14 @@ void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunct mScrolling = true; DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 1 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y); - mScrollStartedSignal.Emit( currentScrollPosition ); + mScrollStartedSignal.Emit(currentScrollPosition); bool animating = AnimateTo(-position, Vector2::ONE * duration, alpha, true, horizontalBias, verticalBias, - Snap); + SNAP); if(!animating) { @@ -1248,404 +977,116 @@ void ScrollView::TransformTo(const Vector2& position, float duration, AlphaFunct // If we have no duration, then in the next update frame, we will be at the position specified as we just set. // In this scenario, we cannot return the currentScrollPosition as this is out-of-date and should instead return the requested final position - Vector2 completedPosition( currentScrollPosition ); - if( duration <= Math::MACHINE_EPSILON_10 ) + Vector2 completedPosition(currentScrollPosition); + if(duration <= Math::MACHINE_EPSILON_10) { completedPosition = position; } DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 2 [%.2f, %.2f]", this, completedPosition.x, completedPosition.y); SetScrollUpdateNotification(false); - mScrollCompletedSignal.Emit( completedPosition ); + mScrollCompletedSignal.Emit(completedPosition); } } void ScrollView::ScrollTo(const Vector2& position) { - ScrollTo(position, mSnapDuration ); + ScrollTo(position, mSnapDuration); } void ScrollView::ScrollTo(const Vector2& position, float duration) { - ScrollTo(position, duration, DirectionBiasNone, DirectionBiasNone); + ScrollTo(position, duration, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE); } void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha) { - ScrollTo(position, duration, alpha, DirectionBiasNone, DirectionBiasNone); + ScrollTo(position, duration, alpha, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE); } -void ScrollView::ScrollTo(const Vector2& position, float duration, - DirectionBias horizontalBias, DirectionBias verticalBias) +void ScrollView::ScrollTo(const Vector2& position, float duration, DirectionBias horizontalBias, DirectionBias verticalBias) { ScrollTo(position, duration, mSnapAlphaFunction, horizontalBias, verticalBias); } -void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha, - DirectionBias horizontalBias, DirectionBias verticalBias) -{ - DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f], bias[%d, %d]", this, position.x, position.y, duration, int(horizontalBias), int(verticalBias)); - TransformTo(position, duration, alpha, horizontalBias, verticalBias); -} - -void ScrollView::ScrollTo(unsigned int page) -{ - ScrollTo(page, mSnapDuration); -} - -void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias) -{ - Vector2 position; - unsigned int volume; - unsigned int libraries; - - // The position to scroll to is continuous and linear - // unless a domain has been enabled on the X axis. - // or if WrapMode has been enabled. - bool carryX = mRulerX->GetDomain().enabled | mWrapMode; - bool carryY = mRulerY->GetDomain().enabled | mWrapMode; - - position.x = mRulerX->GetPositionFromPage(page, volume, carryX); - position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY); - - ScrollTo(position, duration, bias, bias); -} - -void ScrollView::ScrollTo(Actor &actor) -{ - ScrollTo(actor, mSnapDuration); -} - -void ScrollView::ScrollTo(Actor &actor, float duration) -{ - DALI_ASSERT_ALWAYS(actor.GetParent() == Self()); - - Actor self = Self(); - Vector3 size = self.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); - Vector3 position = actor.GetCurrentProperty< Vector3 >( Actor::Property::POSITION ); - Vector2 prePosition = GetPropertyPrePosition(); - position.GetVectorXY() -= prePosition; - - ScrollTo(Vector2(position.x - size.width * 0.5f, position.y - size.height * 0.5f), duration); -} - -Actor ScrollView::FindClosestActor() -{ - Actor self = Self(); - Vector3 size = self.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); - - return FindClosestActorToPosition(Vector3(size.width * 0.5f,size.height * 0.5f,0.0f)); -} - -Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ) -{ - Actor closestChild; - float closestDistance2 = 0.0f; - Vector3 actualPosition = position; - - unsigned int numChildren = Self().GetChildCount(); - - for(unsigned int i = 0; i < numChildren; ++i) - { - Actor child = Self().GetChildAt(i); - - if(mInternalActor == child) // ignore internal actor. - { - continue; - } - - Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER); - - Vector3 delta = childPosition - actualPosition; - - // X-axis checking (only find Actors to the [dirX] of actualPosition) - if(dirX > All) // != All,None - { - FindDirection deltaH = delta.x > 0 ? Right : Left; - if(dirX != deltaH) - { - continue; - } - } - - // Y-axis checking (only find Actors to the [dirY] of actualPosition) - if(dirY > All) // != All,None - { - FindDirection deltaV = delta.y > 0 ? Down : Up; - if(dirY != deltaV) - { - continue; - } - } - - // Z-axis checking (only find Actors to the [dirZ] of actualPosition) - if(dirZ > All) // != All,None - { - FindDirection deltaV = delta.y > 0 ? In : Out; - if(dirZ != deltaV) - { - continue; - } - } - - // compare child to closest child in terms of distance. - float distance2 = 0.0f; - - // distance2 = the Square of the relevant dimensions of delta - if(dirX != None) - { - distance2 += delta.x * delta.x; - } - - if(dirY != None) - { - distance2 += delta.y * delta.y; - } - - if(dirZ != None) - { - distance2 += delta.z * delta.z; - } - - if(closestChild) // Next time. - { - if(distance2 < closestDistance2) - { - closestChild = child; - closestDistance2 = distance2; - } - } - else // First time. - { - closestChild = child; - closestDistance2 = distance2; - } - } - - return closestChild; -} - -bool ScrollView::ScrollToSnapPoint() -{ - DALI_LOG_SCROLL_STATE("[0x%X]", this ); - Vector2 stationaryVelocity = Vector2(0.0f, 0.0f); - return SnapWithVelocity( stationaryVelocity ); -} - -// TODO: In situations where axes are different (X snap, Y free) -// Each axis should really have their own independent animation (time and equation) -// Consider, X axis snapping to nearest grid point (EaseOut over fixed time) -// Consider, Y axis simulating physics to arrive at a point (Physics equation over variable time) -// Currently, the axes have been split however, they both use the same EaseOut equation. -bool ScrollView::SnapWithVelocity(Vector2 velocity) -{ - // Animator takes over now, touches are assumed not to interfere. - // And if touches do interfere, then we'll stop animation, update PrePosition - // to current mScroll's properties, and then resume. - // Note: For Flicking this may work a bit different... - - float angle = atan2(velocity.y, velocity.x); - float speed2 = velocity.LengthSquared(); - AlphaFunction alphaFunction = mSnapAlphaFunction; - Vector2 positionDuration = Vector2::ONE * mSnapDuration; - float biasX = 0.5f; - float biasY = 0.5f; - FindDirection horizontal = None; - FindDirection vertical = None; - - // orthoAngleRange = Angle tolerance within the Exact N,E,S,W direction - // that will be accepted as a general N,E,S,W flick direction. - - const float orthoAngleRange = FLICK_ORTHO_ANGLE_RANGE * M_PI / 180.0f; - const float flickSpeedThreshold2 = mFlickSpeedThreshold * mFlickSpeedThreshold; - - Vector2 positionSnap = mScrollPrePosition; - - // Flick logic X Axis - - if(mRulerX->IsEnabled() && mLockAxis != LockHorizontal) - { - horizontal = All; - - if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold - mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions - { - if((angle >= -orthoAngleRange) && (angle < orthoAngleRange)) // Swiping East - { - biasX = 0.0f, horizontal = Left; - - // This guards against an error where no movement occurs, due to the flick finishing - // before the update-thread has advanced mScrollPostPosition past the the previous snap point. - positionSnap.x += 1.0f; - } - else if((angle >= M_PI-orthoAngleRange) || (angle < -M_PI+orthoAngleRange)) // Swiping West - { - biasX = 1.0f, horizontal = Right; - - // This guards against an error where no movement occurs, due to the flick finishing - // before the update-thread has advanced mScrollPostPosition past the the previous snap point. - positionSnap.x -= 1.0f; - } - } - } - - // Flick logic Y Axis - - if(mRulerY->IsEnabled() && mLockAxis != LockVertical) - { - vertical = All; - - if( speed2 > flickSpeedThreshold2 || // exceeds flick threshold - mInAccessibilityPan ) // With AccessibilityPan its easier to move between snap positions - { - if((angle >= M_PI_2-orthoAngleRange) && (angle < M_PI_2+orthoAngleRange)) // Swiping South - { - biasY = 0.0f, vertical = Up; - } - else if((angle >= -M_PI_2-orthoAngleRange) && (angle < -M_PI_2+orthoAngleRange)) // Swiping North - { - biasY = 1.0f, vertical = Down; - } - } - } - - // isFlick: Whether this gesture is a flick or not. - bool isFlick = (horizontal != All || vertical != All); - // isFreeFlick: Whether this gesture is a flick under free panning criteria. - bool isFreeFlick = velocity.LengthSquared() > (FREE_FLICK_SPEED_THRESHOLD*FREE_FLICK_SPEED_THRESHOLD); - - if(isFlick || isFreeFlick) - { - positionDuration = Vector2::ONE * mFlickDuration; - alphaFunction = mFlickAlphaFunction; - } - - // Calculate next positionSnap //////////////////////////////////////////////////////////// - - if(mActorAutoSnapEnabled) - { - Vector3 size = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); - - Actor child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f), horizontal, vertical ); - - if(!child && isFlick ) - { - // If we conducted a direction limited search and found no actor, then just snap to the closest actor. - child = FindClosestActorToPosition( Vector3(size.width * 0.5f,size.height * 0.5f,0.0f) ); - } - - if(child) - { - Vector2 position = Self().GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ); - - // Get center-point of the Actor. - Vector3 childPosition = GetPositionOfAnchor(child, AnchorPoint::CENTER); - - if(mRulerX->IsEnabled()) - { - positionSnap.x = position.x - childPosition.x + size.width * 0.5f; - } - if(mRulerY->IsEnabled()) - { - positionSnap.y = position.y - childPosition.y + size.height * 0.5f; - } - } - } +void ScrollView::ScrollTo(const Vector2& position, float duration, AlphaFunction alpha, DirectionBias horizontalBias, DirectionBias verticalBias) +{ + DALI_LOG_SCROLL_STATE("[0x%X] position[%.2f, %.2f] duration[%.2f], bias[%d, %d]", this, position.x, position.y, duration, int(horizontalBias), int(verticalBias)); + TransformTo(position, duration, alpha, horizontalBias, verticalBias); +} - Vector2 startPosition = positionSnap; - positionSnap.x = -mRulerX->Snap(-positionSnap.x, biasX); // NOTE: X & Y rulers think in -ve coordinate system. - positionSnap.y = -mRulerY->Snap(-positionSnap.y, biasY); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT. +void ScrollView::ScrollTo(unsigned int page) +{ + ScrollTo(page, mSnapDuration); +} - Vector2 clampDelta(Vector2::ZERO); - ClampPosition(positionSnap); +void ScrollView::ScrollTo(unsigned int page, float duration, DirectionBias bias) +{ + Vector2 position; + unsigned int volume; + unsigned int libraries; - if( (mRulerX->GetType() == Ruler::Free || mRulerY->GetType() == Ruler::Free) - && isFreeFlick && !mActorAutoSnapEnabled) - { - // Calculate target position based on velocity of flick. + // The position to scroll to is continuous and linear + // unless a domain has been enabled on the X axis. + // or if WrapMode has been enabled. + bool carryX = mRulerX->GetDomain().enabled | mWrapMode; + bool carryY = mRulerY->GetDomain().enabled | mWrapMode; - // a = Deceleration (Set to diagonal stage length * friction coefficient) - // u = Initial Velocity (Flick velocity) - // v = 0 (Final Velocity) - // t = Time (Velocity / Deceleration) - Vector2 stageSize = Stage::GetCurrent().GetSize(); - float stageLength = Vector3(stageSize.x, stageSize.y, 0.0f).Length(); - float a = (stageLength * mFrictionCoefficient); - Vector3 u = Vector3(velocity.x, velocity.y, 0.0f) * mFlickSpeedCoefficient; - float speed = u.Length(); - u/= speed; + position.x = mRulerX->GetPositionFromPage(page, volume, carryX); + position.y = mRulerY->GetPositionFromPage(volume, libraries, carryY); - // TODO: Change this to a decay function. (faster you flick, the slower it should be) - speed = std::min(speed, stageLength * mMaxFlickSpeed ); - u*= speed; - alphaFunction = ConstantDecelerationAlphaFunction; + ScrollTo(position, duration, bias, bias); +} - float t = speed / a; +void ScrollView::ScrollTo(Actor& actor) +{ + ScrollTo(actor, mSnapDuration); +} - if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free) - { - positionSnap.x += t*u.x*0.5f; - } +void ScrollView::ScrollTo(Actor& actor, float duration) +{ + DALI_ASSERT_ALWAYS(actor.GetParent() == Self()); - if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free) - { - positionSnap.y += t*u.y*0.5f; - } + Actor self = Self(); + Vector3 size = self.GetCurrentProperty(Actor::Property::SIZE); + Vector3 position = actor.GetCurrentProperty(Actor::Property::POSITION); + Vector2 prePosition = GetPropertyPrePosition(); + position.GetVectorXY() -= prePosition; - clampDelta = positionSnap; - ClampPosition(positionSnap); - if((positionSnap - startPosition).LengthSquared() > Math::MACHINE_EPSILON_0) - { - clampDelta -= positionSnap; - clampDelta.x = clampDelta.x > 0.0f ? std::min(clampDelta.x, mMaxOvershoot.x) : std::max(clampDelta.x, -mMaxOvershoot.x); - clampDelta.y = clampDelta.y > 0.0f ? std::min(clampDelta.y, mMaxOvershoot.y) : std::max(clampDelta.y, -mMaxOvershoot.y); - } - else - { - clampDelta = Vector2::ZERO; - } + ScrollTo(Vector2(position.x - size.width * 0.5f, position.y - size.height * 0.5f), duration); +} - // If Axis is Free and has velocity, then calculate time taken - // to reach target based on velocity in axis. - if(mRulerX->IsEnabled() && mRulerX->GetType() == Ruler::Free) - { - float deltaX = fabsf(startPosition.x - positionSnap.x); +Actor ScrollView::FindClosestActor() +{ + Actor self = Self(); + Vector3 size = self.GetCurrentProperty(Actor::Property::SIZE); - if(fabsf(u.x) > Math::MACHINE_EPSILON_1) - { - positionDuration.x = fabsf(deltaX / u.x); - } - else - { - positionDuration.x = 0; - } - } + return FindClosestActorToPosition(Vector3(size.width * 0.5f, size.height * 0.5f, 0.0f)); +} - if(mRulerY->IsEnabled() && mRulerY->GetType() == Ruler::Free) - { - float deltaY = fabsf(startPosition.y - positionSnap.y); +Actor ScrollView::FindClosestActorToPosition(const Vector3& position, FindDirection dirX, FindDirection dirY, FindDirection dirZ) +{ + return ::FindClosestActorToPosition(Self(), mInternalActor, position, dirX, dirY, dirZ); +} - if(fabsf(u.y) > Math::MACHINE_EPSILON_1) - { - positionDuration.y = fabsf(deltaY / u.y); - } - else - { - positionDuration.y = 0; - } - } - } +bool ScrollView::ScrollToSnapPoint() +{ + DALI_LOG_SCROLL_STATE("[0x%X]", this); + Vector2 stationaryVelocity = Vector2(0.0f, 0.0f); + return SnapWithVelocity(stationaryVelocity); +} - if(IsOvershootEnabled()) - { - // Scroll to the end of the overshoot only when overshoot is enabled. - positionSnap += clampDelta; - } +bool ScrollView::SnapWithVelocity(Vector2 velocity) +{ + Vector2 positionSnap = mScrollPrePosition; + Vector2 positionDuration = Vector2::ONE * mSnapDuration; + AlphaFunction alphaFunction = mSnapAlphaFunction; + bool isFlick; + bool isFreeFlick; - bool animating = AnimateTo(positionSnap, positionDuration, - alphaFunction, false, - DirectionBiasNone, DirectionBiasNone, - isFlick || isFreeFlick ? Flick : Snap); + ::SnapWithVelocity(*this, mRulerX, mRulerY, mLockAxis, velocity, mMaxOvershoot, positionSnap, positionDuration, alphaFunction, mInAccessibilityPan, isFlick, isFreeFlick); + + bool animating = AnimateTo(positionSnap, positionDuration, alphaFunction, false, DIRECTION_BIAS_NONE, DIRECTION_BIAS_NONE, isFlick || isFreeFlick ? FLICK : SNAP); return animating; } @@ -1669,16 +1110,13 @@ void ScrollView::StopAnimation(Animation& animation) } } -bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDuration, - AlphaFunction alpha, bool findShortcuts, - DirectionBias horizontalBias, DirectionBias verticalBias, - SnapType snapType) +bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDuration, AlphaFunction alpha, bool findShortcuts, DirectionBias horizontalBias, DirectionBias verticalBias, SnapType snapType) { // Here we perform an animation on a number of properties (depending on which have changed) // The animation is applied to all ScrollBases - Actor self = Self(); + Actor self = Self(); mScrollTargetPosition = position; - float totalDuration = 0.0f; + float totalDuration = 0.0f; bool positionChanged = (mScrollTargetPosition != mScrollPostPosition); @@ -1690,7 +1128,7 @@ bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDurat else { // try to animate for a frame, on some occasions update will be changing scroll value while event side thinks it hasnt changed - totalDuration = 0.01f; + totalDuration = 0.01f; positionChanged = true; } @@ -1699,7 +1137,7 @@ bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDurat // Position Delta /////////////////////////////////////////////////////// if(positionChanged) { - UpdateMainInternalConstraint(); + mConstraints.UpdateMainInternalConstraint(*this); if(mWrapMode && findShortcuts) { // In Wrap Mode, the shortest distance is a little less intuitive... @@ -1708,13 +1146,13 @@ bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDurat if(mRulerX->IsEnabled()) { - float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias); + float dir = VectorInDomain(-mScrollPrePosition.x, -mScrollTargetPosition.x, rulerDomainX.min, rulerDomainX.max, horizontalBias); mScrollTargetPosition.x = mScrollPrePosition.x + -dir; } if(mRulerY->IsEnabled()) { - float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias); + float dir = VectorInDomain(-mScrollPrePosition.y, -mScrollTargetPosition.y, rulerDomainY.min, rulerDomainY.max, verticalBias); mScrollTargetPosition.y = mScrollPrePosition.y + -dir; } } @@ -1724,38 +1162,38 @@ bool ScrollView::AnimateTo(const Vector2& position, const Vector2& positionDurat AnimateInternalXTo(mScrollTargetPosition.x, positionDuration.x, alpha); AnimateInternalYTo(mScrollTargetPosition.y, positionDuration.y, alpha); - if( !(mScrollStateFlags & SCROLL_ANIMATION_FLAGS) ) + if(!(mScrollStateFlags & SCROLL_ANIMATION_FLAGS)) { - DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y ); + DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y); self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollTargetPosition); - mScrollPrePosition = mScrollTargetPosition; + mScrollPrePosition = mScrollTargetPosition; mScrollPostPosition = mScrollTargetPosition; WrapPosition(mScrollPostPosition); } - DALI_LOG_SCROLL_STATE("[0x%X] position-changed, mScrollTargetPosition[%.2f, %.2f], mScrollPrePosition[%.2f, %.2f], mScrollPostPosition[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y, mScrollPrePosition.x, mScrollPrePosition.y, mScrollPostPosition.x, mScrollPostPosition.y ); - DALI_LOG_SCROLL_STATE("[0x%X] SCROLL_PRE_POSITION[%.2f, %.2f], SCROLL_POSITION[%.2f, %.2f]", this, self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get().x, self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get().y, self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ).Get().x, self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ).Get().y ); + DALI_LOG_SCROLL_STATE("[0x%X] position-changed, mScrollTargetPosition[%.2f, %.2f], mScrollPrePosition[%.2f, %.2f], mScrollPostPosition[%.2f, %.2f]", this, mScrollTargetPosition.x, mScrollTargetPosition.y, mScrollPrePosition.x, mScrollPrePosition.y, mScrollPostPosition.x, mScrollPostPosition.y); + DALI_LOG_SCROLL_STATE("[0x%X] SCROLL_PRE_POSITION[%.2f, %.2f], SCROLL_POSITION[%.2f, %.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get().x, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get().y, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get().x, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get().y); } SetScrollUpdateNotification(true); // Always send a snap event when AnimateTo is called. Toolkit::ScrollView::SnapEvent snapEvent; - snapEvent.type = snapType; + snapEvent.type = snapType; snapEvent.position = -mScrollTargetPosition; snapEvent.duration = totalDuration; DALI_LOG_SCROLL_STATE("[0x%X] mSnapStartedSignal [%.2f, %.2f]", this, snapEvent.position.x, snapEvent.position.y); - mSnapStartedSignal.Emit( snapEvent ); + mSnapStartedSignal.Emit(snapEvent); return (mScrollStateFlags & SCROLL_ANIMATION_FLAGS) != 0; } void ScrollView::EnableScrollOvershoot(bool enable) { - if (enable) + if(enable) { - if (!mOvershootIndicator) + if(!mOvershootIndicator) { mOvershootIndicator = ScrollOvershootIndicator::New(); } @@ -1766,57 +1204,57 @@ void ScrollView::EnableScrollOvershoot(bool enable) { mMaxOvershoot = mUserMaxOvershoot; - if (mOvershootIndicator) + if(mOvershootIndicator) { mOvershootIndicator->DetachFromScrollable(*this); } } - UpdateMainInternalConstraint(); + mConstraints.UpdateMainInternalConstraint(*this); } void ScrollView::AddOverlay(Actor actor) { - actor.SetProperty( Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D ); - mInternalActor.Add( actor ); + actor.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D); + mInternalActor.Add(actor); } void ScrollView::RemoveOverlay(Actor actor) { - mInternalActor.Remove( actor ); + mInternalActor.Remove(actor); } -void ScrollView::SetOvershootSize( const Vector2& size ) +void ScrollView::SetOvershootSize(const Vector2& size) { mOvershootSize = size; - if( IsOvershootEnabled() && mOvershootIndicator ) + if(IsOvershootEnabled() && mOvershootIndicator) { mOvershootIndicator->AttachToScrollable(*this); } } -void ScrollView::SetOvershootEffectColor( const Vector4& color ) +void ScrollView::SetOvershootEffectColor(const Vector4& color) { mOvershootEffectColor = color; - if( mOvershootIndicator ) + if(mOvershootIndicator) { - mOvershootIndicator->SetOvershootEffectColor( color ); + mOvershootIndicator->SetOvershootEffectColor(color); } } -void ScrollView::SetScrollingDirection( Radian direction, Radian threshold ) +void ScrollView::SetScrollingDirection(Radian direction, Radian threshold) { - PanGestureDetector panGesture( GetPanGestureDetector() ); + PanGestureDetector panGesture(GetPanGestureDetector()); // First remove just in case we have some set, then add. - panGesture.RemoveDirection( direction ); - panGesture.AddDirection( direction, threshold ); + panGesture.RemoveDirection(direction); + panGesture.AddDirection(direction, threshold); } -void ScrollView::RemoveScrollingDirection( Radian direction ) +void ScrollView::RemoveScrollingDirection(Radian direction) { - PanGestureDetector panGesture( GetPanGestureDetector() ); - panGesture.RemoveDirection( direction ); + PanGestureDetector panGesture(GetPanGestureDetector()); + panGesture.RemoveDirection(direction); } Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal() @@ -1824,6 +1262,19 @@ Toolkit::ScrollView::SnapStartedSignalType& ScrollView::SnapStartedSignal() return mSnapStartedSignal; } +bool ScrollView::AccessibleImpl::ScrollToChild(Actor child) +{ + auto scrollView = Dali::Toolkit::ScrollView::DownCast(Self()); + if(Toolkit::GetImpl(scrollView).FindClosestActor() == child) + { + return false; + } + + // FIXME: ScrollTo does not work (snaps back to original position) + scrollView.ScrollTo(child, scrollView.GetScrollFlickDuration()); + return true; +} + void ScrollView::FindAndUnbindActor(Actor child) { UnbindActor(child); @@ -1831,14 +1282,14 @@ void ScrollView::FindAndUnbindActor(Actor child) Vector2 ScrollView::GetPropertyPrePosition() const { - Vector2 position = Self().GetCurrentProperty< Vector2 >( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ); + Vector2 position = Self().GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION); WrapPosition(position); return position; } Vector2 ScrollView::GetPropertyPosition() const { - Vector2 position = Self().GetCurrentProperty< Vector2 >( Toolkit::ScrollView::Property::SCROLL_POSITION ); + Vector2 position = Self().GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION); WrapPosition(position); return position; @@ -1860,68 +1311,68 @@ void ScrollView::HandleSnapAnimationFinished() UpdateLocalScrollProperties(); WrapPosition(mScrollPrePosition); - DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y ); + DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y); self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition); Vector2 currentScrollPosition = GetCurrentScrollPosition(); - DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y ); - mScrollCompletedSignal.Emit( currentScrollPosition ); + DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 3 current[%.2f, %.2f], mScrollTargetPosition[%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y, -mScrollTargetPosition.x, -mScrollTargetPosition.y); + mScrollCompletedSignal.Emit(currentScrollPosition); mDomainOffset += deltaPosition - mScrollPostPosition; self.SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset); HandleStoppedAnimation(); } -void ScrollView::SetScrollUpdateNotification( bool enabled ) +void ScrollView::SetScrollUpdateNotification(bool enabled) { Actor self = Self(); - if( mScrollXUpdateNotification ) + if(mScrollXUpdateNotification) { // disconnect now to avoid a notification before removed from update thread mScrollXUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification); self.RemovePropertyNotification(mScrollXUpdateNotification); mScrollXUpdateNotification.Reset(); } - if( enabled && !mScrollUpdatedSignal.Empty()) + if(enabled && !mScrollUpdatedSignal.Empty()) { // Only set up the notification when the application has connected to the updated signal mScrollXUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 0, StepCondition(mScrollUpdateDistance, 0.0f)); - mScrollXUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification ); + mScrollXUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification); } - if( mScrollYUpdateNotification ) + if(mScrollYUpdateNotification) { // disconnect now to avoid a notification before removed from update thread mScrollYUpdateNotification.NotifySignal().Disconnect(this, &ScrollView::OnScrollUpdateNotification); self.RemovePropertyNotification(mScrollYUpdateNotification); mScrollYUpdateNotification.Reset(); } - if( enabled && !mScrollUpdatedSignal.Empty()) + if(enabled && !mScrollUpdatedSignal.Empty()) { // Only set up the notification when the application has connected to the updated signal mScrollYUpdateNotification = self.AddPropertyNotification(Toolkit::ScrollView::Property::SCROLL_POSITION, 1, StepCondition(mScrollUpdateDistance, 0.0f)); - mScrollYUpdateNotification.NotifySignal().Connect( this, &ScrollView::OnScrollUpdateNotification ); + mScrollYUpdateNotification.NotifySignal().Connect(this, &ScrollView::OnScrollUpdateNotification); } } void ScrollView::OnScrollUpdateNotification(Dali::PropertyNotification& source) { // Guard against destruction during signal emission - Toolkit::ScrollView handle( GetOwner() ); + Toolkit::ScrollView handle(GetOwner()); Vector2 currentScrollPosition = GetCurrentScrollPosition(); - mScrollUpdatedSignal.Emit( currentScrollPosition ); + mScrollUpdatedSignal.Emit(currentScrollPosition); } -bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +bool ScrollView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor) { - Dali::BaseHandle handle( object ); + Dali::BaseHandle handle(object); - bool connected( true ); - Toolkit::ScrollView view = Toolkit::ScrollView::DownCast( handle ); + bool connected(true); + Toolkit::ScrollView view = Toolkit::ScrollView::DownCast(handle); - if( 0 == strcmp( signalName.c_str(), SIGNAL_SNAP_STARTED ) ) + if(0 == strcmp(signalName.c_str(), SIGNAL_SNAP_STARTED)) { - view.SnapStartedSignal().Connect( tracker, functor ); + view.SnapStartedSignal().Connect(tracker, functor); } else { @@ -1935,64 +1386,64 @@ bool ScrollView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface void ScrollView::OnSizeAnimation(Animation& animation, const Vector3& targetSize) { // need to update domain properties for new size - UpdatePropertyDomain(); + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); } -void ScrollView::OnSizeSet( const Vector3& size ) +void ScrollView::OnSizeSet(const Vector3& size) { // need to update domain properties for new size - if( mDefaultMaxOvershoot ) + if(mDefaultMaxOvershoot) { mUserMaxOvershoot.x = size.x * 0.5f; mUserMaxOvershoot.y = size.y * 0.5f; - if( !IsOvershootEnabled() ) + if(!IsOvershootEnabled()) { mMaxOvershoot = mUserMaxOvershoot; } } - UpdatePropertyDomain(); - UpdateMainInternalConstraint(); - if( IsOvershootEnabled() ) + ScrollViewPropertyHandler::UpdatePropertyDomain(*this); + mConstraints.UpdateMainInternalConstraint(*this); + if(IsOvershootEnabled()) { mOvershootIndicator->Reset(); } - ScrollBase::OnSizeSet( size ); + ScrollBase::OnSizeSet(size); } void ScrollView::OnChildAdd(Actor& child) { - ScrollBase::OnChildAdd( child ); + ScrollBase::OnChildAdd(child); Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child); - if( scrollBar ) + if(scrollBar) { mScrollBar = scrollBar; - scrollBar.SetProperty( Dali::Actor::Property::NAME,"ScrollBar"); + scrollBar.SetProperty(Dali::Actor::Property::NAME, "ScrollBar"); - mInternalActor.Add( scrollBar ); - if( scrollBar.GetScrollDirection() == Toolkit::ScrollBar::Horizontal ) + mInternalActor.Add(scrollBar); + if(scrollBar.GetScrollDirection() == Toolkit::ScrollBar::HORIZONTAL) { - scrollBar.SetScrollPropertySource( Self(), - Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X, - Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X, - Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X, - Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X ); + scrollBar.SetScrollPropertySource(Self(), + Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_X, + Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_X, + Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_X, + Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_X); } else { - scrollBar.SetScrollPropertySource( Self(), - Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y, - Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y, - Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y, - Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y ); + scrollBar.SetScrollPropertySource(Self(), + Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_Y, + Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y, + Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX_Y, + Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE_Y); } - if( mTransientScrollBar ) + if(mTransientScrollBar) { // Show the scroll-indicator for a brief period Property::Map emptyMap; - scrollBar.DoAction( "ShowTransientIndicator", emptyMap ); + scrollBar.DoAction("ShowTransientIndicator", emptyMap); } } else if(mAlterChild) @@ -2006,15 +1457,15 @@ void ScrollView::OnChildRemove(Actor& child) // TODO: Actor needs a RemoveConstraint method to take out an individual constraint. UnbindActor(child); - ScrollBase::OnChildRemove( child ); + ScrollBase::OnChildRemove(child); } void ScrollView::StartTouchDownTimer() { - if ( !mTouchDownTimer ) + if(!mTouchDownTimer) { - mTouchDownTimer = Timer::New( TOUCH_DOWN_TIMER_INTERVAL ); - mTouchDownTimer.TickSignal().Connect( this, &ScrollView::OnTouchDownTimeout ); + mTouchDownTimer = Timer::New(TOUCH_DOWN_TIMER_INTERVAL); + mTouchDownTimer.TickSignal().Connect(this, &ScrollView::OnTouchDownTimeout); } mTouchDownTimer.Start(); @@ -2022,7 +1473,7 @@ void ScrollView::StartTouchDownTimer() void ScrollView::StopTouchDownTimer() { - if ( mTouchDownTimer ) + if(mTouchDownTimer) { mTouchDownTimer.Stop(); } @@ -2034,13 +1485,13 @@ bool ScrollView::OnTouchDownTimeout() mTouchDownTimeoutReached = true; - unsigned int currentScrollStateFlags( mScrollStateFlags ); // Cleared in StopAnimation so keep local copy for comparison - if( currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS) ) + unsigned int currentScrollStateFlags(mScrollStateFlags); // Cleared in StopAnimation so keep local copy for comparison + if(currentScrollStateFlags & (SCROLL_ANIMATION_FLAGS | SNAP_ANIMATION_FLAGS)) { DALI_LOG_SCROLL_STATE("[0x%X] Scrolling Or snapping flags set, stopping animation", this); StopAnimation(); - if( currentScrollStateFlags & SCROLL_ANIMATION_FLAGS ) + if(currentScrollStateFlags & SCROLL_ANIMATION_FLAGS) { DALI_LOG_SCROLL_STATE("[0x%X] Scrolling flags set, emitting signal", this); @@ -2052,14 +1503,14 @@ bool ScrollView::OnTouchDownTimeout() UpdateLocalScrollProperties(); Vector2 currentScrollPosition = GetCurrentScrollPosition(); DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 4 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y); - mScrollCompletedSignal.Emit( currentScrollPosition ); + mScrollCompletedSignal.Emit(currentScrollPosition); } } return false; } -bool ScrollView::OnTouch( Actor actor, const TouchData& touch ) +bool ScrollView::OnTouch(Actor actor, const TouchEvent& touch) { if(!mSensitive) { @@ -2070,51 +1521,51 @@ bool ScrollView::OnTouch( Actor actor, const TouchData& touch ) } // Ignore events with multiple-touch points - if (touch.GetPointCount() != 1) + if(touch.GetPointCount() != 1) { DALI_LOG_SCROLL_STATE("[0x%X], multiple touch, ignoring", this); return false; } - const PointState::Type pointState = touch.GetState( 0 ); - if( pointState == PointState::DOWN ) + const PointState::Type pointState = touch.GetState(0); + if(pointState == PointState::DOWN) { DALI_LOG_SCROLL_STATE("[0x%X] Down", this); - if(mGestureStackDepth==0) + if(mGestureStackDepth == 0) { mTouchDownTime = touch.GetTime(); // This allows time for a pan-gesture to start, to avoid breaking snap-animation behavior with fast flicks. // If touch-down does not become a pan (after timeout interval), then snap-animation can be interrupted. mTouchDownTimeoutReached = false; - mScrollInterrupted = false; + mScrollInterrupted = false; StartTouchDownTimer(); } } - else if( ( pointState == PointState::UP ) || - ( ( pointState == PointState::INTERRUPTED ) && ( touch.GetHitActor( 0 )== Self() ) ) ) + else if((pointState == PointState::UP) || + ((pointState == PointState::INTERRUPTED) && (touch.GetHitActor(0) == Self()))) { - DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ( ( pointState == TouchPoint::Up ) ? "Up" : "Interrupted" ) ); + DALI_LOG_SCROLL_STATE("[0x%X] %s", this, ((pointState == PointState::UP) ? "Up" : "Interrupted")); StopTouchDownTimer(); // if the user touches and releases without enough movement to go // into a gesture state, then we should snap to nearest point. // otherwise our scroll could be stopped (interrupted) half way through an animation. - if(mGestureStackDepth==0 && mTouchDownTimeoutReached) + if(mGestureStackDepth == 0 && mTouchDownTimeoutReached) { - if( ( pointState == PointState::INTERRUPTED ) || - ( ( touch.GetTime() - mTouchDownTime ) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET ) ) + if((pointState == PointState::INTERRUPTED) || + ((touch.GetTime() - mTouchDownTime) >= MINIMUM_TIME_BETWEEN_DOWN_AND_UP_FOR_RESET)) { // Reset the velocity only if down was received a while ago - mLastVelocity = Vector2( 0.0f, 0.0f ); + mLastVelocity = Vector2(0.0f, 0.0f); } UpdateLocalScrollProperties(); // Only finish the transform if scrolling was interrupted on down or if we are scrolling - if ( mScrollInterrupted || mScrolling ) + if(mScrollInterrupted || mScrolling) { DALI_LOG_SCROLL_STATE("[0x%X] Calling FinishTransform", this); @@ -2122,13 +1573,13 @@ bool ScrollView::OnTouch( Actor actor, const TouchData& touch ) } } mTouchDownTimeoutReached = false; - mScrollInterrupted = false; + mScrollInterrupted = false; } - return true; + return false; } -bool ScrollView::OnWheelEvent(const WheelEvent& event) +bool ScrollView::OnWheelEvent(Actor actor, const WheelEvent& event) { if(!mSensitive) { @@ -2141,33 +1592,33 @@ bool ScrollView::OnWheelEvent(const WheelEvent& event) if(mRulerX->IsEnabled() && !mRulerY->IsEnabled()) { // If only the ruler in the X axis is enabled, scroll in the X axis. - if(mRulerX->GetType() == Ruler::Free) + if(mRulerX->GetType() == Ruler::FREE) { // Free panning mode - targetScrollPosition.x += event.z * mWheelScrollDistanceStep.x; + targetScrollPosition.x += event.GetDelta() * mWheelScrollDistanceStep.x; ClampPosition(targetScrollPosition); ScrollTo(-targetScrollPosition); } else if(!mScrolling) { // Snap mode, only respond to the event when the previous snap animation is finished. - ScrollTo(GetCurrentPage() - event.z); + ScrollTo(GetCurrentPage() - event.GetDelta()); } } else { // If the ruler in the Y axis is enabled, scroll in the Y axis. - if(mRulerY->GetType() == Ruler::Free) + if(mRulerY->GetType() == Ruler::FREE) { // Free panning mode - targetScrollPosition.y += event.z * mWheelScrollDistanceStep.y; + targetScrollPosition.y += event.GetDelta() * mWheelScrollDistanceStep.y; ClampPosition(targetScrollPosition); ScrollTo(-targetScrollPosition); } else if(!mScrolling) { // Snap mode, only respond to the event when the previous snap animation is finished. - ScrollTo(GetCurrentPage() - event.z * mRulerX->GetTotalPages()); + ScrollTo(GetCurrentPage() - event.GetDelta() * mRulerX->GetTotalPages()); } } @@ -2177,17 +1628,17 @@ bool ScrollView::OnWheelEvent(const WheelEvent& event) void ScrollView::ResetScrolling() { Actor self = Self(); - self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ).Get( mScrollPostPosition ); + self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition); mScrollPrePosition = mScrollPostPosition; - DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y ); + DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPostPosition.x, mScrollPostPosition.y); self.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPostPosition); } void ScrollView::UpdateLocalScrollProperties() { Actor self = Self(); - self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get( mScrollPrePosition ); - self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_POSITION ).Get( mScrollPostPosition ); + self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get(mScrollPrePosition); + self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_POSITION).Get(mScrollPostPosition); } // private functions @@ -2204,13 +1655,13 @@ void ScrollView::PreAnimatedScrollSetup() mDomainOffset += deltaPosition - mScrollPostPosition; Self().SetProperty(Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET, mDomainOffset); - if( mScrollStateFlags & SCROLL_X_STATE_MASK ) + if(mScrollStateFlags & SCROLL_X_STATE_MASK) { // already performing animation on internal x position StopAnimation(mInternalXAnimation); } - if( mScrollStateFlags & SCROLL_Y_STATE_MASK ) + if(mScrollStateFlags & SCROLL_Y_STATE_MASK) { // already performing animation on internal y position StopAnimation(mInternalYAnimation); @@ -2226,18 +1677,18 @@ void ScrollView::FinaliseAnimatedScroll() // TODO - common animation finishing code in here } -void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFunction alpha ) +void ScrollView::AnimateInternalXTo(float position, float duration, AlphaFunction alpha) { StopAnimation(mInternalXAnimation); - if( duration > Math::MACHINE_EPSILON_10 ) + if(duration > Math::MACHINE_EPSILON_10) { Actor self = Self(); - DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get< Vector2 >().x, position ); + DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get().x, position); mInternalXAnimation = Animation::New(duration); - DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr() ); + DALI_LOG_SCROLL_STATE("[0x%X], mInternalXAnimation[0x%X]", this, mInternalXAnimation.GetObjectPtr()); mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished); - mInternalXAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration)); + mInternalXAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 0), position, alpha, TimePeriod(duration)); mInternalXAnimation.Play(); // erase current state flags @@ -2247,18 +1698,18 @@ void ScrollView::AnimateInternalXTo( float position, float duration, AlphaFuncti } } -void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFunction alpha ) +void ScrollView::AnimateInternalYTo(float position, float duration, AlphaFunction alpha) { StopAnimation(mInternalYAnimation); - if( duration > Math::MACHINE_EPSILON_10 ) + if(duration > Math::MACHINE_EPSILON_10) { Actor self = Self(); - DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ).Get< Vector2 >().y, position ); + DALI_LOG_SCROLL_STATE("[0x%X], Animating from[%.2f] to[%.2f]", this, self.GetCurrentProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION).Get().y, position); mInternalYAnimation = Animation::New(duration); - DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr() ); + DALI_LOG_SCROLL_STATE("[0x%X], mInternalYAnimation[0x%X]", this, mInternalYAnimation.GetObjectPtr()); mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnScrollAnimationFinished); - mInternalYAnimation.AnimateTo( Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration)); + mInternalYAnimation.AnimateTo(Property(self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, 1), position, alpha, TimePeriod(duration)); mInternalYAnimation.Play(); // erase current state flags @@ -2268,52 +1719,52 @@ void ScrollView::AnimateInternalYTo( float position, float duration, AlphaFuncti } } -void ScrollView::OnScrollAnimationFinished( Animation& source ) +void ScrollView::OnScrollAnimationFinished(Animation& source) { // Guard against destruction during signal emission // Note that ScrollCompletedSignal is emitted from HandleSnapAnimationFinished() - Toolkit::ScrollView handle( GetOwner() ); + Toolkit::ScrollView handle(GetOwner()); bool scrollingFinished = false; // update our local scroll positions UpdateLocalScrollProperties(); - if( source == mInternalXAnimation ) + if(source == mInternalXAnimation) { - DALI_LOG_SCROLL_STATE("[0x%X] mInternalXAnimation[0x%X], expected[%.2f], actual[%.2f], post[%.2f]", this, mInternalXAnimation.GetObjectPtr(), mScrollTargetPosition.x, Self().GetCurrentProperty( SCROLL_PRE_POSITION ).Get< Vector2 >().x, mScrollPostPosition.x ); + DALI_LOG_SCROLL_STATE("[0x%X] mInternalXAnimation[0x%X], expected[%.2f], actual[%.2f], post[%.2f]", this, mInternalXAnimation.GetObjectPtr(), mScrollTargetPosition.x, Self().GetCurrentProperty(SCROLL_PRE_POSITION).Get().x, mScrollPostPosition.x); - if( !(mScrollStateFlags & AnimatingInternalY) ) + if(!(mScrollStateFlags & AnimatingInternalY)) { scrollingFinished = true; } mInternalXAnimation.Reset(); // wrap pre scroll x position and set it - if( mWrapMode ) + if(mWrapMode) { const RulerDomain rulerDomain = mRulerX->GetDomain(); - mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max); - DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y ); + mScrollPrePosition.x = -WrapInDomain(-mScrollPrePosition.x, rulerDomain.min, rulerDomain.max); + DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y); handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition); } SnapInternalXTo(mScrollPostPosition.x); } - if( source == mInternalYAnimation ) + if(source == mInternalYAnimation) { - DALI_LOG_SCROLL_STATE("[0x%X] mInternalYAnimation[0x%X], expected[%.2f], actual[%.2f], post[%.2f]", this, mInternalYAnimation.GetObjectPtr(), mScrollTargetPosition.y, DevelHandle::GetProperty( Self(), SCROLL_PRE_POSITION ).Get< Vector2 >().y, mScrollPostPosition.y ); + DALI_LOG_SCROLL_STATE("[0x%X] mInternalYAnimation[0x%X], expected[%.2f], actual[%.2f], post[%.2f]", this, mInternalYAnimation.GetObjectPtr(), mScrollTargetPosition.y, DevelHandle::GetProperty(Self(), SCROLL_PRE_POSITION).Get().y, mScrollPostPosition.y); - if( !(mScrollStateFlags & AnimatingInternalX) ) + if(!(mScrollStateFlags & AnimatingInternalX)) { scrollingFinished = true; } mInternalYAnimation.Reset(); - if( mWrapMode ) + if(mWrapMode) { // wrap pre scroll y position and set it const RulerDomain rulerDomain = mRulerY->GetDomain(); - mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max); - DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y ); + mScrollPrePosition.y = -WrapInDomain(-mScrollPrePosition.y, rulerDomain.min, rulerDomain.max); + DALI_LOG_SCROLL_STATE("[0x%X] Setting SCROLL_PRE_POSITION To[%.2f, %.2f]", this, mScrollPrePosition.x, mScrollPrePosition.y); handle.SetProperty(Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, mScrollPrePosition); } SnapInternalYTo(mScrollPostPosition.y); @@ -2327,22 +1778,22 @@ void ScrollView::OnScrollAnimationFinished( Animation& source ) } } -void ScrollView::OnSnapInternalPositionFinished( Animation& source ) +void ScrollView::OnSnapInternalPositionFinished(Animation& source) { Actor self = Self(); UpdateLocalScrollProperties(); - if( source == mInternalXAnimation ) + if(source == mInternalXAnimation) { - DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this ); + DALI_LOG_SCROLL_STATE("[0x%X] Finished X PostPosition Animation", this); // clear internal x animation flags mScrollStateFlags &= ~SCROLL_X_STATE_MASK; mInternalXAnimation.Reset(); WrapPosition(mScrollPrePosition); } - if( source == mInternalYAnimation ) + if(source == mInternalYAnimation) { - DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this ); + DALI_LOG_SCROLL_STATE("[0x%X] Finished Y PostPosition Animation", this); mScrollStateFlags &= ~SCROLL_Y_STATE_MASK; mInternalYAnimation.Reset(); @@ -2361,10 +1812,10 @@ void ScrollView::SnapInternalXTo(float position) // if internal x not equal to inputed parameter, animate it float duration = std::min(fabsf((position - mScrollPrePosition.x) / mMaxOvershoot.x) * mSnapOvershootDuration, mSnapOvershootDuration); - DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration ); - if( duration > Math::MACHINE_EPSILON_1 ) + DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration); + if(duration > Math::MACHINE_EPSILON_1) { - DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position ); + DALI_LOG_SCROLL_STATE("[0x%X] Starting X Snap Animation to[%.2f]", this, position); mInternalXAnimation = Animation::New(duration); mInternalXAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished); @@ -2387,10 +1838,10 @@ void ScrollView::SnapInternalYTo(float position) // if internal y not equal to inputed parameter, animate it float duration = std::min(fabsf((position - mScrollPrePosition.y) / mMaxOvershoot.y) * mSnapOvershootDuration, mSnapOvershootDuration); - DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration ); - if( duration > Math::MACHINE_EPSILON_1 ) + DALI_LOG_SCROLL_STATE("[0x%X] duration[%.2f]", this, duration); + if(duration > Math::MACHINE_EPSILON_1) { - DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position ); + DALI_LOG_SCROLL_STATE("[0x%X] Starting Y Snap Animation to[%.2f]", this, position); mInternalYAnimation = Animation::New(duration); mInternalYAnimation.FinishedSignal().Connect(this, &ScrollView::OnSnapInternalPositionFinished); @@ -2407,23 +1858,23 @@ void ScrollView::GestureStarted() // we handle the first gesture. // if we're currently doing a gesture and receive another // we continue and combine the effects of the gesture instead of reseting. - if(mGestureStackDepth++==0) + if(mGestureStackDepth++ == 0) { Actor self = Self(); StopTouchDownTimer(); StopAnimation(); - mPanDelta = Vector2::ZERO; + mPanDelta = Vector2::ZERO; mLastVelocity = Vector2::ZERO; - if( !mScrolling ) + if(!mScrolling) { mLockAxis = LockPossible; } - if( mScrollStateFlags & SCROLL_X_STATE_MASK ) + if(mScrollStateFlags & SCROLL_X_STATE_MASK) { StopAnimation(mInternalXAnimation); } - if( mScrollStateFlags & SCROLL_Y_STATE_MASK ) + if(mScrollStateFlags & SCROLL_Y_STATE_MASK) { StopAnimation(mInternalYAnimation); } @@ -2436,18 +1887,18 @@ void ScrollView::GestureStarted() // send negative scroll position since scroll internal scroll position works as an offset for actors, // give applications the position within the domain from the scroll view's anchor position DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 5 [%.2f, %.2f]", this, -mScrollPostPosition.x, -mScrollPostPosition.y); - mScrollCompletedSignal.Emit( -mScrollPostPosition ); + mScrollCompletedSignal.Emit(-mScrollPostPosition); } } } void ScrollView::GestureContinuing(const Vector2& panDelta) { - mPanDelta.x+= panDelta.x; - mPanDelta.y+= panDelta.y; + mPanDelta.x += panDelta.x; + mPanDelta.y += panDelta.y; // Save the velocity, there is a bug in PanGesture - // Whereby the Gesture::Finished's velocity is either: + // Whereby the GestureState::FINISHED's velocity is either: // NaN (due to time delta of zero between the last two events) // or 0 (due to position being the same between the last two events) @@ -2460,13 +1911,13 @@ void ScrollView::GestureContinuing(const Vector2& panDelta) } // TODO: Upgrade to use a more powerful gesture detector (one that supports multiple touches on pan - so works as pan and flick gesture) -// BUG: Gesture::Finished doesn't always return velocity on release (due to +// BUG: GestureState::FINISHED doesn't always return velocity on release (due to // timeDelta between last two events being 0 sometimes, or posiiton being the same) -void ScrollView::OnPan( const PanGesture& gesture ) +void ScrollView::OnPan(const PanGesture& gesture) { // Guard against destruction during signal emission // Note that Emit() methods are called indirectly e.g. from within ScrollView::OnGestureEx() - Actor self( Self() ); + Actor self(Self()); if(!mSensitive) { @@ -2478,27 +1929,28 @@ void ScrollView::OnPan( const PanGesture& gesture ) } // translate Gesture input to get useful data... - switch(gesture.state) + switch(gesture.GetState()) { - case Gesture::Started: + case GestureState::STARTED: { DALI_LOG_SCROLL_STATE("[0x%X] Pan Started", this); - mPanStartPosition = gesture.position - gesture.displacement; + const Vector2& position = gesture.GetPosition(); + mPanStartPosition = position - gesture.GetDisplacement(); UpdateLocalScrollProperties(); GestureStarted(); mPanning = true; - self.SetProperty( Toolkit::ScrollView::Property::PANNING, true ); - self.SetProperty( Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(gesture.position.x, gesture.position.y, 0.0f) ); + self.SetProperty(Toolkit::ScrollView::Property::PANNING, true); + self.SetProperty(Toolkit::ScrollView::Property::START_PAGE_POSITION, Vector3(position.x, position.y, 0.0f)); - UpdateMainInternalConstraint(); + mConstraints.UpdateMainInternalConstraint(*this); Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle(); - if( scrollBar && mTransientScrollBar ) + if(scrollBar && mTransientScrollBar) { - Vector3 size = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); + Vector3 size = Self().GetCurrentProperty(Actor::Property::SIZE); const Toolkit::RulerDomain& rulerDomainX = mRulerX->GetDomain(); const Toolkit::RulerDomain& rulerDomainY = mRulerY->GetDomain(); - if( ( rulerDomainX.max > size.width ) || ( rulerDomainY.max > size.height ) ) + if((rulerDomainX.max > size.width) || (rulerDomainY.max > size.height)) { scrollBar.ShowIndicator(); } @@ -2506,12 +1958,12 @@ void ScrollView::OnPan( const PanGesture& gesture ) break; } - case Gesture::Continuing: + case GestureState::CONTINUING: { - if ( mPanning ) + if(mPanning) { DALI_LOG_SCROLL_STATE("[0x%X] Pan Continuing", this); - GestureContinuing(gesture.screenDisplacement); + GestureContinuing(gesture.GetScreenDisplacement()); } else { @@ -2521,25 +1973,25 @@ void ScrollView::OnPan( const PanGesture& gesture ) break; } - case Gesture::Finished: - case Gesture::Cancelled: + case GestureState::FINISHED: + case GestureState::CANCELLED: { - if ( mPanning ) + if(mPanning) { - DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ( ( gesture.state == Gesture::Finished ) ? "Finished" : "Cancelled" ) ); + DALI_LOG_SCROLL_STATE("[0x%X] Pan %s", this, ((gesture.GetState() == GestureState::FINISHED) ? "Finished" : "Cancelled")); UpdateLocalScrollProperties(); - mLastVelocity = gesture.velocity; - mPanning = false; - self.SetProperty( Toolkit::ScrollView::Property::PANNING, false ); + mLastVelocity = gesture.GetVelocity(); + mPanning = false; + self.SetProperty(Toolkit::ScrollView::Property::PANNING, false); - if( mScrollMainInternalPrePositionConstraint ) + if(mConstraints.mScrollMainInternalPrePositionConstraint) { - mScrollMainInternalPrePositionConstraint.Remove(); + mConstraints.mScrollMainInternalPrePositionConstraint.Remove(); } Toolkit::ScrollBar scrollBar = mScrollBar.GetHandle(); - if( scrollBar && mTransientScrollBar ) + if(scrollBar && mTransientScrollBar) { scrollBar.HideIndicator(); } @@ -2552,8 +2004,8 @@ void ScrollView::OnPan( const PanGesture& gesture ) break; } - case Gesture::Possible: - case Gesture::Clear: + case GestureState::POSSIBLE: + case GestureState::CLEAR: { // Nothing to do, not needed. break; @@ -2561,34 +2013,33 @@ void ScrollView::OnPan( const PanGesture& gesture ) } // end switch(gesture.state) - OnGestureEx(gesture.state); + OnGestureEx(gesture.GetState()); } -void ScrollView::OnGestureEx(Gesture::State state) +void ScrollView::OnGestureEx(GestureState state) { // call necessary signals for application developer - if(state == Gesture::Started) + if(state == GestureState::STARTED) { Vector2 currentScrollPosition = GetCurrentScrollPosition(); Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, true); mScrolling = true; DALI_LOG_SCROLL_STATE("[0x%X] mScrollStartedSignal 2 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y); - mScrollStartedSignal.Emit( currentScrollPosition ); + mScrollStartedSignal.Emit(currentScrollPosition); } - else if( (state == Gesture::Finished) || - (state == Gesture::Cancelled) ) // Finished/default + else if((state == GestureState::FINISHED) || + (state == GestureState::CANCELLED)) // Finished/default { // when all the gestures have finished, we finish the transform. // so if a user decides to pan (1 gesture), and then pan+zoom (2 gestures) // then stop panning (back to 1 gesture), and then stop zooming (0 gestures). // this is the point we end, and perform necessary snapping. mGestureStackDepth--; - if(mGestureStackDepth==0) + if(mGestureStackDepth == 0) { // no flick if we have not exceeded min flick distance - if( (fabsf(mPanDelta.x) < mMinFlickDistance.x) - && (fabsf(mPanDelta.y) < mMinFlickDistance.y) ) + if((fabsf(mPanDelta.x) < mMinFlickDistance.x) && (fabsf(mPanDelta.y) < mMinFlickDistance.y)) { // reset flick velocity mLastVelocity = Vector2::ZERO; @@ -2619,57 +2070,18 @@ void ScrollView::FinishTransform() mScrolling = false; Self().SetProperty(Toolkit::ScrollView::Property::SCROLLING, false); - if( fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10 ) + if(fabs(mScrollPrePosition.x - mScrollTargetPosition.x) > Math::MACHINE_EPSILON_10) { SnapInternalXTo(mScrollTargetPosition.x); } - if( fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10 ) + if(fabs(mScrollPrePosition.y - mScrollTargetPosition.y) > Math::MACHINE_EPSILON_10) { SnapInternalYTo(mScrollTargetPosition.y); } Vector2 currentScrollPosition = GetCurrentScrollPosition(); DALI_LOG_SCROLL_STATE("[0x%X] mScrollCompletedSignal 6 [%.2f, %.2f]", this, currentScrollPosition.x, currentScrollPosition.y); - mScrollCompletedSignal.Emit( currentScrollPosition ); - } -} - -Vector2 ScrollView::GetOvershoot(Vector2& position) const -{ - Vector3 size = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); - Vector2 overshoot; - - const RulerDomain rulerDomainX = mRulerX->GetDomain(); - const RulerDomain rulerDomainY = mRulerY->GetDomain(); - - if(mRulerX->IsEnabled() && rulerDomainX.enabled) - { - const float left = rulerDomainX.min - position.x; - const float right = size.width - rulerDomainX.max - position.x; - if(left<0) - { - overshoot.x = left; - } - else if(right>0) - { - overshoot.x = right; - } - } - - if(mRulerY->IsEnabled() && rulerDomainY.enabled) - { - const float top = rulerDomainY.min - position.y; - const float bottom = size.height - rulerDomainY.max - position.y; - if(top<0) - { - overshoot.y = top; - } - else if(bottom>0) - { - overshoot.y = bottom; - } + mScrollCompletedSignal.Emit(currentScrollPosition); } - - return overshoot; } bool ScrollView::OnAccessibilityPan(PanGesture gesture) @@ -2688,12 +2100,11 @@ void ScrollView::ClampPosition(Vector2& position) const ClampPosition(position, clamped); } -void ScrollView::ClampPosition(Vector2& position, ClampState2D &clamped) const +void ScrollView::ClampPosition(Vector2& position, ClampState2D& clamped) const { - Vector3 size = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ); + Vector3 size = Self().GetCurrentProperty(Actor::Property::SIZE); - position.x = -mRulerX->Clamp(-position.x, size.width, 1.0f, clamped.x); // NOTE: X & Y rulers think in -ve coordinate system. - position.y = -mRulerY->Clamp(-position.y, size.height, 1.0f, clamped.y); // That is scrolling RIGHT (e.g. 100.0, 0.0) means moving LEFT. + ::ClampPosition(size, mRulerX, mRulerY, position, clamped); } void ScrollView::WrapPosition(Vector2& position) const @@ -2715,330 +2126,39 @@ void ScrollView::WrapPosition(Vector2& position) const } } -void ScrollView::UpdateMainInternalConstraint() -{ - // TODO: Only update the constraints which have changed, rather than remove all and add all again. - // Requires a dali-core ApplyConstraintAt, or a ReplaceConstraint. The former is probably more flexible. - Actor self = Self(); - PanGestureDetector detector( GetPanGestureDetector() ); - - if(mScrollMainInternalPositionConstraint) - { - mScrollMainInternalPositionConstraint.Remove(); - mScrollMainInternalDeltaConstraint.Remove(); - mScrollMainInternalFinalConstraint.Remove(); - mScrollMainInternalRelativeConstraint.Remove(); - mScrollMainInternalDomainConstraint.Remove(); - mScrollMainInternalPrePositionMaxConstraint.Remove(); - } - if( mScrollMainInternalPrePositionConstraint ) - { - mScrollMainInternalPrePositionConstraint.Remove(); - } - - // TODO: It's probably better to use a local displacement value as this will give a displacement when scrolling just commences - // but we need to make sure than the gesture system gives displacement since last frame (60Hz), not displacement since last touch event (90Hz). - - // 1. First calculate the pre-position (this is the scroll position if no clamping has taken place) - Vector2 initialPanMask = Vector2(mRulerX->IsEnabled() ? 1.0f : 0.0f, mRulerY->IsEnabled() ? 1.0f : 0.0f); - - if( mLockAxis == LockVertical ) - { - initialPanMask.y = 0.0f; - } - else if( mLockAxis == LockHorizontal ) - { - initialPanMask.x = 0.0f; - } - - if( mPanning ) - { - mScrollMainInternalPrePositionConstraint = Constraint::New( self, - Toolkit::ScrollView::Property::SCROLL_PRE_POSITION, - InternalPrePositionConstraint( mPanStartPosition, - initialPanMask, - mAxisAutoLock, - mAxisAutoLockGradient, - mLockAxis, - mMaxOvershoot, - mRulerX, - mRulerY ) ); - mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::LOCAL_POSITION ) ); - mScrollMainInternalPrePositionConstraint.AddSource( Source( detector, PanGestureDetector::Property::PANNING ) ); - mScrollMainInternalPrePositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) ); - mScrollMainInternalPrePositionConstraint.Apply(); - } - - // 2. Second calculate the clamped position (actual position) - mScrollMainInternalPositionConstraint = Constraint::New( self, - Toolkit::ScrollView::Property::SCROLL_POSITION, - InternalPositionConstraint( mRulerX->GetDomain(), - mRulerY->GetDomain(), - mWrapMode ) ); - mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) ); - mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) ); - mScrollMainInternalPositionConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) ); - mScrollMainInternalPositionConstraint.AddSource( Source( self, Actor::Property::SIZE ) ); - mScrollMainInternalPositionConstraint.Apply(); - - mScrollMainInternalDeltaConstraint = Constraint::New( self, Toolkit::ScrollView::Property::SCROLL_POSITION_DELTA, InternalPositionDeltaConstraint ); - mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) ); - mScrollMainInternalDeltaConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_DOMAIN_OFFSET ) ); - mScrollMainInternalDeltaConstraint.Apply(); - - mScrollMainInternalFinalConstraint = Constraint::New( self, Toolkit::ScrollView::Property::SCROLL_FINAL, - InternalFinalConstraint( FinalDefaultAlphaFunction, - FinalDefaultAlphaFunction ) ); - mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) ); - mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_X ) ); - mScrollMainInternalFinalConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::OVERSHOOT_Y ) ); - mScrollMainInternalFinalConstraint.Apply(); - - mScrollMainInternalRelativeConstraint = Constraint::New( self, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION, InternalRelativePositionConstraint ); - mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) ); - mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) ); - mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) ); - mScrollMainInternalRelativeConstraint.AddSource( LocalSource( Actor::Property::SIZE ) ); - mScrollMainInternalRelativeConstraint.Apply(); - - mScrollMainInternalDomainConstraint = Constraint::New( self, Toolkit::ScrollView::Property::SCROLL_DOMAIN_SIZE, InternalScrollDomainConstraint ); - mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) ); - mScrollMainInternalDomainConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) ); - mScrollMainInternalDomainConstraint.AddSource( LocalSource( Actor::Property::SIZE ) ); - mScrollMainInternalDomainConstraint.Apply(); - - mScrollMainInternalPrePositionMaxConstraint = Constraint::New( self, Toolkit::ScrollView::Property::SCROLL_PRE_POSITION_MAX, InternalPrePositionMaxConstraint ); - mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) ); - mScrollMainInternalPrePositionMaxConstraint.AddSource( LocalSource( Actor::Property::SIZE ) ); - mScrollMainInternalPrePositionMaxConstraint.Apply(); - - // When panning we want to make sure overshoot values are affected by pre position and post position - SetOvershootConstraintsEnabled(!mWrapMode); -} - -void ScrollView::SetOvershootConstraintsEnabled(bool enabled) -{ - Actor self( Self() ); - // remove and reset, it may now be in wrong order with the main internal constraints - if( mScrollMainInternalOvershootXConstraint ) - { - mScrollMainInternalOvershootXConstraint.Remove(); - mScrollMainInternalOvershootXConstraint.Reset(); - mScrollMainInternalOvershootYConstraint.Remove(); - mScrollMainInternalOvershootYConstraint.Reset(); - } - if( enabled ) - { - mScrollMainInternalOvershootXConstraint= Constraint::New( self, Toolkit::ScrollView::Property::OVERSHOOT_X, OvershootXConstraint(mMaxOvershoot.x) ); - mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) ); - mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) ); - mScrollMainInternalOvershootXConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL ) ); - mScrollMainInternalOvershootXConstraint.Apply(); - - mScrollMainInternalOvershootYConstraint = Constraint::New( self, Toolkit::ScrollView::Property::OVERSHOOT_Y, OvershootYConstraint(mMaxOvershoot.y) ); - mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_PRE_POSITION ) ); - mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::ScrollView::Property::SCROLL_POSITION ) ); - mScrollMainInternalOvershootYConstraint.AddSource( LocalSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) ); - mScrollMainInternalOvershootYConstraint.Apply(); - } - else - { - self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_X, 0.0f); - self.SetProperty(Toolkit::ScrollView::Property::OVERSHOOT_Y, 0.0f); - } -} - -void ScrollView::SetInternalConstraints() -{ - // Internal constraints (applied to target ScrollBase Actor itself) ///////// - UpdateMainInternalConstraint(); - - // User definable constraints to apply to all child actors ////////////////// - Actor self = Self(); - - // Apply some default constraints to ScrollView & its bound actors - // Movement + Wrap function - - Constraint constraint; - - // MoveActor (scrolling) - constraint = Constraint::New( self, Actor::Property::POSITION, MoveActorConstraint ); - constraint.AddSource( Source( self, Toolkit::ScrollView::Property::SCROLL_POSITION ) ); - constraint.SetRemoveAction(Constraint::Discard); - ApplyConstraintToBoundActors(constraint); - - // WrapActor (wrap functionality) - constraint = Constraint::New( self, Actor::Property::POSITION, WrapActorConstraint ); - constraint.AddSource( LocalSource( Actor::Property::SCALE ) ); - constraint.AddSource( LocalSource( Actor::Property::ANCHOR_POINT ) ); - constraint.AddSource( LocalSource( Actor::Property::SIZE ) ); - constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) ); - constraint.AddSource( Source( self, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) ); - constraint.AddSource( Source( self, Toolkit::ScrollView::Property::WRAP ) ); - constraint.SetRemoveAction(Constraint::Discard); - ApplyConstraintToBoundActors(constraint); -} - -void ScrollView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ) +void ScrollView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value) { - Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast( Dali::BaseHandle( object ) ); - - if( scrollView ) - { - ScrollView& scrollViewImpl( GetImpl( scrollView ) ); - switch( index ) - { - case Toolkit::ScrollView::Property::WRAP_ENABLED: - { - scrollViewImpl.SetWrapMode( value.Get() ); - break; - } - case Toolkit::ScrollView::Property::PANNING_ENABLED: - { - scrollViewImpl.SetScrollSensitive( value.Get() ); - break; - } - case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED: - { - scrollViewImpl.SetAxisAutoLock( value.Get() ); - break; - } - case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP: - { - scrollViewImpl.SetWheelScrollDistanceStep( value.Get() ); - break; - } - case Toolkit::ScrollView::Property::SCROLL_MODE: - { - Property::Map* map = value.GetMap(); - if( map ) - { - scrollViewImpl.SetScrollMode( *map ); - } - } - } - } + ScrollViewPropertyHandler::Set(object, index, value); } -Property::Value ScrollView::GetProperty( BaseObject* object, Property::Index index ) +Property::Value ScrollView::GetProperty(BaseObject* object, Property::Index index) { - Property::Value value; - - Toolkit::ScrollView scrollView = Toolkit::ScrollView::DownCast( Dali::BaseHandle( object ) ); - - if( scrollView ) - { - ScrollView& scrollViewImpl( GetImpl( scrollView ) ); - switch( index ) - { - case Toolkit::ScrollView::Property::WRAP_ENABLED: - { - value = scrollViewImpl.GetWrapMode(); - break; - } - case Toolkit::ScrollView::Property::PANNING_ENABLED: - { - value = scrollViewImpl.GetScrollSensitive(); - break; - } - case Toolkit::ScrollView::Property::AXIS_AUTO_LOCK_ENABLED: - { - value = scrollViewImpl.GetAxisAutoLock(); - break; - } - case Toolkit::ScrollView::Property::WHEEL_SCROLL_DISTANCE_STEP: - { - value = scrollViewImpl.GetWheelScrollDistanceStep(); - break; - } - } - } - - return value; + return ScrollViewPropertyHandler::Get(object, index); } -void ScrollView::SetScrollMode( const Property::Map& scrollModeMap ) +ScrollView::LockAxis GetLockAxis(const Vector2& panDelta, ScrollView::LockAxis currentLockAxis, float lockGradient) { - Toolkit::RulerPtr rulerX, rulerY; - - // Check the scroll mode in the X axis - bool xAxisScrollEnabled = true; - Property::Value* valuePtr = scrollModeMap.Find( Toolkit::ScrollMode::X_AXIS_SCROLL_ENABLED, "xAxisScrollEnabled" ); - if( valuePtr && valuePtr->GetType() == Property::BOOLEAN ) - { - valuePtr->Get( xAxisScrollEnabled ); - } - - if( !xAxisScrollEnabled ) - { - // Default ruler and disabled - rulerX = new Toolkit::DefaultRuler(); - rulerX->Disable(); - } - else + if(panDelta.LengthSquared() > AUTOLOCK_AXIS_MINIMUM_DISTANCE2 && + currentLockAxis == ScrollView::LockPossible) { - valuePtr = scrollModeMap.Find( Toolkit::ScrollMode::X_AXIS_SNAP_TO_INTERVAL, "xAxisSnapToInterval" ); - float xAxisSnapToInterval = 0.0f; - if( valuePtr && valuePtr->Get( xAxisSnapToInterval ) ) - { - // Fixed ruler and enabled - rulerX = new Toolkit::FixedRuler( xAxisSnapToInterval ); - } - else - { - // Default ruler and enabled - rulerX = new Toolkit::DefaultRuler(); - } - - valuePtr = scrollModeMap.Find( Toolkit::ScrollMode::X_AXIS_SCROLL_BOUNDARY, "xAxisScrollBoundary" ); - float xAxisScrollBoundary = 0.0f; - if( valuePtr && valuePtr->Get( xAxisScrollBoundary ) ) + float dx = fabsf(panDelta.x); + float dy = fabsf(panDelta.y); + if(dx * lockGradient >= dy) { - // By default ruler domain is disabled unless set - rulerX->SetDomain( Toolkit::RulerDomain( 0, xAxisScrollBoundary, true ) ); + // 0.36:1 gradient to the horizontal (deviate < 20 degrees) + currentLockAxis = ScrollView::LockVertical; } - } - - // Check the scroll mode in the Y axis - bool yAxisScrollEnabled = true; - valuePtr = scrollModeMap.Find( Toolkit::ScrollMode::Y_AXIS_SCROLL_ENABLED, "yAxisScrollEnabled" ); - if( valuePtr && valuePtr->GetType() == Property::BOOLEAN ) - { - valuePtr->Get( yAxisScrollEnabled ); - } - - if( !yAxisScrollEnabled ) - { - // Default ruler and disabled - rulerY = new Toolkit::DefaultRuler(); - rulerY->Disable(); - } - else - { - valuePtr = scrollModeMap.Find( Toolkit::ScrollMode::Y_AXIS_SNAP_TO_INTERVAL, "yAxisSnapToInterval" ); - float yAxisSnapToInterval = 0.0f; - if( valuePtr && valuePtr->Get( yAxisSnapToInterval ) ) + else if(dy * lockGradient > dx) { - // Fixed ruler and enabled - rulerY = new Toolkit::FixedRuler(yAxisSnapToInterval); + // 0.36:1 gradient to the vertical (deviate < 20 degrees) + currentLockAxis = ScrollView::LockHorizontal; } else { - // Default ruler and enabled - rulerY = new Toolkit::DefaultRuler(); - } - - valuePtr = scrollModeMap.Find( Toolkit::ScrollMode::Y_AXIS_SCROLL_BOUNDARY, "yAxisScrollBoundary" ); - float yAxisScrollBoundary = 0.0f; - if( valuePtr && valuePtr->Get( yAxisScrollBoundary ) ) - { - // By default ruler domain is disabled unless set - rulerY->SetDomain( Toolkit::RulerDomain( 0, yAxisScrollBoundary, true ) ); + currentLockAxis = ScrollView::LockNone; } } - - SetRulerX(rulerX); - SetRulerY(rulerY); + return currentLockAxis; } } // namespace Internal