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>
23 #include <dali/public-api/animation/active-constraint.h>
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
91 struct OvershootOverlaySizeConstraint
93 Vector3 operator()(const Vector3& current,
94 const PropertyInput& parentScrollDirectionProperty,
95 const PropertyInput& parentOvershootProperty,
96 const PropertyInput& parentSizeProperty)
98 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
99 const Vector3 parentSize = parentSizeProperty.GetVector3();
100 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
104 if(Toolkit::IsVertical(parentOrientation))
106 overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
110 overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
113 float overlayHeight = (overlayWidth > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD) ? OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height : OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height*0.5f;
115 return Vector3( overlayWidth, overlayHeight, current.depth );
119 struct OvershootOverlayRotationConstraint
121 Quaternion operator()(const Quaternion& current,
122 const PropertyInput& parentScrollDirectionProperty,
123 const PropertyInput& parentOvershootProperty)
125 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
126 const float parentOvershoot = parentOvershootProperty.GetFloat();
127 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
129 float multiplier = 0;
130 if(Toolkit::IsVertical(parentOrientation))
132 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
134 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
135 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
144 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
145 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
156 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
158 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
159 ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
168 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
169 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
179 Quaternion rotation( Radian( multiplier * Math::PI ), Vector3::ZAXIS );
185 struct OvershootOverlayPositionConstraint
187 Vector3 operator()(const Vector3& current,
188 const PropertyInput& parentSizeProperty,
189 const PropertyInput& parentScrollDirectionProperty,
190 const PropertyInput& parentOvershootProperty)
192 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
193 const float parentOvershoot = parentOvershootProperty.GetFloat();
194 const Vector3 parentSize = parentSizeProperty.GetVector3();
195 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
197 Vector3 relativeOffset;
199 if(Toolkit::IsVertical(parentOrientation))
201 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
203 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
204 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
206 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
210 relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
213 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
214 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
216 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
220 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
225 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
227 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
228 || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
230 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
234 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
237 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
238 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
240 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
244 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
248 return relativeOffset * parentSize;
253 struct OvershootOverlayVisibilityConstraint
255 bool operator()(const bool& current,
256 const PropertyInput& parentLayoutScrollableProperty)
258 const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean();
260 return parentLayoutScrollable;
265 * Relative position Constraint
266 * Generates the relative position value of the item view based on the layout position,
267 * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis.
269 Vector3 RelativePositionConstraint(const Vector3& current,
270 const PropertyInput& scrollPositionProperty,
271 const PropertyInput& scrollMinProperty,
272 const PropertyInput& scrollMaxProperty,
273 const PropertyInput& layoutSizeProperty)
275 const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f);
276 const Vector3& min = scrollMinProperty.GetVector3();
277 const Vector3& max = scrollMaxProperty.GetVector3();
279 Vector3 relativePosition;
280 Vector3 domainSize = max - min;
282 relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f;
283 relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f;
285 return relativePosition;
288 } // unnamed namespace
299 namespace // unnamed namespace
302 bool FindById( const ItemContainer& items, ItemId id )
304 for( ConstItemIter iter = items.begin(); items.end() != iter; ++iter )
306 if( iter->first == id )
315 } // unnamed namespace
317 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
319 // Create the implementation
320 ItemViewPtr itemView(new ItemView(factory));
322 // Pass ownership to CustomActor via derived handle
323 Dali::Toolkit::ItemView handle(*itemView);
325 // Second-phase init of the implementation
326 // This can only be done after the CustomActor connection has been made...
327 itemView->Initialize();
332 ItemView::ItemView(ItemFactory& factory)
334 mItemFactory(factory),
336 mAnimatingOvershootOn(false),
337 mAnimateOvershootOff(false),
338 mAnchoringEnabled(true),
339 mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
340 mRefreshIntervalLayoutPositions(0.0f),
341 mRefreshOrderHint(true/*Refresh item 0 first*/),
342 mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
343 mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
344 mMouseWheelScrollDistanceStep(0.0f),
345 mScrollDistance(0.0f),
347 mTotalPanDisplacement(Vector2::ZERO),
348 mScrollOvershoot(0.0f),
350 mGestureState(Gesture::Clear),
352 mPropertyPosition(Property::INVALID_INDEX),
353 mPropertyMinimumLayoutPosition(Property::INVALID_INDEX),
354 mPropertyScrollSpeed(Property::INVALID_INDEX),
355 mRefreshEnabled(true),
356 mItemsParentOrigin( ParentOrigin::CENTER),
357 mItemsAnchorPoint( AnchorPoint::CENTER)
359 SetRequiresMouseWheelEvents(true);
360 SetKeyboardNavigationSupport(true);
363 void ItemView::OnInitialize()
365 RegisterCommonProperties();
369 // Disable size negotiation for item views
370 self.SetRelayoutEnabled( false );
372 mScrollConnector = Dali::Toolkit::ScrollConnector::New();
373 mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
374 mScrollConnector.ScrollPositionChangedSignal().Connect( this, &ItemView::OnScrollPositionChanged );
376 mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
377 mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
378 mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
380 EnableScrollComponent(Toolkit::Scrollable::OvershootIndicator);
382 Constraint constraint = Constraint::New<Vector3>(mPropertyRelativePosition,
383 LocalSource(mPropertyPosition),
384 LocalSource(mPropertyPositionMin),
385 LocalSource(mPropertyPositionMax),
386 LocalSource(Actor::Property::SIZE),
387 RelativePositionConstraint);
388 self.ApplyConstraint(constraint);
390 Vector2 stageSize = Stage::GetCurrent().GetSize();
391 mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
393 EnableGestureDetection(Gesture::Type(Gesture::Pan));
395 mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
396 mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
398 SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS);
401 ItemView::~ItemView()
405 Dali::Toolkit::ScrollConnector ItemView::GetScrollConnector() const
407 return mScrollConnector;
410 unsigned int ItemView::GetLayoutCount() const
412 return mLayouts.size();
415 void ItemView::AddLayout(ItemLayout& layout)
417 mLayouts.push_back(ItemLayoutPtr(&layout));
420 void ItemView::RemoveLayout(unsigned int layoutIndex)
422 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
424 if (mActiveLayout == mLayouts[layoutIndex].Get())
426 mActiveLayout = NULL;
429 mLayouts.erase(mLayouts.begin() + layoutIndex);
432 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
434 return mLayouts[layoutIndex];
437 ItemLayoutPtr ItemView::GetActiveLayout() const
439 return ItemLayoutPtr(mActiveLayout);
442 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
444 return mScrollConnector.GetScrollPosition() + static_cast<float>( itemId );
447 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
449 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
451 mRefreshEnabled = false;
455 // The ItemView size should match the active layout size
456 self.SetSize(targetSize);
457 mActiveLayoutTargetSize = targetSize;
459 // Switch to the new layout
460 mActiveLayout = mLayouts[layoutIndex].Get();
462 // Move the items to the new layout positions...
464 for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
466 unsigned int itemId = iter->first;
467 Actor actor = iter->second;
469 // Remove constraints from previous layout
470 actor.RemoveConstraints();
473 if(mActiveLayout->GetItemSize(itemId, targetSize, size))
475 // resize immediately
476 actor.SetResizePolicy( FIXED, ALL_DIMENSIONS );
477 actor.SetPreferredSize( size.GetVectorXY() );
480 mActiveLayout->ApplyConstraints(actor, itemId, durationSeconds, mScrollPositionObject, Self() );
483 // Refresh the new layout
484 ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false/* don't reserve extra*/);
485 AddActorsWithinRange( range, durationSeconds );
487 // Scroll to an appropriate layout position
489 bool scrollAnimationNeeded(false);
490 float firstItemScrollPosition(0.0f);
492 float current = GetCurrentLayoutPosition(0);
493 float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
494 self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
496 if (current < minimum)
498 scrollAnimationNeeded = true;
499 firstItemScrollPosition = minimum;
501 else if (mAnchoringEnabled)
503 scrollAnimationNeeded = true;
504 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
507 if (scrollAnimationNeeded)
509 RemoveAnimation(mScrollAnimation);
510 mScrollAnimation = Animation::New(durationSeconds);
511 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
512 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
513 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished);
514 mScrollAnimation.Play();
517 self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
518 AnimateScrollOvershoot(0.0f);
519 mScrollOvershoot = 0.0f;
521 Radian scrollDirection(mActiveLayout->GetScrollDirection());
522 float orientation = static_cast<float>(mActiveLayout->GetOrientation());
523 self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
525 self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
527 CalculateDomainSize(targetSize);
530 void ItemView::DeactivateCurrentLayout()
534 for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
536 Actor actor = iter->second;
537 actor.RemoveConstraints();
540 mActiveLayout = NULL;
544 void ItemView::OnRefreshNotification(PropertyNotification& source)
546 if(mRefreshEnabled || mScrollAnimation)
548 // Only refresh the cache during normal scrolling
549 DoRefresh(GetCurrentLayoutPosition(0), true);
553 void ItemView::Refresh()
555 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
557 ReleaseActor( iter->first, iter->second );
561 DoRefresh(GetCurrentLayoutPosition(0), true);
564 void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
568 ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/);
569 RemoveActorsOutsideRange( range );
570 AddActorsWithinRange( range, 0.0f/*immediate*/ );
572 mScrollUpdatedSignal.Emit( Vector3(0.0f, currentLayoutPosition, 0.0f) );
576 void ItemView::SetMinimumSwipeSpeed(float speed)
578 mMinimumSwipeSpeed = speed;
581 float ItemView::GetMinimumSwipeSpeed() const
583 return mMinimumSwipeSpeed;
586 void ItemView::SetMinimumSwipeDistance(float distance)
588 mMinimumSwipeDistance = distance;
591 float ItemView::GetMinimumSwipeDistance() const
593 return mMinimumSwipeDistance;
596 void ItemView::SetMouseWheelScrollDistanceStep(float step)
598 mMouseWheelScrollDistanceStep = step;
601 float ItemView::GetMouseWheelScrollDistanceStep() const
603 return mMouseWheelScrollDistanceStep;
606 void ItemView::SetAnchoring(bool enabled)
608 mAnchoringEnabled = enabled;
611 bool ItemView::GetAnchoring() const
613 return mAnchoringEnabled;
616 void ItemView::SetAnchoringDuration(float durationSeconds)
618 mAnchoringDuration = durationSeconds;
621 float ItemView::GetAnchoringDuration() const
623 return mAnchoringDuration;
626 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
628 if( !Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions) )
630 mRefreshIntervalLayoutPositions = intervalLayoutPositions;
632 if(mRefreshNotification)
634 mScrollPositionObject.RemovePropertyNotification(mRefreshNotification);
636 mRefreshNotification = mScrollPositionObject.AddPropertyNotification( ScrollConnector::SCROLL_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) );
637 mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification );
641 float ItemView::GetRefreshInterval() const
643 return mRefreshIntervalLayoutPositions;
646 void ItemView::SetRefreshEnabled(bool enabled)
648 mRefreshEnabled = enabled;
651 Actor ItemView::GetItem(unsigned int itemId) const
655 ConstItemPoolIter iter = mItemPool.find( itemId );
656 if( iter != mItemPool.end() )
658 actor = iter->second;
664 unsigned int ItemView::GetItemId( Actor actor ) const
666 unsigned int itemId( 0 );
668 for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
670 if( iter->second == actor )
672 itemId = iter->first;
680 void ItemView::InsertItem( Item newItem, float durationSeconds )
684 Actor displacedActor;
685 ItemPoolIter afterDisplacedIter = mItemPool.end();
687 ItemPoolIter foundIter = mItemPool.find( newItem.first );
688 if( mItemPool.end() != foundIter )
690 SetupActor( newItem, durationSeconds );
691 Self().Add( newItem.second );
693 displacedActor = foundIter->second;
694 foundIter->second = newItem.second;
696 afterDisplacedIter = ++foundIter;
700 // Inserting before the existing item range?
701 ItemPoolIter iter = mItemPool.begin();
702 if( iter != mItemPool.end() &&
703 iter->first > newItem.first )
705 displacedActor = iter->second;
706 mItemPool.erase( iter++ ); // iter is still valid after the erase
708 afterDisplacedIter = iter;
714 // Move the existing actors to make room
715 for( ItemPoolIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter )
717 Actor temp = iter->second;
718 iter->second = displacedActor;
719 displacedActor = temp;
721 iter->second.RemoveConstraints();
722 mActiveLayout->ApplyConstraints( iter->second, iter->first, durationSeconds, mScrollPositionObject, Self() );
726 ItemPool::reverse_iterator lastIter = mItemPool.rbegin();
727 if ( lastIter != mItemPool.rend() )
729 ItemId lastId = lastIter->first;
730 Item lastItem( lastId + 1, displacedActor );
731 mItemPool.insert( lastItem );
733 lastItem.second.RemoveConstraints();
734 mActiveLayout->ApplyConstraints( lastItem.second, lastItem.first, durationSeconds, mScrollPositionObject, Self() );
738 CalculateDomainSize(Self().GetCurrentSize());
740 mAddingItems = false;
743 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
747 // Insert from lowest id to highest
748 std::set<Item> sortedItems;
749 for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter )
751 sortedItems.insert( *iter );
754 for( std::set<Item>::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
756 Self().Add( iter->second );
758 ItemPoolIter foundIter = mItemPool.find( iter->first );
759 if( mItemPool.end() != foundIter )
761 Actor moveMe = foundIter->second;
762 foundIter->second = iter->second;
764 // Move the existing actors to make room
765 for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
767 Actor temp = iter->second;
768 iter->second = moveMe;
773 ItemId lastId = mItemPool.rbegin()->first;
774 Item lastItem( lastId + 1, moveMe );
775 mItemPool.insert( lastItem );
779 mItemPool.insert( *iter );
783 // Relayout everything
784 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
787 if( FindById( newItems, iter->first ) )
789 SetupActor( *iter, durationSeconds );
793 iter->second.RemoveConstraints();
794 mActiveLayout->ApplyConstraints( iter->second, iter->first, durationSeconds, mScrollPositionObject, Self() );
798 CalculateDomainSize(Self().GetCurrentSize());
800 mAddingItems = false;
803 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
805 bool actorsReordered = RemoveActor( itemId );
806 if( actorsReordered )
808 ReapplyAllConstraints( durationSeconds );
814 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
816 bool actorsReordered( false );
818 // Remove from highest id to lowest
819 set<ItemId> sortedItems;
820 for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter )
822 sortedItems.insert( *iter );
825 for( set<ItemId>::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
827 if( RemoveActor( *iter ) )
829 actorsReordered = true;
833 if( actorsReordered )
835 ReapplyAllConstraints( durationSeconds );
841 bool ItemView::RemoveActor(unsigned int itemId)
843 bool reordered( false );
845 ItemPoolIter removeIter = mItemPool.find( itemId );
846 if( removeIter != mItemPool.end() )
848 ReleaseActor(itemId, removeIter->second);
852 // Removing before the existing item range?
853 ItemPoolIter iter = mItemPool.begin();
854 if( iter != mItemPool.end() &&
855 iter->first > itemId )
857 // In order to decrement the first visible item ID
858 mItemPool.insert( Item(iter->first - 1, Actor()) );
860 removeIter = mItemPool.begin();
864 if( removeIter != mItemPool.end() )
868 // Adjust the remaining item IDs, for example if item 2 is removed:
869 // Initial actors: After insert:
870 // ID 1 - ActorA ID 1 - ActorA
871 // ID 2 - ActorB ID 2 - ActorC (previously ID 3)
872 // ID 3 - ActorC ID 3 - ActorB (previously ID 4)
874 for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter)
876 if( iter->first < mItemPool.rbegin()->first )
878 iter->second = mItemPool[ iter->first + 1 ];
882 mItemPool.erase( iter );
891 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
895 SetupActor( replacementItem, durationSeconds );
896 Self().Add( replacementItem.second );
898 const ItemPoolIter iter = mItemPool.find( replacementItem.first );
899 if( mItemPool.end() != iter )
901 ReleaseActor(iter->first, iter->second);
902 iter->second = replacementItem.second;
906 mItemPool.insert( replacementItem );
909 CalculateDomainSize(Self().GetCurrentSize());
911 mAddingItems = false;
914 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
916 for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
918 ReplaceItem( *iter, durationSeconds );
922 void ItemView::RemoveActorsOutsideRange( ItemRange range )
924 // Remove unwanted actors from the ItemView & ItemPool
925 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); )
927 unsigned int current = iter->first;
929 if( ! range.Within( current ) )
931 ReleaseActor(iter->first, iter->second);
933 mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid
942 void ItemView::AddActorsWithinRange( ItemRange range, float durationSeconds )
944 range.end = std::min(mItemFactory.GetNumberOfItems(), range.end);
946 // The order of addition depends on the scroll direction.
947 if (mRefreshOrderHint)
949 for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
951 AddNewActor( itemId, durationSeconds );
956 for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
958 AddNewActor( itemId-1, durationSeconds );
962 // Total number of items may change dynamically.
963 // Always recalculate the domain size to reflect that.
964 CalculateDomainSize(Self().GetCurrentSize());
967 void ItemView::AddNewActor( unsigned int itemId, float durationSeconds )
971 if( mItemPool.end() == mItemPool.find( itemId ) )
973 Actor actor = mItemFactory.NewItem( itemId );
977 Item newItem( itemId, actor );
979 mItemPool.insert( newItem );
981 SetupActor( newItem, durationSeconds );
986 mAddingItems = false;
989 void ItemView::SetupActor( Item item, float durationSeconds )
991 item.second.SetParentOrigin( mItemsParentOrigin );
992 item.second.SetAnchorPoint( mItemsAnchorPoint );
997 if( mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size ) )
999 item.second.SetResizePolicy( FIXED, ALL_DIMENSIONS );
1000 item.second.SetPreferredSize( size.GetVectorXY() );
1003 mActiveLayout->ApplyConstraints( item.second, item.first, durationSeconds, mScrollPositionObject, Self() );
1007 void ItemView::ReleaseActor( ItemId item, Actor actor )
1009 Self().Remove( actor );
1010 mItemFactory.ItemReleased(item, actor);
1013 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
1015 unsigned int itemCount = mItemFactory.GetNumberOfItems();
1017 ItemRange available(0u, itemCount);
1019 ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize );
1023 // Add the reserve items for scrolling
1024 unsigned int extra = layout.GetReserveItemCount(layoutSize);
1025 range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
1029 return range.Intersection(available);
1032 void ItemView::OnChildAdd(Actor& child)
1036 // We don't want to do this downcast check for any item added by ItemView itself.
1037 Dali::Toolkit::ScrollComponent scrollComponent = Dali::Toolkit::ScrollComponent::DownCast(child);
1040 // Set the scroll connector when scroll bar is being added
1041 scrollComponent.SetScrollConnector(mScrollConnector);
1046 bool ItemView::OnTouchEvent(const TouchEvent& event)
1048 // Ignore events with multiple-touch points
1049 if (event.GetPointCount() != 1)
1054 if (event.GetPoint(0).state == TouchPoint::Down)
1056 // Cancel ongoing scrolling etc.
1057 mGestureState = Gesture::Clear;
1059 mScrollDistance = 0.0f;
1060 mScrollSpeed = 0.0f;
1061 Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
1063 mScrollOvershoot = 0.0f;
1064 AnimateScrollOvershoot(0.0f);
1066 if(mScrollAnimation)
1068 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1071 RemoveAnimation(mScrollAnimation);
1074 return true; // consume since we're potentially scrolling
1077 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
1079 // Respond the mouse wheel event to scroll
1082 Actor self = Self();
1083 const Vector3 layoutSize = Self().GetCurrentSize();
1084 float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1085 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1087 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1088 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1089 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1090 mRefreshEnabled = true;
1093 if (mMouseWheelEventFinishedTimer.IsRunning())
1095 mMouseWheelEventFinishedTimer.Stop();
1098 mMouseWheelEventFinishedTimer.Start();
1103 bool ItemView::OnMouseWheelEventFinished()
1107 RemoveAnimation(mScrollAnimation);
1109 // No more mouse wheel events coming. Do the anchoring if enabled.
1110 mScrollAnimation = DoAnchoring();
1111 if (mScrollAnimation)
1113 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1114 mScrollAnimation.Play();
1118 mScrollOvershoot = 0.0f;
1119 AnimateScrollOvershoot(0.0f);
1121 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1128 void ItemView::ReapplyAllConstraints( float durationSeconds )
1130 for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1132 unsigned int id = iter->first;
1133 Actor actor = iter->second;
1135 actor.RemoveConstraints();
1136 mActiveLayout->ApplyConstraints(actor, id, durationSeconds, mScrollPositionObject, Self());
1140 void ItemView::OnItemsRemoved()
1142 CalculateDomainSize(Self().GetCurrentSize());
1144 // Adjust scroll-position after an item is removed
1147 float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize(), *mActiveLayout);
1149 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1153 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1155 Actor self = Self();
1156 float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1157 float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, targetPosition));
1158 mScrollOvershoot = targetPosition - clamppedPosition;
1159 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1161 return clamppedPosition;
1164 void ItemView::OnPan( const PanGesture& gesture )
1166 Actor self = Self();
1167 const Vector3 layoutSize = Self().GetCurrentSize();
1169 RemoveAnimation(mScrollAnimation);
1171 // Short-circuit if there is no active layout
1174 mGestureState = Gesture::Clear;
1178 mGestureState = gesture.state;
1180 switch (mGestureState)
1182 case Gesture::Finished:
1185 if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1186 mScrollSpeed > mMinimumSwipeSpeed)
1188 float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1190 mRefreshOrderHint = true;
1192 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1193 float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1197 if (mAnchoringEnabled)
1199 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1202 RemoveAnimation(mScrollAnimation);
1204 float flickAnimationDuration = Clamp( mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)))
1205 , DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION);
1207 mScrollAnimation = Animation::New(flickAnimationDuration);
1208 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1209 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1210 mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1213 // Check whether it has already scrolled to the end
1214 if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1216 AnimateScrollOvershoot(0.0f);
1220 // Anchoring may be triggered when there was no swipe
1221 if (!mScrollAnimation)
1223 mScrollAnimation = DoAnchoring();
1226 // Reset the overshoot if no scroll animation.
1227 if (!mScrollAnimation)
1229 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1231 AnimateScrollOvershoot(0.0f, false);
1236 case Gesture::Started: // Fall through
1238 mTotalPanDisplacement = Vector2::ZERO;
1239 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1240 mRefreshEnabled = true;
1243 case Gesture::Continuing:
1245 mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1246 mScrollSpeed = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1248 // Refresh order depends on the direction of the scroll; negative is towards the last item.
1249 mRefreshOrderHint = mScrollDistance < 0.0f;
1251 float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1253 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1255 float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1257 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1258 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1260 if( (firstItemScrollPosition >= 0.0f && currentOvershoot < 1.0f) || (firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) && currentOvershoot > -1.0f) )
1262 mTotalPanDisplacement += gesture.displacement;
1265 mScrollOvershoot = CalculateScrollOvershoot();
1266 mScrollPositionObject.SetProperty( ScrollConnector::OVERSHOOT, mScrollOvershoot );
1270 case Gesture::Cancelled:
1272 mScrollAnimation = DoAnchoring();
1280 if (mScrollAnimation)
1282 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1283 mScrollAnimation.Play();
1287 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1293 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1295 Actor nextFocusActor;
1299 if(!actor || actor == this->Self())
1301 nextFocusActor = GetItem(nextItemID);
1303 else if(actor && actor.GetParent() == this->Self())
1305 int itemID = GetItemId(actor);
1306 nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1307 nextFocusActor = GetItem(nextItemID);
1308 if(nextFocusActor == actor)
1310 // need to pass NULL actor back to focus manager
1311 nextFocusActor.Reset();
1312 return nextFocusActor;
1315 float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1316 Vector3 layoutSize = Self().GetCurrentSize();
1319 // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1320 ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1321 nextItemID = viewableItems.begin;
1322 nextFocusActor = GetItem(nextItemID);
1325 return nextFocusActor;
1328 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1330 // only in this function if our chosen focus actor was actually used
1331 if(commitedFocusableActor)
1333 int nextItemID = GetItemId(commitedFocusableActor);
1334 float layoutPosition = GetCurrentLayoutPosition(0);
1335 Vector3 layoutSize = Self().GetCurrentSize();
1337 float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1338 ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1342 Animation ItemView::DoAnchoring()
1344 Animation anchoringAnimation;
1345 Actor self = Self();
1347 if (mActiveLayout && mAnchoringEnabled)
1349 float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1351 anchoringAnimation = Animation::New(mAnchoringDuration);
1352 anchoringAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), anchorPosition, AlphaFunctions::EaseOut );
1353 anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1354 anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1357 AnimateScrollOvershoot(0.0f);
1361 return anchoringAnimation;
1364 void ItemView::OnScrollFinished(Animation& source)
1366 Actor self = Self();
1368 RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1370 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1372 if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1374 AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1378 // Reset the overshoot
1379 AnimateScrollOvershoot( 0.0f );
1381 mIsFlicking = false;
1383 mScrollOvershoot = 0.0f;
1386 void ItemView::OnLayoutActivationScrollFinished(Animation& source)
1388 RemoveAnimation(mScrollAnimation);
1389 mRefreshEnabled = true;
1390 DoRefresh(GetCurrentLayoutPosition(0), true);
1393 void ItemView::OnOvershootOnFinished(Animation& animation)
1395 mAnimatingOvershootOn = false;
1396 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1397 RemoveAnimation(mScrollOvershootAnimation);
1398 if(mAnimateOvershootOff)
1400 AnimateScrollOvershoot(0.0f);
1404 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1406 Actor self = Self();
1407 const Vector3 layoutSize = Self().GetCurrentSize();
1408 float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1410 if(durationSeconds > 0.0f)
1412 RemoveAnimation(mScrollAnimation);
1413 mScrollAnimation = Animation::New(durationSeconds);
1414 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1415 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1416 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1417 mScrollAnimation.Play();
1421 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1422 AnimateScrollOvershoot(0.0f);
1425 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1426 mRefreshEnabled = true;
1429 void ItemView::RemoveAnimation(Animation& animation)
1433 // Cease animating, and reset handle.
1439 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1441 Actor self = Self();
1443 Vector3 firstItemPosition(Vector3::ZERO);
1444 Vector3 lastItemPosition(Vector3::ZERO);
1448 firstItemPosition = mActiveLayout->GetItemPosition( 0,0,layoutSize );
1450 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1451 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1452 lastItemPosition = mActiveLayout->GetItemPosition( fabs(minLayoutPosition),fabs(minLayoutPosition),layoutSize );
1456 if(IsHorizontal(mActiveLayout->GetOrientation()))
1458 self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f));
1459 self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f));
1460 domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1464 self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f));
1465 self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f));
1466 domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1469 mScrollConnector.SetScrollDomain(minLayoutPosition, 0.0f, domainSize);
1471 bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1472 self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable);
1473 self.SetProperty(mPropertyCanScrollHorizontal, false);
1477 Vector3 ItemView::GetDomainSize() const
1479 Actor self = Self();
1481 float minScrollPosition = self.GetProperty<float>(mPropertyPositionMin);
1482 float maxScrollPosition = self.GetProperty<float>(mPropertyPositionMax);
1484 return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1487 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1489 Actor self = Self();
1491 float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1492 float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1493 float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1495 return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1498 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1500 Vector3 firstItemPosition( mActiveLayout->GetItemPosition(0, layoutPosition, layoutSize ) );
1501 return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1504 Vector3 ItemView::GetCurrentScrollPosition() const
1506 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1507 return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1510 void ItemView::AddOverlay(Actor actor)
1515 void ItemView::RemoveOverlay(Actor actor)
1517 Self().Remove(actor);
1520 void ItemView::ScrollTo(const Vector3& position, float duration)
1522 Actor self = Self();
1523 const Vector3 layoutSize = Self().GetCurrentSize();
1525 float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1529 RemoveAnimation(mScrollAnimation);
1530 mScrollAnimation = Animation::New(duration);
1531 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1532 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1533 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1534 mScrollAnimation.Play();
1538 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1539 AnimateScrollOvershoot(0.0f);
1542 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1543 mRefreshEnabled = true;
1546 void ItemView::SetOvershootEffectColor( const Vector4& color )
1548 mOvershootEffectColor = color;
1549 if( mOvershootOverlay )
1551 mOvershootOverlay.SetColor( color );
1555 void ItemView::SetOvershootEnabled( bool enable )
1557 Actor self = Self();
1560 Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX;
1561 mOvershootOverlay = CreateBouncingEffectActor( effectOvershootPropertyIndex );
1562 mOvershootOverlay.SetColor(mOvershootEffectColor);
1563 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
1564 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
1565 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
1566 self.Add(mOvershootOverlay);
1568 Constraint constraint = Constraint::New<Vector3>( Actor::Property::SIZE,
1569 ParentSource( mPropertyScrollDirection ),
1570 Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1571 ParentSource( Actor::Property::SIZE ),
1572 OvershootOverlaySizeConstraint() );
1573 mOvershootOverlay.ApplyConstraint(constraint);
1574 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.width, OVERSHOOT_BOUNCE_ACTOR_DEFAULT_SIZE.height);
1576 constraint = Constraint::New<Quaternion>( Actor::Property::ORIENTATION,
1577 ParentSource( mPropertyScrollDirection ),
1578 Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1579 OvershootOverlayRotationConstraint() );
1580 mOvershootOverlay.ApplyConstraint(constraint);
1582 constraint = Constraint::New<Vector3>( Actor::Property::POSITION,
1583 ParentSource( Actor::Property::SIZE ),
1584 ParentSource( mPropertyScrollDirection ),
1585 Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1586 OvershootOverlayPositionConstraint() );
1587 mOvershootOverlay.ApplyConstraint(constraint);
1589 constraint = Constraint::New<bool>( Actor::Property::VISIBLE,
1590 ParentSource( mPropertyCanScrollVertical ),
1591 OvershootOverlayVisibilityConstraint() );
1592 mOvershootOverlay.ApplyConstraint(constraint);
1594 constraint = Constraint::New<float>( effectOvershootPropertyIndex,
1595 Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1596 EqualToConstraint() );
1597 mOvershootOverlay.ApplyConstraint(constraint);
1601 if( mOvershootOverlay )
1603 self.Remove(mOvershootOverlay);
1604 mOvershootOverlay.Reset();
1609 float ItemView::CalculateScrollOvershoot()
1611 float overshoot = 0.0f;
1615 // The overshoot must be calculated from the accumulated pan gesture displacement
1616 // since the pan gesture starts.
1617 Actor self = Self();
1618 float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1619 float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1620 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1621 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1622 float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta));
1623 overshoot = positionDelta - clamppedPosition;
1626 return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f);
1629 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1631 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1633 // make sure we animate back if needed
1634 mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1636 if( mAnimatingOvershootOn )
1638 // animating on, do not allow animate off
1642 if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0)
1644 float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1645 float duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed;
1647 RemoveAnimation(mScrollOvershootAnimation);
1648 mScrollOvershootAnimation = Animation::New(duration);
1649 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1650 mScrollOvershootAnimation.AnimateTo( Property(mScrollPositionObject, ScrollConnector::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1651 mScrollOvershootAnimation.Play();
1653 mAnimatingOvershootOn = animatingOn;
1657 mScrollPositionObject.SetProperty( ScrollConnector::OVERSHOOT, overshootAmount );
1661 void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin )
1663 if( parentOrigin != mItemsParentOrigin )
1665 mItemsParentOrigin = parentOrigin;
1666 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1668 iter->second.SetParentOrigin(parentOrigin);
1673 Vector3 ItemView::GetItemsParentOrigin() const
1675 return mItemsParentOrigin;
1678 void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint )
1680 if( anchorPoint != mItemsAnchorPoint )
1682 mItemsAnchorPoint = anchorPoint;
1683 for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1685 iter->second.SetAnchorPoint(anchorPoint);
1690 Vector3 ItemView::GetItemsAnchorPoint() const
1692 return mItemsAnchorPoint;
1695 void ItemView::GetItemsRange(ItemRange& range)
1697 if( !mItemPool.empty() )
1699 range.begin = mItemPool.begin()->first;
1700 range.end = mItemPool.rbegin()->first + 1;
1709 void ItemView::OnScrollPositionChanged( float position )
1711 // Cancel scroll animation to prevent any fighting of setting the scroll position property.
1712 RemoveAnimation(mScrollAnimation);
1714 // Refresh the cache immediately when the scroll position is changed.
1715 DoRefresh(position, false); // No need to cache extra items.
1718 } // namespace Internal
1720 } // namespace Toolkit