2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/scrollable/item-view/spiral-layout.h>
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
27 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-view.h>
28 #include <dali-toolkit/devel-api/controls/scrollable/item-view/default-item-layout-property.h>
31 using namespace Dali::Toolkit;
33 namespace // unnamed namespace
36 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
37 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
39 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
40 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
42 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
44 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
45 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
46 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
48 float GetDefaultSpiralRadiusFunction(const Vector3& layoutSize)
50 return layoutSize.width*0.4f;
53 struct SpiralPositionConstraint
55 SpiralPositionConstraint( unsigned int itemId, float spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment )
57 mSpiralRadius( spiralRadius ),
58 mItemSpacingRadians( itemSpacingRadians ),
59 mItemDescent( itemDescent ),
60 mTopItemAlignment( topItemAlignment )
64 inline void OrientationUp( Vector3& current, float layoutPosition, const Vector3& layoutSize )
66 float angle = -Math::PI * 0.5f + mItemSpacingRadians * layoutPosition;
68 current.x = -mSpiralRadius * cosf( angle );
69 current.y = ( mItemDescent * layoutPosition ) + layoutSize.height * mTopItemAlignment;
70 current.z = -mSpiralRadius * sinf( angle );
73 inline void OrientationLeft( Vector3& current, float layoutPosition, const Vector3& layoutSize )
75 float angle = Math::PI * 0.5f + mItemSpacingRadians * layoutPosition;
77 current.x = ( mItemDescent * layoutPosition ) + layoutSize.width * mTopItemAlignment;
78 current.y = -mSpiralRadius * cosf( angle );
79 current.z = mSpiralRadius * sinf( angle );
82 inline void OrientationDown( Vector3& current, float layoutPosition, const Vector3& layoutSize )
84 float angle = Math::PI * 0.5f + mItemSpacingRadians * layoutPosition;
86 current.x = -mSpiralRadius * cosf( angle );
87 current.y = ( -mItemDescent * layoutPosition ) - layoutSize.height * mTopItemAlignment;
88 current.z = mSpiralRadius * sinf(angle);
91 inline void OrientationRight( Vector3& current, float layoutPosition, const Vector3& layoutSize )
93 float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
95 current.x = (-mItemDescent * layoutPosition) - layoutSize.width * mTopItemAlignment;
96 current.y = -mSpiralRadius * cosf( angle );
97 current.z = -mSpiralRadius * sinf( angle );
100 void OrientationUp( Vector3& current, const PropertyInputContainer& inputs )
102 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
103 const Vector3& layoutSize = inputs[1]->GetVector3();
104 OrientationUp( current, layoutPosition, layoutSize );
107 void OrientationLeft( Vector3& current, const PropertyInputContainer& inputs )
109 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
110 const Vector3& layoutSize = inputs[1]->GetVector3();
111 OrientationLeft( current, layoutPosition, layoutSize );
114 void OrientationDown( Vector3& current, const PropertyInputContainer& inputs )
116 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
117 const Vector3& layoutSize = inputs[1]->GetVector3();
118 OrientationDown( current, layoutPosition, layoutSize );
121 void OrientationRight( Vector3& current, const PropertyInputContainer& inputs )
123 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
124 const Vector3& layoutSize = inputs[1]->GetVector3();
125 OrientationRight( current, layoutPosition, layoutSize );
128 unsigned int mItemId;
130 float mItemSpacingRadians;
132 float mTopItemAlignment;
135 struct SpiralRotationConstraint
137 SpiralRotationConstraint( unsigned int itemId, float itemSpacingRadians )
139 mItemSpacingRadians( itemSpacingRadians )
143 void OrientationUp( Quaternion& current, const PropertyInputContainer& inputs )
145 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
146 float angle = -mItemSpacingRadians * layoutPosition;
148 current = Quaternion( Radian( angle ), Vector3::YAXIS);
151 void OrientationLeft( Quaternion& current, const PropertyInputContainer& inputs )
153 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
154 float angle = -mItemSpacingRadians * layoutPosition;
156 current = Quaternion( Radian( -Math::PI * 0.5f ), Vector3::ZAXIS ) * Quaternion( Radian( angle ), Vector3::YAXIS );
159 void OrientationDown( Quaternion& current, const PropertyInputContainer& inputs )
161 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
162 float angle = -mItemSpacingRadians * layoutPosition;
164 current = Quaternion( Radian( -Math::PI ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
167 void OrientationRight( Quaternion& current, const PropertyInputContainer& inputs )
169 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
170 float angle = -mItemSpacingRadians * layoutPosition;
172 current = Quaternion( Radian( -Math::PI * 1.5f ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
175 unsigned int mItemId;
176 float mItemSpacingRadians;
179 struct SpiralColorConstraint
181 SpiralColorConstraint( unsigned int itemId, float itemSpacingRadians )
183 mItemSpacingRadians( itemSpacingRadians )
187 void operator()( Vector4& current, const PropertyInputContainer& inputs )
189 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
190 Radian angle( mItemSpacingRadians * fabsf( layoutPosition ) / Dali::ANGLE_360 );
192 float progress = angle - floorf( angle ); // take fractional bit only to get between 0.0 - 1.0
193 progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
195 float darkness(1.0f);
197 const float startMarker = 0.10f; // The progress at which darkening starts
198 const float endMarker = 0.35f; // The progress at which darkening ends
199 const float minDarkness = 0.15f; // The darkness at end marker
201 if (progress > endMarker)
203 darkness = minDarkness;
205 else if (progress > startMarker)
207 darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
211 current.r = current.g = current.b = darkness;
214 unsigned int mItemId;
215 float mItemSpacingRadians;
218 struct SpiralVisibilityConstraint
220 SpiralVisibilityConstraint( unsigned int itemId, float itemSpacingRadians, float itemDescent, float topItemAlignment )
222 mItemSpacingRadians( itemSpacingRadians ),
223 mItemDescent( itemDescent ),
224 mTopItemAlignment( topItemAlignment )
228 void Portrait( bool& current, const PropertyInputContainer& inputs )
230 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
231 const Vector3& layoutSize = inputs[1]->GetVector3();
232 float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
233 current = ( layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= ( layoutSize.height / mItemDescent ) + 1.0f );
236 void Landscape( bool& current, const PropertyInputContainer& inputs )
238 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
239 const Vector3& layoutSize = inputs[1]->GetVector3();
240 float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
241 current = ( layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= ( layoutSize.width / mItemDescent ) + 1.0f );
244 unsigned int mItemId;
245 float mItemSpacingRadians;
247 float mTopItemAlignment;
250 } // unnamed namespace
261 struct SpiralLayout::Impl
264 : mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
265 mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
266 mItemDescent(DEFAULT_ITEM_DESCENT),
267 mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
268 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
269 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
270 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
274 float mItemSpacingRadians;
275 float mRevolutionDistance;
277 float mTopItemAlignment;
278 float mScrollSpeedFactor;
279 float mMaximumSwipeSpeed;
280 float mItemFlickAnimationDuration;
283 SpiralLayoutPtr SpiralLayout::New()
285 return SpiralLayoutPtr(new SpiralLayout());
288 SpiralLayout::~SpiralLayout()
293 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
295 mImpl->mItemSpacingRadians = itemSpacing;
297 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
298 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
301 Radian SpiralLayout::GetItemSpacing() const
303 return Radian( mImpl->mItemSpacingRadians );
306 void SpiralLayout::SetRevolutionDistance(float distance)
308 mImpl->mRevolutionDistance = distance;
310 float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
311 mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
314 float SpiralLayout::GetRevolutionDistance() const
316 return mImpl->mRevolutionDistance;
319 void SpiralLayout::SetTopItemAlignment(float alignment)
321 mImpl->mTopItemAlignment = alignment;
324 float SpiralLayout::GetTopItemAlignment() const
326 return mImpl->mTopItemAlignment;
329 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
331 mImpl->mScrollSpeedFactor = scrollSpeed;
334 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
336 mImpl->mMaximumSwipeSpeed = speed;
339 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
341 mImpl->mItemFlickAnimationDuration = durationSeconds;
344 float SpiralLayout::GetScrollSpeedFactor() const
346 return mImpl->mScrollSpeedFactor;
349 float SpiralLayout::GetMaximumSwipeSpeed() const
351 return mImpl->mMaximumSwipeSpeed;
354 float SpiralLayout::GetItemFlickAnimationDuration() const
356 return mImpl->mItemFlickAnimationDuration;
359 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
361 return 1.0f - static_cast<float>(numberOfItems);
364 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
366 return round(layoutPosition);
369 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
371 return -(static_cast<float>(itemId));
374 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
376 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
377 float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
378 float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
379 float itemsViewable = std::min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
381 unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
382 unsigned int lastItem = static_cast<unsigned int>(std::max(0.0f, firstItem + itemsViewable));
384 return ItemRange(firstItem, lastItem+1);
387 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
389 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
390 return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
393 void SpiralLayout::GetDefaultItemSize( unsigned int itemId, const Vector3& layoutSize, Vector3& itemSize ) const
395 itemSize.width = layoutSize.width * 0.25f;
398 itemSize.height = itemSize.depth = ( itemSize.width / 4.0f ) * 3.0f;
401 Degree SpiralLayout::GetScrollDirection() const
403 Degree scrollDirection(0);
404 const ControlOrientation::Type orientation = GetOrientation();
406 if ( orientation == ControlOrientation::Up )
408 scrollDirection = Degree( -45.0f ); // Allow swiping horizontally & vertically
410 else if ( orientation == ControlOrientation::Left )
412 scrollDirection = Degree( 45.0f );
414 else if ( orientation == ControlOrientation::Down )
416 scrollDirection = Degree( 180.0f - 45.0f );
418 else // orientation == ControlOrientation::Right
420 scrollDirection = Degree( 270.0f - 45.0f );
423 return scrollDirection;
426 void SpiralLayout::ApplyConstraints( Actor& actor, const int itemId, const Vector3& layoutSize, const Actor& itemViewActor )
429 if(HasLayoutChanged())
431 SetSpiralLayoutProperties(GetLayoutProperties());
433 // This just implements the default behaviour of constraint application.
434 // Custom layouts can override this function to apply their custom constraints.
435 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast( itemViewActor );
438 const ControlOrientation::Type orientation = GetOrientation();
440 // Position constraint
441 SpiralPositionConstraint positionConstraint( itemId, GetDefaultSpiralRadiusFunction( layoutSize ), mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment );
442 Constraint constraint;
443 if ( orientation == ControlOrientation::Up )
445 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationUp );
447 else if ( orientation == ControlOrientation::Left )
449 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationLeft );
451 else if ( orientation == ControlOrientation::Down )
453 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationDown );
455 else // orientation == ControlOrientation::Right
457 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &SpiralPositionConstraint::OrientationRight );
459 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
460 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
463 // Rotation constraint
464 SpiralRotationConstraint rotationConstraint( itemId, mImpl->mItemSpacingRadians );
465 if ( orientation == ControlOrientation::Up )
467 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationUp );
469 else if ( orientation == ControlOrientation::Left )
471 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationLeft );
473 else if ( orientation == ControlOrientation::Down )
475 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationDown );
477 else // orientation == ControlOrientation::Right
479 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, rotationConstraint, &SpiralRotationConstraint::OrientationRight );
481 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
485 constraint = Constraint::New< Vector4 >( actor, Actor::Property::COLOR, SpiralColorConstraint( itemId, mImpl->mItemSpacingRadians ) );
486 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
487 constraint.SetRemoveAction(Dali::Constraint::Discard);
490 // Visibility constraint
491 SpiralVisibilityConstraint visibilityConstraint( itemId, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment );
492 if (IsVertical( orientation ) )
494 constraint = Constraint::New< bool >( actor, Actor::Property::VISIBLE, visibilityConstraint, &SpiralVisibilityConstraint::Portrait );
498 constraint = Constraint::New< bool >( actor, Actor::Property::VISIBLE, visibilityConstraint, &SpiralVisibilityConstraint::Landscape );
500 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
501 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
502 constraint.SetRemoveAction(Dali::Constraint::Discard);
507 void SpiralLayout::SetSpiralLayoutProperties(const Property::Map& properties)
509 // Set any properties specified for SpiralLayout.
510 for( unsigned int idx = 0, mapCount = properties.Count(); idx < mapCount; ++idx )
512 KeyValuePair propertyPair = properties.GetKeyValue( idx );
513 switch(DefaultItemLayoutProperty::Property(propertyPair.first.indexKey))
515 case DefaultItemLayoutProperty::SPIRAL_ITEM_SPACING:
517 SetItemSpacing(Radian(propertyPair.second.Get<float>()));
520 case DefaultItemLayoutProperty::SPIRAL_MAXIMUM_SWIPE_SPEED:
522 SetMaximumSwipeSpeed(propertyPair.second.Get<float>());
525 case DefaultItemLayoutProperty::SPIRAL_TOP_ITEM_ALIGNMENT:
527 SetTopItemAlignment(propertyPair.second.Get<float>());
530 case DefaultItemLayoutProperty::SPIRAL_SCROLL_SPEED_FACTOR:
532 SetScrollSpeedFactor(propertyPair.second.Get<float>());
535 case DefaultItemLayoutProperty::SPIRAL_REVOLUTION_DISTANCE:
537 SetRevolutionDistance(propertyPair.second.Get<float>());
540 case DefaultItemLayoutProperty::SPIRAL_ITEM_FLICK_ANIMATION_DURATION:
542 SetItemFlickAnimationDuration(propertyPair.second.Get<float>());
551 ResetLayoutChangedFlag();
554 Vector3 SpiralLayout::GetItemPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) const
556 Vector3 itemPosition = Vector3::ZERO;
557 const ControlOrientation::Type orientation = GetOrientation();
559 SpiralPositionConstraint positionConstraint( itemID, GetDefaultSpiralRadiusFunction( layoutSize ), mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment );
561 if ( orientation == ControlOrientation::Up )
563 positionConstraint.OrientationUp( itemPosition, currentLayoutPosition + itemID, layoutSize );
565 else if ( orientation == ControlOrientation::Left )
567 positionConstraint.OrientationLeft( itemPosition, currentLayoutPosition + itemID, layoutSize );
569 else if ( orientation == ControlOrientation::Down )
571 positionConstraint.OrientationDown( itemPosition, currentLayoutPosition + itemID, layoutSize );
573 else //orientation == ControlOrientation::Right
575 positionConstraint.OrientationRight( itemPosition, currentLayoutPosition + itemID, layoutSize );
581 SpiralLayout::SpiralLayout()
587 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
589 return GetItemScrollToPosition(itemID);
592 } // namespace Internal
594 } // namespace Toolkit