2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
18 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
24 #include <dali/public-api/events/mouse-wheel-event.h>
25 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
30 namespace // unnamed namespace
34 TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL );
36 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
37 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
38 const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
40 const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; // 20 updates per second
41 const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second
43 const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second
44 const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second
46 const float MILLISECONDS_PER_SECONDS = 1000.0f;
48 const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png";
49 const Rect<int> OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
50 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
51 const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels
52 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f; // 0.5 second
53 const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
55 const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" );
56 const string POSITION_PROPERTY_NAME( "item-view-position" );
57 const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" );
58 const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" );
59 const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" );
60 const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" );
62 // Functors which wrap constraint functions with stored item IDs
64 struct WrappedVector3Constraint
66 WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId)
72 Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
74 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
76 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
79 Toolkit::ItemLayout::Vector3Function mWrapMe;
83 struct WrappedQuaternionConstraint
85 WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId)
91 Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
93 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
95 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
98 Toolkit::ItemLayout::QuaternionFunction mWrapMe;
102 struct WrappedVector4Constraint
104 WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId)
110 Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
112 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
114 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
117 Toolkit::ItemLayout::Vector4Function mWrapMe;
118 unsigned int mItemId;
121 struct WrappedBoolConstraint
123 WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId)
129 bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
131 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
133 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
136 Toolkit::ItemLayout::BoolFunction mWrapMe;
137 unsigned int mItemId;
141 * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
143 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
145 Radian scrollDirection(layout.GetScrollDirection());
147 float cosTheta = cosf(scrollDirection);
148 float sinTheta = sinf(scrollDirection);
150 return panDistance.x * sinTheta + panDistance.y * cosTheta;
153 // Overshoot overlay constraints
155 struct OvershootOverlaySizeConstraint
157 float operator()(const float& current,
158 const PropertyInput& parentScrollDirectionProperty,
159 const PropertyInput& parentOvershootProperty,
160 const PropertyInput& parentSizeProperty)
162 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
163 const Vector3 parentSize = parentSizeProperty.GetVector3();
164 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
168 if(Toolkit::IsVertical(parentOrientation))
170 overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
174 overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
181 struct OvershootOverlayRotationConstraint
183 Quaternion operator()(const Quaternion& current,
184 const PropertyInput& parentScrollDirectionProperty,
185 const PropertyInput& parentOvershootProperty)
187 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
188 const float parentOvershoot = parentOvershootProperty.GetFloat();
189 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
193 if(Toolkit::IsVertical(parentOrientation))
195 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
197 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
198 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
200 rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
204 rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
207 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
208 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
210 rotation = Quaternion(0.0f, Vector3::ZAXIS);
214 rotation = Quaternion(Math::PI, Vector3::ZAXIS);
219 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
221 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
222 ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
224 rotation = Quaternion(Math::PI, Vector3::ZAXIS);
228 rotation = Quaternion(0.0f, Vector3::ZAXIS);
231 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
232 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
234 rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
238 rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
246 struct OvershootOverlayPositionConstraint
248 Vector3 operator()(const Vector3& current,
249 const PropertyInput& parentSizeProperty,
250 const PropertyInput& parentScrollDirectionProperty,
251 const PropertyInput& parentOvershootProperty)
253 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
254 const float parentOvershoot = parentOvershootProperty.GetFloat();
255 const Vector3 parentSize = parentSizeProperty.GetVector3();
256 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
258 Vector3 relativeOffset;
260 if(Toolkit::IsVertical(parentOrientation))
262 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
264 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
265 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
267 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
271 relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
274 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
275 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
277 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
281 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
286 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
288 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
289 || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
291 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
295 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
298 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
299 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
301 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
305 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
309 return relativeOffset * parentSize;
314 struct OvershootOverlayVisibilityConstraint
316 bool operator()(const bool& current,
317 const PropertyInput& parentLayoutScrollableProperty)
319 const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean();
321 return parentLayoutScrollable;
326 * Relative position Constraint
327 * Generates the relative position value of the item view based on the layout position,
328 * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis.
330 Vector3 RelativePositionConstraint(const Vector3& current,
331 const PropertyInput& scrollPositionProperty,
332 const PropertyInput& scrollMinProperty,
333 const PropertyInput& scrollMaxProperty,
334 const PropertyInput& layoutSizeProperty)
336 const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f);
337 const Vector3& min = scrollMinProperty.GetVector3();
338 const Vector3& max = scrollMaxProperty.GetVector3();
340 Vector3 relativePosition;
341 Vector3 domainSize = max - min;
343 relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f;
344 relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f;
346 return relativePosition;
349 } // unnamed namespace
360 ///////////////////////////////////////////////////////////////////////////////////////////////////
362 ///////////////////////////////////////////////////////////////////////////////////////////////////
364 void ItemPool::AddItems(bool scrollingTowardsLast, ItemRange range, const Vector3& targetSize)
366 // Add new actors to the ItemPool.
367 // The order of addition depends on the scroll direction.
368 if (scrollingTowardsLast)
370 for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
372 AddItem(itemId, targetSize);
377 for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
379 AddItem(itemId-1, targetSize);
384 void ItemPool::RemoveItems(ItemRange range)
386 // Remove unwanted actors from the ItemView & ItemPool
387 for (IDKeyIter iter = mIdKeyContainer.begin(); iter != mIdKeyContainer.end(); )
389 unsigned int current = iter->first;
391 if (!range.Within(current))
393 mItemView.ActorRemovedFromItemPool(iter->second, iter->first);
395 mActorKeyContainer.erase(iter->second);
396 mIdKeyContainer.erase(iter++); // erase invalidates the current iter; the post-increment is important here
405 void ItemPool::AddItem(unsigned int itemId, const Vector3& targetSize)
407 if (mIdKeyContainer.find(itemId) == mIdKeyContainer.end())
409 Actor actor = mItemView.CreateActor(itemId);
413 mIdKeyContainer.insert(IDKeyPair(itemId, actor));
414 mActorKeyContainer.insert(ActorKeyPair(actor, itemId));
416 mItemView.ActorAddedToItemPool(actor, itemId, targetSize);
421 bool ItemPool::RemoveItem(unsigned int itemId)
425 IDKeyIter iter = mIdKeyContainer.find(itemId);
426 if (iter != mIdKeyContainer.end())
428 mItemView.ActorRemovedFromItemPool(iter->second, iter->first);
430 mActorKeyContainer.erase(iter->second);
431 for (ActorKeyIter iterActorKey = mActorKeyContainer.begin(); iterActorKey != mActorKeyContainer.end(); ++iterActorKey)
433 if(iterActorKey->second > itemId)
435 iterActorKey->second--;
439 for (IDKeyIter iterIDKey = iter; iterIDKey != mIdKeyContainer.end(); ++iterIDKey)
441 if(iterIDKey->first < mIdKeyContainer.rbegin()->first)
443 iterIDKey->second = mIdKeyContainer[iterIDKey->first + 1];
447 mIdKeyContainer.erase(iterIDKey);
458 ///////////////////////////////////////////////////////////////////////////////////////////////////
460 ///////////////////////////////////////////////////////////////////////////////////////////////////
462 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
464 // Create the implementation
465 ItemViewPtr itemView(new ItemView(factory));
467 // Pass ownership to CustomActor via derived handle
468 Dali::Toolkit::ItemView handle(*itemView);
470 // Second-phase init of the implementation
471 // This can only be done after the CustomActor connection has been made...
472 itemView->Initialize();
477 ItemView::ItemView(ItemFactory& factory)
479 mItemFactory(factory),
482 mAnimatingOvershootOn(false),
483 mAnimateOvershootOff(false),
484 mAnchoringEnabled(true),
485 mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
486 mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS),
487 mRefreshOrderHint(true/*Refresh item 0 first*/),
488 mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
489 mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
490 mScrollDistance(0.0f),
492 mTotalPanDisplacement(Vector2::ZERO),
493 mScrollOvershoot(0.0f),
495 mGestureState(Gesture::Clear)
497 SetRequiresMouseWheelEvents(true);
498 SetKeyboardNavigationSupport(true);
501 void ItemView::OnInitialize()
503 SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
505 RegisterCommonProperties();
509 mOvershootEffect = OvershootRippleEffect::New();
510 Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH );
511 mOvershootOverlay = ImageActor::New( overshootImage );
512 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
513 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
514 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
515 mOvershootOverlay.SetShaderEffect(mOvershootEffect);
516 mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA);
517 self.Add(mOvershootOverlay);
519 mPropertyLayoutPosition = self.RegisterProperty(LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
520 mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
521 mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
522 mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
523 mPropertyOvershoot = self.RegisterProperty(OVERSHOOT_PROPERTY_NAME, 0.0f);
525 ApplyOvershootOverlayConstraints();
527 Constraint constraint = Constraint::New<Vector3>(mPropertyRelativePosition,
528 LocalSource(mPropertyPosition),
529 LocalSource(mPropertyPositionMin),
530 LocalSource(mPropertyPositionMax),
531 LocalSource(Actor::SIZE),
532 RelativePositionConstraint);
533 self.ApplyConstraint(constraint);
535 Vector2 stageSize = Stage::GetCurrent().GetSize();
536 mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
538 EnableGestureDetection(Gesture::Type(Gesture::Pan));
540 mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
541 mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
544 ItemView::~ItemView()
548 unsigned int ItemView::GetLayoutCount() const
550 return mLayouts.size();
553 void ItemView::AddLayout(ItemLayout& layout)
555 mLayouts.push_back(ItemLayoutPtr(&layout));
558 void ItemView::RemoveLayout(unsigned int layoutIndex)
560 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
562 if (mActiveLayout == mLayouts[layoutIndex].Get())
564 mActiveLayout = NULL;
567 mLayouts.erase(mLayouts.begin() + layoutIndex);
570 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
572 return mLayouts[layoutIndex];
575 ItemLayoutPtr ItemView::GetActiveLayout() const
577 return ItemLayoutPtr(mActiveLayout);
580 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
582 return Self().GetProperty<float>(mPropertyLayoutPosition) + static_cast<float>(itemId);
585 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
587 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
591 // The ItemView size should match the active layout size
592 self.SetSize(targetSize);
594 // Switch to the new layout
595 ItemLayout* previousLayout = mActiveLayout;
596 mActiveLayout = mLayouts[layoutIndex].Get();
598 // Calculate which items are within either layout
599 ItemRange oldRange = previousLayout ? GetItemRange(*previousLayout, targetSize, false/*don't reserve extra*/) : ItemRange(0u, 0u);
600 ItemRange newRange = GetItemRange(*mActiveLayout, targetSize, false/*don't reserve extra*/);
602 // Move the items to the new layout positions...
604 bool resizeAnimationNeeded(false);
606 const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer();
607 for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter)
609 unsigned int itemId = iter->first;
610 Actor actor = iter->second;
612 // Immediately relayout items that aren't within either layout
613 bool immediate = !oldRange.Within(itemId) &&
614 !newRange.Within(itemId);
616 // Remove constraints from previous layout
617 actor.RemoveConstraints();
620 if(mActiveLayout->GetItemSize(itemId, targetSize, size))
623 durationSeconds > 0.0f)
625 // Use a size animation
626 if (!resizeAnimationNeeded)
628 resizeAnimationNeeded = true;
629 RemoveAnimation(mResizeAnimation);
630 mResizeAnimation = Animation::New(durationSeconds);
633 // The layout provides its own resize animation
634 mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds);
638 // resize immediately
643 ApplyConstraints(actor, *mActiveLayout, itemId, immediate ? 0.0f : durationSeconds);
646 if (resizeAnimationNeeded)
648 mResizeAnimation.Play();
651 // Refresh the new layout
652 ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/);
653 AddItems(*mActiveLayout, targetSize, range);
655 // Scroll to an appropriate layout position
657 bool scrollAnimationNeeded(false);
658 float firstItemScrollPosition(0.0f);
660 float current = self.GetProperty<float>(mPropertyLayoutPosition);
661 float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
662 self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
664 if (current < minimum)
666 scrollAnimationNeeded = true;
667 firstItemScrollPosition = minimum;
669 else if (mAnchoringEnabled)
671 scrollAnimationNeeded = true;
672 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
675 if (scrollAnimationNeeded)
677 RemoveAnimation(mScrollAnimation);
678 mScrollAnimation = Animation::New(mAnchoringDuration);
679 mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut );
680 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
681 mScrollAnimation.Play();
684 self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
685 AnimateScrollOvershoot(0.0f);
686 mScrollOvershoot = 0.0f;
688 Radian scrollDirection(mActiveLayout->GetScrollDirection());
689 float orientation = static_cast<float>(mActiveLayout->GetOrientation());
690 self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
692 self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
694 CalculateDomainSize(targetSize);
697 void ItemView::DeactivateCurrentLayout()
701 const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer();
702 for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter)
704 Actor actor = iter->second;
705 actor.RemoveConstraints();
708 mActiveLayout = NULL;
711 CancelRefreshTimer();
714 bool ItemView::OnRefreshTick()
716 // Short-circuit if there is no active layout
722 const Vector3 layoutSize = Self().GetCurrentSize();
724 ItemRange range = GetItemRange(*mActiveLayout, layoutSize, true/*reserve extra*/);
727 AddItems(*mActiveLayout, layoutSize, range);
729 // Keep refreshing whilst the layout is moving
730 return mScrollAnimation || (mGestureState == Gesture::Started || mGestureState == Gesture::Continuing);
733 void ItemView::SetMinimumSwipeSpeed(float speed)
735 mMinimumSwipeSpeed = speed;
738 float ItemView::GetMinimumSwipeSpeed() const
740 return mMinimumSwipeSpeed;
743 void ItemView::SetMinimumSwipeDistance(float distance)
745 mMinimumSwipeDistance = distance;
748 float ItemView::GetMinimumSwipeDistance() const
750 return mMinimumSwipeDistance;
753 void ItemView::SetMouseWheelScrollDistanceStep(float step)
755 mMouseWheelScrollDistanceStep = step;
758 float ItemView::GetMouseWheelScrollDistanceStep() const
760 return mMouseWheelScrollDistanceStep;
763 void ItemView::SetAnchoring(bool enabled)
765 mAnchoringEnabled = enabled;
768 bool ItemView::GetAnchoring() const
770 return mAnchoringEnabled;
773 void ItemView::SetAnchoringDuration(float durationSeconds)
775 mAnchoringDuration = durationSeconds;
778 float ItemView::GetAnchoringDuration() const
780 return mAnchoringDuration;
783 void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds)
785 mRefreshIntervalMilliseconds = intervalMilliseconds;
788 unsigned int ItemView::GetRefreshInterval() const
790 return mRefreshIntervalMilliseconds;
793 Actor ItemView::GetItem(unsigned int itemId) const
797 ItemPool::IDKeyConstIter found = mItemPool.GetIDKeyContainer().find(itemId);
798 if (found != mItemPool.GetIDKeyContainer().end())
800 actor = found->second;
806 unsigned int ItemView::GetItemId(Actor actor) const
808 unsigned int itemId(0);
810 ItemPool::ActorKeyConstIter found = mItemPool.GetActorKeyContainer().find(actor);
811 if (found != mItemPool.GetActorKeyContainer().end())
813 itemId = found->second;
819 void ItemView::RemoveItem(unsigned int itemId, float durationSeconds)
821 if (mItemPool.RemoveItem(itemId))
823 const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer();
824 for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter)
826 unsigned int id = iter->first;
827 Actor actor = iter->second;
829 // Reposition the items if necessary
830 actor.RemoveConstraints();
831 ApplyConstraints(actor, *mActiveLayout, id, durationSeconds);
834 CalculateDomainSize(Self().GetCurrentSize());
838 Actor ItemView::CreateActor(unsigned int itemId)
840 return mItemFactory.NewItem(itemId);
843 void ItemView::ActorAddedToItemPool(Actor actor, unsigned int itemId, const Vector3& targetSize)
847 actor.SetParentOrigin(ParentOrigin::CENTER);
848 actor.SetAnchorPoint(AnchorPoint::CENTER);
853 if(mActiveLayout->GetItemSize(itemId, targetSize, size))
858 ApplyConstraints(actor, *mActiveLayout, itemId, 0.0f/*immediate*/);
864 void ItemView::ActorRemovedFromItemPool(Actor actor, unsigned int itemId)
866 Self().Remove(actor);
869 void ItemView::RemoveItems(ItemRange range)
871 mItemPool.RemoveItems(range);
874 void ItemView::AddItems(ItemLayout& layout, const Vector3& layoutSize, ItemRange range)
876 range.end = min(mItemFactory.GetNumberOfItems(), range.end);
878 mItemPool.AddItems(mRefreshOrderHint, range, layoutSize);
881 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra)
883 unsigned int itemCount = mItemFactory.GetNumberOfItems();
885 ItemRange available(0u, itemCount);
887 ItemRange range = layout.GetItemsWithinArea(Self().GetProperty<float>(mPropertyLayoutPosition), layoutSize);
891 // Add the reserve items for scrolling
892 unsigned int extra = layout.GetReserveItemCount(layoutSize);
893 range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
897 return range.Intersection(available);
900 bool ItemView::OnTouchEvent(const TouchEvent& event)
902 // Ignore events with multiple-touch points
903 if (event.GetPointCount() != 1)
908 if (event.GetPoint(0).state == TouchPoint::Down)
910 // Cancel ongoing scrolling etc.
911 mGestureState = Gesture::Clear;
913 mScrollDistance = 0.0f;
915 Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
917 mScrollOvershoot = 0.0f;
918 AnimateScrollOvershoot(0.0f);
920 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
922 RemoveAnimation(mScrollAnimation);
925 return true; // consume since we're potentially scrolling
928 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
930 // Respond the mouse wheel event to scroll
934 const Vector3 layoutSize = Self().GetCurrentSize();
935 float layoutPositionDelta = self.GetProperty<float>(mPropertyLayoutPosition) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
936 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
937 self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition);
938 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
939 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
943 if (mMouseWheelEventFinishedTimer.IsRunning())
945 mMouseWheelEventFinishedTimer.Stop();
948 mMouseWheelEventFinishedTimer.Start();
953 bool ItemView::OnMouseWheelEventFinished()
957 RemoveAnimation(mScrollAnimation);
959 // No more mouse wheel events coming. Do the anchoring if enabled.
960 mScrollAnimation = DoAnchoring();
961 if (mScrollAnimation)
965 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
966 mScrollAnimation.Play();
970 mScrollOvershoot = 0.0f;
971 AnimateScrollOvershoot(0.0f);
973 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
980 void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration)
982 ItemLayout::Vector3Function positionConstraint;
983 if (layout.GetPositionConstraint(itemId, positionConstraint))
985 WrappedVector3Constraint wrapped(positionConstraint, itemId);
987 Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
988 ParentSource( mPropertyLayoutPosition ),
989 ParentSource( mPropertyScrollSpeed ),
990 ParentSource( Actor::SIZE ),
992 constraint.SetApplyTime(duration);
994 actor.ApplyConstraint(constraint);
997 ItemLayout::QuaternionFunction rotationConstraint;
998 if (layout.GetRotationConstraint(itemId, rotationConstraint))
1000 WrappedQuaternionConstraint wrapped(rotationConstraint, itemId);
1002 Constraint constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1003 ParentSource( mPropertyLayoutPosition ),
1004 ParentSource( mPropertyScrollSpeed ),
1005 ParentSource( Actor::SIZE ),
1007 constraint.SetApplyTime(duration);
1009 actor.ApplyConstraint(constraint);
1012 ItemLayout::Vector3Function scaleConstraint;
1013 if (layout.GetScaleConstraint(itemId, scaleConstraint))
1015 WrappedVector3Constraint wrapped(scaleConstraint, itemId);
1017 Constraint constraint = Constraint::New<Vector3>( Actor::SCALE,
1018 ParentSource( mPropertyLayoutPosition ),
1019 ParentSource( mPropertyScrollSpeed ),
1020 ParentSource( Actor::SIZE ),
1022 constraint.SetApplyTime(duration);
1024 actor.ApplyConstraint(constraint);
1027 ItemLayout::Vector4Function colorConstraint;
1028 if (layout.GetColorConstraint(itemId, colorConstraint))
1030 WrappedVector4Constraint wrapped(colorConstraint, itemId);
1032 Constraint constraint = Constraint::New<Vector4>( Actor::COLOR,
1033 ParentSource( mPropertyLayoutPosition ),
1034 ParentSource( mPropertyScrollSpeed ),
1035 ParentSource( Actor::SIZE ),
1037 constraint.SetApplyTime(duration);
1039 // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in
1040 constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1041 constraint.SetRemoveAction(Dali::Constraint::Discard);
1043 actor.ApplyConstraint(constraint);
1046 ItemLayout::BoolFunction visibilityConstraint;
1047 if (layout.GetVisibilityConstraint(itemId, visibilityConstraint))
1049 WrappedBoolConstraint wrapped(visibilityConstraint, itemId);
1051 Constraint constraint = Constraint::New<bool>( Actor::VISIBLE,
1052 ParentSource( mPropertyLayoutPosition ),
1053 ParentSource( mPropertyScrollSpeed ),
1054 ParentSource( Actor::SIZE ),
1056 constraint.SetApplyTime(duration);
1058 // Release visibility constraints the same time as the color constraint
1059 constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1060 constraint.SetRemoveAction(Dali::Constraint::Discard);
1062 actor.ApplyConstraint(constraint);
1066 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1068 Actor self = Self();
1069 float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1070 float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition));
1071 mScrollOvershoot = targetPosition - clamppedPosition;
1072 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1074 return clamppedPosition;
1077 void ItemView::OnPan(PanGesture gesture)
1079 Actor self = Self();
1080 const Vector3 layoutSize = Self().GetCurrentSize();
1082 RemoveAnimation(mScrollAnimation);
1084 // Short-circuit if there is no active layout
1087 mGestureState = Gesture::Clear;
1091 mGestureState = gesture.state;
1093 switch (mGestureState)
1095 case Gesture::Finished:
1098 if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1099 mScrollSpeed > mMinimumSwipeSpeed)
1101 float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1103 mRefreshOrderHint = true;
1105 float currentLayoutPosition = self.GetProperty<float>(mPropertyLayoutPosition);
1106 float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1110 if (mAnchoringEnabled)
1112 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1115 RemoveAnimation(mScrollAnimation);
1117 float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)));
1118 mScrollAnimation = Animation::New(flickAnimationDuration);
1119 mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut );
1120 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1121 mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1124 // Check whether it has already scrolled to the end
1125 if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1127 AnimateScrollOvershoot(0.0f);
1131 // Anchoring may be triggered when there was no swipe
1132 if (!mScrollAnimation)
1134 mScrollAnimation = DoAnchoring();
1137 // Reset the overshoot if no scroll animation.
1138 if (!mScrollAnimation)
1140 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1142 AnimateScrollOvershoot(0.0f, false);
1147 case Gesture::Started: // Fall through
1149 mTotalPanDisplacement = Vector2::ZERO;
1152 case Gesture::Continuing:
1154 mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1155 mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1157 // Refresh order depends on the direction of the scroll; negative is towards the last item.
1158 mRefreshOrderHint = mScrollDistance < 0.0f;
1160 RemoveAnimation(mScrollSpeedAnimation);
1161 mScrollSpeedAnimation = Animation::New(0.3f);
1162 mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear );
1163 mScrollSpeedAnimation.Play();
1165 float layoutPositionDelta = self.GetProperty<float>(mPropertyLayoutPosition) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1167 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1169 self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition);
1170 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1171 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1173 mTotalPanDisplacement += gesture.displacement;
1174 mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition;
1175 if( mScrollOvershoot > Math::MACHINE_EPSILON_1 )
1177 AnimateScrollOvershoot(1.0f);
1179 else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 )
1181 AnimateScrollOvershoot(-1.0f);
1185 AnimateScrollOvershoot(0.0f);
1188 StartRefreshTimer();
1192 case Gesture::Cancelled:
1194 mScrollAnimation = DoAnchoring();
1202 if (mScrollAnimation)
1204 StartRefreshTimer();
1206 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1207 mScrollAnimation.Play();
1211 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1217 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1219 Actor nextFocusActor;
1223 if(!actor || actor == this->Self())
1225 nextFocusActor = GetItem(nextItemID);
1227 else if(actor && actor.GetParent() == this->Self())
1229 int itemID = GetItemId(actor);
1230 nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1231 nextFocusActor = GetItem(nextItemID);
1232 if(nextFocusActor == actor)
1234 // need to pass NULL actor back to focus manager
1235 nextFocusActor.Reset();
1236 return nextFocusActor;
1239 float layoutPosition = mActiveLayout->GetClosestAnchorPosition(Self().GetProperty<float>(mPropertyLayoutPosition));
1240 Vector3 layoutSize = Self().GetCurrentSize();
1243 // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1244 ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1245 nextItemID = viewableItems.begin;
1246 nextFocusActor = GetItem(nextItemID);
1249 return nextFocusActor;
1252 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1254 // only in this function if our chosen focus actor was actually used
1255 if(commitedFocusableActor)
1257 int nextItemID = GetItemId(commitedFocusableActor);
1258 float layoutPosition = Self().GetProperty<float>(mPropertyLayoutPosition);
1259 Vector3 layoutSize = Self().GetCurrentSize();
1260 Vector3 focusItemPosition = Vector3::ZERO;
1261 ItemLayout::Vector3Function itemPositionConstraint;
1262 if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint))
1264 focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize);
1267 float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1268 ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1272 Animation ItemView::DoAnchoring()
1274 Animation anchoringAnimation;
1275 Actor self = Self();
1277 if (mActiveLayout && mAnchoringEnabled)
1279 float anchorPosition = mActiveLayout->GetClosestAnchorPosition(Self().GetProperty<float>(mPropertyLayoutPosition));
1281 anchoringAnimation = Animation::New(mAnchoringDuration);
1282 anchoringAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), anchorPosition, AlphaFunctions::EaseOut );
1283 anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1284 anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1287 AnimateScrollOvershoot(0.0f);
1291 return anchoringAnimation;
1294 void ItemView::OnScrollFinished(Animation& source)
1296 Actor self = Self();
1298 RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1300 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1302 if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1304 AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1308 // Reset the overshoot
1309 AnimateScrollOvershoot( 0.0f );
1311 mIsFlicking = false;
1313 mScrollOvershoot = 0.0f;
1316 void ItemView::OnOvershootOnFinished(Animation& animation)
1318 mAnimatingOvershootOn = false;
1319 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1320 RemoveAnimation(mScrollOvershootAnimation);
1321 if(mAnimateOvershootOff)
1323 AnimateScrollOvershoot(0.0f);
1327 void ItemView::StartRefreshTimer()
1331 mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds );
1332 mRefreshTimer.TickSignal().Connect( this, &ItemView::OnRefreshTick );
1335 if (!mRefreshTimer.IsRunning())
1337 mRefreshTimer.Start();
1341 void ItemView::CancelRefreshTimer()
1345 mRefreshTimer.Stop();
1349 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1351 Actor self = Self();
1352 const Vector3 layoutSize = Self().GetCurrentSize();
1353 float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1355 StartRefreshTimer();
1357 if(durationSeconds > 0.0f)
1359 RemoveAnimation(mScrollAnimation);
1360 mScrollAnimation = Animation::New(durationSeconds);
1361 mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut );
1362 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1363 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1364 mScrollAnimation.Play();
1368 self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition);
1369 AnimateScrollOvershoot(0.0f);
1372 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1375 void ItemView::RemoveAnimation(Animation& animation)
1379 // Cease animating, and reset handle.
1385 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1387 Actor self = Self();
1389 Vector3 firstItemPosition(Vector3::ZERO);
1390 Vector3 lastItemPosition(Vector3::ZERO);
1394 ItemLayout::Vector3Function firstItemPositionConstraint;
1395 if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1397 firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize);
1400 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1401 ItemLayout::Vector3Function lastItemPositionConstraint;
1402 if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint))
1404 lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize);
1407 if(IsHorizontal(mActiveLayout->GetOrientation()))
1409 self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f));
1410 self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f));
1414 self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f));
1415 self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f));
1418 bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1419 self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable);
1420 self.SetProperty(mPropertyCanScrollHorizontal, false);
1424 Vector3 ItemView::GetDomainSize() const
1426 Actor self = Self();
1428 float minScrollPosition = self.GetProperty<float>(mPropertyPositionMin);
1429 float maxScrollPosition = self.GetProperty<float>(mPropertyPositionMax);
1431 return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1434 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1436 Actor self = Self();
1438 float currentLayoutPosition = ClampFirstItemPosition(self.GetProperty<float>(mPropertyLayoutPosition), layoutSize, *mActiveLayout);
1439 float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1440 float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1442 return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1445 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1447 Vector3 firstItemPosition(Vector3::ZERO);
1448 ItemLayout::Vector3Function firstItemPositionConstraint;
1449 if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1451 firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize);
1454 return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1457 Vector3 ItemView::GetCurrentScrollPosition() const
1459 float currentLayoutPosition = Self().GetProperty<float>(mPropertyLayoutPosition);
1460 return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1463 void ItemView::AddOverlay(Actor actor)
1468 void ItemView::RemoveOverlay(Actor actor)
1470 Self().Remove(actor);
1473 void ItemView::ScrollTo(const Vector3& position, float duration)
1475 Actor self = Self();
1476 const Vector3 layoutSize = Self().GetCurrentSize();
1478 float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1480 StartRefreshTimer();
1484 RemoveAnimation(mScrollAnimation);
1485 mScrollAnimation = Animation::New(duration);
1486 mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut );
1487 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1488 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1489 mScrollAnimation.Play();
1493 self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition);
1494 AnimateScrollOvershoot(0.0f);
1497 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1500 void ItemView::ApplyOvershootOverlayConstraints()
1502 Constraint constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
1503 ParentSource( mPropertyScrollDirection ),
1504 ParentSource( mPropertyOvershoot ),
1505 ParentSource( Actor::SIZE ),
1506 OvershootOverlaySizeConstraint() );
1507 mOvershootOverlay.ApplyConstraint(constraint);
1508 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height);
1510 constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1511 ParentSource( mPropertyScrollDirection ),
1512 ParentSource( mPropertyOvershoot ),
1513 OvershootOverlayRotationConstraint() );
1514 mOvershootOverlay.ApplyConstraint(constraint);
1516 constraint = Constraint::New<Vector3>( Actor::POSITION,
1517 ParentSource( Actor::SIZE ),
1518 ParentSource( mPropertyScrollDirection ),
1519 ParentSource( mPropertyOvershoot ),
1520 OvershootOverlayPositionConstraint() );
1521 mOvershootOverlay.ApplyConstraint(constraint);
1523 constraint = Constraint::New<bool>( Actor::VISIBLE,
1524 ParentSource( mPropertyCanScrollVertical ),
1525 OvershootOverlayVisibilityConstraint() );
1526 mOvershootOverlay.ApplyConstraint(constraint);
1528 int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName());
1529 Actor self = Self();
1530 constraint = Constraint::New<float>( effectOvershootPropertyIndex,
1531 Source( self, mPropertyOvershoot ),
1532 EqualToConstraint() );
1533 mOvershootEffect.ApplyConstraint(constraint);
1536 float ItemView::CalculateScrollOvershoot()
1538 float overshoot = 0.0f;
1542 // The overshoot must be calculated from the accumulated pan gesture displacement
1543 // since the pan gesture starts.
1544 Actor self = Self();
1545 float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1546 float positionDelta = self.GetProperty<float>(mPropertyLayoutPosition) + scrollDistance;
1547 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1548 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1549 float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta));
1550 overshoot = positionDelta - clamppedPosition;
1556 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1558 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1560 // make sure we animate back if needed
1561 mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1563 if( mAnimatingOvershootOn )
1565 // animating on, do not allow animate off
1569 Actor self = Self();
1570 float currentOvershoot = self.GetProperty<float>(mPropertyOvershoot);
1571 float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
1573 RemoveAnimation(mScrollOvershootAnimation);
1574 mScrollOvershootAnimation = Animation::New(duration);
1575 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1576 mScrollOvershootAnimation.AnimateTo( Property(self, mPropertyOvershoot), overshootAmount, TimePeriod(0.0f, duration) );
1577 mScrollOvershootAnimation.Play();
1579 mAnimatingOvershootOn = animatingOn;
1582 } // namespace Internal
1584 } // namespace Toolkit