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