ScrollBar refactoring
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / controls / scrollable / item-view / spiral-layout.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/public-api/controls/scrollable/item-view/spiral-layout.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <dali/public-api/animation/animation.h>
24
25 using namespace Dali;
26 using namespace Dali::Toolkit;
27
28 namespace // unnamed namespace
29 {
30
31 const float DEFAULT_ITEMS_PER_SPIRAL_TURN = 9.5f;
32 const float DEFAULT_ITEM_SPACING_RADIANS = Math::PI*2.0f/DEFAULT_ITEMS_PER_SPIRAL_TURN;
33
34 const float DEFAULT_REVOLUTION_DISTANCE = 190.0f;
35 const float DEFAULT_ITEM_DESCENT = DEFAULT_REVOLUTION_DISTANCE / DEFAULT_ITEMS_PER_SPIRAL_TURN;
36
37 const float DEFAULT_TOP_ITEM_ALIGNMENT = -0.125f;
38
39 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.01f;
40 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 30.0f;
41 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.1f;
42
43 struct DefaultItemSizeFunction
44 {
45   Vector3 operator()(const Vector3& layoutSize)
46   {
47     float width = layoutSize.width * 0.25f;
48
49     // 4x3 aspect ratio
50     return Vector3(width, (width/4)*3, (width/4)*3);
51   }
52 };
53
54 struct DefaultSpiralRadiusFunction
55 {
56   float operator()(const Vector3& layoutSize)
57   {
58     return layoutSize.width*0.4f;
59   }
60 };
61
62 struct SpiralPositionConstraintUp
63 {
64   SpiralPositionConstraintUp(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
65   : mSpiralRadius(spiralRadius),
66     mItemSpacingRadians(itemSpacingRadians),
67     mItemDescent(itemDescent),
68     mTopItemAlignment(topItemAlignment)
69   {
70   }
71
72   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
73   {
74     float spiralRadius = mSpiralRadius(layoutSize);
75
76     float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
77
78     return Vector3( -spiralRadius * cosf(angle),
79                     (mItemDescent * layoutPosition) + layoutSize.height*mTopItemAlignment,
80                     -spiralRadius * sinf(angle) );
81   }
82
83   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
84   float mItemSpacingRadians;
85   float mItemDescent;
86   float mTopItemAlignment;
87 };
88
89 struct SpiralPositionConstraintLeft
90 {
91   SpiralPositionConstraintLeft(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
92   : mSpiralRadius(spiralRadius),
93     mItemSpacingRadians(itemSpacingRadians),
94     mItemDescent(itemDescent),
95     mTopItemAlignment(topItemAlignment)
96   {
97   }
98
99   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
100   {
101     float spiralRadius = mSpiralRadius(layoutSize);
102
103     float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
104
105     return Vector3(  (mItemDescent * layoutPosition) + layoutSize.width*mTopItemAlignment,
106                      -spiralRadius * cosf(angle),
107                       spiralRadius * sinf(angle) );
108   }
109
110   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
111   float mItemSpacingRadians;
112   float mItemDescent;
113   float mTopItemAlignment;
114 };
115
116 struct SpiralPositionConstraintDown
117 {
118   SpiralPositionConstraintDown(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
119   : mSpiralRadius(spiralRadius),
120     mItemSpacingRadians(itemSpacingRadians),
121     mItemDescent(itemDescent),
122     mTopItemAlignment(topItemAlignment)
123   {
124   }
125
126   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
127   {
128     float spiralRadius = mSpiralRadius(layoutSize);
129
130     float angle = Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
131
132     return Vector3(  -spiralRadius * cosf(angle),
133                     (-mItemDescent * layoutPosition) - layoutSize.height*mTopItemAlignment,
134                       spiralRadius * sinf(angle) );
135   }
136
137   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
138   float mItemSpacingRadians;
139   float mItemDescent;
140   float mTopItemAlignment;
141 };
142
143 struct SpiralPositionConstraintRight
144 {
145   SpiralPositionConstraintRight(SpiralLayout::SpiralRadiusFunction spiralRadius, float itemSpacingRadians, float itemDescent, float topItemAlignment)
146   : mSpiralRadius(spiralRadius),
147     mItemSpacingRadians(itemSpacingRadians),
148     mItemDescent(itemDescent),
149     mTopItemAlignment(topItemAlignment)
150   {
151   }
152
153   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
154   {
155     float spiralRadius = mSpiralRadius(layoutSize);
156
157     float angle = -Math::PI*0.5f + mItemSpacingRadians * layoutPosition;
158
159     return Vector3( (-mItemDescent * layoutPosition) - layoutSize.width*mTopItemAlignment,
160                      -spiralRadius * cosf(angle),
161                      -spiralRadius * sinf(angle) );
162   }
163
164   SpiralLayout::SpiralRadiusFunction mSpiralRadius;
165   float mItemSpacingRadians;
166   float mItemDescent;
167   float mTopItemAlignment;
168 };
169
170 struct SpiralRotationConstraintUp
171 {
172   SpiralRotationConstraintUp(float itemSpacingRadians)
173   : mItemSpacingRadians(itemSpacingRadians)
174   {
175   }
176
177   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
178   {
179     float angle = -mItemSpacingRadians * layoutPosition;
180
181     return Quaternion( Radian( angle ), Vector3::YAXIS);
182   }
183
184   float mItemSpacingRadians;
185 };
186
187 struct SpiralRotationConstraintLeft
188 {
189   SpiralRotationConstraintLeft(float itemSpacingRadians)
190   : mItemSpacingRadians(itemSpacingRadians)
191   {
192   }
193
194   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
195   {
196     float angle = -mItemSpacingRadians * layoutPosition;
197
198     return Quaternion( Radian( -Math::PI * 0.5f ), Vector3::ZAXIS ) * Quaternion( Radian( angle ), Vector3::YAXIS );
199   }
200
201   float mItemSpacingRadians;
202 };
203
204 struct SpiralRotationConstraintDown
205 {
206   SpiralRotationConstraintDown(float itemSpacingRadians)
207   : mItemSpacingRadians(itemSpacingRadians)
208   {
209   }
210
211   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
212   {
213     float angle = -mItemSpacingRadians * layoutPosition;
214
215     return Quaternion( Radian( -Math::PI ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
216   }
217
218   float mItemSpacingRadians;
219 };
220
221 struct SpiralRotationConstraintRight
222 {
223   SpiralRotationConstraintRight(float itemSpacingRadians)
224   : mItemSpacingRadians(itemSpacingRadians)
225   {
226   }
227
228   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
229   {
230     float angle = -mItemSpacingRadians * layoutPosition;
231
232     return Quaternion( Radian( -Math::PI * 1.5f ), Vector3::ZAXIS) * Quaternion( Radian( angle ), Vector3::YAXIS );
233   }
234
235   float mItemSpacingRadians;
236 };
237
238 struct SpiralColorConstraint
239 {
240   SpiralColorConstraint(float itemSpacingRadians)
241   : mItemSpacingRadians(itemSpacingRadians)
242   {
243   }
244
245   Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
246   {
247     Radian angle( mItemSpacingRadians * fabsf(layoutPosition) / Dali::ANGLE_360 );
248
249     float progress = angle - floorf( angle ); // take fractional bit only to get between 0.0 - 1.0
250     progress = (progress > 0.5f) ? 2.0f*(1.0f - progress) : progress*2.0f;
251
252     float darkness(1.0f);
253     {
254       const float startMarker = 0.10f; // The progress at which darkening starts
255       const float endMarker   = 0.35f; // The progress at which darkening ends
256       const float minDarkness = 0.15f; // The darkness at end marker
257
258       if (progress > endMarker)
259       {
260         darkness = minDarkness;
261       }
262       else if (progress > startMarker)
263       {
264         darkness = 1.0f - ( (1.0f - minDarkness) * ((progress-startMarker) / (endMarker-startMarker)) );
265       }
266     }
267
268     return Vector4( darkness, darkness, darkness, current.a );
269   }
270
271   float mItemSpacingRadians;
272 };
273
274 struct SpiralVisibilityConstraintPortrait
275 {
276   SpiralVisibilityConstraintPortrait(float itemSpacingRadians, float itemDescent, float topItemAlignment)
277   : mItemSpacingRadians(itemSpacingRadians),
278     mItemDescent(itemDescent),
279     mTopItemAlignment(topItemAlignment)
280   {
281   }
282
283   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
284   {
285     float itemsCachedBeforeTopItem = layoutSize.height*(mTopItemAlignment+0.5f) / mItemDescent;
286     return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.height / mItemDescent) + 1.0f);
287   }
288
289   float mItemSpacingRadians;
290   float mItemDescent;
291   float mTopItemAlignment;
292 };
293
294 struct SpiralVisibilityConstraintLandscape
295 {
296   SpiralVisibilityConstraintLandscape(float itemSpacingRadians, float itemDescent, float topItemAlignment)
297   : mItemSpacingRadians(itemSpacingRadians),
298     mItemDescent(itemDescent),
299     mTopItemAlignment(topItemAlignment)
300   {
301   }
302
303   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
304   {
305     float itemsCachedBeforeTopItem = layoutSize.width*(mTopItemAlignment+0.5f) / mItemDescent;
306     return (layoutPosition >= -itemsCachedBeforeTopItem - 1.0f && layoutPosition <= (layoutSize.width / mItemDescent) + 1.0f);
307   }
308
309   float mItemSpacingRadians;
310   float mItemDescent;
311   float mTopItemAlignment;
312 };
313
314 } // unnamed namespace
315
316 namespace Dali
317 {
318
319 namespace Toolkit
320 {
321
322 struct SpiralLayout::Impl
323 {
324   Impl()
325   : mItemSizeFunction(DefaultItemSizeFunction()),
326     mSpiralRadiusFunction(DefaultSpiralRadiusFunction()),
327     mItemSpacingRadians(DEFAULT_ITEM_SPACING_RADIANS),
328     mRevolutionDistance(DEFAULT_REVOLUTION_DISTANCE),
329     mItemDescent(DEFAULT_ITEM_DESCENT),
330     mTopItemAlignment(DEFAULT_TOP_ITEM_ALIGNMENT),
331     mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
332     mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
333     mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
334   {
335   }
336
337   ItemSizeFunction     mItemSizeFunction;
338   SpiralRadiusFunction mSpiralRadiusFunction;
339
340   float mItemSpacingRadians;
341   float mRevolutionDistance;
342   float mItemDescent;
343   float mTopItemAlignment;
344   float mScrollSpeedFactor;
345   float mMaximumSwipeSpeed;
346   float mItemFlickAnimationDuration;
347 };
348
349 SpiralLayoutPtr SpiralLayout::New()
350 {
351   return SpiralLayoutPtr(new SpiralLayout());
352 }
353
354 SpiralLayout::~SpiralLayout()
355 {
356   delete mImpl;
357 }
358
359 void SpiralLayout::SetItemSizeFunction(ItemSizeFunction function)
360 {
361   mImpl->mItemSizeFunction = function;
362 }
363
364 SpiralLayout::ItemSizeFunction SpiralLayout::GetItemSizeFunction() const
365 {
366   return mImpl->mItemSizeFunction;
367 }
368
369 void SpiralLayout::SetItemSpacing(Radian itemSpacing)
370 {
371   mImpl->mItemSpacingRadians = itemSpacing;
372
373   float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
374   mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
375 }
376
377 Radian SpiralLayout::GetItemSpacing() const
378 {
379   return Radian( mImpl->mItemSpacingRadians );
380 }
381
382 void SpiralLayout::SetRevolutionDistance(float distance)
383 {
384   mImpl->mRevolutionDistance = distance;
385
386   float itemsPerSpiral = std::max(1.0f, (2.0f*(float)Math::PI) / mImpl->mItemSpacingRadians);
387   mImpl->mItemDescent = mImpl->mRevolutionDistance / itemsPerSpiral;
388 }
389
390 float SpiralLayout::GetRevolutionDistance() const
391 {
392   return mImpl->mRevolutionDistance;
393 }
394
395 void SpiralLayout::SetSpiralRadiusFunction(SpiralRadiusFunction function)
396 {
397   mImpl->mSpiralRadiusFunction = function;
398 }
399
400 SpiralLayout::SpiralRadiusFunction SpiralLayout::GetSpiralRadiusFunction() const
401 {
402   return mImpl->mSpiralRadiusFunction;
403 }
404
405 void SpiralLayout::SetTopItemAlignment(float alignment)
406 {
407   mImpl->mTopItemAlignment = alignment;
408 }
409
410 float SpiralLayout::GetTopItemAlignment() const
411 {
412   return mImpl->mTopItemAlignment;
413 }
414
415 void SpiralLayout::SetScrollSpeedFactor(float scrollSpeed)
416 {
417   mImpl->mScrollSpeedFactor = scrollSpeed;
418 }
419
420 void SpiralLayout::SetMaximumSwipeSpeed(float speed)
421 {
422   mImpl->mMaximumSwipeSpeed = speed;
423 }
424
425 void SpiralLayout::SetItemFlickAnimationDuration(float durationSeconds)
426 {
427   mImpl->mItemFlickAnimationDuration = durationSeconds;
428 }
429
430 float SpiralLayout::GetScrollSpeedFactor() const
431 {
432   return mImpl->mScrollSpeedFactor;
433 }
434
435 float SpiralLayout::GetMaximumSwipeSpeed() const
436 {
437   return mImpl->mMaximumSwipeSpeed;
438 }
439
440 float SpiralLayout::GetItemFlickAnimationDuration() const
441 {
442   return mImpl->mItemFlickAnimationDuration;
443 }
444
445 float SpiralLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
446 {
447   return 1.0f - static_cast<float>(numberOfItems);
448 }
449
450 float SpiralLayout::GetClosestAnchorPosition(float layoutPosition) const
451 {
452   return round(layoutPosition);
453 }
454
455 float SpiralLayout::GetItemScrollToPosition(unsigned int itemId) const
456 {
457   return -(static_cast<float>(itemId));
458 }
459
460 ItemRange SpiralLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
461 {
462   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
463   float itemsPerSpiral = layoutHeight / mImpl->mItemDescent;
464   float itemsCachedBeforeTopItem = layoutHeight * (mImpl->mTopItemAlignment + 0.5f) / mImpl->mItemDescent;
465   float itemsViewable = std::min(itemsPerSpiral, itemsPerSpiral - itemsCachedBeforeTopItem - firstItemPosition + 1.0f);
466
467   unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, -firstItemPosition - itemsCachedBeforeTopItem - 1.0f));
468   unsigned int lastItem  = static_cast<unsigned int>(std::max(0.0f, firstItem + itemsViewable));
469
470   return ItemRange(firstItem, lastItem+1);
471 }
472
473 unsigned int SpiralLayout::GetReserveItemCount(Vector3 layoutSize) const
474 {
475   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
476   return static_cast<unsigned int>(layoutHeight / mImpl->mItemDescent);
477 }
478
479 bool SpiralLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
480 {
481   // Note: itemId is not checked, since every item has the same size
482
483   itemSize = mImpl->mItemSizeFunction(layoutSize);
484   return true;
485 }
486
487 void SpiralLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
488 {
489   if(animation)
490   {
491     animation.AnimateTo( Property( actor, Actor::Property::SIZE ), size );
492   }
493 }
494
495 bool SpiralLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
496 {
497   if (mOrientation == ControlOrientation::Up)
498   {
499     constraint = SpiralPositionConstraintUp(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
500   }
501   else if (mOrientation == ControlOrientation::Left)
502   {
503     constraint = SpiralPositionConstraintLeft(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
504   }
505   else if (mOrientation == ControlOrientation::Down)
506   {
507     constraint = SpiralPositionConstraintDown(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
508   }
509   else // mOrientation == ControlOrientation::Right
510   {
511     constraint = SpiralPositionConstraintRight(mImpl->mSpiralRadiusFunction, mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
512   }
513
514   return true;
515 }
516
517 bool SpiralLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
518 {
519   if (mOrientation == ControlOrientation::Up)
520   {
521     constraint = SpiralRotationConstraintUp(mImpl->mItemSpacingRadians);
522   }
523   else if (mOrientation == ControlOrientation::Left)
524   {
525     constraint = SpiralRotationConstraintLeft(mImpl->mItemSpacingRadians);
526   }
527   else if (mOrientation == ControlOrientation::Down)
528   {
529     constraint = SpiralRotationConstraintDown(mImpl->mItemSpacingRadians);
530   }
531   else // mOrientation == ControlOrientation::Right
532   {
533     constraint = SpiralRotationConstraintRight(mImpl->mItemSpacingRadians);
534   }
535
536   return true;
537 }
538
539 bool SpiralLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
540 {
541   return false; // No scaling
542 }
543
544 bool SpiralLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
545 {
546   constraint = SpiralColorConstraint(mImpl->mItemSpacingRadians);
547   return true;
548 }
549
550 bool SpiralLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
551 {
552   if (IsVertical(mOrientation))
553   {
554     constraint = SpiralVisibilityConstraintPortrait(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
555   }
556   else // horizontal
557   {
558     constraint = SpiralVisibilityConstraintLandscape(mImpl->mItemSpacingRadians, mImpl->mItemDescent, mImpl->mTopItemAlignment);
559   }
560
561   return true;
562 }
563
564 Degree SpiralLayout::GetScrollDirection() const
565 {
566   Degree scrollDirection(0);
567
568   if (mOrientation == ControlOrientation::Up)
569   {
570     scrollDirection = Degree( -45.0f ); // Allow swiping horizontally & vertically
571   }
572   else if (mOrientation == ControlOrientation::Left)
573   {
574     scrollDirection = Degree( 45.0f );
575   }
576   else if (mOrientation == ControlOrientation::Down)
577   {
578     scrollDirection = Degree( 180.0f - 45.0f );
579   }
580   else // mOrientation == ControlOrientation::Right
581   {
582     scrollDirection = Degree( 270.0f - 45.0f );
583   }
584
585   return scrollDirection;
586 }
587
588 SpiralLayout::SpiralLayout()
589 : mImpl(NULL)
590 {
591   mImpl = new Impl();
592 }
593
594 float SpiralLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
595 {
596   return GetItemScrollToPosition(itemID);
597 }
598
599 } // namespace Toolkit
600
601 } // namespace Dali