X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fscrollable%2Fitem-view%2Fitem-view-impl.cpp;h=27054bd59a6ecd7bbaf5a1a7811959f81dd240cd;hp=a0d3be3e0980d13c8a9dce93ef79c14eb17537a6;hb=9e56847af25f4bddb5a2484b724d1f9b6f401538;hpb=e2eda444afbe82e9591fe198eef339227f90a616 diff --git a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp index a0d3be3..27054bd 100644 --- a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp @@ -1,142 +1,80 @@ -// -// Copyright (c) 2014 Samsung Electronics Co., Ltd. -// -// Licensed under the Flora License, Version 1.0 (the License); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://floralicense.org/license/ -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an AS IS BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ // CLASS HEADER #include // EXTERNAL INCLUDES +#include // for strcmp #include +#include +#include +#include +#include +#include +#include +#include +#include // INTERNAL INCLUDES -#include +#include #include +#include -using namespace std; +using std::string; +using std::set; using namespace Dali; -namespace // unnamed namespace +namespace // Unnamed namespace { //Type registration -TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL ); + +DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ItemView, Toolkit::Scrollable, NULL) + +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layout-position", FLOAT, LAYOUT_POSITION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scroll-speed", FLOAT, SCROLL_SPEED) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "overshoot", FLOAT, OVERSHOOT) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scroll-direction", VECTOR2, SCROLL_DIRECTION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layout-orientation", INTEGER, LAYOUT_ORIENTATION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scroll-content-size", FLOAT, SCROLL_CONTENT_SIZE) + +DALI_SIGNAL_REGISTRATION( Toolkit, ItemView, "layout-activated", LAYOUT_ACTIVATED_SIGNAL ) + +DALI_TYPE_REGISTRATION_END() const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f; const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f; -const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f; +const float DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f; -const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; // 20 updates per second -const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second +const float DEFAULT_MINIMUM_SWIPE_DURATION = 0.45f; +const float DEFAULT_MAXIMUM_SWIPE_DURATION = 2.6f; + +const float DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS = 20.0f; // 1 updates per 20 items +const int WHEEL_EVENT_FINISHED_TIME_OUT = 500; // 0.5 second const float DEFAULT_ANCHORING_DURATION = 1.0f; // 1 second -const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second const float MILLISECONDS_PER_SECONDS = 1000.0f; -const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png"; -const Rect OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 ); +const float OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD = 180.0f; const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f); -const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f; // 36 pixels -const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f; // 0.5 second const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f; -const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" ); -const string POSITION_PROPERTY_NAME( "item-view-position" ); -const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" ); -const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" ); -const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" ); -const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" ); - -// Functors which wrap constraint functions with stored item IDs - -struct WrappedVector3Constraint -{ - WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::Vector3Function mWrapMe; - unsigned int mItemId; -}; - -struct WrappedQuaternionConstraint -{ - WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::QuaternionFunction mWrapMe; - unsigned int mItemId; -}; - -struct WrappedVector4Constraint -{ - WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::Vector4Function mWrapMe; - unsigned int mItemId; -}; - -struct WrappedBoolConstraint -{ - WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId) - : mWrapMe(wrapMe), - mItemId(itemId) - { - } - - bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize) - { - float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast(mItemId); - - return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3()); - } - - Toolkit::ItemLayout::BoolFunction mWrapMe; - unsigned int mItemId; -}; - /** * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction */ @@ -151,199 +89,157 @@ float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout) } // Overshoot overlay constraints - struct OvershootOverlaySizeConstraint { - float operator()(const float& current, - const PropertyInput& parentScrollDirectionProperty, - const PropertyInput& parentOvershootProperty, - const PropertyInput& parentSizeProperty) + OvershootOverlaySizeConstraint( float height ) + : mOvershootHeight( height ) { - const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3(); - const Vector3 parentSize = parentSizeProperty.GetVector3(); - const Toolkit::ControlOrientation::Type& parentOrientation = static_cast(parentScrollDirection.z); + } - float overlayWidth; + void operator()( Vector3& current, const PropertyInputContainer& inputs ) + { + const Vector2& parentScrollDirection = inputs[0]->GetVector2(); + const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast(inputs[1]->GetInteger()); + const Vector3& parentSize = inputs[2]->GetVector3(); - if(Toolkit::IsVertical(parentOrientation)) + if(Toolkit::IsVertical(layoutOrientation)) { - overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y; + current.width = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y; } else { - overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x; + current.width = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x; } - return overlayWidth; + current.height = ( current.width > OVERSHOOT_BOUNCE_ACTOR_RESIZE_THRESHOLD ) ? mOvershootHeight : mOvershootHeight*0.5f; } + + float mOvershootHeight; }; -struct OvershootOverlayRotationConstraint +void OvershootOverlayRotationConstraint( Quaternion& current, const PropertyInputContainer& inputs ) { - Quaternion operator()(const Quaternion& current, - const PropertyInput& parentScrollDirectionProperty, - const PropertyInput& parentOvershootProperty) - { - const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3(); - const float parentOvershoot = parentOvershootProperty.GetFloat(); - const Toolkit::ControlOrientation::Type& parentOrientation = static_cast(parentScrollDirection.z); - - Quaternion rotation; + const Vector2& parentScrollDirection = inputs[0]->GetVector2(); + const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast(inputs[1]->GetInteger()); + const float parentOvershoot = inputs[2]->GetFloat(); - if(Toolkit::IsVertical(parentOrientation)) + float multiplier = 0; + if(Toolkit::IsVertical(layoutOrientation)) + { + if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1) { - if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1) + if( (layoutOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) + || (layoutOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) ) { - if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) - || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) ) - { - rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS); - } - else - { - rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS); - } - } - else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) - || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) ) - { - rotation = Quaternion(0.0f, Vector3::ZAXIS); + multiplier = 0.5f; } else { - rotation = Quaternion(Math::PI, Vector3::ZAXIS); + multiplier = 1.5f; } } + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) ) + { + multiplier = 0.0f; + } else { - if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1) - { - if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0) - ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) ) - { - rotation = Quaternion(Math::PI, Vector3::ZAXIS); - } - else - { - rotation = Quaternion(0.0f, Vector3::ZAXIS); - } - } - else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) - || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) ) + multiplier = 1.0f; + } + } + else + { + if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1) + { + if( (layoutOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0) + ||(layoutOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) ) { - rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS); + multiplier = 1.0f; } else { - rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS); + multiplier = 0.0f; } } - - return rotation; + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) ) + { + multiplier = 1.5f; + } + else + { + multiplier = 0.5f; + } } -}; -struct OvershootOverlayPositionConstraint + current = Quaternion( Radian( multiplier * Math::PI ), Vector3::ZAXIS ); +} + +void OvershootOverlayPositionConstraint( Vector3& current, const PropertyInputContainer& inputs ) { - Vector3 operator()(const Vector3& current, - const PropertyInput& parentSizeProperty, - const PropertyInput& parentScrollDirectionProperty, - const PropertyInput& parentOvershootProperty) - { - const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3(); - const float parentOvershoot = parentOvershootProperty.GetFloat(); - const Vector3 parentSize = parentSizeProperty.GetVector3(); - const Toolkit::ControlOrientation::Type& parentOrientation = static_cast(parentScrollDirection.z); + const Vector3& parentSize = inputs[0]->GetVector3(); + const Vector2& parentScrollDirection = inputs[1]->GetVector2(); + const Toolkit::ControlOrientation::Type& layoutOrientation = static_cast(inputs[2]->GetInteger()); + const float parentOvershoot = inputs[3]->GetFloat(); - Vector3 relativeOffset; + Vector3 relativeOffset; - if(Toolkit::IsVertical(parentOrientation)) + if(Toolkit::IsVertical(layoutOrientation)) + { + if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1) { - if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1) + if( (layoutOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) + || (layoutOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) ) { - if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0) - || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) ) - { - relativeOffset = Vector3(1.0f, 0.0f, 0.0f); - } - else - { - relativeOffset =Vector3(0.0f, 1.0f, 0.0f); - } - } - else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) - || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) ) - { - relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); } else { - relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + relativeOffset =Vector3(0.0f, 1.0f, 0.0f); } } + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) ) + { + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); + } else { - if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1) - { - if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0) - || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) ) - { - relativeOffset = Vector3(0.0f, 0.0f, 0.0f); - } - else - { - relativeOffset = Vector3(1.0f, 1.0f, 0.0f); - } - } - else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) - || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) ) + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); + } + } + else + { + if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1) + { + if( (layoutOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0) + || (layoutOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) ) { - relativeOffset = Vector3(0.0f, 1.0f, 0.0f); + relativeOffset = Vector3(0.0f, 0.0f, 0.0f); } else { - relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + relativeOffset = Vector3(1.0f, 1.0f, 0.0f); } } - - return relativeOffset * parentSize; - + else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0) + || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) ) + { + relativeOffset = Vector3(0.0f, 1.0f, 0.0f); + } + else + { + relativeOffset = Vector3(1.0f, 0.0f, 0.0f); + } } -}; -struct OvershootOverlayVisibilityConstraint -{ - bool operator()(const bool& current, - const PropertyInput& parentLayoutScrollableProperty) - { - const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean(); - - return parentLayoutScrollable; - } -}; + current = relativeOffset * parentSize; +} -/** - * Relative position Constraint - * Generates the relative position value of the item view based on the layout position, - * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis. - */ -Vector3 RelativePositionConstraint(const Vector3& current, - const PropertyInput& scrollPositionProperty, - const PropertyInput& scrollMinProperty, - const PropertyInput& scrollMaxProperty, - const PropertyInput& layoutSizeProperty) +void OvershootOverlayVisibilityConstraint( bool& current, const PropertyInputContainer& inputs ) { - const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f); - const Vector3& min = scrollMinProperty.GetVector3(); - const Vector3& max = scrollMaxProperty.GetVector3(); - - Vector3 relativePosition; - Vector3 domainSize = max - min; - - relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f; - relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f; - - return relativePosition; + current = inputs[0]->GetBoolean(); } } // unnamed namespace @@ -357,107 +253,23 @@ namespace Toolkit namespace Internal { -/////////////////////////////////////////////////////////////////////////////////////////////////// -// ItemPool -/////////////////////////////////////////////////////////////////////////////////////////////////// - -void ItemPool::AddItems(bool scrollingTowardsLast, ItemRange range, const Vector3& targetSize) -{ - // Add new actors to the ItemPool. - // The order of addition depends on the scroll direction. - if (scrollingTowardsLast) - { - for (unsigned int itemId = range.begin; itemId < range.end; ++itemId) - { - AddItem(itemId, targetSize); - } - } - else - { - for (unsigned int itemId = range.end; itemId > range.begin; --itemId) - { - AddItem(itemId-1, targetSize); - } - } -} - -void ItemPool::RemoveItems(ItemRange range) -{ - // Remove unwanted actors from the ItemView & ItemPool - for (IDKeyIter iter = mIdKeyContainer.begin(); iter != mIdKeyContainer.end(); ) - { - unsigned int current = iter->first; - - if (!range.Within(current)) - { - mItemView.ActorRemovedFromItemPool(iter->second, iter->first); - - mActorKeyContainer.erase(iter->second); - mIdKeyContainer.erase(iter++); // erase invalidates the current iter; the post-increment is important here - } - else - { - ++iter; - } - } -} - -void ItemPool::AddItem(unsigned int itemId, const Vector3& targetSize) +namespace // unnamed namespace { - if (mIdKeyContainer.find(itemId) == mIdKeyContainer.end()) - { - Actor actor = mItemView.CreateActor(itemId); - - if (actor) - { - mIdKeyContainer.insert(IDKeyPair(itemId, actor)); - mActorKeyContainer.insert(ActorKeyPair(actor, itemId)); - mItemView.ActorAddedToItemPool(actor, itemId, targetSize); - } - } -} - -bool ItemPool::RemoveItem(unsigned int itemId) +bool FindById( const ItemContainer& items, ItemId id ) { - bool found = false; - - IDKeyIter iter = mIdKeyContainer.find(itemId); - if (iter != mIdKeyContainer.end()) + for( ConstItemIter iter = items.begin(); items.end() != iter; ++iter ) { - mItemView.ActorRemovedFromItemPool(iter->second, iter->first); - - mActorKeyContainer.erase(iter->second); - for (ActorKeyIter iterActorKey = mActorKeyContainer.begin(); iterActorKey != mActorKeyContainer.end(); ++iterActorKey) - { - if(iterActorKey->second > itemId) - { - iterActorKey->second--; - } - } - - for (IDKeyIter iterIDKey = iter; iterIDKey != mIdKeyContainer.end(); ++iterIDKey) + if( iter->first == id ) { - if(iterIDKey->first < mIdKeyContainer.rbegin()->first) - { - iterIDKey->second = mIdKeyContainer[iterIDKey->first + 1]; - } - else - { - mIdKeyContainer.erase(iterIDKey); - break; - } + return true; } - - found = true; } - return found; + return false; } -/////////////////////////////////////////////////////////////////////////////////////////////////// -// ItemView -/////////////////////////////////////////////////////////////////////////////////////////////////// +} // unnamed namespace Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory) { @@ -475,70 +287,44 @@ Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory) } ItemView::ItemView(ItemFactory& factory) -: Scrollable(), +: Scrollable( ControlBehaviour( DISABLE_SIZE_NEGOTIATION | REQUIRES_WHEEL_EVENTS | REQUIRES_KEYBOARD_NAVIGATION_SUPPORT ) ), mItemFactory(factory), - mItemPool(*this), mActiveLayout(NULL), mAnimatingOvershootOn(false), mAnimateOvershootOff(false), - mAnchoringEnabled(true), + mAnchoringEnabled(false), mAnchoringDuration(DEFAULT_ANCHORING_DURATION), - mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS), + mRefreshIntervalLayoutPositions(0.0f), mRefreshOrderHint(true/*Refresh item 0 first*/), mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED), mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE), + mWheelScrollDistanceStep(0.0f), mScrollDistance(0.0f), mScrollSpeed(0.0f), mTotalPanDisplacement(Vector2::ZERO), mScrollOvershoot(0.0f), mIsFlicking(false), - mGestureState(Gesture::Clear) + mGestureState(Gesture::Clear), + mAddingItems(false), + mRefreshEnabled(true), + mItemsParentOrigin( ParentOrigin::CENTER), + mItemsAnchorPoint( AnchorPoint::CENTER) { - SetRequiresMouseWheelEvents(true); - SetKeyboardNavigationSupport(true); } void ItemView::OnInitialize() { - SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed ); - - RegisterCommonProperties(); - Actor self = Self(); - mOvershootEffect = OvershootRippleEffect::New(); - Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH ); - mOvershootOverlay = ImageActor::New( overshootImage ); - mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT); - mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT); - mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY); - mOvershootOverlay.SetShaderEffect(mOvershootEffect); - mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA); - self.Add(mOvershootOverlay); - - mPropertyLayoutPosition = self.RegisterProperty(LAYOUT_POSITION_PROPERTY_NAME, 0.0f); - mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f); - mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f); - mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f); - mPropertyOvershoot = self.RegisterProperty(OVERSHOOT_PROPERTY_NAME, 0.0f); - - ApplyOvershootOverlayConstraints(); - - Constraint constraint = Constraint::New(mPropertyRelativePosition, - LocalSource(mPropertyPosition), - LocalSource(mPropertyPositionMin), - LocalSource(mPropertyPositionMax), - LocalSource(Actor::SIZE), - RelativePositionConstraint); - self.ApplyConstraint(constraint); - Vector2 stageSize = Stage::GetCurrent().GetSize(); - mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION; + mWheelScrollDistanceStep = stageSize.y * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION; EnableGestureDetection(Gesture::Type(Gesture::Pan)); - mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT ); - mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished ); + mWheelEventFinishedTimer = Timer::New( WHEEL_EVENT_FINISHED_TIME_OUT ); + mWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnWheelEventFinished ); + + SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS); } ItemView::~ItemView() @@ -579,87 +365,52 @@ ItemLayoutPtr ItemView::GetActiveLayout() const float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const { - return Self().GetProperty(mPropertyLayoutPosition) + static_cast(itemId); + return Self().GetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION ) + static_cast( itemId ); } void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds) { DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size()); + mRefreshEnabled = false; + Actor self = Self(); // The ItemView size should match the active layout size self.SetSize(targetSize); + mActiveLayoutTargetSize = targetSize; // Switch to the new layout - ItemLayout* previousLayout = mActiveLayout; mActiveLayout = mLayouts[layoutIndex].Get(); - // Calculate which items are within either layout - ItemRange oldRange = previousLayout ? GetItemRange(*previousLayout, targetSize, false/*don't reserve extra*/) : ItemRange(0u, 0u); - ItemRange newRange = GetItemRange(*mActiveLayout, targetSize, false/*don't reserve extra*/); - // Move the items to the new layout positions... - bool resizeAnimationNeeded(false); - - const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer(); - for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter) + for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) { unsigned int itemId = iter->first; Actor actor = iter->second; - // Immediately relayout items that aren't within either layout - bool immediate = !oldRange.Within(itemId) && - !newRange.Within(itemId); - // Remove constraints from previous layout actor.RemoveConstraints(); Vector3 size; - if(mActiveLayout->GetItemSize(itemId, targetSize, size)) - { - if (!immediate && - durationSeconds > 0.0f) - { - // Use a size animation - if (!resizeAnimationNeeded) - { - resizeAnimationNeeded = true; - RemoveAnimation(mResizeAnimation); - mResizeAnimation = Animation::New(durationSeconds); - } - - // The layout provides its own resize animation - mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds); - } - else - { - // resize immediately - actor.SetSize(size); - } - } - - ApplyConstraints(actor, *mActiveLayout, itemId, immediate ? 0.0f : durationSeconds); - } + mActiveLayout->GetItemSize( itemId, targetSize, size ); + actor.SetSize( size.GetVectorXY() ); - if (resizeAnimationNeeded) - { - mResizeAnimation.Play(); + mActiveLayout->ApplyConstraints(actor, itemId, targetSize, Self() ); } // Refresh the new layout - ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/); - AddItems(*mActiveLayout, targetSize, range); + ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false/* don't reserve extra*/); + AddActorsWithinRange( range, targetSize ); // Scroll to an appropriate layout position bool scrollAnimationNeeded(false); float firstItemScrollPosition(0.0f); - float current = self.GetProperty(mPropertyLayoutPosition); + float current = GetCurrentLayoutPosition(0); float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout); - self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize)); if (current < minimum) { @@ -675,21 +426,24 @@ void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSiz if (scrollAnimationNeeded) { RemoveAnimation(mScrollAnimation); - mScrollAnimation = Animation::New(mAnchoringDuration); - mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); - mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut ); + mScrollAnimation = Animation::New(durationSeconds); + mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT ); + mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished); mScrollAnimation.Play(); } + else + { + // Emit the layout activated signal + mLayoutActivatedSignal.Emit(); + } - self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize)); AnimateScrollOvershoot(0.0f); mScrollOvershoot = 0.0f; Radian scrollDirection(mActiveLayout->GetScrollDirection()); - float orientation = static_cast(mActiveLayout->GetOrientation()); - self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation)); - - self.SetProperty(mPropertyScrollSpeed, mScrollSpeed); + self.SetProperty(Toolkit::ItemView::Property::SCROLL_DIRECTION, Vector2(sinf(scrollDirection), cosf(scrollDirection))); + self.SetProperty(Toolkit::ItemView::Property::LAYOUT_ORIENTATION, static_cast(mActiveLayout->GetOrientation())); + self.SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed); CalculateDomainSize(targetSize); } @@ -698,8 +452,7 @@ void ItemView::DeactivateCurrentLayout() { if (mActiveLayout) { - const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer(); - for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter) + for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) { Actor actor = iter->second; actor.RemoveConstraints(); @@ -707,184 +460,488 @@ void ItemView::DeactivateCurrentLayout() mActiveLayout = NULL; } +} - CancelRefreshTimer(); +void ItemView::OnRefreshNotification(PropertyNotification& source) +{ + // Cancel scroll animation to prevent any fighting of setting the scroll position property by scroll bar during fast scroll. + if(!mRefreshEnabled && mScrollAnimation) + { + RemoveAnimation(mScrollAnimation); + } + + // Only cache extra items when it is not a fast scroll + DoRefresh(GetCurrentLayoutPosition(0), mRefreshEnabled || mScrollAnimation); } -bool ItemView::OnRefreshTick() +void ItemView::Refresh() { - // Short-circuit if there is no active layout - if (!mActiveLayout) + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter ) { - return false; + ReleaseActor( iter->first, iter->second ); + } + mItemPool.clear(); + + DoRefresh(GetCurrentLayoutPosition(0), true); +} + +void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra) +{ + if (mActiveLayout) + { + ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/); + RemoveActorsOutsideRange( range ); + AddActorsWithinRange( range, Self().GetCurrentSize() ); + + mScrollUpdatedSignal.Emit( Vector2(0.0f, currentLayoutPosition) ); + } +} + +void ItemView::SetMinimumSwipeSpeed(float speed) +{ + mMinimumSwipeSpeed = speed; +} + +float ItemView::GetMinimumSwipeSpeed() const +{ + return mMinimumSwipeSpeed; +} + +void ItemView::SetMinimumSwipeDistance(float distance) +{ + mMinimumSwipeDistance = distance; +} + +float ItemView::GetMinimumSwipeDistance() const +{ + return mMinimumSwipeDistance; +} + +void ItemView::SetWheelScrollDistanceStep(float step) +{ + mWheelScrollDistanceStep = step; +} + +float ItemView::GetWheelScrollDistanceStep() const +{ + return mWheelScrollDistanceStep; +} + +void ItemView::SetAnchoring(bool enabled) +{ + mAnchoringEnabled = enabled; +} + +bool ItemView::GetAnchoring() const +{ + return mAnchoringEnabled; +} + +void ItemView::SetAnchoringDuration(float durationSeconds) +{ + mAnchoringDuration = durationSeconds; +} + +float ItemView::GetAnchoringDuration() const +{ + return mAnchoringDuration; +} + +void ItemView::SetRefreshInterval(float intervalLayoutPositions) +{ + if( !Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions) ) + { + mRefreshIntervalLayoutPositions = intervalLayoutPositions; + + Actor self = Self(); + if(mRefreshNotification) + { + self.RemovePropertyNotification(mRefreshNotification); + } + mRefreshNotification = self.AddPropertyNotification( Toolkit::ItemView::Property::LAYOUT_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) ); + mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification ); + } +} + +float ItemView::GetRefreshInterval() const +{ + return mRefreshIntervalLayoutPositions; +} + +void ItemView::SetRefreshEnabled(bool enabled) +{ + mRefreshEnabled = enabled; +} + +Actor ItemView::GetItem(unsigned int itemId) const +{ + Actor actor; + + ConstItemPoolIter iter = mItemPool.find( itemId ); + if( iter != mItemPool.end() ) + { + actor = iter->second; + } + + return actor; +} + +unsigned int ItemView::GetItemId( Actor actor ) const +{ + unsigned int itemId( 0 ); + + for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter ) + { + if( iter->second == actor ) + { + itemId = iter->first; + break; + } + } + + return itemId; +} + +void ItemView::InsertItem( Item newItem, float durationSeconds ) +{ + mAddingItems = true; + Vector3 layoutSize = Self().GetCurrentSize(); + + Actor displacedActor; + ItemPoolIter afterDisplacedIter = mItemPool.end(); + + ItemPoolIter foundIter = mItemPool.find( newItem.first ); + if( mItemPool.end() != foundIter ) + { + SetupActor( newItem, layoutSize ); + Self().Add( newItem.second ); + + displacedActor = foundIter->second; + foundIter->second = newItem.second; + + afterDisplacedIter = ++foundIter; + } + else + { + // Inserting before the existing item range? + ItemPoolIter iter = mItemPool.begin(); + if( iter != mItemPool.end() && + iter->first > newItem.first ) + { + displacedActor = iter->second; + mItemPool.erase( iter++ ); // iter is still valid after the erase + + afterDisplacedIter = iter; + } + } + + if( displacedActor ) + { + // Move the existing actors to make room + for( ItemPoolIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter ) + { + Actor temp = iter->second; + iter->second = displacedActor; + displacedActor = temp; + + iter->second.RemoveConstraints(); + mActiveLayout->ApplyConstraints( iter->second, iter->first, layoutSize, Self() ); + } + + // Create last item + ItemPool::reverse_iterator lastIter = mItemPool.rbegin(); + if ( lastIter != mItemPool.rend() ) + { + ItemId lastId = lastIter->first; + Item lastItem( lastId + 1, displacedActor ); + mItemPool.insert( lastItem ); + + lastItem.second.RemoveConstraints(); + mActiveLayout->ApplyConstraints( lastItem.second, lastItem.first, layoutSize, Self() ); + } + } + + CalculateDomainSize( layoutSize ); + + mAddingItems = false; +} + +void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds ) +{ + mAddingItems = true; + Vector3 layoutSize = Self().GetCurrentSize(); + + // Insert from lowest id to highest + std::set sortedItems; + for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter ) + { + sortedItems.insert( *iter ); + } + + for( std::set::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter ) + { + Self().Add( iter->second ); + + ItemPoolIter foundIter = mItemPool.find( iter->first ); + if( mItemPool.end() != foundIter ) + { + Actor moveMe = foundIter->second; + foundIter->second = iter->second; + + // Move the existing actors to make room + for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter ) + { + Actor temp = iter->second; + iter->second = moveMe; + moveMe = temp; + } + + // Create last item + ItemId lastId = mItemPool.rbegin()->first; + Item lastItem( lastId + 1, moveMe ); + mItemPool.insert( lastItem ); + } + else + { + mItemPool.insert( *iter ); + } + } + + // Relayout everything + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) + { + // If newly inserted + if( FindById( newItems, iter->first ) ) + { + SetupActor( *iter, layoutSize ); + } + else + { + iter->second.RemoveConstraints(); + mActiveLayout->ApplyConstraints( iter->second, iter->first, layoutSize, Self() ); + } } - const Vector3 layoutSize = Self().GetCurrentSize(); - - ItemRange range = GetItemRange(*mActiveLayout, layoutSize, true/*reserve extra*/); + CalculateDomainSize( layoutSize ); - RemoveItems(range); - AddItems(*mActiveLayout, layoutSize, range); - - // Keep refreshing whilst the layout is moving - return mScrollAnimation || (mGestureState == Gesture::Started || mGestureState == Gesture::Continuing); + mAddingItems = false; } -void ItemView::SetMinimumSwipeSpeed(float speed) +void ItemView::RemoveItem( unsigned int itemId, float durationSeconds ) { - mMinimumSwipeSpeed = speed; -} + bool actorsReordered = RemoveActor( itemId ); + if( actorsReordered ) + { + ReapplyAllConstraints(); -float ItemView::GetMinimumSwipeSpeed() const -{ - return mMinimumSwipeSpeed; + OnItemsRemoved(); + } } -void ItemView::SetMinimumSwipeDistance(float distance) +void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds ) { - mMinimumSwipeDistance = distance; -} + bool actorsReordered( false ); -float ItemView::GetMinimumSwipeDistance() const -{ - return mMinimumSwipeDistance; -} + // Remove from highest id to lowest + set sortedItems; + for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter ) + { + sortedItems.insert( *iter ); + } -void ItemView::SetMouseWheelScrollDistanceStep(float step) -{ - mMouseWheelScrollDistanceStep = step; -} + for( set::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter ) + { + if( RemoveActor( *iter ) ) + { + actorsReordered = true; + } + } -float ItemView::GetMouseWheelScrollDistanceStep() const -{ - return mMouseWheelScrollDistanceStep; -} + if( actorsReordered ) + { + ReapplyAllConstraints(); -void ItemView::SetAnchoring(bool enabled) -{ - mAnchoringEnabled = enabled; + OnItemsRemoved(); + } } -bool ItemView::GetAnchoring() const +bool ItemView::RemoveActor(unsigned int itemId) { - return mAnchoringEnabled; -} + bool reordered( false ); -void ItemView::SetAnchoringDuration(float durationSeconds) -{ - mAnchoringDuration = durationSeconds; -} + ItemPoolIter removeIter = mItemPool.find( itemId ); + if( removeIter != mItemPool.end() ) + { + ReleaseActor(itemId, removeIter->second); + } + else + { + // Removing before the existing item range? + ItemPoolIter iter = mItemPool.begin(); + if( iter != mItemPool.end() && + iter->first > itemId ) + { + // In order to decrement the first visible item ID + mItemPool.insert( Item(iter->first - 1, Actor()) ); -float ItemView::GetAnchoringDuration() const -{ - return mAnchoringDuration; -} + removeIter = mItemPool.begin(); + } + } -void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds) -{ - mRefreshIntervalMilliseconds = intervalMilliseconds; -} + if( removeIter != mItemPool.end() ) + { + reordered = true; + + // Adjust the remaining item IDs, for example if item 2 is removed: + // Initial actors: After insert: + // ID 1 - ActorA ID 1 - ActorA + // ID 2 - ActorB ID 2 - ActorC (previously ID 3) + // ID 3 - ActorC ID 3 - ActorB (previously ID 4) + // ID 4 - ActorD + for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter) + { + if( iter->first < mItemPool.rbegin()->first ) + { + iter->second = mItemPool[ iter->first + 1 ]; + } + else + { + mItemPool.erase( iter ); + break; + } + } + } -unsigned int ItemView::GetRefreshInterval() const -{ - return mRefreshIntervalMilliseconds; + return reordered; } -Actor ItemView::GetItem(unsigned int itemId) const +void ItemView::ReplaceItem( Item replacementItem, float durationSeconds ) { - Actor actor; + mAddingItems = true; + Vector3 layoutSize = Self().GetCurrentSize(); - ItemPool::IDKeyConstIter found = mItemPool.GetIDKeyContainer().find(itemId); - if (found != mItemPool.GetIDKeyContainer().end()) + SetupActor( replacementItem, layoutSize ); + Self().Add( replacementItem.second ); + + const ItemPoolIter iter = mItemPool.find( replacementItem.first ); + if( mItemPool.end() != iter ) + { + ReleaseActor(iter->first, iter->second); + iter->second = replacementItem.second; + } + else { - actor = found->second; + mItemPool.insert( replacementItem ); } - return actor; + CalculateDomainSize( layoutSize ); + + mAddingItems = false; } -unsigned int ItemView::GetItemId(Actor actor) const +void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds ) { - unsigned int itemId(0); - - ItemPool::ActorKeyConstIter found = mItemPool.GetActorKeyContainer().find(actor); - if (found != mItemPool.GetActorKeyContainer().end()) + for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter ) { - itemId = found->second; + ReplaceItem( *iter, durationSeconds ); } - - return itemId; } -void ItemView::RemoveItem(unsigned int itemId, float durationSeconds) +void ItemView::RemoveActorsOutsideRange( ItemRange range ) { - if (mItemPool.RemoveItem(itemId)) + // Remove unwanted actors from the ItemView & ItemPool + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ) { - const ItemPool::IDKeyContainer& itemPool = mItemPool.GetIDKeyContainer(); - for (ItemPool::IDKeyConstIter iter = itemPool.begin(); iter != itemPool.end(); ++iter) + unsigned int current = iter->first; + + if( ! range.Within( current ) ) { - unsigned int id = iter->first; - Actor actor = iter->second; + ReleaseActor(iter->first, iter->second); - // Reposition the items if necessary - actor.RemoveConstraints(); - ApplyConstraints(actor, *mActiveLayout, id, durationSeconds); + mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid + } + else + { + ++iter; } - - CalculateDomainSize(Self().GetCurrentSize()); } } -Actor ItemView::CreateActor(unsigned int itemId) +void ItemView::AddActorsWithinRange( ItemRange range, const Vector3& layoutSize ) { - return mItemFactory.NewItem(itemId); + range.end = std::min(mItemFactory.GetNumberOfItems(), range.end); + + // The order of addition depends on the scroll direction. + if (mRefreshOrderHint) + { + for (unsigned int itemId = range.begin; itemId < range.end; ++itemId) + { + AddNewActor( itemId, layoutSize ); + } + } + else + { + for (unsigned int itemId = range.end; itemId > range.begin; --itemId) + { + AddNewActor( itemId-1, layoutSize ); + } + } + + // Total number of items may change dynamically. + // Always recalculate the domain size to reflect that. + CalculateDomainSize(Self().GetCurrentSize()); } -void ItemView::ActorAddedToItemPool(Actor actor, unsigned int itemId, const Vector3& targetSize) +void ItemView::AddNewActor( unsigned int itemId, const Vector3& layoutSize ) { - Actor self = Self(); + mAddingItems = true; - actor.SetParentOrigin(ParentOrigin::CENTER); - actor.SetAnchorPoint(AnchorPoint::CENTER); - - if (mActiveLayout) + if( mItemPool.end() == mItemPool.find( itemId ) ) { - Vector3 size; - if(mActiveLayout->GetItemSize(itemId, targetSize, size)) + Actor actor = mItemFactory.NewItem( itemId ); + + if( actor ) { - actor.SetSize(size); - } + Item newItem( itemId, actor ); + + mItemPool.insert( newItem ); - ApplyConstraints(actor, *mActiveLayout, itemId, 0.0f/*immediate*/); + SetupActor( newItem, layoutSize ); + Self().Add( actor ); + } } - self.Add(actor); + mAddingItems = false; } -void ItemView::ActorRemovedFromItemPool(Actor actor, unsigned int itemId) +void ItemView::SetupActor( Item item, const Vector3& layoutSize ) { - Self().Remove(actor); -} + item.second.SetParentOrigin( mItemsParentOrigin ); + item.second.SetAnchorPoint( mItemsAnchorPoint ); -void ItemView::RemoveItems(ItemRange range) -{ - mItemPool.RemoveItems(range); + if( mActiveLayout ) + { + Vector3 size; + mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size ); + item.second.SetSize( size.GetVectorXY() ); + + mActiveLayout->ApplyConstraints( item.second, item.first, layoutSize, Self() ); + } } -void ItemView::AddItems(ItemLayout& layout, const Vector3& layoutSize, ItemRange range) +void ItemView::ReleaseActor( ItemId item, Actor actor ) { - range.end = min(mItemFactory.GetNumberOfItems(), range.end); - - mItemPool.AddItems(mRefreshOrderHint, range, layoutSize); + Self().Remove( actor ); + mItemFactory.ItemReleased(item, actor); } -ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra) +ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra) { unsigned int itemCount = mItemFactory.GetNumberOfItems(); ItemRange available(0u, itemCount); - ItemRange range = layout.GetItemsWithinArea(Self().GetProperty(mPropertyLayoutPosition), layoutSize); + ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize ); if (reserveExtra) { @@ -897,6 +954,23 @@ ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, return range.Intersection(available); } +void ItemView::OnChildAdd(Actor& child) +{ + if(!mAddingItems) + { + // We don't want to do this downcast check for any item added by ItemView itself. + Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child); + if(scrollBar) + { + scrollBar.SetScrollPropertySource(Self(), + Toolkit::ItemView::Property::LAYOUT_POSITION, + Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y, + Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y, + Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE); + } + } +} + bool ItemView::OnTouchEvent(const TouchEvent& event) { // Ignore events with multiple-touch points @@ -912,12 +986,15 @@ bool ItemView::OnTouchEvent(const TouchEvent& event) mScrollDistance = 0.0f; mScrollSpeed = 0.0f; - Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed); + Self().SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed); mScrollOvershoot = 0.0f; AnimateScrollOvershoot(0.0f); - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + if(mScrollAnimation) + { + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); + } RemoveAnimation(mScrollAnimation); } @@ -925,43 +1002,42 @@ bool ItemView::OnTouchEvent(const TouchEvent& event) return true; // consume since we're potentially scrolling } -bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event) +bool ItemView::OnWheelEvent(const WheelEvent& event) { - // Respond the mouse wheel event to scroll + // Respond the wheel event to scroll if (mActiveLayout) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); - float layoutPositionDelta = self.GetProperty(mPropertyLayoutPosition) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor()); + float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor()); float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout); - self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); - self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize)); - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); - StartRefreshTimer(); + + self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition ); + + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } - if (mMouseWheelEventFinishedTimer.IsRunning()) + if (mWheelEventFinishedTimer.IsRunning()) { - mMouseWheelEventFinishedTimer.Stop(); + mWheelEventFinishedTimer.Stop(); } - mMouseWheelEventFinishedTimer.Start(); + mWheelEventFinishedTimer.Start(); return true; } -bool ItemView::OnMouseWheelEventFinished() +bool ItemView::OnWheelEventFinished() { if (mActiveLayout) { RemoveAnimation(mScrollAnimation); - // No more mouse wheel events coming. Do the anchoring if enabled. + // No more wheel events coming. Do the anchoring if enabled. mScrollAnimation = DoAnchoring(); if (mScrollAnimation) { - StartRefreshTimer(); - mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); mScrollAnimation.Play(); } @@ -970,96 +1046,36 @@ bool ItemView::OnMouseWheelEventFinished() mScrollOvershoot = 0.0f; AnimateScrollOvershoot(0.0f); - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); } } return false; } -void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration) +void ItemView::ReapplyAllConstraints() { - ItemLayout::Vector3Function positionConstraint; - if (layout.GetPositionConstraint(itemId, positionConstraint)) - { - WrappedVector3Constraint wrapped(positionConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::POSITION, - ParentSource( mPropertyLayoutPosition ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - - actor.ApplyConstraint(constraint); - } - - ItemLayout::QuaternionFunction rotationConstraint; - if (layout.GetRotationConstraint(itemId, rotationConstraint)) - { - WrappedQuaternionConstraint wrapped(rotationConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::ROTATION, - ParentSource( mPropertyLayoutPosition ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); + Vector3 layoutSize = Self().GetCurrentSize(); - actor.ApplyConstraint(constraint); - } - - ItemLayout::Vector3Function scaleConstraint; - if (layout.GetScaleConstraint(itemId, scaleConstraint)) + for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) { - WrappedVector3Constraint wrapped(scaleConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::SCALE, - ParentSource( mPropertyLayoutPosition ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); + unsigned int id = iter->first; + Actor actor = iter->second; - actor.ApplyConstraint(constraint); + actor.RemoveConstraints(); + mActiveLayout->ApplyConstraints(actor, id, layoutSize, Self()); } +} - ItemLayout::Vector4Function colorConstraint; - if (layout.GetColorConstraint(itemId, colorConstraint)) - { - WrappedVector4Constraint wrapped(colorConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::COLOR, - ParentSource( mPropertyLayoutPosition ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - - // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in - constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME); - constraint.SetRemoveAction(Dali::Constraint::Discard); - - actor.ApplyConstraint(constraint); - } +void ItemView::OnItemsRemoved() +{ + CalculateDomainSize(Self().GetCurrentSize()); - ItemLayout::BoolFunction visibilityConstraint; - if (layout.GetVisibilityConstraint(itemId, visibilityConstraint)) + // Adjust scroll-position after an item is removed + if( mActiveLayout ) { - WrappedBoolConstraint wrapped(visibilityConstraint, itemId); - - Constraint constraint = Constraint::New( Actor::VISIBLE, - ParentSource( mPropertyLayoutPosition ), - ParentSource( mPropertyScrollSpeed ), - ParentSource( Actor::SIZE ), - wrapped ); - constraint.SetApplyTime(duration); - - // Release visibility constraints the same time as the color constraint - constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME); - constraint.SetRemoveAction(Dali::Constraint::Discard); - - actor.ApplyConstraint(constraint); + float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize(), *mActiveLayout); + Self().SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition ); } } @@ -1067,14 +1083,14 @@ float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targ { Actor self = Self(); float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize); - float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition)); + float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, targetPosition)); mScrollOvershoot = targetPosition - clamppedPosition; - self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); + self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition)); return clamppedPosition; } -void ItemView::OnPan(PanGesture gesture) +void ItemView::OnPan( const PanGesture& gesture ) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); @@ -1102,7 +1118,7 @@ void ItemView::OnPan(PanGesture gesture) mRefreshOrderHint = true; - float currentLayoutPosition = self.GetProperty(mPropertyLayoutPosition); + float currentLayoutPosition = GetCurrentLayoutPosition(0); float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction, layoutSize, *mActiveLayout); @@ -1114,11 +1130,12 @@ void ItemView::OnPan(PanGesture gesture) RemoveAnimation(mScrollAnimation); - float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0))); + float flickAnimationDuration = Clamp( mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0))) + , DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION); + mScrollAnimation = Animation::New(flickAnimationDuration); - mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); - mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); - mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION ), firstItemScrollPosition, AlphaFunction::EASE_OUT ); + mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT ); mIsFlicking = true; // Check whether it has already scrolled to the end @@ -1137,7 +1154,7 @@ void ItemView::OnPan(PanGesture gesture) // Reset the overshoot if no scroll animation. if (!mScrollAnimation) { - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); AnimateScrollOvershoot(0.0f, false); } @@ -1147,45 +1164,36 @@ void ItemView::OnPan(PanGesture gesture) case Gesture::Started: // Fall through { mTotalPanDisplacement = Vector2::ZERO; + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } case Gesture::Continuing: { mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout); - mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed()); + mScrollSpeed = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed()); // Refresh order depends on the direction of the scroll; negative is towards the last item. mRefreshOrderHint = mScrollDistance < 0.0f; - RemoveAnimation(mScrollSpeedAnimation); - mScrollSpeedAnimation = Animation::New(0.3f); - mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear ); - mScrollSpeedAnimation.Play(); - - float layoutPositionDelta = self.GetProperty(mPropertyLayoutPosition) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor()); + float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor()); float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout); - self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); - self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize)); - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + float currentOvershoot = self.GetProperty(Toolkit::ItemView::Property::OVERSHOOT); - mTotalPanDisplacement += gesture.displacement; - mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition; - if( mScrollOvershoot > Math::MACHINE_EPSILON_1 ) - { - AnimateScrollOvershoot(1.0f); - } - else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 ) - { - AnimateScrollOvershoot(-1.0f); - } - else + self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition ); + + if( ( firstItemScrollPosition >= 0.0f && + currentOvershoot < 1.0f ) || + ( firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) && + currentOvershoot > -1.0f ) ) { - AnimateScrollOvershoot(0.0f); + mTotalPanDisplacement += gesture.displacement; } - StartRefreshTimer(); + mScrollOvershoot = CalculateScrollOvershoot(); + self.SetProperty( Toolkit::ItemView::Property::OVERSHOOT, mScrollOvershoot ); } break; @@ -1201,8 +1209,6 @@ void ItemView::OnPan(PanGesture gesture) if (mScrollAnimation) { - StartRefreshTimer(); - mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); mScrollAnimation.Play(); } @@ -1214,7 +1220,7 @@ bool ItemView::OnAccessibilityPan(PanGesture gesture) return true; } -Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) +Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled) { Actor nextFocusActor; if(mActiveLayout) @@ -1236,7 +1242,7 @@ Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocu return nextFocusActor; } } - float layoutPosition = mActiveLayout->GetClosestAnchorPosition(Self().GetProperty(mPropertyLayoutPosition)); + float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) ); Vector3 layoutSize = Self().GetCurrentSize(); if(!nextFocusActor) { @@ -1255,17 +1261,11 @@ void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor) if(commitedFocusableActor) { int nextItemID = GetItemId(commitedFocusableActor); - float layoutPosition = Self().GetProperty(mPropertyLayoutPosition); + float layoutPosition = GetCurrentLayoutPosition(0); Vector3 layoutSize = Self().GetCurrentSize(); - Vector3 focusItemPosition = Vector3::ZERO; - ItemLayout::Vector3Function itemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint)) - { - focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize); - } float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize); - ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION); + ScrollTo(Vector2(0.0f, scrollTo), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION); } } @@ -1276,12 +1276,11 @@ Animation ItemView::DoAnchoring() if (mActiveLayout && mAnchoringEnabled) { - float anchorPosition = mActiveLayout->GetClosestAnchorPosition(Self().GetProperty(mPropertyLayoutPosition)); + float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) ); anchoringAnimation = Animation::New(mAnchoringDuration); - anchoringAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), anchorPosition, AlphaFunctions::EaseOut ); - anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut ); - anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut ); + anchoringAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), anchorPosition, AlphaFunction::EASE_OUT ); + anchoringAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT ); if(!mIsFlicking) { AnimateScrollOvershoot(0.0f); @@ -1297,7 +1296,7 @@ void ItemView::OnScrollFinished(Animation& source) RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling - mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollCompletedSignal.Emit(GetCurrentScrollPosition()); if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1) { @@ -1313,6 +1312,16 @@ void ItemView::OnScrollFinished(Animation& source) mScrollOvershoot = 0.0f; } +void ItemView::OnLayoutActivationScrollFinished(Animation& source) +{ + RemoveAnimation(mScrollAnimation); + mRefreshEnabled = true; + DoRefresh(GetCurrentLayoutPosition(0), true); + + // Emit the layout activated signal + mLayoutActivatedSignal.Emit(); +} + void ItemView::OnOvershootOnFinished(Animation& animation) { mAnimatingOvershootOn = false; @@ -1324,52 +1333,28 @@ void ItemView::OnOvershootOnFinished(Animation& animation) } } -void ItemView::StartRefreshTimer() -{ - if (!mRefreshTimer) - { - mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds ); - mRefreshTimer.TickSignal().Connect( this, &ItemView::OnRefreshTick ); - } - - if (!mRefreshTimer.IsRunning()) - { - mRefreshTimer.Start(); - } -} - -void ItemView::CancelRefreshTimer() -{ - if (mRefreshTimer) - { - mRefreshTimer.Stop(); - } -} - void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout); - StartRefreshTimer(); - if(durationSeconds > 0.0f) { RemoveAnimation(mScrollAnimation); mScrollAnimation = Animation::New(durationSeconds); - mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); - mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT ); mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); mScrollAnimation.Play(); } else { - self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); + self.SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition ); AnimateScrollOvershoot(0.0f); } - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } void ItemView::RemoveAnimation(Animation& animation) @@ -1391,51 +1376,48 @@ void ItemView::CalculateDomainSize(const Vector3& layoutSize) if(mActiveLayout) { - ItemLayout::Vector3Function firstItemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint)) - { - firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize); - } + firstItemPosition = mActiveLayout->GetItemPosition( 0,0,layoutSize ); float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize); - ItemLayout::Vector3Function lastItemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint)) - { - lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize); - } + lastItemPosition = mActiveLayout->GetItemPosition( fabs(minLayoutPosition),fabs(minLayoutPosition),layoutSize ); + + float domainSize; if(IsHorizontal(mActiveLayout->GetOrientation())) { - self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f)); - self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f)); + domainSize = fabs(firstItemPosition.x - lastItemPosition.x); } else { - self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f)); - self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f)); + domainSize = fabs(firstItemPosition.y - lastItemPosition.y); } + self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, Vector2::ZERO); + self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition)); + + self.SetProperty(Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE, domainSize); + bool isLayoutScrollable = IsLayoutScrollable(layoutSize); - self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable); - self.SetProperty(mPropertyCanScrollHorizontal, false); + self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, isLayoutScrollable); + self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, false); } } -Vector3 ItemView::GetDomainSize() const +Vector2 ItemView::GetDomainSize() const { Actor self = Self(); - float minScrollPosition = self.GetProperty(mPropertyPositionMin); - float maxScrollPosition = self.GetProperty(mPropertyPositionMax); + float minScrollPosition = self.GetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y); + float maxScrollPosition = self.GetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y); - return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f); + return Vector2(0.0f, fabs(GetScrollPosition(minScrollPosition, self.GetCurrentSize()) - GetScrollPosition(-maxScrollPosition, self.GetCurrentSize()))); } bool ItemView::IsLayoutScrollable(const Vector3& layoutSize) { Actor self = Self(); - float currentLayoutPosition = ClampFirstItemPosition(self.GetProperty(mPropertyLayoutPosition), layoutSize, *mActiveLayout); + float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout ); float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout); float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout); @@ -1444,24 +1426,18 @@ bool ItemView::IsLayoutScrollable(const Vector3& layoutSize) float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const { - Vector3 firstItemPosition(Vector3::ZERO); - ItemLayout::Vector3Function firstItemPositionConstraint; - if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint)) - { - firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize); - } - + Vector3 firstItemPosition( mActiveLayout->GetItemPosition(0, layoutPosition, layoutSize ) ); return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y; } -Vector3 ItemView::GetCurrentScrollPosition() const +Vector2 ItemView::GetCurrentScrollPosition() const { - float currentLayoutPosition = Self().GetProperty(mPropertyLayoutPosition); - return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f); + return Vector2(0.0f, GetScrollPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize())); } void ItemView::AddOverlay(Actor actor) { + actor.SetDrawMode( DrawMode::OVERLAY_2D ); Self().Add(actor); } @@ -1470,67 +1446,90 @@ void ItemView::RemoveOverlay(Actor actor) Self().Remove(actor); } -void ItemView::ScrollTo(const Vector3& position, float duration) +void ItemView::ScrollTo(const Vector2& position, float duration) { Actor self = Self(); const Vector3 layoutSize = Self().GetCurrentSize(); float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout); - StartRefreshTimer(); - if(duration > 0.0f) { RemoveAnimation(mScrollAnimation); mScrollAnimation = Animation::New(duration); - mScrollAnimation.AnimateTo( Property(self, mPropertyLayoutPosition), firstItemScrollPosition, AlphaFunctions::EaseOut ); - mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut ); + mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT ); mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished); mScrollAnimation.Play(); } else { - self.SetProperty(mPropertyLayoutPosition, firstItemScrollPosition); + self.SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition ); AnimateScrollOvershoot(0.0f); } - mScrollStartedSignalV2.Emit(GetCurrentScrollPosition()); + mScrollStartedSignal.Emit(GetCurrentScrollPosition()); + mRefreshEnabled = true; } -void ItemView::ApplyOvershootOverlayConstraints() +void ItemView::SetOvershootEffectColor( const Vector4& color ) { - Constraint constraint = Constraint::New( Actor::SIZE_WIDTH, - ParentSource( mPropertyScrollDirection ), - ParentSource( mPropertyOvershoot ), - ParentSource( Actor::SIZE ), - OvershootOverlaySizeConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); - mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height); - - constraint = Constraint::New( Actor::ROTATION, - ParentSource( mPropertyScrollDirection ), - ParentSource( mPropertyOvershoot ), - OvershootOverlayRotationConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); - - constraint = Constraint::New( Actor::POSITION, - ParentSource( Actor::SIZE ), - ParentSource( mPropertyScrollDirection ), - ParentSource( mPropertyOvershoot ), - OvershootOverlayPositionConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); - - constraint = Constraint::New( Actor::VISIBLE, - ParentSource( mPropertyCanScrollVertical ), - OvershootOverlayVisibilityConstraint() ); - mOvershootOverlay.ApplyConstraint(constraint); + mOvershootEffectColor = color; + if( mOvershootOverlay ) + { + mOvershootOverlay.SetColor( color ); + } +} - int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName()); +void ItemView::EnableScrollOvershoot( bool enable ) +{ Actor self = Self(); - constraint = Constraint::New( effectOvershootPropertyIndex, - Source( self, mPropertyOvershoot ), - EqualToConstraint() ); - mOvershootEffect.ApplyConstraint(constraint); + if( enable ) + { + Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX; + mOvershootOverlay = CreateBouncingEffectActor( effectOvershootPropertyIndex ); + mOvershootOverlay.SetColor(mOvershootEffectColor); + mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT); + mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mOvershootOverlay.SetDrawMode( DrawMode::OVERLAY_2D ); + self.Add(mOvershootOverlay); + + Constraint constraint = Constraint::New( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint(mOvershootSize.height) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) ); + constraint.AddSource( ParentSource( Actor::Property::SIZE ) ); + constraint.Apply(); + + mOvershootOverlay.SetSize(mOvershootSize.width, mOvershootSize.height); + + constraint = Constraint::New( mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) ); + constraint.Apply(); + + constraint = Constraint::New( mOvershootOverlay, Actor::Property::POSITION, OvershootOverlayPositionConstraint ); + constraint.AddSource( ParentSource( Actor::Property::SIZE ) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) ); + constraint.Apply(); + + constraint = Constraint::New( mOvershootOverlay, Actor::Property::VISIBLE, OvershootOverlayVisibilityConstraint ); + constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) ); + constraint.Apply(); + + constraint = Constraint::New( mOvershootOverlay, effectOvershootPropertyIndex, EqualToConstraint() ); + constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) ); + constraint.Apply(); + } + else + { + if( mOvershootOverlay ) + { + self.Remove(mOvershootOverlay); + mOvershootOverlay.Reset(); + } + } } float ItemView::CalculateScrollOvershoot() @@ -1543,14 +1542,14 @@ float ItemView::CalculateScrollOvershoot() // since the pan gesture starts. Actor self = Self(); float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor(); - float positionDelta = self.GetProperty(mPropertyLayoutPosition) + scrollDistance; + float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance; float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize()); - self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition); - float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta)); + self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition)); + float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta)); overshoot = positionDelta - clamppedPosition; } - return overshoot; + return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f); } void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack) @@ -1567,16 +1566,97 @@ void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack) } Actor self = Self(); - float currentOvershoot = self.GetProperty(mPropertyOvershoot); - float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)); - RemoveAnimation(mScrollOvershootAnimation); - mScrollOvershootAnimation = Animation::New(duration); - mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished); - mScrollOvershootAnimation.AnimateTo( Property(self, mPropertyOvershoot), overshootAmount, TimePeriod(0.0f, duration) ); - mScrollOvershootAnimation.Play(); + if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0) + { + float currentOvershoot = self.GetProperty(Toolkit::ItemView::Property::OVERSHOOT); + float duration = 0.0f; + + if (mOvershootOverlay) + { + duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed; + } + + RemoveAnimation(mScrollOvershootAnimation); + mScrollOvershootAnimation = Animation::New(duration); + mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished); + mScrollOvershootAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) ); + mScrollOvershootAnimation.Play(); + + mAnimatingOvershootOn = animatingOn; + } + else + { + self.SetProperty( Toolkit::ItemView::Property::OVERSHOOT, overshootAmount ); + } +} + +void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin ) +{ + if( parentOrigin != mItemsParentOrigin ) + { + mItemsParentOrigin = parentOrigin; + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) + { + iter->second.SetParentOrigin(parentOrigin); + } + } +} + +Vector3 ItemView::GetItemsParentOrigin() const +{ + return mItemsParentOrigin; +} + +void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint ) +{ + if( anchorPoint != mItemsAnchorPoint ) + { + mItemsAnchorPoint = anchorPoint; + for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter) + { + iter->second.SetAnchorPoint(anchorPoint); + } + } +} + +Vector3 ItemView::GetItemsAnchorPoint() const +{ + return mItemsAnchorPoint; +} + +void ItemView::GetItemsRange(ItemRange& range) +{ + if( !mItemPool.empty() ) + { + range.begin = mItemPool.begin()->first; + range.end = mItemPool.rbegin()->first + 1; + } + else + { + range.begin = 0; + range.end = 0; + } +} + +bool ItemView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + Dali::BaseHandle handle( object ); + + bool connected( true ); + Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( handle ); + + if( 0 == strcmp( signalName.c_str(), LAYOUT_ACTIVATED_SIGNAL ) ) + { + itemView.LayoutActivatedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } - mAnimatingOvershootOn = animatingOn; + return connected; } } // namespace Internal