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