2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
22 #include <cstring> // for strcmp
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/animation/constraints.h>
26 #include <dali/public-api/common/set-wrapper.h>
27 #include <dali/public-api/common/stage.h>
28 #include <dali/public-api/events/mouse-wheel-event.h>
29 #include <dali/public-api/events/touch-event.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/type-registry-helper.h>
34 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
35 #include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
36 #include <dali-toolkit/internal/controls/scrollable/scroll-connector-impl.h>
42 namespace // Unnamed namespace
47 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ItemView, Toolkit::Scrollable, NULL )
48 DALI_TYPE_REGISTRATION_END()
50 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
51 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
52 const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
54 const float DEFAULT_MINIMUM_SWIPE_DURATION = 0.45f;
55 const float DEFAULT_MAXIMUM_SWIPE_DURATION = 2.6f;
57 const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items
58 const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second
60 const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second
62 const float MILLISECONDS_PER_SECONDS = 1000.0f;
64 const Vector2 OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE( 720.0f, 42.0f );
65 const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f;
66 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
67 const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
69 const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" );
70 const string POSITION_PROPERTY_NAME( "item-view-position" );
71 const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" );
72 const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" );
73 const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" );
74 const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" );
77 * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
79 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
81 Radian scrollDirection(layout.GetScrollDirection());
83 float cosTheta = cosf(scrollDirection);
84 float sinTheta = sinf(scrollDirection);
86 return panDistance.x * sinTheta + panDistance.y * cosTheta;
89 // Overshoot overlay constraints
90 void OvershootOverlaySizeConstraint( Vector3& current, const PropertyInputContainer& inputs )
92 const Vector3& parentScrollDirection = inputs[0]->GetVector3();
93 const Vector3& parentSize = inputs[1]->GetVector3();
94 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
96 if(Toolkit::IsVertical(parentOrientation))
98 current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
102 current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
105 current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height*0.5f;
108 void OvershootOverlayRotationConstraint( Quaternion& current, const PropertyInputContainer& inputs )
110 const Vector3& parentScrollDirection = inputs[0]->GetVector3();
111 const float parentOvershoot = inputs[1]->GetFloat();
112 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
114 float multiplier = 0;
115 if(Toolkit::IsVertical(parentOrientation))
117 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
119 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
120 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
129 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
130 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
141 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
143 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
144 ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
153 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
154 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
164 current = Quaternion( Radian( multiplier * Math::PI ), Vector3::ZAXIS );
167 void OvershootOverlayPositionConstraint( Vector3& current, const PropertyInputContainer& inputs )
169 const Vector3& parentSize = inputs[0]->GetVector3();
170 const Vector3& parentScrollDirection = inputs[1]->GetVector3();
171 const float parentOvershoot = inputs[2]->GetFloat();
172 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
174 Vector3 relativeOffset;
176 if(Toolkit::IsVertical(parentOrientation))
178 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
180 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
181 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
183 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
187 relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
190 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
191 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
193 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
197 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
202 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
204 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
205 || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
207 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
211 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
214 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
215 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
217 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
221 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
225 current = relativeOffset * parentSize;
228 void OvershootOverlayVisibilityConstraint( bool& current, const PropertyInputContainer& inputs )
230 current = inputs[0]->GetBoolean();
234 * Relative position Constraint
235 * Generates the relative position value of the item view based on the layout position,
236 * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis.
238 void RelativePositionConstraint( Vector3& current, const PropertyInputContainer& inputs )
240 const Vector3& position = Vector3(0.0f, inputs[0]->GetFloat(), 0.0f);
241 const Vector3& min = inputs[1]->GetVector3();
242 const Vector3& max = inputs[2]->GetVector3();
244 Vector3 domainSize = max - min;
246 current.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f;
247 current.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f;
251 } // unnamed namespace
262 namespace // unnamed namespace
265 bool FindById( const ItemContainer& items, ItemId id )
267 for( ConstItemIter iter = items.begin(); items.end() != iter; ++iter )
269 if( iter->first == id )
278 } // unnamed namespace
280 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
282 // Create the implementation
283 ItemViewPtr itemView(new ItemView(factory));
285 // Pass ownership to CustomActor via derived handle
286 Dali::Toolkit::ItemView handle(*itemView);
288 // Second-phase init of the implementation
289 // This can only be done after the CustomActor connection has been made...
290 itemView->Initialize();
295 ItemView::ItemView(ItemFactory& factory)
297 mItemFactory(factory),
299 mAnimatingOvershootOn(false),
300 mAnimateOvershootOff(false),
301 mAnchoringEnabled(true),
302 mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
303 mRefreshIntervalLayoutPositions(0.0f),
304 mRefreshOrderHint(true/*Refresh item 0 first*/),
305 mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
306 mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
307 mMouseWheelScrollDistanceStep(0.0f),
308 mScrollDistance(0.0f),
310 mTotalPanDisplacement(Vector2::ZERO),
311 mScrollOvershoot(0.0f),
313 mGestureState(Gesture::Clear),
315 mPropertyPosition(Property::INVALID_INDEX),
316 mPropertyMinimumLayoutPosition(Property::INVALID_INDEX),
317 mPropertyScrollSpeed(Property::INVALID_INDEX),
318 mRefreshEnabled(true),
319 mItemsParentOrigin( ParentOrigin::CENTER),
320 mItemsAnchorPoint( AnchorPoint::CENTER)
322 SetRequiresMouseWheelEvents(true);
323 SetKeyboardNavigationSupport(true);
326 void ItemView::OnInitialize()
330 // Disable size negotiation for item views
331 self.SetRelayoutEnabled( false );
333 mScrollConnector = Dali::Toolkit::ScrollConnector::New();
334 mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
335 mScrollConnector.ScrollPositionChangedSignal().Connect( this, &ItemView::OnScrollPositionChanged );
337 mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
338 mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
339 mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
341 EnableScrollComponent(Toolkit::Scrollable::OvershootIndicator);
343 Constraint constraint = Constraint::New<Vector3>( self, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION, RelativePositionConstraint );
344 constraint.AddSource( LocalSource( mPropertyPosition ) );
345 constraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
346 constraint.AddSource( LocalSource( Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
349 Vector2 stageSize = Stage::GetCurrent().GetSize();
350 mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
352 EnableGestureDetection(Gesture::Type(Gesture::Pan));
354 mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
355 mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
357 SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS);
360 ItemView::~ItemView()
364 Dali::Toolkit::ScrollConnector ItemView::GetScrollConnector() const
366 return mScrollConnector;
369 unsigned int ItemView::GetLayoutCount() const
371 return mLayouts.size();
374 void ItemView::AddLayout(ItemLayout& layout)
376 mLayouts.push_back(ItemLayoutPtr(&layout));
379 void ItemView::RemoveLayout(unsigned int layoutIndex)
381 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
383 if (mActiveLayout == mLayouts[layoutIndex].Get())
385 mActiveLayout = NULL;
388 mLayouts.erase(mLayouts.begin() + layoutIndex);
391 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
393 return mLayouts[layoutIndex];
396 ItemLayoutPtr ItemView::GetActiveLayout() const
398 return ItemLayoutPtr(mActiveLayout);
401 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
403 return mScrollConnector.GetScrollPosition() + static_cast<float>( itemId );
406 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
408 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
410 mRefreshEnabled = false;
414 // The ItemView size should match the active layout size
415 self.SetSize(targetSize);
416 mActiveLayoutTargetSize = targetSize;
418 // Switch to the new layout
419 mActiveLayout = mLayouts[layoutIndex].Get();
421 // Move the items to the new layout positions...
423 for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
425 unsigned int itemId = iter->first;
426 Actor actor = iter->second;
428 // Remove constraints from previous layout
429 actor.RemoveConstraints();
432 if(mActiveLayout->GetItemSize(itemId, targetSize, size))
434 // resize immediately
435 actor.SetSize( size.GetVectorXY() );
438 mActiveLayout->ApplyConstraints(actor, itemId, durationSeconds, mScrollPositionObject, Self() );
441 // Refresh the new layout
442 ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false/* don't reserve extra*/);
443 AddActorsWithinRange( range, durationSeconds );
445 // Scroll to an appropriate layout position
447 bool scrollAnimationNeeded(false);
448 float firstItemScrollPosition(0.0f);
450 float current = GetCurrentLayoutPosition(0);
451 float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
452 self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
454 if (current < minimum)
456 scrollAnimationNeeded = true;
457 firstItemScrollPosition = minimum;
459 else if (mAnchoringEnabled)
461 scrollAnimationNeeded = true;
462 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
465 if (scrollAnimationNeeded)
467 RemoveAnimation(mScrollAnimation);
468 mScrollAnimation = Animation::New(durationSeconds);
469 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
470 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
471 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished);
472 mScrollAnimation.Play();
475 self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
476 AnimateScrollOvershoot(0.0f);
477 mScrollOvershoot = 0.0f;
479 Radian scrollDirection(mActiveLayout->GetScrollDirection());
480 float orientation = static_cast<float>(mActiveLayout->GetOrientation());
481 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_DIRECTION, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
483 self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
485 CalculateDomainSize(targetSize);
488 void ItemView::DeactivateCurrentLayout()
492 for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
494 Actor actor = iter->second;
495 actor.RemoveConstraints();
498 mActiveLayout = NULL;
502 void ItemView::OnRefreshNotification(PropertyNotification& source)
504 if(mRefreshEnabled || mScrollAnimation)
506 // Only refresh the cache during normal scrolling
507 DoRefresh(GetCurrentLayoutPosition(0), true);
511 void ItemView::Refresh()
513 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
515 ReleaseActor( iter->first, iter->second );
519 DoRefresh(GetCurrentLayoutPosition(0), true);
522 void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
526 ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/);
527 RemoveActorsOutsideRange( range );
528 AddActorsWithinRange( range, 0.0f/*immediate*/ );
530 mScrollUpdatedSignal.Emit( Vector3(0.0f, currentLayoutPosition, 0.0f) );
534 void ItemView::SetMinimumSwipeSpeed(float speed)
536 mMinimumSwipeSpeed = speed;
539 float ItemView::GetMinimumSwipeSpeed() const
541 return mMinimumSwipeSpeed;
544 void ItemView::SetMinimumSwipeDistance(float distance)
546 mMinimumSwipeDistance = distance;
549 float ItemView::GetMinimumSwipeDistance() const
551 return mMinimumSwipeDistance;
554 void ItemView::SetMouseWheelScrollDistanceStep(float step)
556 mMouseWheelScrollDistanceStep = step;
559 float ItemView::GetMouseWheelScrollDistanceStep() const
561 return mMouseWheelScrollDistanceStep;
564 void ItemView::SetAnchoring(bool enabled)
566 mAnchoringEnabled = enabled;
569 bool ItemView::GetAnchoring() const
571 return mAnchoringEnabled;
574 void ItemView::SetAnchoringDuration(float durationSeconds)
576 mAnchoringDuration = durationSeconds;
579 float ItemView::GetAnchoringDuration() const
581 return mAnchoringDuration;
584 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
586 if( !Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions) )
588 mRefreshIntervalLayoutPositions = intervalLayoutPositions;
590 if(mRefreshNotification)
592 mScrollPositionObject.RemovePropertyNotification(mRefreshNotification);
594 mRefreshNotification = mScrollPositionObject.AddPropertyNotification( ScrollConnector::SCROLL_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) );
595 mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification );
599 float ItemView::GetRefreshInterval() const
601 return mRefreshIntervalLayoutPositions;
604 void ItemView::SetRefreshEnabled(bool enabled)
606 mRefreshEnabled = enabled;
609 Actor ItemView::GetItem(unsigned int itemId) const
613 ConstItemPoolIter iter = mItemPool.find( itemId );
614 if( iter != mItemPool.end() )
616 actor = iter->second;
622 unsigned int ItemView::GetItemId( Actor actor ) const
624 unsigned int itemId( 0 );
626 for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
628 if( iter->second == actor )
630 itemId = iter->first;
638 void ItemView::InsertItem( Item newItem, float durationSeconds )
642 Actor displacedActor;
643 ItemPoolIter afterDisplacedIter = mItemPool.end();
645 ItemPoolIter foundIter = mItemPool.find( newItem.first );
646 if( mItemPool.end() != foundIter )
648 SetupActor( newItem, durationSeconds );
649 Self().Add( newItem.second );
651 displacedActor = foundIter->second;
652 foundIter->second = newItem.second;
654 afterDisplacedIter = ++foundIter;
658 // Inserting before the existing item range?
659 ItemPoolIter iter = mItemPool.begin();
660 if( iter != mItemPool.end() &&
661 iter->first > newItem.first )
663 displacedActor = iter->second;
664 mItemPool.erase( iter++ ); // iter is still valid after the erase
666 afterDisplacedIter = iter;
672 // Move the existing actors to make room
673 for( ItemPoolIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter )
675 Actor temp = iter->second;
676 iter->second = displacedActor;
677 displacedActor = temp;
679 iter->second.RemoveConstraints();
680 mActiveLayout->ApplyConstraints( iter->second, iter->first, durationSeconds, mScrollPositionObject, Self() );
684 ItemPool::reverse_iterator lastIter = mItemPool.rbegin();
685 if ( lastIter != mItemPool.rend() )
687 ItemId lastId = lastIter->first;
688 Item lastItem( lastId + 1, displacedActor );
689 mItemPool.insert( lastItem );
691 lastItem.second.RemoveConstraints();
692 mActiveLayout->ApplyConstraints( lastItem.second, lastItem.first, durationSeconds, mScrollPositionObject, Self() );
696 CalculateDomainSize(Self().GetCurrentSize());
698 mAddingItems = false;
701 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
705 // Insert from lowest id to highest
706 std::set<Item> sortedItems;
707 for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter )
709 sortedItems.insert( *iter );
712 for( std::set<Item>::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
714 Self().Add( iter->second );
716 ItemPoolIter foundIter = mItemPool.find( iter->first );
717 if( mItemPool.end() != foundIter )
719 Actor moveMe = foundIter->second;
720 foundIter->second = iter->second;
722 // Move the existing actors to make room
723 for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
725 Actor temp = iter->second;
726 iter->second = moveMe;
731 ItemId lastId = mItemPool.rbegin()->first;
732 Item lastItem( lastId + 1, moveMe );
733 mItemPool.insert( lastItem );
737 mItemPool.insert( *iter );
741 // Relayout everything
742 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
745 if( FindById( newItems, iter->first ) )
747 SetupActor( *iter, durationSeconds );
751 iter->second.RemoveConstraints();
752 mActiveLayout->ApplyConstraints( iter->second, iter->first, durationSeconds, mScrollPositionObject, Self() );
756 CalculateDomainSize(Self().GetCurrentSize());
758 mAddingItems = false;
761 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
763 bool actorsReordered = RemoveActor( itemId );
764 if( actorsReordered )
766 ReapplyAllConstraints( durationSeconds );
772 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
774 bool actorsReordered( false );
776 // Remove from highest id to lowest
777 set<ItemId> sortedItems;
778 for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter )
780 sortedItems.insert( *iter );
783 for( set<ItemId>::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
785 if( RemoveActor( *iter ) )
787 actorsReordered = true;
791 if( actorsReordered )
793 ReapplyAllConstraints( durationSeconds );
799 bool ItemView::RemoveActor(unsigned int itemId)
801 bool reordered( false );
803 ItemPoolIter removeIter = mItemPool.find( itemId );
804 if( removeIter != mItemPool.end() )
806 ReleaseActor(itemId, removeIter->second);
810 // Removing before the existing item range?
811 ItemPoolIter iter = mItemPool.begin();
812 if( iter != mItemPool.end() &&
813 iter->first > itemId )
815 // In order to decrement the first visible item ID
816 mItemPool.insert( Item(iter->first - 1, Actor()) );
818 removeIter = mItemPool.begin();
822 if( removeIter != mItemPool.end() )
826 // Adjust the remaining item IDs, for example if item 2 is removed:
827 // Initial actors: After insert:
828 // ID 1 - ActorA ID 1 - ActorA
829 // ID 2 - ActorB ID 2 - ActorC (previously ID 3)
830 // ID 3 - ActorC ID 3 - ActorB (previously ID 4)
832 for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter)
834 if( iter->first < mItemPool.rbegin()->first )
836 iter->second = mItemPool[ iter->first + 1 ];
840 mItemPool.erase( iter );
849 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
853 SetupActor( replacementItem, durationSeconds );
854 Self().Add( replacementItem.second );
856 const ItemPoolIter iter = mItemPool.find( replacementItem.first );
857 if( mItemPool.end() != iter )
859 ReleaseActor(iter->first, iter->second);
860 iter->second = replacementItem.second;
864 mItemPool.insert( replacementItem );
867 CalculateDomainSize(Self().GetCurrentSize());
869 mAddingItems = false;
872 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
874 for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
876 ReplaceItem( *iter, durationSeconds );
880 void ItemView::RemoveActorsOutsideRange( ItemRange range )
882 // Remove unwanted actors from the ItemView & ItemPool
883 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); )
885 unsigned int current = iter->first;
887 if( ! range.Within( current ) )
889 ReleaseActor(iter->first, iter->second);
891 mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid
900 void ItemView::AddActorsWithinRange( ItemRange range, float durationSeconds )
902 range.end = std::min(mItemFactory.GetNumberOfItems(), range.end);
904 // The order of addition depends on the scroll direction.
905 if (mRefreshOrderHint)
907 for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
909 AddNewActor( itemId, durationSeconds );
914 for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
916 AddNewActor( itemId-1, durationSeconds );
920 // Total number of items may change dynamically.
921 // Always recalculate the domain size to reflect that.
922 CalculateDomainSize(Self().GetCurrentSize());
925 void ItemView::AddNewActor( unsigned int itemId, float durationSeconds )
929 if( mItemPool.end() == mItemPool.find( itemId ) )
931 Actor actor = mItemFactory.NewItem( itemId );
935 Item newItem( itemId, actor );
937 mItemPool.insert( newItem );
939 SetupActor( newItem, durationSeconds );
944 mAddingItems = false;
947 void ItemView::SetupActor( Item item, float durationSeconds )
949 item.second.SetParentOrigin( mItemsParentOrigin );
950 item.second.SetAnchorPoint( mItemsAnchorPoint );
955 if( mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size ) )
957 item.second.SetSize( size.GetVectorXY() );
960 mActiveLayout->ApplyConstraints( item.second, item.first, durationSeconds, mScrollPositionObject, Self() );
964 void ItemView::ReleaseActor( ItemId item, Actor actor )
966 Self().Remove( actor );
967 mItemFactory.ItemReleased(item, actor);
970 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
972 unsigned int itemCount = mItemFactory.GetNumberOfItems();
974 ItemRange available(0u, itemCount);
976 ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize );
980 // Add the reserve items for scrolling
981 unsigned int extra = layout.GetReserveItemCount(layoutSize);
982 range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
986 return range.Intersection(available);
989 void ItemView::OnChildAdd(Actor& child)
993 // We don't want to do this downcast check for any item added by ItemView itself.
994 Dali::Toolkit::ScrollComponent scrollComponent = Dali::Toolkit::ScrollComponent::DownCast(child);
997 // Set the scroll connector when scroll bar is being added
998 scrollComponent.SetScrollConnector(mScrollConnector);
1003 bool ItemView::OnTouchEvent(const TouchEvent& event)
1005 // Ignore events with multiple-touch points
1006 if (event.GetPointCount() != 1)
1011 if (event.GetPoint(0).state == TouchPoint::Down)
1013 // Cancel ongoing scrolling etc.
1014 mGestureState = Gesture::Clear;
1016 mScrollDistance = 0.0f;
1017 mScrollSpeed = 0.0f;
1018 Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
1020 mScrollOvershoot = 0.0f;
1021 AnimateScrollOvershoot(0.0f);
1023 if(mScrollAnimation)
1025 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1028 RemoveAnimation(mScrollAnimation);
1031 return true; // consume since we're potentially scrolling
1034 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
1036 // Respond the mouse wheel event to scroll
1039 Actor self = Self();
1040 const Vector3 layoutSize = Self().GetCurrentSize();
1041 float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1042 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1044 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1045 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1046 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1047 mRefreshEnabled = true;
1050 if (mMouseWheelEventFinishedTimer.IsRunning())
1052 mMouseWheelEventFinishedTimer.Stop();
1055 mMouseWheelEventFinishedTimer.Start();
1060 bool ItemView::OnMouseWheelEventFinished()
1064 RemoveAnimation(mScrollAnimation);
1066 // No more mouse wheel events coming. Do the anchoring if enabled.
1067 mScrollAnimation = DoAnchoring();
1068 if (mScrollAnimation)
1070 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1071 mScrollAnimation.Play();
1075 mScrollOvershoot = 0.0f;
1076 AnimateScrollOvershoot(0.0f);
1078 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1085 void ItemView::ReapplyAllConstraints( float durationSeconds )
1087 for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1089 unsigned int id = iter->first;
1090 Actor actor = iter->second;
1092 actor.RemoveConstraints();
1093 mActiveLayout->ApplyConstraints(actor, id, durationSeconds, mScrollPositionObject, Self());
1097 void ItemView::OnItemsRemoved()
1099 CalculateDomainSize(Self().GetCurrentSize());
1101 // Adjust scroll-position after an item is removed
1104 float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize(), *mActiveLayout);
1106 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1110 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1112 Actor self = Self();
1113 float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1114 float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, targetPosition));
1115 mScrollOvershoot = targetPosition - clamppedPosition;
1116 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1118 return clamppedPosition;
1121 void ItemView::OnPan( const PanGesture& gesture )
1123 Actor self = Self();
1124 const Vector3 layoutSize = Self().GetCurrentSize();
1126 RemoveAnimation(mScrollAnimation);
1128 // Short-circuit if there is no active layout
1131 mGestureState = Gesture::Clear;
1135 mGestureState = gesture.state;
1137 switch (mGestureState)
1139 case Gesture::Finished:
1142 if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1143 mScrollSpeed > mMinimumSwipeSpeed)
1145 float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1147 mRefreshOrderHint = true;
1149 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1150 float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1154 if (mAnchoringEnabled)
1156 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1159 RemoveAnimation(mScrollAnimation);
1161 float flickAnimationDuration = Clamp( mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)))
1162 , DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION);
1164 mScrollAnimation = Animation::New(flickAnimationDuration);
1165 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1166 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1167 mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1170 // Check whether it has already scrolled to the end
1171 if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1173 AnimateScrollOvershoot(0.0f);
1177 // Anchoring may be triggered when there was no swipe
1178 if (!mScrollAnimation)
1180 mScrollAnimation = DoAnchoring();
1183 // Reset the overshoot if no scroll animation.
1184 if (!mScrollAnimation)
1186 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1188 AnimateScrollOvershoot(0.0f, false);
1193 case Gesture::Started: // Fall through
1195 mTotalPanDisplacement = Vector2::ZERO;
1196 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1197 mRefreshEnabled = true;
1200 case Gesture::Continuing:
1202 mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1203 mScrollSpeed = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1205 // Refresh order depends on the direction of the scroll; negative is towards the last item.
1206 mRefreshOrderHint = mScrollDistance < 0.0f;
1208 float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1210 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1212 float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1214 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1215 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1217 if( (firstItemScrollPosition >= 0.0f && currentOvershoot < 1.0f) || (firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) && currentOvershoot > -1.0f) )
1219 mTotalPanDisplacement += gesture.displacement;
1222 mScrollOvershoot = CalculateScrollOvershoot();
1223 mScrollPositionObject.SetProperty( ScrollConnector::OVERSHOOT, mScrollOvershoot );
1227 case Gesture::Cancelled:
1229 mScrollAnimation = DoAnchoring();
1237 if (mScrollAnimation)
1239 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1240 mScrollAnimation.Play();
1244 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1250 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1252 Actor nextFocusActor;
1256 if(!actor || actor == this->Self())
1258 nextFocusActor = GetItem(nextItemID);
1260 else if(actor && actor.GetParent() == this->Self())
1262 int itemID = GetItemId(actor);
1263 nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1264 nextFocusActor = GetItem(nextItemID);
1265 if(nextFocusActor == actor)
1267 // need to pass NULL actor back to focus manager
1268 nextFocusActor.Reset();
1269 return nextFocusActor;
1272 float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1273 Vector3 layoutSize = Self().GetCurrentSize();
1276 // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1277 ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1278 nextItemID = viewableItems.begin;
1279 nextFocusActor = GetItem(nextItemID);
1282 return nextFocusActor;
1285 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1287 // only in this function if our chosen focus actor was actually used
1288 if(commitedFocusableActor)
1290 int nextItemID = GetItemId(commitedFocusableActor);
1291 float layoutPosition = GetCurrentLayoutPosition(0);
1292 Vector3 layoutSize = Self().GetCurrentSize();
1294 float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1295 ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1299 Animation ItemView::DoAnchoring()
1301 Animation anchoringAnimation;
1302 Actor self = Self();
1304 if (mActiveLayout && mAnchoringEnabled)
1306 float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1308 anchoringAnimation = Animation::New(mAnchoringDuration);
1309 anchoringAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), anchorPosition, AlphaFunctions::EaseOut );
1310 anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1311 anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1314 AnimateScrollOvershoot(0.0f);
1318 return anchoringAnimation;
1321 void ItemView::OnScrollFinished(Animation& source)
1323 Actor self = Self();
1325 RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1327 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1329 if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1331 AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1335 // Reset the overshoot
1336 AnimateScrollOvershoot( 0.0f );
1338 mIsFlicking = false;
1340 mScrollOvershoot = 0.0f;
1343 void ItemView::OnLayoutActivationScrollFinished(Animation& source)
1345 RemoveAnimation(mScrollAnimation);
1346 mRefreshEnabled = true;
1347 DoRefresh(GetCurrentLayoutPosition(0), true);
1350 void ItemView::OnOvershootOnFinished(Animation& animation)
1352 mAnimatingOvershootOn = false;
1353 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1354 RemoveAnimation(mScrollOvershootAnimation);
1355 if(mAnimateOvershootOff)
1357 AnimateScrollOvershoot(0.0f);
1361 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1363 Actor self = Self();
1364 const Vector3 layoutSize = Self().GetCurrentSize();
1365 float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1367 if(durationSeconds > 0.0f)
1369 RemoveAnimation(mScrollAnimation);
1370 mScrollAnimation = Animation::New(durationSeconds);
1371 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1372 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1373 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1374 mScrollAnimation.Play();
1378 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1379 AnimateScrollOvershoot(0.0f);
1382 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1383 mRefreshEnabled = true;
1386 void ItemView::RemoveAnimation(Animation& animation)
1390 // Cease animating, and reset handle.
1396 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1398 Actor self = Self();
1400 Vector3 firstItemPosition(Vector3::ZERO);
1401 Vector3 lastItemPosition(Vector3::ZERO);
1405 firstItemPosition = mActiveLayout->GetItemPosition( 0,0,layoutSize );
1407 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1408 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1409 lastItemPosition = mActiveLayout->GetItemPosition( fabs(minLayoutPosition),fabs(minLayoutPosition),layoutSize );
1413 if(IsHorizontal(mActiveLayout->GetOrientation()))
1415 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, Vector3(0.0f, firstItemPosition.x, 0.0f));
1416 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector3(0.0f, lastItemPosition.x, 0.0f));
1417 domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1421 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, Vector3(0.0f, firstItemPosition.y, 0.0f));
1422 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector3(0.0f, lastItemPosition.y, 0.0f));
1423 domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1426 mScrollConnector.SetScrollDomain(minLayoutPosition, 0.0f, domainSize);
1428 bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1429 self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, isLayoutScrollable);
1430 self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, false);
1434 Vector3 ItemView::GetDomainSize() const
1436 Actor self = Self();
1438 float minScrollPosition = self.GetProperty<float>(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN);
1439 float maxScrollPosition = self.GetProperty<float>(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX);
1441 return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1444 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1446 Actor self = Self();
1448 float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1449 float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1450 float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1452 return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1455 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1457 Vector3 firstItemPosition( mActiveLayout->GetItemPosition(0, layoutPosition, layoutSize ) );
1458 return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1461 Vector3 ItemView::GetCurrentScrollPosition() const
1463 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1464 return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1467 void ItemView::AddOverlay(Actor actor)
1472 void ItemView::RemoveOverlay(Actor actor)
1474 Self().Remove(actor);
1477 void ItemView::ScrollTo(const Vector3& position, float duration)
1479 Actor self = Self();
1480 const Vector3 layoutSize = Self().GetCurrentSize();
1482 float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1486 RemoveAnimation(mScrollAnimation);
1487 mScrollAnimation = Animation::New(duration);
1488 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1489 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1490 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1491 mScrollAnimation.Play();
1495 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1496 AnimateScrollOvershoot(0.0f);
1499 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1500 mRefreshEnabled = true;
1503 void ItemView::SetOvershootEffectColor( const Vector4& color )
1505 mOvershootEffectColor = color;
1506 if( mOvershootOverlay )
1508 mOvershootOverlay.SetColor( color );
1512 void ItemView::SetOvershootEnabled( bool enable )
1514 Actor self = Self();
1517 Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX;
1518 mOvershootOverlay = CreateBouncingEffectActor( effectOvershootPropertyIndex );
1519 mOvershootOverlay.SetColor(mOvershootEffectColor);
1520 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
1521 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
1522 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
1523 self.Add(mOvershootOverlay);
1525 Constraint constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint );
1526 constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
1527 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
1530 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
1532 constraint = Constraint::New<Quaternion>( mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint );
1533 constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
1534 constraint.AddSource( Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ) );
1537 constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::POSITION, OvershootOverlayPositionConstraint );
1538 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
1539 constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
1540 constraint.AddSource( Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ) );
1543 constraint = Constraint::New<bool>( mOvershootOverlay, Actor::Property::VISIBLE, OvershootOverlayVisibilityConstraint );
1544 constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) );
1547 constraint = Constraint::New<float>( mOvershootOverlay, effectOvershootPropertyIndex, EqualToConstraint() );
1548 constraint.AddSource( Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ) );
1553 if( mOvershootOverlay )
1555 self.Remove(mOvershootOverlay);
1556 mOvershootOverlay.Reset();
1561 float ItemView::CalculateScrollOvershoot()
1563 float overshoot = 0.0f;
1567 // The overshoot must be calculated from the accumulated pan gesture displacement
1568 // since the pan gesture starts.
1569 Actor self = Self();
1570 float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1571 float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1572 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1573 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1574 float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta));
1575 overshoot = positionDelta - clamppedPosition;
1578 return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f);
1581 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1583 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1585 // make sure we animate back if needed
1586 mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1588 if( mAnimatingOvershootOn )
1590 // animating on, do not allow animate off
1594 if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0)
1596 float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1597 float duration = 0.0f;
1599 if (mOvershootOverlay)
1601 duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed;
1604 RemoveAnimation(mScrollOvershootAnimation);
1605 mScrollOvershootAnimation = Animation::New(duration);
1606 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1607 mScrollOvershootAnimation.AnimateTo( Property(mScrollPositionObject, ScrollConnector::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1608 mScrollOvershootAnimation.Play();
1610 mAnimatingOvershootOn = animatingOn;
1614 mScrollPositionObject.SetProperty( ScrollConnector::OVERSHOOT, overshootAmount );
1618 void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin )
1620 if( parentOrigin != mItemsParentOrigin )
1622 mItemsParentOrigin = parentOrigin;
1623 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1625 iter->second.SetParentOrigin(parentOrigin);
1630 Vector3 ItemView::GetItemsParentOrigin() const
1632 return mItemsParentOrigin;
1635 void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint )
1637 if( anchorPoint != mItemsAnchorPoint )
1639 mItemsAnchorPoint = anchorPoint;
1640 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1642 iter->second.SetAnchorPoint(anchorPoint);
1647 Vector3 ItemView::GetItemsAnchorPoint() const
1649 return mItemsAnchorPoint;
1652 void ItemView::GetItemsRange(ItemRange& range)
1654 if( !mItemPool.empty() )
1656 range.begin = mItemPool.begin()->first;
1657 range.end = mItemPool.rbegin()->first + 1;
1666 void ItemView::OnScrollPositionChanged( float position )
1668 // Cancel scroll animation to prevent any fighting of setting the scroll position property.
1669 RemoveAnimation(mScrollAnimation);
1671 // Refresh the cache immediately when the scroll position is changed.
1672 DoRefresh(position, false); // No need to cache extra items.
1675 } // namespace Internal
1677 } // namespace Toolkit