2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/controls/scrollable/item-view/grid-layout.h>
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
27 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-view.h>
30 using namespace Dali::Toolkit;
32 namespace // unnamed namespace
35 const unsigned int DEFAULT_NUMBER_OF_COLUMNS = 4;
36 const float DEFAULT_TOP_MARGIN = 95.0f;
37 const float DEFAULT_BOTTOM_MARGIN = 20.0f;
38 const float DEFAULT_SIDE_MARGIN = 20.0f;
39 const float DEFAULT_COLUMN_SPACING = 20.0f;
40 const float DEFAULT_ROW_SPACING = 20.0f;
41 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.03f;
42 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 100.0f;
43 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.015f;
45 struct GridPositionConstraint
47 GridPositionConstraint(
49 const unsigned int columnIndex,
50 const unsigned int numberOfColumns,
51 const float rowSpacing,
52 const float columnSpacing,
53 const float topMargin,
54 const float sideMargin,
55 const Vector3& itemSize,
57 : mItemSize( itemSize ),
59 mColumnIndex( columnIndex ),
60 mNumberOfColumns( numberOfColumns ),
61 mRowSpacing( rowSpacing ),
62 mColumnSpacing( columnSpacing ),
63 mTopMargin( topMargin ),
64 mSideMargin( sideMargin ),
69 inline void Orientation0( Vector3& current, float layoutPosition, const Vector3& layoutSize )
71 current.x = mSideMargin + ( mColumnIndex * ( mItemSize.x + mColumnSpacing ) ) + mItemSize.x * 0.5f - layoutSize.x * 0.5f;
72 current.y = ( ( mItemSize.y + mRowSpacing ) * ( layoutPosition - mColumnIndex) ) / mNumberOfColumns - layoutSize.height * 0.5f + mItemSize.y * 0.5f + mTopMargin;
73 current.z = mColumnIndex * mZGap;
76 inline void Orientation90( Vector3& current, float layoutPosition, const Vector3& layoutSize )
78 current.x = ( ( mItemSize.y + mRowSpacing ) * ( layoutPosition - mColumnIndex ) ) / mNumberOfColumns - layoutSize.width * 0.5f + mItemSize.y * 0.5f + mTopMargin;
79 current.y = -( mSideMargin + ( mColumnIndex * ( mItemSize.x + mColumnSpacing ) ) + mItemSize.x * 0.5f - layoutSize.y * 0.5f );
80 current.z = mColumnIndex * mZGap;
83 inline void Orientation180( Vector3& current, float layoutPosition, const Vector3& layoutSize )
85 current.x = -(mSideMargin + (mColumnIndex * (mItemSize.x + mColumnSpacing)) + mItemSize.x * 0.5f - layoutSize.x * 0.5f);
86 current.y = -( ( ( mItemSize.y + mRowSpacing ) * ( layoutPosition - mColumnIndex ) ) / mNumberOfColumns - layoutSize.height * 0.5f + mItemSize.y * 0.5f + mTopMargin );
87 current.z = mColumnIndex * mZGap;
90 inline void Orientation270( Vector3& current, float layoutPosition, const Vector3& layoutSize )
92 current.x = -( ( ( mItemSize.y + mRowSpacing ) * ( layoutPosition - mColumnIndex ) ) / mNumberOfColumns - layoutSize.width * 0.5f + mItemSize.y * 0.5f + mTopMargin );
93 current.y = mSideMargin + ( mColumnIndex * ( mItemSize.x + mColumnSpacing ) ) + mItemSize.x * 0.5f - layoutSize.y * 0.5f;
94 current.z = mColumnIndex * mZGap;
97 void Orientation0( Vector3& current, const PropertyInputContainer& inputs )
99 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
100 const Vector3& layoutSize = inputs[1]->GetVector3();
101 Orientation0( current, layoutPosition, layoutSize );
104 void Orientation90( Vector3& current, const PropertyInputContainer& inputs )
106 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
107 const Vector3& layoutSize = inputs[1]->GetVector3();
108 Orientation90( current, layoutPosition, layoutSize );
111 void Orientation180( Vector3& current, const PropertyInputContainer& inputs )
113 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
114 const Vector3& layoutSize = inputs[1]->GetVector3();
115 Orientation180( current, layoutPosition, layoutSize );
118 void Orientation270( Vector3& current, const PropertyInputContainer& inputs )
120 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
121 const Vector3& layoutSize = inputs[1]->GetVector3();
122 Orientation270( current, layoutPosition, layoutSize );
128 unsigned int mItemId;
129 unsigned int mColumnIndex;
130 unsigned int mNumberOfColumns;
132 float mColumnSpacing;
138 void GridRotationConstraint0( Quaternion& current, const PropertyInputContainer& /* inputs */ )
140 current = Quaternion( Radian( 0.0f ), Vector3::ZAXIS );
143 void GridRotationConstraint90( Quaternion& current, const PropertyInputContainer& /* inputs */ )
145 current = Quaternion( Radian( 1.5f * Math::PI ), Vector3::ZAXIS );
148 void GridRotationConstraint180( Quaternion& current, const PropertyInputContainer& /* inputs */ )
150 current = Quaternion( Radian( Math::PI ), Vector3::ZAXIS );
153 void GridRotationConstraint270( Quaternion& current, const PropertyInputContainer& /* inputs */ )
155 current = Quaternion( Radian( 0.5f * Math::PI ), Vector3::ZAXIS );
158 void GridColorConstraint( Vector4& current, const PropertyInputContainer& /* inputs */ )
160 current.r = current.g = current.b = 1.0f;
163 struct GridVisibilityConstraint
165 GridVisibilityConstraint(
167 const unsigned int columnIndex,
168 const unsigned int numberOfColumns,
169 const float rowSpacing,
170 const float columnSpacing,
171 const float sideMargin,
172 const Vector3& itemSize )
173 : mItemSize( itemSize ),
175 mColumnIndex( columnIndex ),
176 mNumberOfColumns( numberOfColumns ),
177 mRowSpacing( rowSpacing ),
178 mColumnSpacing( columnSpacing ),
179 mSideMargin( sideMargin )
183 void Portrait( bool& current, const PropertyInputContainer& inputs )
185 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
186 const Vector3& layoutSize = inputs[1]->GetVector3();
188 float row = ( layoutPosition - static_cast< float >( mColumnIndex ) ) / mNumberOfColumns;
189 int rowsPerPage = ceil( layoutSize.height / ( mItemSize.y + mRowSpacing ) );
191 current = ( row > -2.0f ) && ( row < rowsPerPage );
194 void Landscape( bool& current, const PropertyInputContainer& inputs )
196 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
197 const Vector3& layoutSize = inputs[1]->GetVector3();
199 float row = ( layoutPosition - static_cast< float >( mColumnIndex ) ) / mNumberOfColumns;
200 int rowsPerPage = ceil( layoutSize.width / ( mItemSize.y + mRowSpacing ) );
202 current = ( row > -2.0f ) && ( row < rowsPerPage );
208 unsigned int mItemId;
209 unsigned int mColumnIndex;
210 unsigned int mNumberOfColumns;
212 float mColumnSpacing;
216 } // unnamed namespace
227 struct GridLayout::Impl
230 : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS),
231 mRowSpacing(DEFAULT_ROW_SPACING),
232 mColumnSpacing(DEFAULT_COLUMN_SPACING),
233 mTopMargin(DEFAULT_TOP_MARGIN),
234 mBottomMargin(DEFAULT_BOTTOM_MARGIN),
235 mSideMargin(DEFAULT_SIDE_MARGIN),
237 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
238 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
239 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
243 unsigned int mNumberOfColumns;
245 float mColumnSpacing;
251 float mScrollSpeedFactor;
252 float mMaximumSwipeSpeed;
253 float mItemFlickAnimationDuration;
256 GridLayoutPtr GridLayout::New()
258 return GridLayoutPtr(new GridLayout());
261 GridLayout::~GridLayout()
266 void GridLayout::SetNumberOfColumns(unsigned int columns)
268 mImpl->mNumberOfColumns = columns;
271 unsigned int GridLayout::GetNumberOfColumns() const
273 return mImpl->mNumberOfColumns;
276 void GridLayout::SetRowSpacing(float spacing)
278 mImpl->mRowSpacing = spacing;
281 float GridLayout::GetRowSpacing() const
283 return mImpl->mRowSpacing;
286 void GridLayout::SetColumnSpacing(float spacing)
288 mImpl->mColumnSpacing = spacing;
291 float GridLayout::GetColumnSpacing() const
293 return mImpl->mColumnSpacing;
296 void GridLayout::SetTopMargin(float margin)
298 mImpl->mTopMargin = margin;
301 float GridLayout::GetTopMargin() const
303 return mImpl->mTopMargin;
306 void GridLayout::SetBottomMargin(float margin)
308 mImpl->mBottomMargin = margin;
311 float GridLayout::GetBottomMargin() const
313 return mImpl->mBottomMargin;
316 void GridLayout::SetSideMargin(float margin)
318 mImpl->mSideMargin = margin;
321 float GridLayout::GetSideMargin() const
323 return mImpl->mSideMargin;
326 void GridLayout::SetZGap(float gap)
331 float GridLayout::GetZGap() const
336 void GridLayout::SetScrollSpeedFactor(float scrollSpeed)
338 mImpl->mScrollSpeedFactor = scrollSpeed;
341 void GridLayout::SetMaximumSwipeSpeed(float speed)
343 mImpl->mMaximumSwipeSpeed = speed;
346 void GridLayout::SetItemFlickAnimationDuration(float durationSeconds)
348 mImpl->mItemFlickAnimationDuration = durationSeconds;
351 float GridLayout::GetScrollSpeedFactor() const
353 return mImpl->mScrollSpeedFactor;
356 float GridLayout::GetMaximumSwipeSpeed() const
358 return mImpl->mMaximumSwipeSpeed;
361 float GridLayout::GetItemFlickAnimationDuration() const
363 return mImpl->mItemFlickAnimationDuration;
366 float GridLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
368 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
371 GetItemSize( 0, layoutSize, itemSize );
373 unsigned int itemsLastRow = numberOfItems % mImpl->mNumberOfColumns;
374 if (itemsLastRow == 0)
376 itemsLastRow = mImpl->mNumberOfColumns;
379 float rowsLastPage = (layoutHeight - mImpl->mBottomMargin - mImpl->mTopMargin + mImpl->mRowSpacing) / (itemSize.y + mImpl->mRowSpacing);
380 float itemsLastPage = (rowsLastPage - 1.0f) * static_cast<float>(mImpl->mNumberOfColumns) + static_cast<float>(itemsLastRow);
382 return itemsLastPage - static_cast<float>(numberOfItems);
385 float GridLayout::GetClosestAnchorPosition(float layoutPosition) const
387 float rowIndex = static_cast<float>(round(layoutPosition / mImpl->mNumberOfColumns));
388 return rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
391 float GridLayout::GetItemScrollToPosition(unsigned int itemId) const
393 float rowIndex = static_cast<float>(itemId / mImpl->mNumberOfColumns);
394 return -rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
397 ItemRange GridLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
399 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
402 GetItemSize( 0, layoutSize, itemSize );
404 int itemsPerPage = mImpl->mNumberOfColumns * ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing));
405 int firstVisibleItem = -(static_cast<int>(firstItemPosition / mImpl->mNumberOfColumns)) * mImpl->mNumberOfColumns;
407 int firstItemIndex = std::max(0, firstVisibleItem - static_cast<int>(mImpl->mNumberOfColumns));
408 int lastItemIndex = std::max(0, firstVisibleItem + itemsPerPage);
410 return ItemRange(firstItemIndex, lastItemIndex);
413 float GridLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
415 Vector3 itemPosition = GetItemPosition( itemID, currentLayoutPosition, layoutSize );
417 ControlOrientation::Type orientation = GetOrientation();
419 GetItemSize(itemID, layoutSize, itemSize);
420 Vector3 onScreenArea = ( layoutSize - ( IsVertical( orientation ) ? itemSize : Vector3( itemSize.y, itemSize.x, itemSize.z ) ) ) * 0.5f;
421 if (itemPosition.x < -onScreenArea.x
422 || itemPosition.x > onScreenArea.x
423 || itemPosition.y < -onScreenArea.y
424 || itemPosition.y > onScreenArea.y)
426 // item not within viewable area
427 float rowHeight = itemSize.y + mImpl->mRowSpacing;
428 Vector3 firstItemPosition = GetItemPosition( itemID, 0.0f, layoutSize );
430 switch( orientation )
432 case ControlOrientation::Up:
434 if(itemPosition.y > onScreenArea.y)
436 offset = ((layoutSize.y - rowHeight) * 0.5f) - firstItemPosition.y;
440 offset = ((-layoutSize.y + rowHeight) * 0.5f) - firstItemPosition.y;
444 case ControlOrientation::Down:
446 if(itemPosition.y < -onScreenArea.y)
448 offset = ((layoutSize.y - rowHeight) * 0.5f) - firstItemPosition.y;
452 offset = ((-layoutSize.y + rowHeight) * 0.5f) - firstItemPosition.y;
456 case ControlOrientation::Left:
458 if(itemPosition.x > onScreenArea.x)
460 offset = ((layoutSize.x - rowHeight) * 0.5f) - firstItemPosition.x;
464 offset = ((-layoutSize.x + rowHeight) * 0.5f) - firstItemPosition.x;
468 case ControlOrientation::Right:
470 if(itemPosition.x < -onScreenArea.x)
472 offset = ((layoutSize.x - rowHeight) * 0.5f) - firstItemPosition.x;
476 offset = ((-layoutSize.x + rowHeight) * 0.5f) - firstItemPosition.x;
481 // work out number of rows from first item position to an item aligned to bottom of screen
482 float rowDiff = offset / rowHeight;
483 float layoutPositionOffset = rowDiff * mImpl->mNumberOfColumns;
484 float scrollTo = GetItemScrollToPosition(itemID) + layoutPositionOffset;
487 return currentLayoutPosition;
490 unsigned int GridLayout::GetReserveItemCount(Vector3 layoutSize) const
492 float layoutHeight = IsHorizontal( GetOrientation() ) ? layoutSize.width : layoutSize.height;
495 GetItemSize( 0, layoutSize, itemSize );
496 int itemsPerPage = mImpl->mNumberOfColumns * ceil(layoutHeight / (itemSize.y + mImpl->mRowSpacing));
500 void GridLayout::GetDefaultItemSize( unsigned int itemId, const Vector3& layoutSize, Vector3& itemSize ) const
502 float layoutWidth = IsHorizontal( GetOrientation() ) ? layoutSize.height : layoutSize.width;
503 itemSize.width = ( layoutWidth - mImpl->mSideMargin * 2.0f - mImpl->mColumnSpacing * static_cast<float>( mImpl->mNumberOfColumns - 1 ) ) / static_cast<float>( mImpl->mNumberOfColumns );
506 itemSize.height = itemSize.depth = itemSize.width * 0.75f;
509 void GridLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
513 Vector3 currentSize( actor.GetCurrentSize() );
514 Vector3 shrink( currentSize );
516 shrink.width = std::min(size.width, currentSize.width);
517 shrink.height = std::min(size.height, currentSize.height);
519 // Do a nonlinear size animation to shrink the actor first when the actor size changes,
520 // so that we can avoid the actors overlapping during orientation change.
521 animation.AnimateTo( Property( actor, Actor::Property::SIZE ), shrink, AlphaFunction::EASE_OUT, TimePeriod( 0.0f, durationSeconds * 0.5f ) );
522 animation.AnimateTo( Property( actor, Actor::Property::SIZE ), size, AlphaFunction::EASE_IN, TimePeriod( 0.0f, durationSeconds ) );
526 Degree GridLayout::GetScrollDirection() const
528 Degree scrollDirection(0.0f);
529 ControlOrientation::Type orientation = GetOrientation();
531 if ( orientation == ControlOrientation::Up )
533 scrollDirection = Degree( 0.0f );
535 else if ( orientation == ControlOrientation::Left )
537 scrollDirection = Degree( 90.0f );
539 else if ( orientation == ControlOrientation::Down )
541 scrollDirection = Degree( 180.0f );
543 else // orientation == ControlOrientation::Right
545 scrollDirection = Degree( 270.0f );
548 return scrollDirection;
551 void GridLayout::ApplyConstraints( Actor& actor, const int itemId, const Vector3& layoutSize, const Actor& itemViewActor )
553 // This just implements the default behaviour of constraint application.
554 // Custom layouts can override this function to apply their custom constraints.
555 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast( itemViewActor );
559 GetItemSize( itemId, layoutSize, itemSize );
560 const unsigned int columnIndex = itemId % mImpl->mNumberOfColumns;
561 const ControlOrientation::Type orientation = GetOrientation();
563 // Position constraint
564 GridPositionConstraint positionConstraint( itemId,
566 mImpl->mNumberOfColumns,
568 mImpl->mColumnSpacing,
573 Constraint constraint;
574 if ( orientation == ControlOrientation::Up )
576 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &GridPositionConstraint::Orientation0 );
578 else if ( orientation == ControlOrientation::Left )
580 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &GridPositionConstraint::Orientation90 );
582 else if ( orientation == ControlOrientation::Down )
584 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &GridPositionConstraint::Orientation180 );
586 else // orientation == ControlOrientation::Right
588 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, positionConstraint, &GridPositionConstraint::Orientation270 );
590 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
591 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
594 // Rotation constraint
595 if ( orientation == ControlOrientation::Up )
597 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, &GridRotationConstraint0 );
599 else if ( orientation == ControlOrientation::Left )
601 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, &GridRotationConstraint90 );
603 else if ( orientation == ControlOrientation::Down )
605 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, &GridRotationConstraint180 );
607 else // orientation == ControlOrientation::Right
609 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, &GridRotationConstraint270 );
614 constraint = Constraint::New< Vector4 >( actor, Actor::Property::COLOR, &GridColorConstraint );
615 constraint.SetRemoveAction( Dali::Constraint::Discard );
618 // Visibility constraint
619 GridVisibilityConstraint visibilityConstraint( itemId,
621 mImpl->mNumberOfColumns,
623 mImpl->mColumnSpacing,
626 if ( IsVertical( orientation ) )
628 constraint = Constraint::New<bool>( actor, Actor::Property::VISIBLE, visibilityConstraint, &GridVisibilityConstraint::Portrait );
632 constraint = Constraint::New<bool>( actor, Actor::Property::VISIBLE, visibilityConstraint, &GridVisibilityConstraint::Landscape );
634 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
635 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
636 constraint.SetRemoveAction( Dali::Constraint::Discard );
641 Vector3 GridLayout::GetItemPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) const
643 Vector3 itemPosition = Vector3::ZERO;
644 const unsigned int columnIndex = itemID % mImpl->mNumberOfColumns;
645 const ControlOrientation::Type orientation = GetOrientation();
647 GetItemSize( itemID, layoutSize, itemSize );
649 GridPositionConstraint positionConstraintStruct( itemID,
651 mImpl->mNumberOfColumns,
653 mImpl->mColumnSpacing,
659 if ( orientation == ControlOrientation::Up )
661 positionConstraintStruct.Orientation0( itemPosition, currentLayoutPosition + itemID, layoutSize );
663 else if ( orientation == ControlOrientation::Left )
665 positionConstraintStruct.Orientation90( itemPosition, currentLayoutPosition + itemID, layoutSize );
667 else if ( orientation == ControlOrientation::Down )
669 positionConstraintStruct.Orientation180( itemPosition, currentLayoutPosition + itemID, layoutSize );
671 else // orientation == ControlOrientation::Right
673 positionConstraintStruct.Orientation270( itemPosition, currentLayoutPosition + itemID, layoutSize );
679 int GridLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
683 case Toolkit::Control::Left:
688 itemID = loopEnabled ? maxItems - 1 : 0;
692 case Toolkit::Control::Up:
694 itemID -= mImpl->mNumberOfColumns;
697 itemID = loopEnabled ? itemID + maxItems : itemID + mImpl->mNumberOfColumns;
701 case Toolkit::Control::Right:
704 if( itemID >= maxItems )
706 itemID = loopEnabled ? 0 : maxItems - 1;
710 case Toolkit::Control::Down:
712 itemID += mImpl->mNumberOfColumns;
713 if( itemID >= maxItems )
715 itemID = loopEnabled ? 0 : itemID - mImpl->mNumberOfColumns;
723 GridLayout::GridLayout()
729 } // namespace Internal
731 } // namespace Toolkit