65986dbf7de51fddbaf488f4acffe4586b020b76
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / public-api / controls / scrollable / item-view / roll-layout.cpp
1 /*
2  * Copyright (c) 2014 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/roll-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 using namespace std;
28
29 namespace // unnamed namespace
30 {
31
32 const float DEFAULT_ROW_SPACING = 20.0f;
33 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.0015f;
34 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 8.0f;
35 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.4f;
36
37 // 4 orientations are supported
38 static const unsigned int ORIENTATION_COUNT = 4;
39
40 static Vector3 GetItemSizeDefaultFunction(float layoutWidth, float layoutHeight, float rowSpacing)
41 {
42   float height = (layoutHeight - rowSpacing) * 0.5f;
43   return Vector3(layoutWidth, height, height);
44 }
45
46 struct RollPositionConstraint0
47 {
48   RollPositionConstraint0(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction)
49   : mRowSpacing(rowSpacing),
50     mItemSizeFunction(itemSizeFunction)
51   {
52   }
53
54   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
55   {
56     Vector3 itemSize = mItemSizeFunction(layoutSize.width, layoutSize.height, mRowSpacing);
57
58     float adjustedLayoutPosition = layoutPosition;
59     float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed * scrollSpeed;
60     float y = 0.0f;
61
62     float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor;
63
64     if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0)
65     {
66       float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f);
67       adjustedLayoutPosition = adjustment * 2.0f;
68       y = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.height * 0.5f + itemSize.y * 0.5f;
69     }
70     else
71     {
72       float yStep = max(50.0f, min(itemSize.y, scrollSpeedFactor));
73       y = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * yStep : (layoutSize.height * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * yStep;
74       y += itemSize.y * 0.5f - layoutSize.height * 0.5f;
75     }
76
77     float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor);
78     z -= min(3000.0f, scrollSpeedFactor * 2.0f);
79
80     return Vector3(itemSize.x * 0.5f - layoutSize.x * 0.5f, y, z);
81   }
82
83 public:
84
85   float mRowSpacing;
86   RollLayout::ItemSizeFunction mItemSizeFunction;
87 };
88
89 struct RollPositionConstraint90
90 {
91   RollPositionConstraint90(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction)
92   : mRowSpacing(rowSpacing),
93     mItemSizeFunction(itemSizeFunction)
94   {
95   }
96
97   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
98   {
99     Vector3 itemSize = mItemSizeFunction(layoutSize.height, layoutSize.width, mRowSpacing);
100
101     float adjustedLayoutPosition = layoutPosition;
102     float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed;
103     float x = 0.0f;
104
105     float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor;
106
107     if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0)
108     {
109       float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f);
110       adjustedLayoutPosition = adjustment * 2.0f;
111       x = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.width * 0.5f + itemSize.y * 0.5f;
112     }
113     else
114     {
115       float xStep = max(50.0f, min(itemSize.y, scrollSpeedFactor));
116       x = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * xStep : (layoutSize.width * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * xStep;
117       x += itemSize.y * 0.5f - layoutSize.width * 0.5f;
118     }
119
120     float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor);
121     z -= min(3000.0f, scrollSpeedFactor * 2.0f);
122
123     return Vector3(x, itemSize.x * 0.5f - layoutSize.y * 0.5f, z);
124   }
125
126 public:
127
128   float mRowSpacing;
129   RollLayout::ItemSizeFunction mItemSizeFunction;
130 };
131
132 struct RollPositionConstraint180
133 {
134   RollPositionConstraint180(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction)
135   : mRowSpacing(rowSpacing),
136     mItemSizeFunction(itemSizeFunction)
137   {
138   }
139
140   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
141   {
142     Vector3 itemSize = mItemSizeFunction(layoutSize.width, layoutSize.height, mRowSpacing);
143
144     float adjustedLayoutPosition = layoutPosition;
145     float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed;
146     float y = 0.0f;
147
148     float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor;
149
150     if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0)
151     {
152       float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f);
153       adjustedLayoutPosition = adjustment * 2.0f;
154       y = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.height * 0.5f + itemSize.y * 0.5f;
155     }
156     else
157     {
158       float yStep = max(50.0f, min(itemSize.y, scrollSpeedFactor));
159       y = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * yStep : (layoutSize.height * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * yStep;
160       y += itemSize.y * 0.5f - layoutSize.height * 0.5f;
161     }
162
163     float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor);
164     z -= min(3000.0f, scrollSpeedFactor * 2.0f);
165
166
167     return Vector3(-(itemSize.x * 0.5f - layoutSize.x * 0.5f),
168                    -y,
169                    z);
170   }
171
172 public:
173
174   float mRowSpacing;
175   RollLayout::ItemSizeFunction mItemSizeFunction;
176 };
177
178 struct RollPositionConstraint270
179 {
180   RollPositionConstraint270(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction)
181   : mRowSpacing(rowSpacing),
182     mItemSizeFunction(itemSizeFunction)
183   {
184   }
185
186   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
187   {
188     Vector3 itemSize = mItemSizeFunction(layoutSize.height, layoutSize.width, mRowSpacing);
189
190     float adjustedLayoutPosition = layoutPosition;
191     float scrollSpeedFactor = scrollSpeed * scrollSpeed * scrollSpeed;
192     float x = 0.0f;
193
194     float adjustedRowSpacing = mRowSpacing + scrollSpeedFactor;
195
196     if(adjustedLayoutPosition > Math::MACHINE_EPSILON_0 && adjustedLayoutPosition -2.0f < Math::MACHINE_EPSILON_0)
197     {
198       float adjustment = 1.0f - Dali::AlphaFunctions::EaseInOutSine60((2.0f - adjustedLayoutPosition) * 0.5f);
199       adjustedLayoutPosition = adjustment * 2.0f;
200       x = ((itemSize.y + adjustedRowSpacing ) * adjustedLayoutPosition) - layoutSize.width * 0.5f + itemSize.y * 0.5f;
201     }
202     else
203     {
204       float xStep = max(50.0f, min(itemSize.y, scrollSpeedFactor));
205       x = adjustedLayoutPosition < Math::MACHINE_EPSILON_0 ? adjustedLayoutPosition * xStep : (layoutSize.width * 0.5f + adjustedRowSpacing) + (adjustedLayoutPosition - 1.0f) * xStep;
206       x += itemSize.y * 0.5f - layoutSize.width * 0.5f;
207     }
208
209     float z = adjustedLayoutPosition * (10.0f + scrollSpeedFactor);
210     z -= min(3000.0f, scrollSpeedFactor * 2.0f);
211
212     return Vector3(-x,
213                    itemSize.x * 0.5f - layoutSize.y * 0.5f,
214                    z);
215   }
216
217 public:
218
219   float mRowSpacing;
220   RollLayout::ItemSizeFunction mItemSizeFunction;
221 };
222
223 struct RollRotationConstraint0
224 {
225   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
226   {
227     return Quaternion(0.0f, Vector3::ZAXIS);
228   }
229 };
230
231 struct RollRotationConstraint90
232 {
233   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
234   {
235     return Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
236   }
237 };
238
239 struct RollRotationConstraint180
240 {
241   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
242   {
243     return Quaternion(Math::PI, Vector3::ZAXIS);
244   }
245 };
246
247 struct RollRotationConstraint270
248 {
249   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
250   {
251     return Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
252   }
253 };
254
255 struct RollScaleConstraint
256 {
257   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
258   {
259     float adjustedLayoutPosition = layoutPosition;
260     float factor = 0.0f;
261     if(adjustedLayoutPosition < Math::MACHINE_EPSILON_0)
262     {
263       factor = fabsf(adjustedLayoutPosition);
264     }
265     if(adjustedLayoutPosition - 1.0f > Math::MACHINE_EPSILON_0)
266     {
267       factor = adjustedLayoutPosition - 1.0f;
268     }
269
270     float scale = min(1.0f, max(0.1f, 1.0f - 0.1f * factor));
271     if(scrollSpeed > 0.0f)
272     {
273       scale *= min(1.0f, max(0.1f, 1.0f / (scrollSpeed * 0.05f)));
274     }
275
276     return Vector3(scale, scale, scale);
277   }
278 };
279
280 struct RollColorConstraint
281 {
282   Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
283   {
284     float adjustedLayoutPosition = layoutPosition;
285
286     float factor = 0.0f;
287     if(adjustedLayoutPosition < Math::MACHINE_EPSILON_0)
288     {
289       factor = fabsf(adjustedLayoutPosition);
290     }
291     if(adjustedLayoutPosition - 1.0f > Math::MACHINE_EPSILON_0)
292     {
293       factor = adjustedLayoutPosition - 1.0f;
294     }
295
296     float darkness = min(1.0f, max(0.5f, 1.0f - 0.5f * factor));
297     float alpha = min(1.0f, max(0.0f, 1.0f - 0.9f * factor));
298     return Vector4(darkness, darkness, darkness, alpha);
299   }
300 };
301
302 struct RollVisibilityConstraintPortrait
303 {
304   RollVisibilityConstraintPortrait(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction)
305   : mRowSpacing(rowSpacing),
306     mItemSizeFunction(itemSizeFunction)
307   {
308   }
309
310   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
311   {
312     Vector3 itemSize = mItemSizeFunction(layoutSize.width, layoutSize.height, mRowSpacing);
313     int rowsPerPage = ceil(layoutSize.height / (itemSize.y + mRowSpacing));
314     return (layoutPosition > -rowsPerPage) && (layoutPosition < rowsPerPage);
315   }
316
317 public:
318
319   float mRowSpacing;
320   RollLayout::ItemSizeFunction mItemSizeFunction;
321 };
322
323 struct RollVisibilityConstraintLandscape
324 {
325   RollVisibilityConstraintLandscape(const float rowSpacing, RollLayout::ItemSizeFunction itemSizeFunction)
326   : mRowSpacing(rowSpacing),
327     mItemSizeFunction(itemSizeFunction)
328   {
329   }
330
331   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
332   {
333     Vector3 itemSize = mItemSizeFunction(layoutSize.height, layoutSize.width, mRowSpacing);
334     int rowsPerPage = ceil(layoutSize.width / (itemSize.y + mRowSpacing));
335     return (layoutPosition + 2.0f > Math::MACHINE_EPSILON_0) && (layoutPosition < rowsPerPage);
336   }
337
338 public:
339
340   float mRowSpacing;
341   RollLayout::ItemSizeFunction mItemSizeFunction;
342 };
343
344 } // unnamed namespace
345
346 namespace Dali
347 {
348
349 namespace Toolkit
350 {
351
352 struct RollLayout::Impl
353 {
354   Impl()
355   : mRowSpacing(DEFAULT_ROW_SPACING),
356     mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
357     mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
358     mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION),
359     mItemSizeFunction(GetItemSizeDefaultFunction)
360   {
361     mScaleConstraint = RollScaleConstraint();
362     mColorConstraint = RollColorConstraint();
363
364     mRotationConstraint[0] = RollRotationConstraint0();
365     mRotationConstraint[1] = RollRotationConstraint90();
366     mRotationConstraint[2] = RollRotationConstraint180();
367     mRotationConstraint[3] = RollRotationConstraint270();
368   }
369
370   float mRowSpacing;
371
372   float mScrollSpeedFactor;
373   float mMaximumSwipeSpeed;
374   float mItemFlickAnimationDuration;
375
376   ItemLayout::QuaternionFunction mRotationConstraint[ORIENTATION_COUNT];
377   ItemLayout::Vector3Function mScaleConstraint;
378   ItemLayout::Vector4Function mColorConstraint;
379
380   ItemSizeFunction mItemSizeFunction;
381 };
382
383 RollLayoutPtr RollLayout::New()
384 {
385   return RollLayoutPtr(new RollLayout());
386 }
387
388 RollLayout::~RollLayout()
389 {
390   delete mImpl;
391 }
392
393 void RollLayout::SetRowSpacing(float spacing)
394 {
395   mImpl->mRowSpacing = spacing;
396 }
397
398 float RollLayout::GetRowSpacing() const
399 {
400   return mImpl->mRowSpacing;
401 }
402
403 void RollLayout::SetItemSizeFunction(ItemSizeFunction function)
404 {
405   mImpl->mItemSizeFunction = function;
406 }
407
408 RollLayout::ItemSizeFunction RollLayout::GetItemSizeFunction() const
409 {
410   return mImpl->mItemSizeFunction;
411 }
412
413 void RollLayout::SetScrollSpeedFactor(float scrollSpeed)
414 {
415   mImpl->mScrollSpeedFactor = scrollSpeed;
416 }
417
418 void RollLayout::SetMaximumSwipeSpeed(float speed)
419 {
420   mImpl->mMaximumSwipeSpeed = speed;
421 }
422
423 void RollLayout::SetItemFlickAnimationDuration(float durationSeconds)
424 {
425   mImpl->mItemFlickAnimationDuration = durationSeconds;
426 }
427
428 float RollLayout::GetScrollSpeedFactor() const
429 {
430   return mImpl->mScrollSpeedFactor;
431 }
432
433 float RollLayout::GetMaximumSwipeSpeed() const
434 {
435   return mImpl->mMaximumSwipeSpeed;
436 }
437
438 float RollLayout::GetItemFlickAnimationDuration() const
439 {
440   return mImpl->mItemFlickAnimationDuration;
441 }
442
443 float RollLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
444 {
445   return 2.0f - static_cast<float>(numberOfItems);
446 }
447
448 float RollLayout::GetClosestAnchorPosition(float layoutPosition) const
449 {
450   return static_cast<float>(round(layoutPosition));
451 }
452
453 float RollLayout::GetItemScrollToPosition(unsigned int itemId) const
454 {
455   return 0.0f - static_cast<float>(itemId);
456 }
457
458 ItemRange RollLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
459 {
460   float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width;
461   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
462
463   Vector3 itemSize = mImpl->mItemSizeFunction( layoutWidth,layoutHeight, mImpl->mRowSpacing);
464
465   float itemsPerPage = (layoutHeight / (itemSize.y + mImpl->mRowSpacing));
466   if(firstItemPosition + 0.001f >= Math::MACHINE_EPSILON_0)
467   {
468     itemsPerPage = std::max(0.0f, itemsPerPage - 1.0f);
469   }
470   int firstVisibleItem = -(static_cast<int>(firstItemPosition));
471
472   int firstItemIndex = std::max(0, firstVisibleItem);
473   int lastItemIndex  = std::max(0, static_cast<int>(ceil(firstVisibleItem + itemsPerPage - 1)));
474   return ItemRange(firstItemIndex, lastItemIndex);
475 }
476
477 unsigned int RollLayout::GetReserveItemCount(Vector3 layoutSize) const
478 {
479   float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width;
480   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
481
482   Vector3 itemSize = mImpl->mItemSizeFunction(layoutWidth, layoutHeight, mImpl->mRowSpacing);
483   int itemsPerPage = ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing));
484   return itemsPerPage * 5;
485 }
486
487 bool RollLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
488 {
489   // Note: itemId is not checked, since every item has the same size
490   float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width;
491   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
492
493   itemSize = mImpl->mItemSizeFunction(layoutWidth, layoutHeight, mImpl->mRowSpacing);
494
495   return true;
496 }
497
498 void RollLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
499 {
500   if(animation)
501   {
502     animation.Resize(actor, size);
503   }
504 }
505
506 bool RollLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
507 {
508   if (mOrientation == ControlOrientation::Up)
509   {
510     constraint = RollPositionConstraint0(mImpl->mRowSpacing, mImpl->mItemSizeFunction);
511   }
512   else if (mOrientation == ControlOrientation::Left)
513   {
514     constraint = RollPositionConstraint90(mImpl->mRowSpacing, mImpl->mItemSizeFunction);
515   }
516   else if (mOrientation == ControlOrientation::Down)
517   {
518     constraint = RollPositionConstraint180(mImpl->mRowSpacing, mImpl->mItemSizeFunction);
519   }
520   else // mOrientation == ControlOrientation::Right
521   {
522     constraint = RollPositionConstraint270(mImpl->mRowSpacing, mImpl->mItemSizeFunction);
523   }
524
525   return true;
526 }
527
528 bool RollLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
529 {
530   constraint = mImpl->mRotationConstraint[mOrientation];
531   return true;
532 }
533
534 bool RollLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
535 {
536   constraint = mImpl->mScaleConstraint;
537   return true;
538 }
539
540 bool RollLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
541 {
542   constraint = mImpl->mColorConstraint;
543   return true;
544 }
545
546 bool RollLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
547 {
548   if (IsVertical(mOrientation))
549   {
550     constraint = RollVisibilityConstraintPortrait(mImpl->mRowSpacing, mImpl->mItemSizeFunction);
551   }
552   else // horizontal
553   {
554     constraint = RollVisibilityConstraintLandscape(mImpl->mRowSpacing, mImpl->mItemSizeFunction);
555   }
556
557   return true;
558 }
559
560 Degree RollLayout::GetScrollDirection() const
561 {
562   Degree scrollDirection(0.0f);
563
564   if (mOrientation == ControlOrientation::Up)
565   {
566     scrollDirection = 0.0f;
567   }
568   else if (mOrientation == ControlOrientation::Left)
569   {
570     scrollDirection = 90.0f;
571   }
572   else if (mOrientation == ControlOrientation::Down)
573   {
574     scrollDirection = 180.0f;
575   }
576   else // mOrientation == ControlOrientation::Right
577   {
578     scrollDirection = 270.0f;
579   }
580
581   return scrollDirection;
582 }
583
584 RollLayout::RollLayout()
585 : mImpl(NULL)
586 {
587   mImpl = new Impl();
588 }
589
590 } // namespace Toolkit
591
592 } // namespace Dali