X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fscrollable%2Fitem-view%2Fitem-view-impl.cpp;h=e41568b0695052fad67bca870215f5996e33de47;hp=d38f69c3b21c8baa73e9568909b4140b9141eeb8;hb=refs%2Ftags%2Faccepted%2Ftizen%2Fcommon%2F20150309.091951;hpb=0e2183dc02211556cd97479a6352e9ac291c5957 diff --git a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp index d38f69c..e41568b 100644 --- a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp @@ -1,56 +1,69 @@ -// -// Copyright (c) 2014 Samsung Electronics Co., Ltd. -// -// Licensed under the Flora License, Version 1.0 (the License); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://floralicense.org/license/ -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an AS IS BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// +/* + * Copyright (c) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ // CLASS HEADER #include // EXTERNAL INCLUDES #include +#include +#include +#include +#include +#include +#include +#include +#include +#include // INTERNAL INCLUDES -#include #include +#include #include -using namespace std; +using std::string; +using std::set; using namespace Dali; -namespace // unnamed namespace +namespace // Unnamed namespace { //Type registration -TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL ); + +DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ItemView, Toolkit::Scrollable, NULL ) +DALI_TYPE_REGISTRATION_END() const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f; const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f; const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f; -const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; // 20 updates per second +const float DEFAULT_MINIMUM_SWIPE_DURATION = 0.45f; +const float DEFAULT_MAXIMUM_SWIPE_DURATION = 2.6f; + +const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second -const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second const float MILLISECONDS_PER_SECONDS = 1000.0f; -const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png"; -const Rect OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 ); +const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f ); +const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f; const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f); -const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels -const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f; // 0.5 second const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f; const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" ); @@ -60,84 +73,6 @@ const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" ); const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" ); const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" ); -// Functors which wrap constraint functions with stored item IDs - -struct WrappedVector3Constraint -{ - WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::Vector3Function mWrapMe; - unsigned int mItemId; -}; - -struct WrappedQuaternionConstraint -{ - WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::QuaternionFunction mWrapMe; - unsigned int mItemId; -}; - -struct WrappedVector4Constraint -{ - WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::Vector4Function mWrapMe; - unsigned int mItemId; -}; - -struct WrappedBoolConstraint -{ - WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::BoolFunction mWrapMe; - unsigned int mItemId; -}; - /** * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction */ @@ -155,7 +90,7 @@ float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout) struct OvershootOverlaySizeConstraint { - float operator()(const float& current, + Vector3 operator()(const Vector3& current, const PropertyInput& parentScrollDirectionProperty, const PropertyInput& parentOvershootProperty, const PropertyInput& parentSizeProperty) @@ -175,7 +110,9 @@ struct OvershootOverlaySizeConstraint overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x; } - return overlayWidth; + float overlayHeight = (overlayWidth > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height*0.5f; + + return Vector3( overlayWidth, overlayHeight, current.depth ); } }; @@ -395,21 +332,28 @@ ItemView::ItemView(ItemFactory& factory) : Scrollable(), mItemFactory(factory), mActiveLayout(NULL), - mDefaultAlphaFunction(Dali::Constraint::DEFAULT_ALPHA_FUNCTION), mAnimatingOvershootOn(false), mAnimateOvershootOff(false), mAnchoringEnabled(true), mAnchoringDuration(DEFAULT_ANCHORING_DURATION), - mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS), + mRefreshIntervalLayoutPositions(0.0f), mRefreshOrderHint(true/*Refresh item 0 first*/), mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED), mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE), + mMouseWheelScrollDistanceStep(0.0f), mScrollDistance(0.0f), mScrollSpeed(0.0f), mTotalPanDisplacement(Vector2::ZERO), mScrollOvershoot(0.0f), mIsFlicking(false), - mGestureState(Gesture::Clear) + mGestureState(Gesture::Clear), + mAddingItems(false), + mPropertyPosition(Property::INVALID_INDEX), + mPropertyMinimumLayoutPosition(Property::INVALID_INDEX), + mPropertyScrollSpeed(Property::INVALID_INDEX), + mRefreshEnabled(true), + mItemsParentOrigin( ParentOrigin::CENTER), + mItemsAnchorPoint( AnchorPoint::CENTER) { SetRequiresMouseWheelEvents(true); SetKeyboardNavigationSupport(true); @@ -423,31 +367,21 @@ void ItemView::OnInitialize() Actor self = Self(); - mOvershootEffect = OvershootRippleEffect::New(); - Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH ); - mOvershootOverlay = ImageActor::New( overshootImage ); - mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT); - mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT); - mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY); - mOvershootOverlay.SetShaderEffect(mOvershootEffect); - mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA); - self.Add(mOvershootOverlay); - mScrollConnector = Dali::Toolkit::ScrollConnector::New(); mScrollPositionObject = mScrollConnector.GetScrollPositionObject(); + mScrollConnector.ScrollPositionChangedSignal().Connect( this, &ItemView::OnScrollPositionChanged ); mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f); mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f); mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f); - mPropertyOvershoot = self.RegisterProperty(OVERSHOOT_PROPERTY_NAME, 0.0f); - ApplyOvershootOverlayConstraints(); + EnableScrollComponent(Toolkit::Scrollable::OvershootIndicator); Constraint constraint = Constraint::New(mPropertyRelativePosition, LocalSource(mPropertyPosition), LocalSource(mPropertyPositionMin), LocalSource(mPropertyPositionMax), - LocalSource(Actor::SIZE), + LocalSource(Actor::Property::SIZE), RelativePositionConstraint); self.ApplyConstraint(constraint); @@ -458,6 +392,8 @@ void ItemView::OnInitialize() mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT ); mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished ); + + SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS); } ItemView::~ItemView() @@ -503,13 +439,15 @@ ItemLayoutPtr ItemView::GetActiveLayout() const float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const { - return mScrollPositionObject.GetProperty( ScrollConnector::SCROLL_POSITION ) + static_cast( itemId ); + return mScrollConnector.GetScrollPosition() + static_cast( itemId ); } void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds) { DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size()); + mRefreshEnabled = false; + Actor self = Self(); // The ItemView size should match the active layout size @@ -517,34 +455,23 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz mActiveLayoutTargetSize = targetSize; // Switch to the new layout - ItemLayout* previousLayout = mActiveLayout; mActiveLayout = mLayouts[layoutIndex].Get(); - // Calculate which items are within either layout - ItemRange oldRange = previousLayout ? GetItemRange(*previousLayout, targetSize, false/*don't reserve extra*/) : ItemRange(0u, 0u); - ItemRange newRange = GetItemRange(*mActiveLayout, targetSize, false/*don't reserve extra*/); - // Move the items to the new layout positions... bool resizeAnimationNeeded(false); - for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) { unsigned int itemId = iter->first; Actor actor = iter->second; - // Immediately relayout items that aren't within either layout - bool immediate = !oldRange.Within(itemId) && - !newRange.Within(itemId); - // Remove constraints from previous layout actor.RemoveConstraints(); Vector3 size; if(mActiveLayout->GetItemSize(itemId, targetSize, size)) { - if (!immediate && - durationSeconds > 0.0f) + if( durationSeconds > 0.0f ) { // Use a size animation if (!resizeAnimationNeeded) @@ -564,7 +491,7 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz } } - ApplyConstraints(actor, *mActiveLayout, itemId, immediate ? 0.0f : durationSeconds); + mActiveLayout->ApplyConstraints(actor, itemId, durationSeconds, mScrollPositionObject, Self() ); } if (resizeAnimationNeeded) @@ -573,8 +500,8 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz } // Refresh the new layout - ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/); - AddActorsWithinRange( range ); + ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false/* don't reserve extra*/); + AddActorsWithinRange( range, durationSeconds ); // Scroll to an appropriate layout position @@ -599,9 +526,10 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz if (scrollAnimationNeeded) { RemoveAnimation(mScrollAnimation); - mScrollAnimation = Animation::New(mAnchoringDuration); + mScrollAnimation = Animation::New(durationSeconds); mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut ); mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut ); + mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished); mScrollAnimation.Play(); } @@ -630,36 +558,27 @@ void ItemView::DeactivateCurrentLayout() mActiveLayout = NULL; } - - CancelRefreshTimer(); -} - -void ItemView::SetDefaultAlphaFunction(AlphaFunction func) -{ - mDefaultAlphaFunction = func; } -AlphaFunction ItemView::GetDefaultAlphaFunction() const +void ItemView::OnRefreshNotification(PropertyNotification& source) { - return mDefaultAlphaFunction; + if(mRefreshEnabled || mScrollAnimation) + { + // Only refresh the cache during normal scrolling + DoRefresh(GetCurrentLayoutPosition(0), true); + } } -bool ItemView::OnRefreshTick() +void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra) { - // Short-circuit if there is no active layout - if (!mActiveLayout) + if (mActiveLayout) { - return false; - } - - ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, true/*reserve extra*/); + ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/); + RemoveActorsOutsideRange( range ); + AddActorsWithinRange( range, 0.0f/*immediate*/ ); - RemoveActorsOutsideRange( range ); - - AddActorsWithinRange( range ); - - // Keep refreshing whilst the layout is moving - return mScrollAnimation || (mGestureState == Gesture::Started || mGestureState == Gesture::Continuing); + mScrollUpdatedSignal.Emit( Vector3(0.0f, currentLayoutPosition, 0.0f) ); + } } void ItemView::SetMinimumSwipeSpeed(float speed) @@ -712,14 +631,29 @@ float ItemView::GetAnchoringDuration() const return mAnchoringDuration; } -void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds) +void ItemView::SetRefreshInterval(float intervalLayoutPositions) { - mRefreshIntervalMilliseconds = intervalMilliseconds; + if( !Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions) ) + { + mRefreshIntervalLayoutPositions = intervalLayoutPositions; + + if(mRefreshNotification) + { + mScrollPositionObject.RemovePropertyNotification(mRefreshNotification); + } + mRefreshNotification = mScrollPositionObject.AddPropertyNotification( ScrollConnector::SCROLL_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) ); + mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification ); + } } -unsigned int ItemView::GetRefreshInterval() const +float ItemView::GetRefreshInterval() const { - return mRefreshIntervalMilliseconds; + return mRefreshIntervalLayoutPositions; +} + +void ItemView::SetRefreshEnabled(bool enabled) +{ + mRefreshEnabled = enabled; } Actor ItemView::GetItem(unsigned int itemId) const @@ -753,55 +687,82 @@ unsigned int ItemView::GetItemId( Actor actor ) const void ItemView::InsertItem( Item newItem, float durationSeconds ) { - SetupActor( newItem, durationSeconds ); - Self().Add( newItem.second ); + mAddingItems = true; + + Actor displacedActor; + ItemPoolIter afterDisplacedIter = mItemPool.end(); ItemPoolIter foundIter = mItemPool.find( newItem.first ); if( mItemPool.end() != foundIter ) { - Actor moveMe = foundIter->second; + SetupActor( newItem, durationSeconds ); + Self().Add( newItem.second ); + + displacedActor = foundIter->second; foundIter->second = newItem.second; + afterDisplacedIter = ++foundIter; + } + else + { + // Inserting before the existing item range? + ItemPoolIter iter = mItemPool.begin(); + if( iter != mItemPool.end() && + iter->first > newItem.first ) + { + displacedActor = iter->second; + mItemPool.erase( iter++ ); // iter is still valid after the erase + + afterDisplacedIter = iter; + } + } + + if( displacedActor ) + { // Move the existing actors to make room - for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter ) + for( ItemPoolIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter ) { Actor temp = iter->second; - iter->second = moveMe; - moveMe = temp; + iter->second = displacedActor; + displacedActor = temp; iter->second.RemoveConstraints(); - ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds ); + mActiveLayout->ApplyConstraints( iter->second, iter->first, durationSeconds, mScrollPositionObject, Self() ); } // Create last item - ItemId lastId = mItemPool.rbegin()->first; - Item lastItem( lastId + 1, moveMe ); - mItemPool.insert( lastItem ); + ItemPool::reverse_iterator lastIter = mItemPool.rbegin(); + if ( lastIter != mItemPool.rend() ) + { + ItemId lastId = lastIter->first; + Item lastItem( lastId + 1, displacedActor ); + mItemPool.insert( lastItem ); - lastItem.second.RemoveConstraints(); - ApplyConstraints( lastItem.second, *mActiveLayout, lastItem.first, durationSeconds ); - } - else - { - mItemPool.insert( newItem ); + lastItem.second.RemoveConstraints(); + mActiveLayout->ApplyConstraints( lastItem.second, lastItem.first, durationSeconds, mScrollPositionObject, Self() ); + } } + + CalculateDomainSize(Self().GetCurrentSize()); + + mAddingItems = false; } void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds ) { + mAddingItems = true; + // Insert from lowest id to highest - set sortedItems; + std::set sortedItems; for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter ) { sortedItems.insert( *iter ); } - for( set::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter ) + for( std::set::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter ) { Self().Add( iter->second ); - cout << "inserting item: " << iter->first << endl; - ItemPoolIter foundIter = mItemPool.find( iter->first ); if( mItemPool.end() != foundIter ) { @@ -838,23 +799,29 @@ void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds else { iter->second.RemoveConstraints(); - ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds ); + mActiveLayout->ApplyConstraints( iter->second, iter->first, durationSeconds, mScrollPositionObject, Self() ); } } + + CalculateDomainSize(Self().GetCurrentSize()); + + mAddingItems = false; } void ItemView::RemoveItem( unsigned int itemId, float durationSeconds ) { - bool actorRemoved = RemoveActor( itemId ); - if( actorRemoved ) + bool actorsReordered = RemoveActor( itemId ); + if( actorsReordered ) { ReapplyAllConstraints( durationSeconds ); + + OnItemsRemoved(); } } void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds ) { - bool actorRemoved( false ); + bool actorsReordered( false ); // Remove from highest id to lowest set sortedItems; @@ -867,26 +834,44 @@ void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSecond { if( RemoveActor( *iter ) ) { - actorRemoved = true; + actorsReordered = true; } } - if( actorRemoved ) + if( actorsReordered ) { ReapplyAllConstraints( durationSeconds ); + + OnItemsRemoved(); } } bool ItemView::RemoveActor(unsigned int itemId) { - bool removed( false ); + bool reordered( false ); - const ItemPoolIter removeIter = mItemPool.find( itemId ); + ItemPoolIter removeIter = mItemPool.find( itemId ); + if( removeIter != mItemPool.end() ) + { + ReleaseActor(itemId, removeIter->second); + } + else + { + // Removing before the existing item range? + ItemPoolIter iter = mItemPool.begin(); + if( iter != mItemPool.end() && + iter->first > itemId ) + { + // In order to decrement the first visible item ID + mItemPool.insert( Item(iter->first - 1, Actor()) ); + + removeIter = mItemPool.begin(); + } + } if( removeIter != mItemPool.end() ) { - Self().Remove( removeIter->second ); - removed = true; + reordered = true; // Adjust the remaining item IDs, for example if item 2 is removed: // Initial actors: After insert: @@ -908,24 +893,30 @@ bool ItemView::RemoveActor(unsigned int itemId) } } - return removed; + return reordered; } void ItemView::ReplaceItem( Item replacementItem, float durationSeconds ) { + mAddingItems = true; + SetupActor( replacementItem, durationSeconds ); Self().Add( replacementItem.second ); const ItemPoolIter iter = mItemPool.find( replacementItem.first ); if( mItemPool.end() != iter ) { - Self().Remove( iter->second ); + ReleaseActor(iter->first, iter->second); iter->second = replacementItem.second; } else { mItemPool.insert( replacementItem ); } + + CalculateDomainSize(Self().GetCurrentSize()); + + mAddingItems = false; } void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds ) @@ -945,7 +936,7 @@ void ItemView::RemoveActorsOutsideRange( ItemRange range ) if( ! range.Within( current ) ) { - Self().Remove( iter->second ); + ReleaseActor(iter->first, iter->second); mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid } @@ -956,29 +947,35 @@ void ItemView::RemoveActorsOutsideRange( ItemRange range ) } } -void ItemView::AddActorsWithinRange( ItemRange range ) +void ItemView::AddActorsWithinRange( ItemRange range, float durationSeconds ) { - range.end = min(mItemFactory.GetNumberOfItems(), range.end); + range.end = std::min(mItemFactory.GetNumberOfItems(), range.end); // The order of addition depends on the scroll direction. if (mRefreshOrderHint) { for (unsigned int itemId = range.begin; itemId < range.end; ++itemId) { - AddNewActor( itemId ); + AddNewActor( itemId, durationSeconds ); } } else { for (unsigned int itemId = range.end; itemId > range.begin; --itemId) { - AddNewActor( itemId-1 ); + AddNewActor( itemId-1, durationSeconds ); } } + + // Total number of items may change dynamically. + // Always recalculate the domain size to reflect that. + CalculateDomainSize(Self().GetCurrentSize()); } -void ItemView::AddNewActor( unsigned int itemId ) +void ItemView::AddNewActor( unsigned int itemId, float durationSeconds ) { + mAddingItems = true; + if( mItemPool.end() == mItemPool.find( itemId ) ) { Actor actor = mItemFactory.NewItem( itemId ); @@ -989,16 +986,18 @@ void ItemView::AddNewActor( unsigned int itemId ) mItemPool.insert( newItem ); - SetupActor( newItem, 0.0f/*immediate*/ ); + SetupActor( newItem, durationSeconds ); Self().Add( actor ); } } + + mAddingItems = false; } void ItemView::SetupActor( Item item, float durationSeconds ) { - item.second.SetParentOrigin( ParentOrigin::CENTER ); - item.second.SetAnchorPoint( AnchorPoint::CENTER ); + item.second.SetParentOrigin( mItemsParentOrigin ); + item.second.SetAnchorPoint( mItemsAnchorPoint ); if( mActiveLayout ) { @@ -1008,17 +1007,23 @@ void ItemView::SetupActor( Item item, float durationSeconds ) item.second.SetSize( size ); } - ApplyConstraints( item.second, *mActiveLayout, item.first, durationSeconds ); + mActiveLayout->ApplyConstraints( item.second, item.first, durationSeconds, mScrollPositionObject, Self() ); } } -ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra) +void ItemView::ReleaseActor( ItemId item, Actor actor ) +{ + Self().Remove( actor ); + mItemFactory.ItemReleased(item, actor); +} + +ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra) { unsigned int itemCount = mItemFactory.GetNumberOfItems(); ItemRange available(0u, itemCount); - ItemRange range = layout.GetItemsWithinArea( GetCurrentLayoutPosition(0), layoutSize ); + ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize ); if (reserveExtra) { @@ -1031,6 +1036,20 @@ ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, return range.Intersection(available); } +void ItemView::OnChildAdd(Actor& child) +{ + if(!mAddingItems) + { + // We don't want to do this downcast check for any item added by ItemView itself. + Dali::Toolkit::ScrollComponent scrollComponent = Dali::Toolkit::ScrollComponent::DownCast(child); + if(scrollComponent) + { + // Set the scroll connector when scroll bar is being added + scrollComponent.SetScrollConnector(mScrollConnector); + } + } +} + bool ItemView::OnTouchEvent(const TouchEvent& event) { // Ignore events with multiple-touch points @@ -1051,7 +1070,10 @@ bool ItemView::OnTouchEvent(const TouchEvent& event) mScrollOvershoot = 0.0f; AnimateScrollOvershoot(0.0f); - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + if(mScrollAnimation) + { + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); + } RemoveAnimation(mScrollAnimation); } @@ -1066,13 +1088,13 @@ bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); - float layoutPositionDelta = GetCurrentLayoutPosition(0) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor()); + float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor()); float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout); mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition ); self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize)); - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); - StartRefreshTimer(); + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } if (mMouseWheelEventFinishedTimer.IsRunning()) @@ -1095,8 +1117,6 @@ bool ItemView::OnMouseWheelEventFinished() mScrollAnimation = DoAnchoring(); if (mScrollAnimation) { - StartRefreshTimer(); - mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); mScrollAnimation.Play(); } @@ -1105,104 +1125,13 @@ bool ItemView::OnMouseWheelEventFinished() mScrollOvershoot = 0.0f; AnimateScrollOvershoot(0.0f); - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); } } return false; } -void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration) -{ - ItemLayout::Vector3Function positionConstraint; - if (layout.GetPositionConstraint(itemId, positionConstraint)) - { - WrappedVector3Constraint wrapped(positionConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::POSITION, - Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - constraint.SetAlphaFunction(mDefaultAlphaFunction); - - actor.ApplyConstraint(constraint); - } - - ItemLayout::QuaternionFunction rotationConstraint; - if (layout.GetRotationConstraint(itemId, rotationConstraint)) - { - WrappedQuaternionConstraint wrapped(rotationConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::ROTATION, - Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - constraint.SetAlphaFunction(mDefaultAlphaFunction); - - actor.ApplyConstraint(constraint); - } - - ItemLayout::Vector3Function scaleConstraint; - if (layout.GetScaleConstraint(itemId, scaleConstraint)) - { - WrappedVector3Constraint wrapped(scaleConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::SCALE, - Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - constraint.SetAlphaFunction(mDefaultAlphaFunction); - - actor.ApplyConstraint(constraint); - } - - ItemLayout::Vector4Function colorConstraint; - if (layout.GetColorConstraint(itemId, colorConstraint)) - { - WrappedVector4Constraint wrapped(colorConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::COLOR, - Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - constraint.SetAlphaFunction(mDefaultAlphaFunction); - - // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in - constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME); - constraint.SetRemoveAction(Dali::Constraint::Discard); - - actor.ApplyConstraint(constraint); - } - - ItemLayout::BoolFunction visibilityConstraint; - if (layout.GetVisibilityConstraint(itemId, visibilityConstraint)) - { - WrappedBoolConstraint wrapped(visibilityConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::VISIBLE, - Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - constraint.SetAlphaFunction(mDefaultAlphaFunction); - - // Release visibility constraints the same time as the color constraint - constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME); - constraint.SetRemoveAction(Dali::Constraint::Discard); - - actor.ApplyConstraint(constraint); - } -} - void ItemView::ReapplyAllConstraints( float durationSeconds ) { for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) @@ -1211,24 +1140,35 @@ void ItemView::ReapplyAllConstraints( float durationSeconds ) Actor actor = iter->second; actor.RemoveConstraints(); - ApplyConstraints(actor, *mActiveLayout, id, durationSeconds); + mActiveLayout->ApplyConstraints(actor, id, durationSeconds, mScrollPositionObject, Self()); } +} +void ItemView::OnItemsRemoved() +{ CalculateDomainSize(Self().GetCurrentSize()); + + // Adjust scroll-position after an item is removed + if( mActiveLayout ) + { + float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize(), *mActiveLayout); + + mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition ); + } } float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout) { Actor self = Self(); float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize); - float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition)); + float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, targetPosition)); mScrollOvershoot = targetPosition - clamppedPosition; self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); return clamppedPosition; } -void ItemView::OnPan(PanGesture gesture) +void ItemView::OnPan( const PanGesture& gesture ) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); @@ -1268,7 +1208,9 @@ void ItemView::OnPan(PanGesture gesture) RemoveAnimation(mScrollAnimation); - float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0))); + float flickAnimationDuration = Clamp( mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0))) + , DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION); + mScrollAnimation = Animation::New(flickAnimationDuration); mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut ); mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); @@ -1291,7 +1233,7 @@ void ItemView::OnPan(PanGesture gesture) // Reset the overshoot if no scroll animation. if (!mScrollAnimation) { - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); AnimateScrollOvershoot(0.0f, false); } @@ -1301,45 +1243,34 @@ void ItemView::OnPan(PanGesture gesture) case Gesture::Started: // Fall through { mTotalPanDisplacement = Vector2::ZERO; + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } case Gesture::Continuing: { mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout); - mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed()); + mScrollSpeed = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed()); // Refresh order depends on the direction of the scroll; negative is towards the last item. mRefreshOrderHint = mScrollDistance < 0.0f; - RemoveAnimation(mScrollSpeedAnimation); - mScrollSpeedAnimation = Animation::New(0.3f); - mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear ); - mScrollSpeedAnimation.Play(); - float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor()); float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout); + float currentOvershoot = mScrollPositionObject.GetProperty(ScrollConnector::OVERSHOOT); + mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition ); self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize)); - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); - mTotalPanDisplacement += gesture.displacement; - mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition; - if( mScrollOvershoot > Math::MACHINE_EPSILON_1 ) + if( (firstItemScrollPosition >= 0.0f && currentOvershoot < 1.0f) || (firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) && currentOvershoot > -1.0f) ) { - AnimateScrollOvershoot(1.0f); - } - else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 ) - { - AnimateScrollOvershoot(-1.0f); - } - else - { - AnimateScrollOvershoot(0.0f); + mTotalPanDisplacement += gesture.displacement; } - StartRefreshTimer(); + mScrollOvershoot = CalculateScrollOvershoot(); + mScrollPositionObject.SetProperty( ScrollConnector::OVERSHOOT, mScrollOvershoot ); } break; @@ -1355,8 +1286,6 @@ void ItemView::OnPan(PanGesture gesture) if (mScrollAnimation) { - StartRefreshTimer(); - mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); mScrollAnimation.Play(); } @@ -1368,7 +1297,7 @@ bool ItemView::OnAccessibilityPan(PanGesture gesture) return true; } -Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) { Actor nextFocusActor; if(mActiveLayout) @@ -1411,12 +1340,6 @@ void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor) int nextItemID = GetItemId(commitedFocusableActor); float layoutPosition = GetCurrentLayoutPosition(0); Vector3 layoutSize = Self().GetCurrentSize(); - Vector3 focusItemPosition = Vector3::ZERO; - ItemLayout::Vector3Function itemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint)) - { - focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize); - } float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize); ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION); @@ -1451,7 +1374,7 @@ void ItemView::OnScrollFinished(Animation& source) RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1) { @@ -1467,6 +1390,13 @@ void ItemView::OnScrollFinished(Animation& source) mScrollOvershoot = 0.0f; } +void ItemView::OnLayoutActivationScrollFinished(Animation& source) +{ + RemoveAnimation(mScrollAnimation); + mRefreshEnabled = true; + DoRefresh(GetCurrentLayoutPosition(0), true); +} + void ItemView::OnOvershootOnFinished(Animation& animation) { mAnimatingOvershootOn = false; @@ -1478,36 +1408,12 @@ void ItemView::OnOvershootOnFinished(Animation& animation) } } -void ItemView::StartRefreshTimer() -{ - if (!mRefreshTimer) - { - mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds ); - mRefreshTimer.TickSignal().Connect( this, &ItemView::OnRefreshTick ); - } - - if (!mRefreshTimer.IsRunning()) - { - mRefreshTimer.Start(); - } -} - -void ItemView::CancelRefreshTimer() -{ - if (mRefreshTimer) - { - mRefreshTimer.Stop(); - } -} - void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout); - StartRefreshTimer(); - if(durationSeconds > 0.0f) { RemoveAnimation(mScrollAnimation); @@ -1523,7 +1429,8 @@ void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds) AnimateScrollOvershoot(0.0f); } - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } void ItemView::RemoveAnimation(Animation& animation) @@ -1545,30 +1452,29 @@ void ItemView::CalculateDomainSize(const Vector3& layoutSize) if(mActiveLayout) { - ItemLayout::Vector3Function firstItemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint)) - { - firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize); - } + firstItemPosition = mActiveLayout->GetItemPosition( 0,0,layoutSize ); float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize); - ItemLayout::Vector3Function lastItemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint)) - { - lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize); - } + self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); + lastItemPosition = mActiveLayout->GetItemPosition( fabs(minLayoutPosition),fabs(minLayoutPosition),layoutSize ); + + float domainSize; if(IsHorizontal(mActiveLayout->GetOrientation())) { self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f)); self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f)); + domainSize = fabs(firstItemPosition.x - lastItemPosition.x); } else { self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f)); self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f)); + domainSize = fabs(firstItemPosition.y - lastItemPosition.y); } + mScrollConnector.SetScrollDomain(minLayoutPosition, 0.0f, domainSize); + bool isLayoutScrollable = IsLayoutScrollable(layoutSize); self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable); self.SetProperty(mPropertyCanScrollHorizontal, false); @@ -1598,13 +1504,7 @@ bool ItemView::IsLayoutScrollable(const Vector3& layoutSize) float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const { - Vector3 firstItemPosition(Vector3::ZERO); - ItemLayout::Vector3Function firstItemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint)) - { - firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize); - } - + Vector3 firstItemPosition( mActiveLayout->GetItemPosition(0, layoutPosition, layoutSize ) ); return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y; } @@ -1631,8 +1531,6 @@ void ItemView::ScrollTo(const Vector3& position, float duration) float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout); - StartRefreshTimer(); - if(duration > 0.0f) { RemoveAnimation(mScrollAnimation); @@ -1648,43 +1546,71 @@ void ItemView::ScrollTo(const Vector3& position, float duration) AnimateScrollOvershoot(0.0f); } - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } -void ItemView::ApplyOvershootOverlayConstraints() +void ItemView::SetOvershootEffectColor( const Vector4& color ) { - Constraint constraint = Constraint::New( Actor::SIZE_WIDTH, - ParentSource( mPropertyScrollDirection ), - ParentSource( mPropertyOvershoot ), - ParentSource( Actor::SIZE ), - OvershootOverlaySizeConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); - mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height); - - constraint = Constraint::New( Actor::ROTATION, - ParentSource( mPropertyScrollDirection ), - ParentSource( mPropertyOvershoot ), - OvershootOverlayRotationConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); - - constraint = Constraint::New( Actor::POSITION, - ParentSource( Actor::SIZE ), - ParentSource( mPropertyScrollDirection ), - ParentSource( mPropertyOvershoot ), - OvershootOverlayPositionConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); - - constraint = Constraint::New( Actor::VISIBLE, - ParentSource( mPropertyCanScrollVertical ), - OvershootOverlayVisibilityConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); + mOvershootEffectColor = color; + if( mOvershootOverlay ) + { + mOvershootOverlay.SetColor( color ); + } +} - int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName()); +void ItemView::SetOvershootEnabled( bool enable ) +{ Actor self = Self(); - constraint = Constraint::New( effectOvershootPropertyIndex, - Source( self, mPropertyOvershoot ), - EqualToConstraint() ); - mOvershootEffect.ApplyConstraint(constraint); + if( enable ) + { + Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX; + mOvershootOverlay = CreateBouncingEffectActor( effectOvershootPropertyIndex ); + mOvershootOverlay.SetColor(mOvershootEffectColor); + mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT); + mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY); + self.Add(mOvershootOverlay); + + Constraint constraint = Constraint::New( Actor::Property::SIZE, + ParentSource( mPropertyScrollDirection ), + Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ), + ParentSource( Actor::Property::SIZE ), + OvershootOverlaySizeConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height); + + constraint = Constraint::New( Actor::Property::ROTATION, + ParentSource( mPropertyScrollDirection ), + Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ), + OvershootOverlayRotationConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::Property::POSITION, + ParentSource( Actor::Property::SIZE ), + ParentSource( mPropertyScrollDirection ), + Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ), + OvershootOverlayPositionConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + + constraint = Constraint::New( Actor::Property::VISIBLE, + ParentSource( mPropertyCanScrollVertical ), + OvershootOverlayVisibilityConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + + constraint = Constraint::New( effectOvershootPropertyIndex, + Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ), + EqualToConstraint() ); + mOvershootOverlay.ApplyConstraint(constraint); + } + else + { + if( mOvershootOverlay ) + { + self.Remove(mOvershootOverlay); + mOvershootOverlay.Reset(); + } + } } float ItemView::CalculateScrollOvershoot() @@ -1700,11 +1626,11 @@ float ItemView::CalculateScrollOvershoot() float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance; float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize()); self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); - float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta)); + float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta)); overshoot = positionDelta - clamppedPosition; } - return overshoot; + return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f); } void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack) @@ -1720,17 +1646,80 @@ void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack) return; } - Actor self = Self(); - float currentOvershoot = self.GetProperty(mPropertyOvershoot); - float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)); + if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0) + { + float currentOvershoot = mScrollPositionObject.GetProperty(ScrollConnector::OVERSHOOT); + float duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed; - RemoveAnimation(mScrollOvershootAnimation); - mScrollOvershootAnimation = Animation::New(duration); - mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished); - mScrollOvershootAnimation.AnimateTo( Property(self, mPropertyOvershoot), overshootAmount, TimePeriod(0.0f, duration) ); - mScrollOvershootAnimation.Play(); + RemoveAnimation(mScrollOvershootAnimation); + mScrollOvershootAnimation = Animation::New(duration); + mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished); + mScrollOvershootAnimation.AnimateTo( Property(mScrollPositionObject, ScrollConnector::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) ); + mScrollOvershootAnimation.Play(); + + mAnimatingOvershootOn = animatingOn; + } + else + { + mScrollPositionObject.SetProperty( ScrollConnector::OVERSHOOT, overshootAmount ); + } +} + +void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin ) +{ + if( parentOrigin != mItemsParentOrigin ) + { + mItemsParentOrigin = parentOrigin; + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) + { + iter->second.SetParentOrigin(parentOrigin); + } + } +} + +Vector3 ItemView::GetItemsParentOrigin() const +{ + return mItemsParentOrigin; +} + +void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint ) +{ + if( anchorPoint != mItemsAnchorPoint ) + { + mItemsAnchorPoint = anchorPoint; + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) + { + iter->second.SetAnchorPoint(anchorPoint); + } + } +} + +Vector3 ItemView::GetItemsAnchorPoint() const +{ + return mItemsAnchorPoint; +} + +void ItemView::GetItemsRange(ItemRange& range) +{ + if( !mItemPool.empty() ) + { + range.begin = mItemPool.begin()->first; + range.end = mItemPool.rbegin()->first + 1; + } + else + { + range.begin = 0; + range.end = 0; + } +} + +void ItemView::OnScrollPositionChanged( float position ) +{ + // Cancel scroll animation to prevent any fighting of setting the scroll position property. + RemoveAnimation(mScrollAnimation); - mAnimatingOvershootOn = animatingOn; + // Refresh the cache immediately when the scroll position is changed. + DoRefresh(position, false); // No need to cache extra items. } } // namespace Internal