d7fca94ddc5968a404eba1b4a063ee9a4c6ff0b7
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / controls / scrollable / item-view / grid-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/grid-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 unsigned int DEFAULT_NUMBER_OF_COLUMNS = 4;
32 const float DEFAULT_TOP_MARGIN     =  95.0f;
33 const float DEFAULT_BOTTOM_MARGIN  =  20.0f;
34 const float DEFAULT_SIDE_MARGIN    =  20.0f;
35 const float DEFAULT_COLUMN_SPACING =  20.0f;
36 const float DEFAULT_ROW_SPACING    =  20.0f;
37 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.03f;
38 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 100.0f;
39 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.015f;
40
41 // 4 orientations are supported
42 static const unsigned int ORIENTATION_COUNT = 4;
43
44 static Vector3 GetItemSizeDefaultFunction(unsigned int numberOfColumns, float layoutWidth, float sideMargin, float columnSpacing)
45 {
46   float width = (layoutWidth - sideMargin * 2.0f - columnSpacing * static_cast<float>(numberOfColumns - 1)) / static_cast<float>(numberOfColumns);
47
48   // 4x3 aspect ratio
49   return Vector3(width, width * 0.75f, width * 0.75f);
50 }
51
52 struct GridPositionConstraint0
53 {
54   GridPositionConstraint0(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap)
55   : mColumnIndex(columnIndex),
56     mNumberOfColumns(numberOfColumns),
57     mRowSpacing(rowSpacing),
58     mColumnSpacing(columnSpacing),
59     mTopMargin(topMargin),
60     mSideMargin(sideMargin),
61     mItemSizeFunction(itemSizeFunction),
62     mZGap(gap)
63   {
64   }
65
66   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
67   {
68     Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width, mSideMargin, mColumnSpacing);
69
70     return Vector3(mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.x * 0.5f,
71                    ((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.height * 0.5f + itemSize.y * 0.5f + mTopMargin,
72                    mColumnIndex * mZGap);
73   }
74
75 public:
76
77   unsigned int mColumnIndex;
78   unsigned int mNumberOfColumns;
79   float mRowSpacing;
80   float mColumnSpacing;
81   float mTopMargin;
82   float mSideMargin;
83   GridLayout::ItemSizeFunction mItemSizeFunction;
84   float mZGap;
85 };
86
87 struct GridPositionConstraint90
88 {
89   GridPositionConstraint90(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap)
90   : mColumnIndex(columnIndex),
91     mNumberOfColumns(numberOfColumns),
92     mRowSpacing(rowSpacing),
93     mColumnSpacing(columnSpacing),
94     mTopMargin(topMargin),
95     mSideMargin(sideMargin),
96     mItemSizeFunction(itemSizeFunction),
97     mZGap(gap)
98   {
99   }
100
101   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
102   {
103     Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height, mSideMargin, mColumnSpacing);
104
105     return Vector3(((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.width * 0.5f + itemSize.y * 0.5f + mTopMargin,
106                    -(mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.y*0.5f),
107                    mColumnIndex * mZGap);
108   }
109
110 public:
111
112   unsigned int mColumnIndex;
113   unsigned int mNumberOfColumns;
114   float mRowSpacing;
115   float mColumnSpacing;
116   float mTopMargin;
117   float mSideMargin;
118   GridLayout::ItemSizeFunction mItemSizeFunction;
119   float mZGap;
120 };
121
122 struct GridPositionConstraint180
123 {
124   GridPositionConstraint180(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap)
125   : mColumnIndex(columnIndex),
126     mNumberOfColumns(numberOfColumns),
127     mRowSpacing(rowSpacing),
128     mColumnSpacing(columnSpacing),
129     mTopMargin(topMargin),
130     mSideMargin(sideMargin),
131     mItemSizeFunction(itemSizeFunction),
132     mZGap(gap)
133   {
134   }
135
136   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
137   {
138     Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width, mSideMargin, mColumnSpacing);
139
140     return Vector3(-(mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.x * 0.5f),
141                    -(((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.height * 0.5f + itemSize.y * 0.5f + mTopMargin),
142                    mColumnIndex * mZGap);
143   }
144
145 public:
146
147   unsigned int mColumnIndex;
148   unsigned int mNumberOfColumns;
149   float mRowSpacing;
150   float mColumnSpacing;
151   float mTopMargin;
152   float mSideMargin;
153   GridLayout::ItemSizeFunction mItemSizeFunction;
154   float mZGap;
155 };
156
157 struct GridPositionConstraint270
158 {
159   GridPositionConstraint270(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float topMargin, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction, const float gap)
160   : mColumnIndex(columnIndex),
161     mNumberOfColumns(numberOfColumns),
162     mRowSpacing(rowSpacing),
163     mColumnSpacing(columnSpacing),
164     mTopMargin(topMargin),
165     mSideMargin(sideMargin),
166     mItemSizeFunction(itemSizeFunction),
167     mZGap(gap)
168   {
169   }
170
171   Vector3 operator()(const Vector3& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
172   {
173     Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height, mSideMargin, mColumnSpacing);
174
175     return Vector3(-(((itemSize.y + mRowSpacing) * (layoutPosition - mColumnIndex)) / mNumberOfColumns - layoutSize.width * 0.5f + itemSize.y * 0.5f + mTopMargin),
176                    mSideMargin + (mColumnIndex * (itemSize.x + mColumnSpacing)) + itemSize.x * 0.5f - layoutSize.y * 0.5f,
177                    mColumnIndex * mZGap);
178   }
179
180 public:
181
182   unsigned int mColumnIndex;
183   unsigned int mNumberOfColumns;
184   float mRowSpacing;
185   float mColumnSpacing;
186   float mTopMargin;
187   float mSideMargin;
188   GridLayout::ItemSizeFunction mItemSizeFunction;
189   float mZGap;
190 };
191
192 struct GridRotationConstraint0
193 {
194   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
195   {
196     return Quaternion( Radian( 0.0f ), Vector3::ZAXIS);
197   }
198 };
199
200 struct GridRotationConstraint90
201 {
202   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
203   {
204     return Quaternion( Radian( 1.5f * Math::PI ), Vector3::ZAXIS);
205   }
206 };
207
208 struct GridRotationConstraint180
209 {
210   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
211   {
212     return Quaternion( Radian( Math::PI ), Vector3::ZAXIS);
213   }
214 };
215
216 struct GridRotationConstraint270
217 {
218   Quaternion operator()(const Quaternion& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
219   {
220     return Quaternion( Radian( 0.5f * Math::PI ), Vector3::ZAXIS);
221   }
222 };
223
224 struct GridColorConstraint
225 {
226   Vector4 operator()(const Vector4& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
227   {
228     return Vector4( 1.0f, 1.0f, 1.0f, current.a );
229   }
230 };
231
232 struct GridVisibilityConstraintPortrait
233 {
234   GridVisibilityConstraintPortrait(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction)
235   : mColumnIndex(columnIndex),
236     mNumberOfColumns(numberOfColumns),
237     mRowSpacing(rowSpacing),
238     mColumnSpacing(columnSpacing),
239     mSideMargin(sideMargin),
240     mItemSizeFunction(itemSizeFunction)
241   {
242   }
243
244   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
245   {
246     Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.width, mSideMargin, mColumnSpacing);
247
248     float row = (layoutPosition - static_cast<float>(mColumnIndex)) / mNumberOfColumns;
249     int rowsPerPage = ceil(layoutSize.height / (itemSize.y + mRowSpacing));
250
251     return (row > -2.0f) && (row < rowsPerPage);
252   }
253
254 public:
255
256   unsigned int mColumnIndex;
257   unsigned int mNumberOfColumns;
258   float mRowSpacing;
259   float mColumnSpacing;
260   float mSideMargin;
261   GridLayout::ItemSizeFunction mItemSizeFunction;
262 };
263
264 struct GridVisibilityConstraintLandscape
265 {
266   GridVisibilityConstraintLandscape(const unsigned int columnIndex, const unsigned int numberOfColumns, const float rowSpacing, const float columnSpacing, const float sideMargin, GridLayout::ItemSizeFunction itemSizeFunction)
267   : mColumnIndex(columnIndex),
268     mNumberOfColumns(numberOfColumns),
269     mRowSpacing(rowSpacing),
270     mColumnSpacing(columnSpacing),
271     mSideMargin(sideMargin),
272     mItemSizeFunction(itemSizeFunction)
273   {
274   }
275
276   bool operator()(const bool& current, const float& layoutPosition, const float& scrollSpeed, const Vector3& layoutSize)
277   {
278     Vector3 itemSize = mItemSizeFunction(mNumberOfColumns, layoutSize.height, mSideMargin, mColumnSpacing);
279
280     float row = (layoutPosition - static_cast<float>(mColumnIndex)) / mNumberOfColumns;
281     int rowsPerPage = ceil(layoutSize.width / (itemSize.y + mRowSpacing));
282
283     return (row > -2.0f) && (row < rowsPerPage);
284   }
285
286 public:
287
288   unsigned int mColumnIndex;
289   unsigned int mNumberOfColumns;
290   float mRowSpacing;
291   float mColumnSpacing;
292   float mSideMargin;
293   GridLayout::ItemSizeFunction mItemSizeFunction;
294 };
295
296 } // unnamed namespace
297
298 namespace Dali
299 {
300
301 namespace Toolkit
302 {
303
304 struct GridLayout::Impl
305 {
306   Impl()
307   : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS),
308     mRowSpacing(DEFAULT_ROW_SPACING),
309     mColumnSpacing(DEFAULT_COLUMN_SPACING),
310     mTopMargin(DEFAULT_TOP_MARGIN),
311     mBottomMargin(DEFAULT_BOTTOM_MARGIN),
312     mSideMargin(DEFAULT_SIDE_MARGIN),
313     mZGap(0.f),
314     mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
315     mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
316     mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION),
317     mItemSizeFunction(GetItemSizeDefaultFunction)
318   {
319     mColorConstraint = GridColorConstraint();
320
321     mRotationConstraint[0] = GridRotationConstraint0();
322     mRotationConstraint[1] = GridRotationConstraint90();
323     mRotationConstraint[2] = GridRotationConstraint180();
324     mRotationConstraint[3] = GridRotationConstraint270();
325   }
326
327   unsigned int mNumberOfColumns;
328   float mRowSpacing;
329   float mColumnSpacing;
330   float mTopMargin;
331   float mBottomMargin;
332   float mSideMargin;
333   float mZGap;
334
335   float mScrollSpeedFactor;
336   float mMaximumSwipeSpeed;
337   float mItemFlickAnimationDuration;
338
339   ItemLayout::QuaternionFunction mRotationConstraint[ORIENTATION_COUNT];
340   ItemLayout::Vector4Function mColorConstraint;
341
342   ItemSizeFunction mItemSizeFunction;
343 };
344
345 GridLayoutPtr GridLayout::New()
346 {
347   return GridLayoutPtr(new GridLayout());
348 }
349
350 GridLayout::~GridLayout()
351 {
352   delete mImpl;
353 }
354
355 void GridLayout::SetNumberOfColumns(unsigned int columns)
356 {
357   mImpl->mNumberOfColumns = columns;
358 }
359
360 unsigned int GridLayout::GetNumberOfColumns() const
361 {
362   return mImpl->mNumberOfColumns;
363 }
364
365 void GridLayout::SetRowSpacing(float spacing)
366 {
367   mImpl->mRowSpacing = spacing;
368 }
369
370 float GridLayout::GetRowSpacing() const
371 {
372   return mImpl->mRowSpacing;
373 }
374
375 void GridLayout::SetColumnSpacing(float spacing)
376 {
377   mImpl->mColumnSpacing = spacing;
378 }
379
380 float GridLayout::GetColumnSpacing() const
381 {
382   return mImpl->mColumnSpacing;
383 }
384
385 void GridLayout::SetTopMargin(float margin)
386 {
387   mImpl->mTopMargin = margin;
388 }
389
390 float GridLayout::GetTopMargin() const
391 {
392   return mImpl->mTopMargin;
393 }
394
395 void GridLayout::SetBottomMargin(float margin)
396 {
397   mImpl->mBottomMargin = margin;
398 }
399
400 float GridLayout::GetBottomMargin() const
401 {
402   return mImpl->mBottomMargin;
403 }
404
405 void GridLayout::SetSideMargin(float margin)
406 {
407   mImpl->mSideMargin = margin;
408 }
409
410 float GridLayout::GetSideMargin() const
411 {
412   return mImpl->mSideMargin;
413 }
414
415 void GridLayout::SetZGap(float gap)
416 {
417   mImpl->mZGap = gap;
418 }
419
420 float GridLayout::GetZGap() const
421 {
422   return mImpl->mZGap;
423 }
424
425 void GridLayout::SetItemSizeFunction(ItemSizeFunction function)
426 {
427   mImpl->mItemSizeFunction = function;
428 }
429
430 GridLayout::ItemSizeFunction GridLayout::GetItemSizeFunction() const
431 {
432   return mImpl->mItemSizeFunction;
433 }
434
435 void GridLayout::SetScrollSpeedFactor(float scrollSpeed)
436 {
437   mImpl->mScrollSpeedFactor = scrollSpeed;
438 }
439
440 void GridLayout::SetMaximumSwipeSpeed(float speed)
441 {
442   mImpl->mMaximumSwipeSpeed = speed;
443 }
444
445 void GridLayout::SetItemFlickAnimationDuration(float durationSeconds)
446 {
447   mImpl->mItemFlickAnimationDuration = durationSeconds;
448 }
449
450 float GridLayout::GetScrollSpeedFactor() const
451 {
452   return mImpl->mScrollSpeedFactor;
453 }
454
455 float GridLayout::GetMaximumSwipeSpeed() const
456 {
457   return mImpl->mMaximumSwipeSpeed;
458 }
459
460 float GridLayout::GetItemFlickAnimationDuration() const
461 {
462   return mImpl->mItemFlickAnimationDuration;
463 }
464
465 float GridLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
466 {
467   float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width;
468   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
469
470   Vector3 itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, layoutWidth, mImpl->mSideMargin, mImpl->mColumnSpacing);
471
472   unsigned int itemsLastRow = numberOfItems % mImpl->mNumberOfColumns;
473   if (itemsLastRow == 0)
474   {
475     itemsLastRow = mImpl->mNumberOfColumns;
476   }
477
478   float rowsLastPage = (layoutHeight - mImpl->mBottomMargin - mImpl->mTopMargin + mImpl->mRowSpacing) / (itemSize.y + mImpl->mRowSpacing);
479   float itemsLastPage = (rowsLastPage - 1.0f) * static_cast<float>(mImpl->mNumberOfColumns) + static_cast<float>(itemsLastRow);
480
481   return itemsLastPage - static_cast<float>(numberOfItems);
482 }
483
484 float GridLayout::GetClosestAnchorPosition(float layoutPosition) const
485 {
486   float rowIndex = static_cast<float>(round(layoutPosition / mImpl->mNumberOfColumns));
487   return rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
488 }
489
490 float GridLayout::GetItemScrollToPosition(unsigned int itemId) const
491 {
492   float rowIndex = static_cast<float>(itemId / mImpl->mNumberOfColumns);
493   return -rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
494 }
495
496 ItemRange GridLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
497 {
498   float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width;
499   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
500
501   Vector3 itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, layoutWidth, mImpl->mSideMargin, mImpl->mColumnSpacing);
502
503   int itemsPerPage = mImpl->mNumberOfColumns * ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing));
504   int firstVisibleItem = -(static_cast<int>(firstItemPosition / mImpl->mNumberOfColumns)) * mImpl->mNumberOfColumns;
505
506   int firstItemIndex = std::max(0, firstVisibleItem - static_cast<int>(mImpl->mNumberOfColumns));
507   int lastItemIndex  = std::max(0, firstVisibleItem + itemsPerPage);
508
509   return ItemRange(firstItemIndex, lastItemIndex);
510 }
511
512 float GridLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
513 {
514   ItemLayout::Vector3Function positionConstraint;
515   Vector3 itemPosition = Vector3::ZERO;
516   if (GetPositionConstraint(itemID, positionConstraint))
517   {
518     itemPosition = positionConstraint(Vector3::ZERO, currentLayoutPosition + itemID, 0.0f, layoutSize);
519   }
520   Vector3 itemSize;
521   GetItemSize(itemID, layoutSize, itemSize);
522   Vector3 onScreenArea = (layoutSize - (IsVertical(mOrientation) ? itemSize : Vector3(itemSize.y, itemSize.x, itemSize.z))) * 0.5f;
523   if (itemPosition.x < -onScreenArea.x
524       || itemPosition.x > onScreenArea.x
525       || itemPosition.y < -onScreenArea.y
526       || itemPosition.y > onScreenArea.y)
527   {
528     // item not within viewable area
529     float rowHeight = itemSize.y + mImpl->mRowSpacing;
530     ItemLayout::Vector3Function firstItemPositionConstraint;
531     Vector3 firstItemPosition = Vector3::ZERO;
532     float offset = 0.0f;
533     if (GetPositionConstraint(0, firstItemPositionConstraint))
534     {
535       firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0.0f, 0.0f, layoutSize);
536     }
537     switch( mOrientation )
538     {
539       case ControlOrientation::Up:
540       {
541         if(itemPosition.y > onScreenArea.y)
542         {
543           offset = ((layoutSize.y - rowHeight) * 0.5f) - firstItemPosition.y;
544         }
545         else
546         {
547           offset = ((-layoutSize.y + rowHeight) * 0.5f) - firstItemPosition.y;
548         }
549         break;
550       }
551       case ControlOrientation::Down:
552       {
553         if(itemPosition.y < -onScreenArea.y)
554         {
555           offset = ((layoutSize.y - rowHeight) * 0.5f) - firstItemPosition.y;
556         }
557         else
558         {
559           offset = ((-layoutSize.y + rowHeight) * 0.5f) - firstItemPosition.y;
560         }
561         break;
562       }
563       case ControlOrientation::Left:
564       {
565         if(itemPosition.x > onScreenArea.x)
566         {
567           offset = ((layoutSize.x - rowHeight) * 0.5f) - firstItemPosition.x;
568         }
569         else
570         {
571           offset = ((-layoutSize.x + rowHeight) * 0.5f) - firstItemPosition.x;
572         }
573         break;
574       }
575       case ControlOrientation::Right:
576       {
577         if(itemPosition.x < -onScreenArea.x)
578         {
579           offset = ((layoutSize.x - rowHeight) * 0.5f) - firstItemPosition.x;
580         }
581         else
582         {
583           offset = ((-layoutSize.x + rowHeight) * 0.5f) - firstItemPosition.x;
584         }
585         break;
586       }
587     }
588     // work out number of rows from first item position to an item aligned to bottom of screen
589     float rowDiff = offset / rowHeight;
590     float layoutPositionOffset = rowDiff * mImpl->mNumberOfColumns;
591     float scrollTo = GetItemScrollToPosition(itemID) + layoutPositionOffset;
592     return scrollTo;
593   }
594   return currentLayoutPosition;
595 }
596
597 unsigned int GridLayout::GetReserveItemCount(Vector3 layoutSize) const
598 {
599   float layoutWidth = IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width;
600   float layoutHeight = IsHorizontal(mOrientation) ? layoutSize.width : layoutSize.height;
601
602   Vector3 itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, layoutWidth, mImpl->mSideMargin, mImpl->mColumnSpacing);
603   int itemsPerPage = mImpl->mNumberOfColumns * ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing));
604   return itemsPerPage;
605 }
606
607 bool GridLayout::GetItemSize(unsigned int itemId, Vector3 layoutSize, Vector3& itemSize) const
608 {
609   // Note: itemId is not checked, since every item has the same size
610
611   itemSize = mImpl->mItemSizeFunction( mImpl->mNumberOfColumns, (IsHorizontal(mOrientation) ? layoutSize.height : layoutSize.width), mImpl->mSideMargin, mImpl->mColumnSpacing);
612   return true;
613 }
614
615 void GridLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
616 {
617   if(animation)
618   {
619     Vector3 currentSize( actor.GetCurrentSize() );
620     Vector3 shrink( currentSize );
621
622     shrink.width = std::min(size.width, currentSize.width);
623     shrink.height = std::min(size.height, currentSize.height);
624
625     // Do a nonlinear size animation to shrink the actor first when the actor size changes,
626     // so that we can avoid the actors overlapping during orientation change.
627     animation.AnimateTo( Property( actor, Actor::Property::SIZE ), shrink, AlphaFunctions::EaseOut, TimePeriod( 0.0f, durationSeconds * 0.5f ) );
628     animation.AnimateTo( Property( actor, Actor::Property::SIZE ), size, AlphaFunctions::EaseIn, TimePeriod( 0.0f, durationSeconds ) );
629   }
630 }
631
632 bool GridLayout::GetPositionConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
633 {
634   unsigned int columnIndex = itemId % mImpl->mNumberOfColumns;
635
636   if (mOrientation == ControlOrientation::Up)
637   {
638     constraint = GridPositionConstraint0(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap);
639   }
640   else if (mOrientation == ControlOrientation::Left)
641   {
642     constraint = GridPositionConstraint90(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap);
643   }
644   else if (mOrientation == ControlOrientation::Down)
645   {
646     constraint = GridPositionConstraint180(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap);
647   }
648   else // mOrientation == ControlOrientation::Right
649   {
650     constraint = GridPositionConstraint270(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mTopMargin, mImpl->mSideMargin, mImpl->mItemSizeFunction, mImpl->mZGap);
651   }
652
653   return true;
654 }
655
656 bool GridLayout::GetRotationConstraint(unsigned int itemId, ItemLayout::QuaternionFunction& constraint) const
657 {
658   constraint = mImpl->mRotationConstraint[mOrientation];
659   return true;
660 }
661
662 bool GridLayout::GetScaleConstraint(unsigned int itemId, ItemLayout::Vector3Function& constraint) const
663 {
664   return false; // No scaling
665 }
666
667 bool GridLayout::GetColorConstraint(unsigned int itemId, ItemLayout::Vector4Function& constraint) const
668 {
669   constraint = mImpl->mColorConstraint;
670   return true;
671 }
672
673 bool GridLayout::GetVisibilityConstraint(unsigned int itemId, ItemLayout::BoolFunction& constraint) const
674 {
675   unsigned int columnIndex = itemId % mImpl->mNumberOfColumns;
676
677   if (IsVertical(mOrientation))
678   {
679     constraint = GridVisibilityConstraintPortrait(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mSideMargin, mImpl->mItemSizeFunction);
680   }
681   else // horizontal
682   {
683     constraint = GridVisibilityConstraintLandscape(columnIndex, mImpl->mNumberOfColumns, mImpl->mRowSpacing, mImpl->mColumnSpacing, mImpl->mSideMargin, mImpl->mItemSizeFunction);
684   }
685
686   return true;
687 }
688
689 Degree GridLayout::GetScrollDirection() const
690 {
691   Degree scrollDirection(0.0f);
692
693   if (mOrientation == ControlOrientation::Up)
694   {
695     scrollDirection = Degree( 0.0f );
696   }
697   else if (mOrientation == ControlOrientation::Left)
698   {
699     scrollDirection = Degree( 90.0f );
700   }
701   else if (mOrientation == ControlOrientation::Down)
702   {
703     scrollDirection = Degree( 180.0f );
704   }
705   else // mOrientation == ControlOrientation::Right
706   {
707     scrollDirection = Degree( 270.0f );
708   }
709
710   return scrollDirection;
711 }
712
713 int GridLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
714 {
715   switch( direction )
716   {
717     case Control::Left:
718     {
719       itemID--;
720       if( itemID < 0 )
721       {
722         itemID = loopEnabled ? maxItems - 1 : 0;
723       }
724       break;
725     }
726     case Control::Up:
727     {
728       itemID -= mImpl->mNumberOfColumns;
729       if( itemID < 0 )
730       {
731         itemID = loopEnabled ? itemID + maxItems : itemID + mImpl->mNumberOfColumns;
732       }
733       break;
734     }
735     case Control::Right:
736     {
737       itemID++;
738       if( itemID >= maxItems )
739       {
740         itemID = loopEnabled ? 0 : maxItems - 1;
741       }
742       break;
743     }
744     case Control::Down:
745     {
746       itemID += mImpl->mNumberOfColumns;
747       if( itemID >= maxItems )
748       {
749         itemID = loopEnabled ? 0 : itemID - mImpl->mNumberOfColumns;
750       }
751       break;
752     }
753   }
754   return itemID;
755 }
756
757 GridLayout::GridLayout()
758 : mImpl(NULL)
759 {
760   mImpl = new Impl();
761 }
762
763 } // namespace Toolkit
764
765 } // namespace Dali