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>
26 #include <dali-toolkit/internal/controls/scrollable/scroll-connector-impl.h>
31 namespace // unnamed namespace
35 TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL );
37 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
38 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
39 const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
41 const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; // 20 updates per second
42 const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second
44 const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second
45 const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second
47 const float MILLISECONDS_PER_SECONDS = 1000.0f;
49 const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png";
50 const Rect<int> OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
51 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
52 const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels
53 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f; // 0.5 second
54 const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
56 const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" );
57 const string POSITION_PROPERTY_NAME( "item-view-position" );
58 const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" );
59 const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" );
60 const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" );
61 const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" );
63 // Functors which wrap constraint functions with stored item IDs
65 struct WrappedVector3Constraint
67 WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId)
73 Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
75 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
77 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
80 Toolkit::ItemLayout::Vector3Function mWrapMe;
84 struct WrappedQuaternionConstraint
86 WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId)
92 Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
94 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
96 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
99 Toolkit::ItemLayout::QuaternionFunction mWrapMe;
100 unsigned int mItemId;
103 struct WrappedVector4Constraint
105 WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId)
111 Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
113 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
115 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
118 Toolkit::ItemLayout::Vector4Function mWrapMe;
119 unsigned int mItemId;
122 struct WrappedBoolConstraint
124 WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId)
130 bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
132 float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
134 return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
137 Toolkit::ItemLayout::BoolFunction mWrapMe;
138 unsigned int mItemId;
142 * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
144 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
146 Radian scrollDirection(layout.GetScrollDirection());
148 float cosTheta = cosf(scrollDirection);
149 float sinTheta = sinf(scrollDirection);
151 return panDistance.x * sinTheta + panDistance.y * cosTheta;
154 // Overshoot overlay constraints
156 struct OvershootOverlaySizeConstraint
158 float operator()(const float& current,
159 const PropertyInput& parentScrollDirectionProperty,
160 const PropertyInput& parentOvershootProperty,
161 const PropertyInput& parentSizeProperty)
163 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
164 const Vector3 parentSize = parentSizeProperty.GetVector3();
165 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
169 if(Toolkit::IsVertical(parentOrientation))
171 overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
175 overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
182 struct OvershootOverlayRotationConstraint
184 Quaternion operator()(const Quaternion& current,
185 const PropertyInput& parentScrollDirectionProperty,
186 const PropertyInput& parentOvershootProperty)
188 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
189 const float parentOvershoot = parentOvershootProperty.GetFloat();
190 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
194 if(Toolkit::IsVertical(parentOrientation))
196 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
198 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
199 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
201 rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
205 rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
208 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
209 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
211 rotation = Quaternion(0.0f, Vector3::ZAXIS);
215 rotation = Quaternion(Math::PI, Vector3::ZAXIS);
220 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
222 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
223 ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
225 rotation = Quaternion(Math::PI, Vector3::ZAXIS);
229 rotation = Quaternion(0.0f, Vector3::ZAXIS);
232 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
233 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
235 rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
239 rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
247 struct OvershootOverlayPositionConstraint
249 Vector3 operator()(const Vector3& current,
250 const PropertyInput& parentSizeProperty,
251 const PropertyInput& parentScrollDirectionProperty,
252 const PropertyInput& parentOvershootProperty)
254 const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
255 const float parentOvershoot = parentOvershootProperty.GetFloat();
256 const Vector3 parentSize = parentSizeProperty.GetVector3();
257 const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
259 Vector3 relativeOffset;
261 if(Toolkit::IsVertical(parentOrientation))
263 if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
265 if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
266 || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
268 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
272 relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
275 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
276 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
278 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
282 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
287 if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
289 if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
290 || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
292 relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
296 relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
299 else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
300 || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
302 relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
306 relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
310 return relativeOffset * parentSize;
315 struct OvershootOverlayVisibilityConstraint
317 bool operator()(const bool& current,
318 const PropertyInput& parentLayoutScrollableProperty)
320 const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean();
322 return parentLayoutScrollable;
327 * Relative position Constraint
328 * Generates the relative position value of the item view based on the layout position,
329 * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis.
331 Vector3 RelativePositionConstraint(const Vector3& current,
332 const PropertyInput& scrollPositionProperty,
333 const PropertyInput& scrollMinProperty,
334 const PropertyInput& scrollMaxProperty,
335 const PropertyInput& layoutSizeProperty)
337 const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f);
338 const Vector3& min = scrollMinProperty.GetVector3();
339 const Vector3& max = scrollMaxProperty.GetVector3();
341 Vector3 relativePosition;
342 Vector3 domainSize = max - min;
344 relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f;
345 relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f;
347 return relativePosition;
350 } // unnamed namespace
361 ///////////////////////////////////////////////////////////////////////////////////////////////////
363 ///////////////////////////////////////////////////////////////////////////////////////////////////
365 void ItemPool::AddItems(bool scrollingTowardsLast, ItemRange range, const Vector3& targetSize)
367 // Add new actors to the ItemPool.
368 // The order of addition depends on the scroll direction.
369 if (scrollingTowardsLast)
371 for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
373 AddItem(itemId, targetSize);
378 for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
380 AddItem(itemId-1, targetSize);
385 void ItemPool::RemoveItems(ItemRange range)
387 // Remove unwanted actors from the ItemView & ItemPool
388 for (IDKeyIter iter = mIdKeyContainer.begin(); iter != mIdKeyContainer.end(); )
390 unsigned int current = iter->first;
392 if (!range.Within(current))
394 mItemView.ActorRemovedFromItemPool(iter->second, iter->first);
396 mActorKeyContainer.erase(iter->second);
397 mIdKeyContainer.erase(iter++); // erase invalidates the current iter; the post-increment is important here
406 void ItemPool::AddItem(unsigned int itemId, const Vector3& targetSize)
408 if (mIdKeyContainer.find(itemId) == mIdKeyContainer.end())
410 Actor actor = mItemView.CreateActor(itemId);
414 mIdKeyContainer.insert(IDKeyPair(itemId, actor));
415 mActorKeyContainer.insert(ActorKeyPair(actor, itemId));
417 mItemView.ActorAddedToItemPool(actor, itemId, targetSize);
422 bool ItemPool::RemoveItem(unsigned int itemId)
426 IDKeyIter iter = mIdKeyContainer.find(itemId);
427 if (iter != mIdKeyContainer.end())
429 mItemView.ActorRemovedFromItemPool(iter->second, iter->first);
431 mActorKeyContainer.erase(iter->second);
432 for (ActorKeyIter iterActorKey = mActorKeyContainer.begin(); iterActorKey != mActorKeyContainer.end(); ++iterActorKey)
434 if(iterActorKey->second > itemId)
436 iterActorKey->second--;
440 for (IDKeyIter iterIDKey = iter; iterIDKey != mIdKeyContainer.end(); ++iterIDKey)
442 if(iterIDKey->first < mIdKeyContainer.rbegin()->first)
444 iterIDKey->second = mIdKeyContainer[iterIDKey->first + 1];
448 mIdKeyContainer.erase(iterIDKey);
459 ///////////////////////////////////////////////////////////////////////////////////////////////////
461 ///////////////////////////////////////////////////////////////////////////////////////////////////
463 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
465 // Create the implementation
466 ItemViewPtr itemView(new ItemView(factory));
468 // Pass ownership to CustomActor via derived handle
469 Dali::Toolkit::ItemView handle(*itemView);
471 // Second-phase init of the implementation
472 // This can only be done after the CustomActor connection has been made...
473 itemView->Initialize();
478 ItemView::ItemView(ItemFactory& factory)
480 mItemFactory(factory),
483 mAnimatingOvershootOn(false),
484 mAnimateOvershootOff(false),
485 mAnchoringEnabled(true),
486 mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
487 mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS),
488 mRefreshOrderHint(true/*Refresh item 0 first*/),
489 mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
490 mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
491 mScrollDistance(0.0f),
493 mTotalPanDisplacement(Vector2::ZERO),
494 mScrollOvershoot(0.0f),
496 mGestureState(Gesture::Clear)
498 SetRequiresMouseWheelEvents(true);
499 SetKeyboardNavigationSupport(true);
502 void ItemView::OnInitialize()
504 SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
506 RegisterCommonProperties();
510 mOvershootEffect = OvershootRippleEffect::New();
511 Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH );
512 mOvershootOverlay = ImageActor::New( overshootImage );
513 mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
514 mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
515 mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
516 mOvershootOverlay.SetShaderEffect(mOvershootEffect);
517 mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA);
518 self.Add(mOvershootOverlay);
520 mScrollConnector = Dali::Toolkit::ScrollConnector::New();
521 mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
523 mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
524 mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
525 mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
526 mPropertyOvershoot = self.RegisterProperty(OVERSHOOT_PROPERTY_NAME, 0.0f);
528 ApplyOvershootOverlayConstraints();
530 Constraint constraint = Constraint::New<Vector3>(mPropertyRelativePosition,
531 LocalSource(mPropertyPosition),
532 LocalSource(mPropertyPositionMin),
533 LocalSource(mPropertyPositionMax),
534 LocalSource(Actor::SIZE),
535 RelativePositionConstraint);
536 self.ApplyConstraint(constraint);
538 Vector2 stageSize = Stage::GetCurrent().GetSize();
539 mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
541 EnableGestureDetection(Gesture::Type(Gesture::Pan));
543 mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
544 mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
547 ItemView::~ItemView()
551 Dali::Toolkit::ScrollConnector ItemView::GetScrollConnector() const
553 return mScrollConnector;
556 unsigned int ItemView::GetLayoutCount() const
558 return mLayouts.size();
561 void ItemView::AddLayout(ItemLayout& layout)
563 mLayouts.push_back(ItemLayoutPtr(&layout));
566 void ItemView::RemoveLayout(unsigned int layoutIndex)
568 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
570 if (mActiveLayout == mLayouts[layoutIndex].Get())
572 mActiveLayout = NULL;
575 mLayouts.erase(mLayouts.begin() + layoutIndex);
578 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
580 return mLayouts[layoutIndex];
583 ItemLayoutPtr ItemView::GetActiveLayout() const
585 return ItemLayoutPtr(mActiveLayout);
588 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
590 return mScrollPositionObject.GetProperty<float>( ScrollConnector::SCROLL_POSITION ) + static_cast<float>( itemId );
593 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
595 DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
599 // The ItemView size should match the active layout size
600 self.SetSize(targetSize);
602 // Switch to the new layout
603 ItemLayout* previousLayout = mActiveLayout;
604 mActiveLayout = mLayouts[layoutIndex].Get();
606 // Calculate which items are within either layout
607 ItemRange oldRange = previousLayout ? GetItemRange(*previousLayout, targetSize, false/*don't reserve extra*/) : ItemRange(0u, 0u);
608 ItemRange newRange = GetItemRange(*mActiveLayout, targetSize, false/*don't reserve extra*/);
610 // Move the items to the new layout positions...
612 bool resizeAnimationNeeded(false);
614 const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer();
615 for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter)
617 unsigned int itemId = iter->first;
618 Actor actor = iter->second;
620 // Immediately relayout items that aren't within either layout
621 bool immediate = !oldRange.Within(itemId) &&
622 !newRange.Within(itemId);
624 // Remove constraints from previous layout
625 actor.RemoveConstraints();
628 if(mActiveLayout->GetItemSize(itemId, targetSize, size))
631 durationSeconds > 0.0f)
633 // Use a size animation
634 if (!resizeAnimationNeeded)
636 resizeAnimationNeeded = true;
637 RemoveAnimation(mResizeAnimation);
638 mResizeAnimation = Animation::New(durationSeconds);
641 // The layout provides its own resize animation
642 mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds);
646 // resize immediately
651 ApplyConstraints(actor, *mActiveLayout, itemId, immediate ? 0.0f : durationSeconds);
654 if (resizeAnimationNeeded)
656 mResizeAnimation.Play();
659 // Refresh the new layout
660 ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/);
661 AddItems(*mActiveLayout, targetSize, range);
663 // Scroll to an appropriate layout position
665 bool scrollAnimationNeeded(false);
666 float firstItemScrollPosition(0.0f);
668 float current = GetCurrentLayoutPosition(0);
669 float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
670 self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
672 if (current < minimum)
674 scrollAnimationNeeded = true;
675 firstItemScrollPosition = minimum;
677 else if (mAnchoringEnabled)
679 scrollAnimationNeeded = true;
680 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
683 if (scrollAnimationNeeded)
685 RemoveAnimation(mScrollAnimation);
686 mScrollAnimation = Animation::New(mAnchoringDuration);
687 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
688 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
689 mScrollAnimation.Play();
692 self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
693 AnimateScrollOvershoot(0.0f);
694 mScrollOvershoot = 0.0f;
696 Radian scrollDirection(mActiveLayout->GetScrollDirection());
697 float orientation = static_cast<float>(mActiveLayout->GetOrientation());
698 self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
700 self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
702 CalculateDomainSize(targetSize);
705 void ItemView::DeactivateCurrentLayout()
709 const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer();
710 for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter)
712 Actor actor = iter->second;
713 actor.RemoveConstraints();
716 mActiveLayout = NULL;
719 CancelRefreshTimer();
722 bool ItemView::OnRefreshTick()
724 // Short-circuit if there is no active layout
730 const Vector3 layoutSize = Self().GetCurrentSize();
732 ItemRange range = GetItemRange(*mActiveLayout, layoutSize, true/*reserve extra*/);
735 AddItems(*mActiveLayout, layoutSize, range);
737 // Keep refreshing whilst the layout is moving
738 return mScrollAnimation || (mGestureState == Gesture::Started || mGestureState == Gesture::Continuing);
741 void ItemView::SetMinimumSwipeSpeed(float speed)
743 mMinimumSwipeSpeed = speed;
746 float ItemView::GetMinimumSwipeSpeed() const
748 return mMinimumSwipeSpeed;
751 void ItemView::SetMinimumSwipeDistance(float distance)
753 mMinimumSwipeDistance = distance;
756 float ItemView::GetMinimumSwipeDistance() const
758 return mMinimumSwipeDistance;
761 void ItemView::SetMouseWheelScrollDistanceStep(float step)
763 mMouseWheelScrollDistanceStep = step;
766 float ItemView::GetMouseWheelScrollDistanceStep() const
768 return mMouseWheelScrollDistanceStep;
771 void ItemView::SetAnchoring(bool enabled)
773 mAnchoringEnabled = enabled;
776 bool ItemView::GetAnchoring() const
778 return mAnchoringEnabled;
781 void ItemView::SetAnchoringDuration(float durationSeconds)
783 mAnchoringDuration = durationSeconds;
786 float ItemView::GetAnchoringDuration() const
788 return mAnchoringDuration;
791 void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds)
793 mRefreshIntervalMilliseconds = intervalMilliseconds;
796 unsigned int ItemView::GetRefreshInterval() const
798 return mRefreshIntervalMilliseconds;
801 Actor ItemView::GetItem(unsigned int itemId) const
805 ItemPool::IDKeyConstIter found = mItemPool.GetIDKeyContainer().find(itemId);
806 if (found != mItemPool.GetIDKeyContainer().end())
808 actor = found->second;
814 unsigned int ItemView::GetItemId(Actor actor) const
816 unsigned int itemId(0);
818 ItemPool::ActorKeyConstIter found = mItemPool.GetActorKeyContainer().find(actor);
819 if (found != mItemPool.GetActorKeyContainer().end())
821 itemId = found->second;
827 void ItemView::RemoveItem(unsigned int itemId, float durationSeconds)
829 if (mItemPool.RemoveItem(itemId))
831 const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer();
832 for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter)
834 unsigned int id = iter->first;
835 Actor actor = iter->second;
837 // Reposition the items if necessary
838 actor.RemoveConstraints();
839 ApplyConstraints(actor, *mActiveLayout, id, durationSeconds);
842 CalculateDomainSize(Self().GetCurrentSize());
846 Actor ItemView::CreateActor(unsigned int itemId)
848 return mItemFactory.NewItem(itemId);
851 void ItemView::ActorAddedToItemPool(Actor actor, unsigned int itemId, const Vector3& targetSize)
855 actor.SetParentOrigin(ParentOrigin::CENTER);
856 actor.SetAnchorPoint(AnchorPoint::CENTER);
861 if(mActiveLayout->GetItemSize(itemId, targetSize, size))
866 ApplyConstraints(actor, *mActiveLayout, itemId, 0.0f/*immediate*/);
872 void ItemView::ActorRemovedFromItemPool(Actor actor, unsigned int itemId)
874 Self().Remove(actor);
877 void ItemView::RemoveItems(ItemRange range)
879 mItemPool.RemoveItems(range);
882 void ItemView::AddItems(ItemLayout& layout, const Vector3& layoutSize, ItemRange range)
884 range.end = min(mItemFactory.GetNumberOfItems(), range.end);
886 mItemPool.AddItems(mRefreshOrderHint, range, layoutSize);
889 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra)
891 unsigned int itemCount = mItemFactory.GetNumberOfItems();
893 ItemRange available(0u, itemCount);
895 ItemRange range = layout.GetItemsWithinArea( GetCurrentLayoutPosition(0), layoutSize );
899 // Add the reserve items for scrolling
900 unsigned int extra = layout.GetReserveItemCount(layoutSize);
901 range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
905 return range.Intersection(available);
908 bool ItemView::OnTouchEvent(const TouchEvent& event)
910 // Ignore events with multiple-touch points
911 if (event.GetPointCount() != 1)
916 if (event.GetPoint(0).state == TouchPoint::Down)
918 // Cancel ongoing scrolling etc.
919 mGestureState = Gesture::Clear;
921 mScrollDistance = 0.0f;
923 Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
925 mScrollOvershoot = 0.0f;
926 AnimateScrollOvershoot(0.0f);
928 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
930 RemoveAnimation(mScrollAnimation);
933 return true; // consume since we're potentially scrolling
936 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
938 // Respond the mouse wheel event to scroll
942 const Vector3 layoutSize = Self().GetCurrentSize();
943 float layoutPositionDelta = GetCurrentLayoutPosition(0) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
944 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
946 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
947 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
948 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
952 if (mMouseWheelEventFinishedTimer.IsRunning())
954 mMouseWheelEventFinishedTimer.Stop();
957 mMouseWheelEventFinishedTimer.Start();
962 bool ItemView::OnMouseWheelEventFinished()
966 RemoveAnimation(mScrollAnimation);
968 // No more mouse wheel events coming. Do the anchoring if enabled.
969 mScrollAnimation = DoAnchoring();
970 if (mScrollAnimation)
974 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
975 mScrollAnimation.Play();
979 mScrollOvershoot = 0.0f;
980 AnimateScrollOvershoot(0.0f);
982 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
989 void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration)
991 ItemLayout::Vector3Function positionConstraint;
992 if (layout.GetPositionConstraint(itemId, positionConstraint))
994 WrappedVector3Constraint wrapped(positionConstraint, itemId);
996 Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
997 Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
998 ParentSource( mPropertyScrollSpeed ),
999 ParentSource( Actor::SIZE ),
1001 constraint.SetApplyTime(duration);
1003 actor.ApplyConstraint(constraint);
1006 ItemLayout::QuaternionFunction rotationConstraint;
1007 if (layout.GetRotationConstraint(itemId, rotationConstraint))
1009 WrappedQuaternionConstraint wrapped(rotationConstraint, itemId);
1011 Constraint constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1012 Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1013 ParentSource( mPropertyScrollSpeed ),
1014 ParentSource( Actor::SIZE ),
1016 constraint.SetApplyTime(duration);
1018 actor.ApplyConstraint(constraint);
1021 ItemLayout::Vector3Function scaleConstraint;
1022 if (layout.GetScaleConstraint(itemId, scaleConstraint))
1024 WrappedVector3Constraint wrapped(scaleConstraint, itemId);
1026 Constraint constraint = Constraint::New<Vector3>( Actor::SCALE,
1027 Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1028 ParentSource( mPropertyScrollSpeed ),
1029 ParentSource( Actor::SIZE ),
1031 constraint.SetApplyTime(duration);
1033 actor.ApplyConstraint(constraint);
1036 ItemLayout::Vector4Function colorConstraint;
1037 if (layout.GetColorConstraint(itemId, colorConstraint))
1039 WrappedVector4Constraint wrapped(colorConstraint, itemId);
1041 Constraint constraint = Constraint::New<Vector4>( Actor::COLOR,
1042 Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1043 ParentSource( mPropertyScrollSpeed ),
1044 ParentSource( Actor::SIZE ),
1046 constraint.SetApplyTime(duration);
1048 // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in
1049 constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1050 constraint.SetRemoveAction(Dali::Constraint::Discard);
1052 actor.ApplyConstraint(constraint);
1055 ItemLayout::BoolFunction visibilityConstraint;
1056 if (layout.GetVisibilityConstraint(itemId, visibilityConstraint))
1058 WrappedBoolConstraint wrapped(visibilityConstraint, itemId);
1060 Constraint constraint = Constraint::New<bool>( Actor::VISIBLE,
1061 Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1062 ParentSource( mPropertyScrollSpeed ),
1063 ParentSource( Actor::SIZE ),
1065 constraint.SetApplyTime(duration);
1067 // Release visibility constraints the same time as the color constraint
1068 constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1069 constraint.SetRemoveAction(Dali::Constraint::Discard);
1071 actor.ApplyConstraint(constraint);
1075 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1077 Actor self = Self();
1078 float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1079 float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition));
1080 mScrollOvershoot = targetPosition - clamppedPosition;
1081 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1083 return clamppedPosition;
1086 void ItemView::OnPan(PanGesture gesture)
1088 Actor self = Self();
1089 const Vector3 layoutSize = Self().GetCurrentSize();
1091 RemoveAnimation(mScrollAnimation);
1093 // Short-circuit if there is no active layout
1096 mGestureState = Gesture::Clear;
1100 mGestureState = gesture.state;
1102 switch (mGestureState)
1104 case Gesture::Finished:
1107 if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1108 mScrollSpeed > mMinimumSwipeSpeed)
1110 float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1112 mRefreshOrderHint = true;
1114 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1115 float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1119 if (mAnchoringEnabled)
1121 firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1124 RemoveAnimation(mScrollAnimation);
1126 float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)));
1127 mScrollAnimation = Animation::New(flickAnimationDuration);
1128 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1129 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1130 mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1133 // Check whether it has already scrolled to the end
1134 if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1136 AnimateScrollOvershoot(0.0f);
1140 // Anchoring may be triggered when there was no swipe
1141 if (!mScrollAnimation)
1143 mScrollAnimation = DoAnchoring();
1146 // Reset the overshoot if no scroll animation.
1147 if (!mScrollAnimation)
1149 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1151 AnimateScrollOvershoot(0.0f, false);
1156 case Gesture::Started: // Fall through
1158 mTotalPanDisplacement = Vector2::ZERO;
1161 case Gesture::Continuing:
1163 mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1164 mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1166 // Refresh order depends on the direction of the scroll; negative is towards the last item.
1167 mRefreshOrderHint = mScrollDistance < 0.0f;
1169 RemoveAnimation(mScrollSpeedAnimation);
1170 mScrollSpeedAnimation = Animation::New(0.3f);
1171 mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear );
1172 mScrollSpeedAnimation.Play();
1174 float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1176 float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1178 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1179 self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1180 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1182 mTotalPanDisplacement += gesture.displacement;
1183 mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition;
1184 if( mScrollOvershoot > Math::MACHINE_EPSILON_1 )
1186 AnimateScrollOvershoot(1.0f);
1188 else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 )
1190 AnimateScrollOvershoot(-1.0f);
1194 AnimateScrollOvershoot(0.0f);
1197 StartRefreshTimer();
1201 case Gesture::Cancelled:
1203 mScrollAnimation = DoAnchoring();
1211 if (mScrollAnimation)
1213 StartRefreshTimer();
1215 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1216 mScrollAnimation.Play();
1220 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1226 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1228 Actor nextFocusActor;
1232 if(!actor || actor == this->Self())
1234 nextFocusActor = GetItem(nextItemID);
1236 else if(actor && actor.GetParent() == this->Self())
1238 int itemID = GetItemId(actor);
1239 nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1240 nextFocusActor = GetItem(nextItemID);
1241 if(nextFocusActor == actor)
1243 // need to pass NULL actor back to focus manager
1244 nextFocusActor.Reset();
1245 return nextFocusActor;
1248 float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1249 Vector3 layoutSize = Self().GetCurrentSize();
1252 // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1253 ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1254 nextItemID = viewableItems.begin;
1255 nextFocusActor = GetItem(nextItemID);
1258 return nextFocusActor;
1261 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1263 // only in this function if our chosen focus actor was actually used
1264 if(commitedFocusableActor)
1266 int nextItemID = GetItemId(commitedFocusableActor);
1267 float layoutPosition = GetCurrentLayoutPosition(0);
1268 Vector3 layoutSize = Self().GetCurrentSize();
1269 Vector3 focusItemPosition = Vector3::ZERO;
1270 ItemLayout::Vector3Function itemPositionConstraint;
1271 if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint))
1273 focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize);
1276 float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1277 ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1281 Animation ItemView::DoAnchoring()
1283 Animation anchoringAnimation;
1284 Actor self = Self();
1286 if (mActiveLayout && mAnchoringEnabled)
1288 float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1290 anchoringAnimation = Animation::New(mAnchoringDuration);
1291 anchoringAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), anchorPosition, AlphaFunctions::EaseOut );
1292 anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1293 anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1296 AnimateScrollOvershoot(0.0f);
1300 return anchoringAnimation;
1303 void ItemView::OnScrollFinished(Animation& source)
1305 Actor self = Self();
1307 RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1309 mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1311 if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1313 AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1317 // Reset the overshoot
1318 AnimateScrollOvershoot( 0.0f );
1320 mIsFlicking = false;
1322 mScrollOvershoot = 0.0f;
1325 void ItemView::OnOvershootOnFinished(Animation& animation)
1327 mAnimatingOvershootOn = false;
1328 mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1329 RemoveAnimation(mScrollOvershootAnimation);
1330 if(mAnimateOvershootOff)
1332 AnimateScrollOvershoot(0.0f);
1336 void ItemView::StartRefreshTimer()
1340 mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds );
1341 mRefreshTimer.TickSignal().Connect( this, &ItemView::OnRefreshTick );
1344 if (!mRefreshTimer.IsRunning())
1346 mRefreshTimer.Start();
1350 void ItemView::CancelRefreshTimer()
1354 mRefreshTimer.Stop();
1358 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1360 Actor self = Self();
1361 const Vector3 layoutSize = Self().GetCurrentSize();
1362 float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1364 StartRefreshTimer();
1366 if(durationSeconds > 0.0f)
1368 RemoveAnimation(mScrollAnimation);
1369 mScrollAnimation = Animation::New(durationSeconds);
1370 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1371 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1372 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1373 mScrollAnimation.Play();
1377 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1378 AnimateScrollOvershoot(0.0f);
1381 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1384 void ItemView::RemoveAnimation(Animation& animation)
1388 // Cease animating, and reset handle.
1394 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1396 Actor self = Self();
1398 Vector3 firstItemPosition(Vector3::ZERO);
1399 Vector3 lastItemPosition(Vector3::ZERO);
1403 ItemLayout::Vector3Function firstItemPositionConstraint;
1404 if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1406 firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize);
1409 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1410 ItemLayout::Vector3Function lastItemPositionConstraint;
1411 if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint))
1413 lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize);
1416 if(IsHorizontal(mActiveLayout->GetOrientation()))
1418 self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f));
1419 self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f));
1423 self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f));
1424 self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f));
1427 bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1428 self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable);
1429 self.SetProperty(mPropertyCanScrollHorizontal, false);
1433 Vector3 ItemView::GetDomainSize() const
1435 Actor self = Self();
1437 float minScrollPosition = self.GetProperty<float>(mPropertyPositionMin);
1438 float maxScrollPosition = self.GetProperty<float>(mPropertyPositionMax);
1440 return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1443 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1445 Actor self = Self();
1447 float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1448 float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1449 float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1451 return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1454 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1456 Vector3 firstItemPosition(Vector3::ZERO);
1457 ItemLayout::Vector3Function firstItemPositionConstraint;
1458 if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1460 firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize);
1463 return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1466 Vector3 ItemView::GetCurrentScrollPosition() const
1468 float currentLayoutPosition = GetCurrentLayoutPosition(0);
1469 return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1472 void ItemView::AddOverlay(Actor actor)
1477 void ItemView::RemoveOverlay(Actor actor)
1479 Self().Remove(actor);
1482 void ItemView::ScrollTo(const Vector3& position, float duration)
1484 Actor self = Self();
1485 const Vector3 layoutSize = Self().GetCurrentSize();
1487 float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1489 StartRefreshTimer();
1493 RemoveAnimation(mScrollAnimation);
1494 mScrollAnimation = Animation::New(duration);
1495 mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1496 mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1497 mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1498 mScrollAnimation.Play();
1502 mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1503 AnimateScrollOvershoot(0.0f);
1506 mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1509 void ItemView::ApplyOvershootOverlayConstraints()
1511 Constraint constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
1512 ParentSource( mPropertyScrollDirection ),
1513 ParentSource( mPropertyOvershoot ),
1514 ParentSource( Actor::SIZE ),
1515 OvershootOverlaySizeConstraint() );
1516 mOvershootOverlay.ApplyConstraint(constraint);
1517 mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height);
1519 constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1520 ParentSource( mPropertyScrollDirection ),
1521 ParentSource( mPropertyOvershoot ),
1522 OvershootOverlayRotationConstraint() );
1523 mOvershootOverlay.ApplyConstraint(constraint);
1525 constraint = Constraint::New<Vector3>( Actor::POSITION,
1526 ParentSource( Actor::SIZE ),
1527 ParentSource( mPropertyScrollDirection ),
1528 ParentSource( mPropertyOvershoot ),
1529 OvershootOverlayPositionConstraint() );
1530 mOvershootOverlay.ApplyConstraint(constraint);
1532 constraint = Constraint::New<bool>( Actor::VISIBLE,
1533 ParentSource( mPropertyCanScrollVertical ),
1534 OvershootOverlayVisibilityConstraint() );
1535 mOvershootOverlay.ApplyConstraint(constraint);
1537 int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName());
1538 Actor self = Self();
1539 constraint = Constraint::New<float>( effectOvershootPropertyIndex,
1540 Source( self, mPropertyOvershoot ),
1541 EqualToConstraint() );
1542 mOvershootEffect.ApplyConstraint(constraint);
1545 float ItemView::CalculateScrollOvershoot()
1547 float overshoot = 0.0f;
1551 // The overshoot must be calculated from the accumulated pan gesture displacement
1552 // since the pan gesture starts.
1553 Actor self = Self();
1554 float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1555 float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1556 float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1557 self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1558 float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta));
1559 overshoot = positionDelta - clamppedPosition;
1565 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1567 bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1569 // make sure we animate back if needed
1570 mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1572 if( mAnimatingOvershootOn )
1574 // animating on, do not allow animate off
1578 Actor self = Self();
1579 float currentOvershoot = self.GetProperty<float>(mPropertyOvershoot);
1580 float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
1582 RemoveAnimation(mScrollOvershootAnimation);
1583 mScrollOvershootAnimation = Animation::New(duration);
1584 mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1585 mScrollOvershootAnimation.AnimateTo( Property(self, mPropertyOvershoot), overshootAmount, TimePeriod(0.0f, duration) );
1586 mScrollOvershootAnimation.Play();
1588 mAnimatingOvershootOn = animatingOn;
1591 } // namespace Internal
1593 } // namespace Toolkit