2 * Copyright (c) 2020 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/actors/layer.h>
26 #include <dali/public-api/animation/constraint.h>
27 #include <dali/public-api/animation/constraints.h>
28 #include <dali/devel-api/common/stage.h>
29 #include <dali/public-api/events/wheel-event.h>
30 #include <dali/public-api/events/touch-event.h>
31 #include <dali/public-api/object/type-registry.h>
32 #include <dali/public-api/object/type-registry-helper.h>
33 #include <dali/devel-api/object/property-helper-devel.h>
36 #include <dali-toolkit/devel-api/controls/scroll-bar/scroll-bar.h>
37 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
38 #include <dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout.h>
39 #include <dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout-property.h>
40 #include <dali-toolkit/internal/controls/scrollable/item-view/grid-layout.h>
41 #include <dali-toolkit/internal/controls/scrollable/item-view/depth-layout.h>
42 #include <dali-toolkit/internal/controls/scrollable/item-view/spiral-layout.h>
43 #include <dali-toolkit/internal/controls/scrollable/bouncing-effect-actor.h>
48 namespace // Unnamed namespace
51 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
52 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
53 const float DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
55 const float DEFAULT_MINIMUM_SWIPE_DURATION = 0.45f;
56 const float DEFAULT_MAXIMUM_SWIPE_DURATION = 2.6f;
58 const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items
59 const int WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second
61 const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second
63 const float MILLISECONDS_PER_SECONDS = 1000.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 unsigned int OVERSHOOT_SIZE_CONSTRAINT_TAG(42);
72 * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
74 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
76 Radian scrollDirection(layout.GetScrollDirection());
78 float cosTheta = cosf(scrollDirection);
79 float sinTheta = sinf(scrollDirection);
81 return panDistance.x * sinTheta + panDistance.y * cosTheta;
84 // Overshoot overlay constraints
85 struct OvershootOverlaySizeConstraint
87 OvershootOverlaySizeConstraint( float height )
88 : mOvershootHeight( height )
92 void operator()( Vector3& current, const PropertyInputContainer& inputs )
94 const Vector2& parentScrollDirection = inputs[0]->GetVector2();
95 const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
96 const Vector3& parentSize = inputs[2]->GetVector3();
98 if(Toolkit::IsVertical(layoutOrientation))
100 current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
104 current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
107 current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? mOvershootHeight : mOvershootHeight*0.5f;
110 float mOvershootHeight;
113 void OvershootOverlayRotationConstraint( Quaternion& current, const PropertyInputContainer& inputs )
115 const Vector2& parentScrollDirection = inputs[0]->GetVector2();
116 const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[1]->GetInteger());
117 const float parentOvershoot = inputs[2]->GetFloat();
119 float multiplier = 0;
120 if(Toolkit::IsVertical(layoutOrientation))
122 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
124 if( (layoutOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
125 || (layoutOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
134 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
135 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
146 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
148 if( (layoutOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
149 ||(layoutOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
158 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
159 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
169 current = Quaternion( Radian( multiplier * Math::PI ), Vector3::ZAXIS );
172 void OvershootOverlayPositionConstraint( Vector3& current, const PropertyInputContainer& inputs )
174 const Vector3& parentSize = inputs[0]->GetVector3();
175 const Vector2& parentScrollDirection = inputs[1]->GetVector2();
176 const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast<Toolkit::ControlOrientation::Type>(inputs[2]->GetInteger());
177 const float parentOvershoot = inputs[3]->GetFloat();
179 Vector3 relativeOffset;
181 if(Toolkit::IsVertical(layoutOrientation))
183 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
185 if( (layoutOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
186 || (layoutOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
188 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
192 relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
195 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
196 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
198 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
202 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
207 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
209 if( (layoutOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
210 || (layoutOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
212 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
216 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
219 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
220 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
222 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
226 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
230 current = relativeOffset * parentSize;
233 void OvershootOverlayVisibilityConstraint( bool& current, const PropertyInputContainer& inputs )
235 current = inputs[0]->GetBoolean();
238 } // unnamed namespace
249 namespace // unnamed namespace
254 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ItemView, Toolkit::Scrollable, NULL)
256 DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "minimumSwipeSpeed", FLOAT, MINIMUM_SWIPE_SPEED )
257 DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "minimumSwipeDistance", FLOAT, MINIMUM_SWIPE_DISTANCE )
258 DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "wheelScrollDistanceStep", FLOAT, WHEEL_SCROLL_DISTANCE_STEP )
259 DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "snapToItemEnabled", BOOLEAN, SNAP_TO_ITEM_ENABLED )
260 DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "refreshInterval", FLOAT, REFRESH_INTERVAL )
261 DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "layout", ARRAY, LAYOUT )
264 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layoutPosition", FLOAT, LAYOUT_POSITION)
265 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollSpeed", FLOAT, SCROLL_SPEED)
266 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "overshoot", FLOAT, OVERSHOOT)
267 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollDirection", VECTOR2, SCROLL_DIRECTION)
268 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layoutOrientation", INTEGER, LAYOUT_ORIENTATION)
269 DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollContentSize", FLOAT, SCROLL_CONTENT_SIZE)
271 DALI_SIGNAL_REGISTRATION( Toolkit, ItemView, "layoutActivated", LAYOUT_ACTIVATED_SIGNAL )
273 DALI_ACTION_REGISTRATION( Toolkit, ItemView, "stopScrolling", ACTION_STOP_SCROLLING )
275 DALI_ACTION_REGISTRATION( Toolkit, ItemView, "enableRefresh", ACTION_ENABLE_REFRESH )
276 DALI_ACTION_REGISTRATION( Toolkit, ItemView, "disableRefresh", ACTION_DISABLE_REFRESH )
278 DALI_TYPE_REGISTRATION_END()
280 const ItemIter FindItemById( ItemContainer& items, ItemId id )
282 for( ItemIter iter = items.begin(); items.end() != iter; ++iter )
284 if( iter->first == id )
293 void InsertToItemContainer( ItemContainer& items, Item item )
295 if( items.end() == FindItemById( items, item.first ) )
297 ItemIter iterToInsert = std::lower_bound( items.begin(), items.end(), item );
298 items.insert( iterToInsert, item );
304 * Helper to apply size constraint to mOvershootOverlay
305 * @param[in] overshootOverlay The overshootOverlay actor
306 * @param[in] The required height
308 void ApplyOvershootSizeConstraint( Actor overshootOverlay, float height )
310 Constraint constraint = Constraint::New<Vector3>( overshootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint( height ) );
311 constraint.AddSource( ParentSource( Dali::Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
312 constraint.AddSource( ParentSource( Dali::Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
313 constraint.AddSource( ParentSource( Dali::Actor::Property::SIZE ) );
314 constraint.SetTag( OVERSHOOT_SIZE_CONSTRAINT_TAG );
318 } // unnamed namespace
320 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
322 // Create the implementation
323 ItemViewPtr itemView(new ItemView(factory));
325 // Pass ownership to CustomActor via derived handle
326 Dali::Toolkit::ItemView handle(*itemView);
328 // Second-phase init of the implementation
329 // This can only be done after the CustomActor connection has been made...
330 itemView->Initialize();
335 ItemView::ItemView(ItemFactory& factory)
336 : Scrollable( ControlBehaviour( DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS | REQUIRES_WHEEL_EVENTS | REQUIRES_KEYBOARD_NAVIGATION_SUPPORT ) ),
337 mItemFactory(factory),
338 mItemsParentOrigin(ParentOrigin::CENTER),
339 mItemsAnchorPoint(AnchorPoint::CENTER),
340 mTotalPanDisplacement(Vector2::ZERO),
342 mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
343 mRefreshIntervalLayoutPositions(0.0f),
344 mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
345 mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
346 mWheelScrollDistanceStep(0.0f),
347 mScrollDistance(0.0f),
349 mScrollOvershoot(0.0f),
350 mGestureState(Gesture::Clear),
351 mAnimatingOvershootOn(false),
352 mAnimateOvershootOff(false),
353 mAnchoringEnabled(false),
354 mRefreshOrderHint(true/*Refresh item 0 first*/),
357 mRefreshEnabled(true),
358 mRefreshNotificationEnabled(true),
363 void ItemView::OnInitialize()
367 Vector2 stageSize = Stage::GetCurrent().GetSize();
368 mWheelScrollDistanceStep = stageSize.y * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
370 self.TouchSignal().Connect( this, &ItemView::OnTouch );
371 EnableGestureDetection(Gesture::Type(Gesture::Pan));
373 mWheelEventFinishedTimer = Timer::New( WHEEL_EVENT_FINISHED_TIME_OUT );
374 mWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnWheelEventFinished );
376 SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS);
379 ItemView::~ItemView()
383 unsigned int ItemView::GetLayoutCount() const
385 return mLayouts.size();
388 void ItemView::AddLayout(ItemLayout& layout)
390 mLayouts.push_back(ItemLayoutPtr(&layout));
393 void ItemView::RemoveLayout(unsigned int layoutIndex)
395 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
397 if (mActiveLayout == mLayouts[layoutIndex].Get())
399 mActiveLayout = NULL;
402 mLayouts.erase(mLayouts.begin() + layoutIndex);
405 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
407 return mLayouts[layoutIndex];
410 ItemLayoutPtr ItemView::GetActiveLayout() const
412 return ItemLayoutPtr(mActiveLayout);
415 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
417 return Self().GetCurrentProperty< float >( Toolkit::ItemView::Property::LAYOUT_POSITION ) + static_cast<float>( itemId );
420 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
422 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
424 mRefreshEnabled = false;
428 // The ItemView size should match the active layout size
429 self.SetProperty( Actor::Property::SIZE, targetSize);
430 mActiveLayoutTargetSize = targetSize;
432 // Switch to the new layout
433 mActiveLayout = mLayouts[layoutIndex].Get();
435 // Move the items to the new layout positions...
437 for (ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
439 unsigned int itemId = iter->first;
440 Actor actor = iter->second;
442 // Remove constraints from previous layout
443 actor.RemoveConstraints();
445 mActiveLayout->ApplyConstraints(actor, itemId, targetSize, Self() );
448 mActiveLayout->GetItemSize( itemId, targetSize, size );
449 actor.SetProperty( Actor::Property::SIZE, size.GetVectorXY() );
452 // Refresh the new layout
453 ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false/* don't reserve extra*/);
454 AddActorsWithinRange( range, targetSize );
456 // Scroll to an appropriate layout position
458 bool scrollAnimationNeeded(false);
459 float firstItemScrollPosition(0.0f);
461 float current = GetCurrentLayoutPosition(0);
462 float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
464 if (current < minimum)
466 scrollAnimationNeeded = true;
467 firstItemScrollPosition = minimum;
469 else if (mAnchoringEnabled)
471 scrollAnimationNeeded = true;
472 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
475 if (scrollAnimationNeeded)
477 RemoveAnimation(mScrollAnimation);
478 mScrollAnimation = Animation::New(durationSeconds);
479 mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT );
480 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished);
481 mScrollAnimation.Play();
485 // Emit the layout activated signal
486 mLayoutActivatedSignal.Emit();
489 AnimateScrollOvershoot(0.0f);
490 mScrollOvershoot = 0.0f;
492 Radian scrollDirection(mActiveLayout->GetScrollDirection());
493 self.SetProperty(Toolkit::ItemView::Property::SCROLL_DIRECTION, Vector2(sinf(scrollDirection), cosf(scrollDirection)));
494 self.SetProperty(Toolkit::ItemView::Property::LAYOUT_ORIENTATION, static_cast<int>(mActiveLayout->GetOrientation()));
495 self.SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed);
497 CalculateDomainSize(targetSize);
500 void ItemView::DeactivateCurrentLayout()
504 for (ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
506 Actor actor = iter->second;
507 actor.RemoveConstraints();
510 mActiveLayout = NULL;
514 void ItemView::OnRefreshNotification(PropertyNotification& source)
516 if( mRefreshNotificationEnabled )
518 // Cancel scroll animation to prevent any fighting of setting the scroll position property by scroll bar during fast scroll.
519 if(!mRefreshEnabled && mScrollAnimation)
521 RemoveAnimation(mScrollAnimation);
524 // Only cache extra items when it is not a fast scroll
525 DoRefresh(GetCurrentLayoutPosition(0), mRefreshEnabled || mScrollAnimation);
529 void ItemView::Refresh()
531 for (ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
533 ReleaseActor( iter->first, iter->second );
537 DoRefresh(GetCurrentLayoutPosition(0), true);
540 void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
544 ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/);
545 RemoveActorsOutsideRange( range );
546 AddActorsWithinRange( range, Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ) );
548 mScrollUpdatedSignal.Emit( Vector2(0.0f, currentLayoutPosition) );
552 void ItemView::SetMinimumSwipeSpeed(float speed)
554 mMinimumSwipeSpeed = speed;
557 float ItemView::GetMinimumSwipeSpeed() const
559 return mMinimumSwipeSpeed;
562 void ItemView::SetMinimumSwipeDistance(float distance)
564 mMinimumSwipeDistance = distance;
567 float ItemView::GetMinimumSwipeDistance() const
569 return mMinimumSwipeDistance;
572 void ItemView::SetWheelScrollDistanceStep(float step)
574 mWheelScrollDistanceStep = step;
577 float ItemView::GetWheelScrollDistanceStep() const
579 return mWheelScrollDistanceStep;
582 void ItemView::SetAnchoring(bool enabled)
584 mAnchoringEnabled = enabled;
587 bool ItemView::GetAnchoring() const
589 return mAnchoringEnabled;
592 void ItemView::SetAnchoringDuration(float durationSeconds)
594 mAnchoringDuration = durationSeconds;
597 float ItemView::GetAnchoringDuration() const
599 return mAnchoringDuration;
602 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
604 if( !Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions) )
606 mRefreshIntervalLayoutPositions = intervalLayoutPositions;
609 if(mRefreshNotification)
611 self.RemovePropertyNotification(mRefreshNotification);
613 mRefreshNotification = self.AddPropertyNotification( Toolkit::ItemView::Property::LAYOUT_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) );
614 mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification );
618 float ItemView::GetRefreshInterval() const
620 return mRefreshIntervalLayoutPositions;
623 void ItemView::SetRefreshEnabled(bool enabled)
625 mRefreshEnabled = enabled;
628 Actor ItemView::GetItem(unsigned int itemId) const
632 for ( ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
634 if( iter->first == itemId )
636 actor = iter->second;
644 unsigned int ItemView::GetItemId( Actor actor ) const
646 unsigned int itemId( 0 );
648 for ( ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
650 if( iter->second == actor )
652 itemId = iter->first;
660 void ItemView::InsertItem( Item newItem, float durationSeconds )
663 Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
665 Actor displacedActor;
666 ItemIter afterDisplacedIter = mItemPool.end();
668 ItemIter foundIter = FindItemById( mItemPool, newItem.first );
669 if( mItemPool.end() != foundIter )
671 SetupActor( newItem, layoutSize );
672 Self().Add( newItem.second );
674 displacedActor = foundIter->second;
675 foundIter->second = newItem.second;
677 afterDisplacedIter = ++foundIter;
681 // Inserting before the existing item range?
682 ItemIter iter = mItemPool.begin();
683 if( iter != mItemPool.end() &&
684 iter->first > newItem.first )
686 displacedActor = iter->second;
687 iter = mItemPool.erase( iter ); // iter is still valid after the erase
689 afterDisplacedIter = iter;
695 // Move the existing actors to make room
696 for( ItemIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter )
698 Actor temp = iter->second;
699 iter->second = displacedActor;
700 displacedActor = temp;
702 iter->second.RemoveConstraints();
703 mActiveLayout->ApplyConstraints( iter->second, iter->first, layoutSize, Self() );
707 ItemContainer::reverse_iterator lastIter = mItemPool.rbegin();
708 if ( lastIter != mItemPool.rend() )
710 ItemId lastId = lastIter->first;
711 Item lastItem( lastId + 1, displacedActor );
712 InsertToItemContainer( mItemPool, lastItem );
714 lastItem.second.RemoveConstraints();
715 mActiveLayout->ApplyConstraints( lastItem.second, lastItem.first, layoutSize, Self() );
719 CalculateDomainSize( layoutSize );
721 mAddingItems = false;
724 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
727 Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
729 // Insert from lowest id to highest
730 ItemContainer sortedItems(newItems);
731 std::sort( sortedItems.begin(), sortedItems.end() );
733 for( ItemIter iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
735 Self().Add( iter->second );
737 ItemIter foundIter = FindItemById( mItemPool, iter->first );
738 if( mItemPool.end() != foundIter )
740 Actor moveMe = foundIter->second;
741 foundIter->second = iter->second;
743 // Move the existing actors to make room
744 for( ItemIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
746 Actor temp = iter->second;
747 iter->second = moveMe;
752 ItemId lastId = mItemPool.rbegin()->first;
753 Item lastItem( lastId + 1, moveMe );
754 InsertToItemContainer( mItemPool, lastItem );
758 InsertToItemContainer( mItemPool, *iter );
762 // Relayout everything
763 for (ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
766 if( std::binary_search( sortedItems.begin(), sortedItems.end(), *iter ) )
768 SetupActor( *iter, layoutSize );
772 iter->second.RemoveConstraints();
773 mActiveLayout->ApplyConstraints( iter->second, iter->first, layoutSize, Self() );
777 CalculateDomainSize( layoutSize );
779 mAddingItems = false;
782 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
784 bool actorsReordered = RemoveActor( itemId );
785 if( actorsReordered )
787 ReapplyAllConstraints();
793 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
795 bool actorsReordered( false );
797 // Remove from highest id to lowest
798 ItemIdContainer sortedItems(itemIds);
799 std::sort( sortedItems.begin(), sortedItems.end() );
801 for( ItemIdContainer::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
803 if( RemoveActor( *iter ) )
805 actorsReordered = true;
809 if( actorsReordered )
811 ReapplyAllConstraints();
817 bool ItemView::RemoveActor(unsigned int itemId)
819 bool reordered( false );
821 ItemIter removeIter = FindItemById( mItemPool, itemId );
822 if( removeIter != mItemPool.end() )
824 ReleaseActor(itemId, removeIter->second);
828 // Removing before the existing item range?
829 ItemIter iter = mItemPool.begin();
830 if( iter != mItemPool.end() &&
831 iter->first > itemId )
833 // In order to decrement the first visible item ID
834 InsertToItemContainer( mItemPool, Item(iter->first - 1, Actor()) );
836 removeIter = mItemPool.begin();
840 if( removeIter != mItemPool.end() )
844 // Adjust the remaining item IDs, for example if item 2 is removed:
845 // Initial actors: After insert:
846 // ID 1 - ActorA ID 1 - ActorA
847 // ID 2 - ActorB ID 2 - ActorC (previously ID 3)
848 // ID 3 - ActorC ID 3 - ActorB (previously ID 4)
850 for (ItemIter iter = removeIter; iter != mItemPool.end(); ++iter)
852 if( iter->first < mItemPool.rbegin()->first )
854 iter->second = ( iter + 1 )->second;
858 mItemPool.erase( iter );
867 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
870 Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
872 SetupActor( replacementItem, layoutSize );
873 Self().Add( replacementItem.second );
875 const ItemIter iter = FindItemById( mItemPool, replacementItem.first );
876 if( mItemPool.end() != iter )
878 ReleaseActor(iter->first, iter->second);
879 iter->second = replacementItem.second;
883 InsertToItemContainer( mItemPool, replacementItem );
886 CalculateDomainSize( layoutSize );
888 mAddingItems = false;
891 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
893 for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
895 ReplaceItem( *iter, durationSeconds );
899 void ItemView::RemoveActorsOutsideRange( ItemRange range )
901 // Remove unwanted actors from the ItemView & ItemPool
902 for (ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); )
904 unsigned int current = iter->first;
906 if( ! range.Within( current ) )
908 ReleaseActor(iter->first, iter->second);
910 iter = mItemPool.erase( iter ); // iter is still valid after the erase
919 void ItemView::AddActorsWithinRange( ItemRange range, const Vector3& layoutSize )
921 range.end = std::min(mItemFactory.GetNumberOfItems(), range.end);
923 // The order of addition depends on the scroll direction.
924 if (mRefreshOrderHint)
926 for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
928 AddNewActor( itemId, layoutSize );
933 for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
935 AddNewActor( itemId-1, layoutSize );
939 // Total number of items may change dynamically.
940 // Always recalculate the domain size to reflect that.
941 CalculateDomainSize(Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ));
944 void ItemView::AddNewActor( unsigned int itemId, const Vector3& layoutSize )
948 if( mItemPool.end() == FindItemById( mItemPool, itemId ) )
950 Actor actor = mItemFactory.NewItem( itemId );
954 Item newItem( itemId, actor );
956 InsertToItemContainer( mItemPool, newItem );
958 SetupActor( newItem, layoutSize );
963 mAddingItems = false;
966 void ItemView::SetupActor( Item item, const Vector3& layoutSize )
968 item.second.SetProperty( Actor::Property::PARENT_ORIGIN, mItemsParentOrigin );
969 item.second.SetProperty( Actor::Property::ANCHOR_POINT, mItemsAnchorPoint );
974 mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size );
975 item.second.SetProperty( Actor::Property::SIZE, size.GetVectorXY() );
977 mActiveLayout->ApplyConstraints( item.second, item.first, layoutSize, Self() );
981 void ItemView::ReleaseActor( ItemId item, Actor actor )
983 Self().Remove( actor );
984 mItemFactory.ItemReleased(item, actor);
987 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
989 unsigned int itemCount = mItemFactory.GetNumberOfItems();
991 ItemRange available(0u, itemCount);
993 ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize );
997 // Add the reserve items for scrolling
998 unsigned int extra = layout.GetReserveItemCount(layoutSize);
999 range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
1003 return range.Intersection(available);
1006 void ItemView::OnChildAdd(Actor& child)
1010 // We don't want to do this downcast check for any item added by ItemView itself.
1011 Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
1014 scrollBar.SetScrollPropertySource(Self(),
1015 Toolkit::ItemView::Property::LAYOUT_POSITION,
1016 Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
1017 Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y,
1018 Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE);
1022 Scrollable::OnChildAdd( child );
1025 bool ItemView::OnWheelEvent(const WheelEvent& event)
1027 // Respond the wheel event to scroll
1030 Actor self = Self();
1031 const Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1032 float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.GetDelta() * mWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1033 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1035 self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1037 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1038 mRefreshEnabled = true;
1041 if (mWheelEventFinishedTimer.IsRunning())
1043 mWheelEventFinishedTimer.Stop();
1046 mWheelEventFinishedTimer.Start();
1051 bool ItemView::OnWheelEventFinished()
1055 RemoveAnimation(mScrollAnimation);
1057 // No more wheel events coming. Do the anchoring if enabled.
1058 mScrollAnimation = DoAnchoring();
1059 if (mScrollAnimation)
1061 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1062 mScrollAnimation.Play();
1066 mScrollOvershoot = 0.0f;
1067 AnimateScrollOvershoot(0.0f);
1069 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1076 void ItemView::ReapplyAllConstraints()
1078 Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1080 for (ConstItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1082 unsigned int id = iter->first;
1083 Actor actor = iter->second;
1085 actor.RemoveConstraints();
1086 mActiveLayout->ApplyConstraints(actor, id, layoutSize, Self());
1090 void ItemView::OnItemsRemoved()
1092 CalculateDomainSize(Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ));
1094 // Adjust scroll-position after an item is removed
1097 float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ), *mActiveLayout);
1098 Self().SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1102 float ItemView::ClampFirstItemPosition( float targetPosition, const Vector3& targetSize, ItemLayout& layout, bool updateOvershoot )
1104 Actor self = Self();
1105 float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1106 float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, targetPosition));
1107 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1109 if( updateOvershoot )
1111 mScrollOvershoot = targetPosition - clamppedPosition;
1114 return clamppedPosition;
1117 bool ItemView::OnTouch( Actor actor, const TouchEvent& touch )
1119 // Ignore events with multiple-touch points
1120 if (touch.GetPointCount() != 1)
1125 if ( touch.GetState( 0 ) == PointState::DOWN )
1127 // Cancel ongoing scrolling etc.
1128 mGestureState = Gesture::Clear;
1130 mScrollDistance = 0.0f;
1131 mScrollSpeed = 0.0f;
1132 Self().SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed);
1134 mScrollOvershoot = 0.0f;
1135 AnimateScrollOvershoot(0.0f);
1137 if(mScrollAnimation)
1139 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1142 RemoveAnimation(mScrollAnimation);
1145 return false; // Do not consume as we're potentially scrolling (detecting pan gestures)
1148 void ItemView::OnPan( const PanGesture& gesture )
1150 Actor self = Self();
1151 const Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1153 RemoveAnimation(mScrollAnimation);
1155 // Short-circuit if there is no active layout
1158 mGestureState = Gesture::Clear;
1162 mGestureState = gesture.state;
1164 switch (mGestureState)
1166 case Gesture::Finished:
1169 if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1170 mScrollSpeed > mMinimumSwipeSpeed)
1172 float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1174 mRefreshOrderHint = true;
1176 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1177 float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1181 if (mAnchoringEnabled)
1183 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1186 RemoveAnimation(mScrollAnimation);
1188 float flickAnimationDuration = Clamp( mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)))
1189 , DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION);
1191 mScrollAnimation = Animation::New(flickAnimationDuration);
1192 mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION ), firstItemScrollPosition, AlphaFunction::EASE_OUT );
1193 mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT );
1197 // Check whether it has already scrolled to the end
1198 if( fabs(currentLayoutPosition - firstItemScrollPosition) < Math::MACHINE_EPSILON_0 )
1200 AnimateScrollOvershoot( 0.0f );
1201 RemoveAnimation( mScrollAnimation );
1205 // Anchoring may be triggered when there was no swipe
1206 if (!mScrollAnimation)
1208 mScrollAnimation = DoAnchoring();
1211 // Reset the overshoot if no scroll animation.
1212 if (!mScrollAnimation)
1214 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1216 AnimateScrollOvershoot(0.0f, false);
1221 case Gesture::Started: // Fall through
1223 mTotalPanDisplacement = Vector2::ZERO;
1224 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1225 mRefreshEnabled = true;
1228 case Gesture::Continuing:
1230 mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1231 mScrollSpeed = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1233 // Refresh order depends on the direction of the scroll; negative is towards the last item.
1234 mRefreshOrderHint = mScrollDistance < 0.0f;
1236 float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1238 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1240 float currentOvershoot = self.GetCurrentProperty< float >( Toolkit::ItemView::Property::OVERSHOOT );
1242 self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1244 if( ( firstItemScrollPosition >= 0.0f &&
1245 currentOvershoot < 1.0f ) ||
1246 ( firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) &&
1247 currentOvershoot > -1.0f ) )
1249 mTotalPanDisplacement += gesture.displacement;
1252 mScrollOvershoot = CalculateScrollOvershoot();
1254 // If the view is moved in a direction against the overshoot indicator, then the indicator should be animated off.
1255 // First make sure we are not in an animation, otherwise a previously started
1256 // off-animation will be overwritten as the user continues scrolling.
1259 // Check if the movement is against the current overshoot amount (if we are currently displaying the indicator).
1260 if( ( ( mScrollOvershoot > Math::MACHINE_EPSILON_0 ) && ( mScrollDistance < -Math::MACHINE_EPSILON_0 ) ) ||
1261 ( ( mScrollOvershoot < Math::MACHINE_EPSILON_0 ) && ( mScrollDistance > Math::MACHINE_EPSILON_0 ) ) )
1263 // The user has moved against the indicator direction.
1264 // First, we reset the total displacement. This means the overshoot amount will become zero the next frame,
1265 // and if the user starts dragging in the overshoot direction again, the indicator will appear once more.
1266 mTotalPanDisplacement = Vector2::ZERO;
1267 // Animate the overshoot indicator off.
1268 AnimateScrollOvershoot( 0.0f, false );
1272 // Only set the property directly if we are not animating the overshoot away,
1273 // as otherwise this will overwrite the animation generated value.
1274 self.SetProperty( Toolkit::ItemView::Property::OVERSHOOT, mScrollOvershoot );
1280 case Gesture::Cancelled:
1282 mScrollAnimation = DoAnchoring();
1290 if (mScrollAnimation)
1292 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1293 mScrollAnimation.Play();
1297 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1303 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1305 Actor nextFocusActor;
1309 if(!actor || actor == this->Self())
1311 nextFocusActor = GetItem(nextItemID);
1313 else if(actor && actor.GetParent() == this->Self())
1315 int itemID = GetItemId(actor);
1316 nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1317 nextFocusActor = GetItem(nextItemID);
1318 if(nextFocusActor == actor)
1320 // need to pass NULL actor back to focus manager
1321 nextFocusActor.Reset();
1322 return nextFocusActor;
1325 float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1326 Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1329 // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1330 ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1331 nextItemID = viewableItems.begin;
1332 nextFocusActor = GetItem(nextItemID);
1335 return nextFocusActor;
1338 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1340 // only in this function if our chosen focus actor was actually used
1341 if(commitedFocusableActor)
1343 int nextItemID = GetItemId(commitedFocusableActor);
1344 float layoutPosition = GetCurrentLayoutPosition(0);
1345 Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1347 float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1348 ScrollTo(Vector2(0.0f, scrollTo), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1352 Animation ItemView::DoAnchoring()
1354 Animation anchoringAnimation;
1355 Actor self = Self();
1357 if (mActiveLayout && mAnchoringEnabled)
1359 float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1361 anchoringAnimation = Animation::New(mAnchoringDuration);
1362 anchoringAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), anchorPosition, AlphaFunction::EASE_OUT );
1363 anchoringAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT );
1366 AnimateScrollOvershoot(0.0f);
1370 return anchoringAnimation;
1373 void ItemView::OnScrollFinished(Animation& source)
1375 Actor self = Self();
1377 RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1379 mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1381 if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1383 AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1387 // Reset the overshoot
1388 AnimateScrollOvershoot( 0.0f );
1390 mIsFlicking = false;
1392 mScrollOvershoot = 0.0f;
1395 void ItemView::OnLayoutActivationScrollFinished(Animation& source)
1397 RemoveAnimation(mScrollAnimation);
1398 mRefreshEnabled = true;
1399 DoRefresh(GetCurrentLayoutPosition(0), true);
1401 // Emit the layout activated signal
1402 mLayoutActivatedSignal.Emit();
1405 void ItemView::OnOvershootOnFinished(Animation& animation)
1407 mAnimatingOvershootOn = false;
1408 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1409 RemoveAnimation(mScrollOvershootAnimation);
1410 if(mAnimateOvershootOff)
1412 AnimateScrollOvershoot(0.0f);
1414 mInAnimation = false;
1417 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1419 Actor self = Self();
1420 const Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1421 float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1423 if(durationSeconds > 0.0f)
1425 RemoveAnimation(mScrollAnimation);
1426 mScrollAnimation = Animation::New(durationSeconds);
1427 mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, mScrollToAlphaFunction );
1428 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1429 mScrollAnimation.Play();
1433 self.SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1434 AnimateScrollOvershoot(0.0f);
1437 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1438 mRefreshEnabled = true;
1441 void ItemView::RemoveAnimation(Animation& animation)
1445 // Cease animating, and reset handle.
1451 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1453 Actor self = Self();
1455 Vector3 firstItemPosition(Vector3::ZERO);
1456 Vector3 lastItemPosition(Vector3::ZERO);
1460 firstItemPosition = mActiveLayout->GetItemPosition( 0,0,layoutSize );
1462 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1463 lastItemPosition = mActiveLayout->GetItemPosition( fabs(minLayoutPosition),fabs(minLayoutPosition),layoutSize );
1467 if(IsHorizontal(mActiveLayout->GetOrientation()))
1469 domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1473 domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1476 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, Vector2::ZERO);
1477 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1479 self.SetProperty(Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE, domainSize);
1481 bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1482 self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, isLayoutScrollable);
1483 self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, false);
1487 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1489 Actor self = Self();
1491 float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout, false );
1492 float forwardClampedPosition = ClampFirstItemPosition( currentLayoutPosition + 1.0, layoutSize, *mActiveLayout, false );
1493 float backwardClampedPosition = ClampFirstItemPosition( currentLayoutPosition - 1.0, layoutSize, *mActiveLayout, false );
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 Vector2 ItemView::GetCurrentScrollPosition() const
1506 return Vector2(0.0f, GetScrollPosition(GetCurrentLayoutPosition(0), Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE )));
1509 void ItemView::AddOverlay(Actor actor)
1511 actor.SetProperty( Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D );
1515 void ItemView::RemoveOverlay(Actor actor)
1517 Self().Remove(actor);
1520 void ItemView::ScrollTo(const Vector2& position, float duration)
1522 Actor self = Self();
1523 const Vector3 layoutSize = Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE );
1525 float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1529 RemoveAnimation(mScrollAnimation);
1530 mScrollAnimation = Animation::New(duration);
1531 mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, mScrollToAlphaFunction );
1532 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1533 mScrollAnimation.Play();
1537 self.SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1538 AnimateScrollOvershoot(0.0f);
1541 mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1542 mRefreshEnabled = true;
1545 void ItemView::SetOvershootSize( const Vector2& size )
1547 mOvershootSize = size;
1549 if( mOvershootOverlay )
1551 // Remove old & add new size constraint
1552 mOvershootOverlay.RemoveConstraints( OVERSHOOT_SIZE_CONSTRAINT_TAG );
1553 ApplyOvershootSizeConstraint( mOvershootOverlay, mOvershootSize.height );
1557 void ItemView::SetOvershootEffectColor( const Vector4& color )
1559 mOvershootEffectColor = color;
1560 if( mOvershootOverlay )
1562 mOvershootOverlay.SetProperty( Actor::Property::COLOR, color );
1566 void ItemView::EnableScrollOvershoot( bool enable )
1568 Actor self = Self();
1571 if( !mOvershootOverlay )
1573 Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX;
1574 mOvershootOverlay = CreateBouncingEffectActor( effectOvershootPropertyIndex );
1575 mOvershootOverlay.SetProperty( Actor::Property::COLOR,mOvershootEffectColor);
1576 mOvershootOverlay.SetProperty( Actor::Property::PARENT_ORIGIN,ParentOrigin::TOP_LEFT );
1577 mOvershootOverlay.SetProperty( Actor::Property::ANCHOR_POINT,AnchorPoint::TOP_LEFT);
1578 mOvershootOverlay.SetProperty( Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D );
1579 self.Add(mOvershootOverlay);
1581 ApplyOvershootSizeConstraint( mOvershootOverlay, mOvershootSize.height );
1583 Constraint constraint = Constraint::New<Quaternion>( mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint );
1584 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
1585 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
1586 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) );
1589 constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::POSITION, OvershootOverlayPositionConstraint );
1590 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
1591 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
1592 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
1593 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) );
1596 constraint = Constraint::New<bool>( mOvershootOverlay, Actor::Property::VISIBLE, OvershootOverlayVisibilityConstraint );
1597 constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) );
1600 constraint = Constraint::New<float>( mOvershootOverlay, effectOvershootPropertyIndex, EqualToConstraint() );
1601 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) );
1607 if( mOvershootOverlay )
1609 self.Remove(mOvershootOverlay);
1610 mOvershootOverlay.Reset();
1615 float ItemView::CalculateScrollOvershoot()
1617 float overshoot = 0.0f;
1621 // The overshoot must be calculated from the accumulated pan gesture displacement
1622 // since the pan gesture starts.
1623 Actor self = Self();
1624 float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1625 float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1626 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentProperty< Vector3 >( Actor::Property::SIZE ));
1627 self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1628 float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta));
1629 overshoot = positionDelta - clamppedPosition;
1632 return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f);
1635 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1637 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1639 // make sure we animate back if needed
1640 mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1642 if( mAnimatingOvershootOn )
1644 // animating on, do not allow animate off
1648 Actor self = Self();
1650 if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0)
1652 float currentOvershoot = self.GetCurrentProperty< float >( Toolkit::ItemView::Property::OVERSHOOT );
1653 float duration = 0.0f;
1655 if (mOvershootOverlay)
1657 duration = mOvershootOverlay.GetCurrentProperty< Vector3 >( Actor::Property::SIZE ).height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed;
1660 // Mark the animation as in progress to prevent manual property sets overwriting it.
1661 mInAnimation = true;
1662 mAnimatingOvershootOn = animatingOn;
1663 RemoveAnimation(mScrollOvershootAnimation);
1664 mScrollOvershootAnimation = Animation::New(duration);
1665 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1666 mScrollOvershootAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1667 mScrollOvershootAnimation.Play();
1671 self.SetProperty( Toolkit::ItemView::Property::OVERSHOOT, overshootAmount );
1675 void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin )
1677 if( parentOrigin != mItemsParentOrigin )
1679 mItemsParentOrigin = parentOrigin;
1680 for (ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1682 iter->second.SetProperty( Actor::Property::PARENT_ORIGIN,parentOrigin );
1687 Vector3 ItemView::GetItemsParentOrigin() const
1689 return mItemsParentOrigin;
1692 void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint )
1694 if( anchorPoint != mItemsAnchorPoint )
1696 mItemsAnchorPoint = anchorPoint;
1697 for (ItemIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1699 iter->second.SetProperty( Actor::Property::ANCHOR_POINT,anchorPoint);
1704 Vector3 ItemView::GetItemsAnchorPoint() const
1706 return mItemsAnchorPoint;
1709 void ItemView::GetItemsRange(ItemRange& range)
1711 if( !mItemPool.empty() )
1713 range.begin = mItemPool.begin()->first;
1714 range.end = mItemPool.rbegin()->first + 1;
1723 bool ItemView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1725 Dali::BaseHandle handle( object );
1727 bool connected( true );
1728 Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( handle );
1730 if( 0 == strcmp( signalName.c_str(), LAYOUT_ACTIVATED_SIGNAL ) )
1732 itemView.LayoutActivatedSignal().Connect( tracker, functor );
1736 // signalName does not match any signal
1743 void ItemView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
1745 Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( Dali::BaseHandle( object ) );
1749 ItemView& itemViewImpl( GetImpl( itemView ) );
1752 case Toolkit::ItemView::Property::MINIMUM_SWIPE_SPEED:
1754 itemViewImpl.SetMinimumSwipeSpeed( value.Get<float>() );
1758 case Toolkit::ItemView::Property::MINIMUM_SWIPE_DISTANCE:
1760 itemViewImpl.SetMinimumSwipeDistance( value.Get<float>() );
1764 case Toolkit::ItemView::Property::WHEEL_SCROLL_DISTANCE_STEP:
1766 itemViewImpl.SetWheelScrollDistanceStep( value.Get<float>() );
1770 case Toolkit::ItemView::Property::SNAP_TO_ITEM_ENABLED:
1772 itemViewImpl.SetAnchoring( value.Get<bool>() );
1776 case Toolkit::ItemView::Property::REFRESH_INTERVAL:
1778 itemViewImpl.SetRefreshInterval( value.Get<float>() );
1782 case Toolkit::ItemView::Property::LAYOUT:
1784 // Get a Property::Array from the property if possible.
1785 Property::Array layoutArray;
1786 if( value.Get( layoutArray ) )
1788 itemViewImpl.SetLayoutArray( layoutArray );
1796 Property::Array ItemView::GetLayoutArray()
1798 return mlayoutArray;
1801 void ItemView::SetLayoutArray( const Property::Array& layouts )
1803 mlayoutArray = layouts;
1804 const int layoutCount = GetLayoutCount();
1805 if( layoutCount > 0 )
1807 for(int index = layoutCount - 1; index >= 0; --index)
1809 RemoveLayout(index);
1810 if(index == 0) break;
1814 for( unsigned int arrayIdx = 0, arrayCount = layouts.Count(); arrayIdx < arrayCount; ++arrayIdx )
1816 const Property::Value& element = layouts.GetElementAt( arrayIdx );
1818 Property::Map* layout = element.GetMap();
1819 if( layout != NULL )
1821 for( unsigned int mapIdx = 0, mapCount = (*layout).Count(); mapIdx < mapCount; ++mapIdx )
1823 KeyValuePair propertyPair( (*layout).GetKeyValue( mapIdx ) );
1825 if(propertyPair.first == DefaultItemLayoutProperty::TYPE)
1827 int layoutType = propertyPair.second.Get<int>();
1828 if(layoutType <= DefaultItemLayout::SPIRAL && layoutType >= DefaultItemLayout::DEPTH)
1830 //DEPTH, GRID, LIST, SPIRAL
1831 switch(DefaultItemLayout::Type(layoutType))
1833 case DefaultItemLayout::DEPTH:
1835 Internal::DepthLayoutPtr depthLayout = Internal::DepthLayout::New();
1836 (*depthLayout).SetLayoutProperties(*layout);
1837 (*depthLayout).SetDepthLayoutProperties(*layout);
1838 AddLayout(*depthLayout);
1841 case DefaultItemLayout::GRID:
1843 Internal::GridLayoutPtr gridLayout = Internal::GridLayout::New();
1844 (*gridLayout).SetLayoutProperties(*layout);
1845 (*gridLayout).SetGridLayoutProperties(*layout);
1846 AddLayout(*gridLayout);
1849 case DefaultItemLayout::LIST:
1851 Internal::GridLayoutPtr listLayout = Internal::GridLayout::New();
1852 listLayout->SetNumberOfColumns( 1 );
1853 (*listLayout).SetLayoutProperties(*layout);
1854 (*listLayout).SetGridLayoutProperties(*layout);
1855 AddLayout(*listLayout);
1858 case DefaultItemLayout::SPIRAL:
1860 Internal::SpiralLayoutPtr spiralLayout = Internal::SpiralLayout::New();
1861 (*spiralLayout).SetLayoutProperties(*layout);
1862 (*spiralLayout).SetSpiralLayoutProperties(*layout);
1863 AddLayout(*spiralLayout);
1874 Property::Value ItemView::GetProperty( BaseObject* object, Property::Index index )
1876 Property::Value value;
1878 Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( Dali::BaseHandle( object ) );
1882 ItemView& itemViewImpl( GetImpl( itemView ) );
1885 case Toolkit::ItemView::Property::MINIMUM_SWIPE_SPEED:
1887 value = itemViewImpl.GetMinimumSwipeSpeed();
1891 case Toolkit::ItemView::Property::MINIMUM_SWIPE_DISTANCE:
1893 value = itemViewImpl.GetMinimumSwipeDistance();
1897 case Toolkit::ItemView::Property::WHEEL_SCROLL_DISTANCE_STEP:
1899 value = itemViewImpl.GetWheelScrollDistanceStep();
1903 case Toolkit::ItemView::Property::SNAP_TO_ITEM_ENABLED:
1905 value = itemViewImpl.GetAnchoring();
1909 case Toolkit::ItemView::Property::REFRESH_INTERVAL:
1911 value = itemViewImpl.GetRefreshInterval();
1915 case Toolkit::ItemView::Property::LAYOUT:
1917 Property::Array layouts= itemViewImpl.GetLayoutArray();
1927 bool ItemView::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
1929 Dali::BaseHandle handle( object );
1931 Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( handle );
1933 DALI_ASSERT_ALWAYS( itemView );
1935 if( 0 == strcmp( actionName.c_str(), ACTION_STOP_SCROLLING ) )
1937 GetImpl( itemView ).DoStopScrolling();
1939 else if ( 0 == strcmp( actionName.c_str(), ACTION_ENABLE_REFRESH ) )
1941 GetImpl( itemView ).SetRefreshNotificationEnabled( true );
1943 else if ( 0 == strcmp( actionName.c_str(), ACTION_DISABLE_REFRESH ) )
1945 GetImpl( itemView ).SetRefreshNotificationEnabled( false );
1951 void ItemView::DoStopScrolling()
1953 if( mScrollAnimation )
1955 mScrollAnimation.Stop();
1956 mScrollAnimation.Reset();
1960 void ItemView::SetRefreshNotificationEnabled( bool enabled )
1962 mRefreshNotificationEnabled = enabled;
1965 } // namespace Internal
1967 } // namespace Toolkit