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/depth-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 = 3;
36 const float DEFAULT_NUMBER_OF_ROWS = 26.0f;
37 const float DEFAULT_ROW_SPACING = 55.0f;
38 const float DEFAULT_BOTTOM_MARGIN_FACTOR = 0.2f;
39 const Radian DEFAULT_TILT_ANGLE ( Math::PI*0.15f );
40 const Radian DEFAULT_ITEM_TILT_ANGLE ( -Math::PI*0.025f );
41 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.02f;
42 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 50.0f;
43 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.03f;
45 inline float GetColumnPosition( unsigned int numberOfColumns, unsigned int columnNumber, const Vector3& itemSize, float layoutWidth )
47 // Share the available space between margins & column spacings
48 float availableSpace = std::max( 0.0f, ( layoutWidth - itemSize.width * numberOfColumns ) );
50 float leftMargin = availableSpace / numberOfColumns * 0.5f;
52 float columnPosition = leftMargin + itemSize.width * 0.5f + columnNumber * ( itemSize.width + availableSpace / numberOfColumns );
54 return columnPosition - layoutWidth * 0.5f;
57 struct DepthPositionConstraint
59 DepthPositionConstraint( unsigned int itemId,
60 unsigned int numberOfColumns,
61 unsigned int columnNumber,
62 const Vector3& itemSize,
65 : mItemSize( itemSize ),
67 mNumberOfColumns( numberOfColumns ),
68 mColumnNumber( columnNumber ),
69 mHeightScale( heightScale ),
70 mDepthScale( depthScale )
74 inline void Orientation0( Vector3& current, float layoutPosition, const Vector3& layoutSize )
76 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber );
78 current.x = GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.width );
79 current.y = rowLayoutPositon * mHeightScale + layoutSize.height * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.height - mItemSize.height * 0.5f;
80 current.z = -rowLayoutPositon * mDepthScale;
83 inline void Orientation90( Vector3& current, float layoutPosition, const Vector3& layoutSize )
85 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber ) + mNumberOfColumns * 0.5f;
87 current.x = rowLayoutPositon * mHeightScale + layoutSize.width * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.width - mItemSize.height * 0.5f;
88 current.y = -GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.height );
89 current.z = -rowLayoutPositon * mDepthScale;
92 inline void Orientation180( Vector3& current, float layoutPosition, const Vector3& layoutSize )
94 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber );
96 current.x = -GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.width );
97 current.y = -( rowLayoutPositon * mHeightScale + layoutSize.height * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.height - mItemSize.height * 0.5f );
98 current.z = -rowLayoutPositon * mDepthScale;
101 inline void Orientation270( Vector3& current, float layoutPosition, const Vector3& layoutSize )
103 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber ) + mNumberOfColumns * 0.5f;
105 current.x = -( rowLayoutPositon * mHeightScale + layoutSize.width * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.width - mItemSize.height * 0.5f );
106 current.y = GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.height );
107 current.z = -rowLayoutPositon * mDepthScale;
110 void Orientation0( Vector3& current, const PropertyInputContainer& inputs )
112 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
113 const Vector3& layoutSize = inputs[1]->GetVector3();
114 Orientation0( current, layoutPosition, layoutSize );
117 void Orientation90( Vector3& current, const PropertyInputContainer& inputs )
119 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
120 const Vector3& layoutSize = inputs[1]->GetVector3();
121 Orientation90( current, layoutPosition, layoutSize );
124 void Orientation180( Vector3& current, const PropertyInputContainer& inputs )
126 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
127 const Vector3& layoutSize = inputs[1]->GetVector3();
128 Orientation180( current, layoutPosition, layoutSize );
131 void Orientation270( Vector3& current, const PropertyInputContainer& inputs )
133 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
134 const Vector3& layoutSize = inputs[1]->GetVector3();
135 Orientation270( current, layoutPosition, layoutSize );
139 unsigned int mItemId;
140 unsigned int mNumberOfColumns;
141 unsigned int mColumnNumber;
146 struct DepthRotationConstraint
148 DepthRotationConstraint( Radian angleRadians, ControlOrientation::Type orientation )
149 : mTiltAngle( angleRadians ),
152 if ( orientation == ControlOrientation::Up )
156 else if ( orientation == ControlOrientation::Left )
160 else if ( orientation == ControlOrientation::Down )
164 else // orientation == ControlOrientation::Right
170 void operator()( Quaternion& current, const PropertyInputContainer& /* inputs */ )
172 current = Quaternion( Radian( mMultiplier * Math::PI ), Vector3::ZAXIS ) * Quaternion( mTiltAngle, Vector3::XAXIS );
179 struct DepthColorConstraint
181 DepthColorConstraint( unsigned int itemId, unsigned int numberOfColumns, float numberOfRows, unsigned int columnNumber )
183 mNumberOfColumns( numberOfColumns ),
184 mNumberOfRows( numberOfRows ),
185 mColumnNumber( columnNumber )
189 void operator()( Vector4& current, const Dali::PropertyInputContainer& inputs )
191 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
192 float row = ( layoutPosition - static_cast<float>( mColumnNumber ) ) / mNumberOfColumns;
194 float darkness(1.0f);
199 darkness = alpha = std::max(0.0f, 1.0f + row);
203 if (row > mNumberOfRows)
209 darkness = 1.0f - ( 1.0f * (row / mNumberOfRows) );
212 if (row > (mNumberOfRows-1.0f))
214 alpha = std::max(0.0f, 1.0f - (row-(mNumberOfRows-1.0f)));
218 current.r = current.g = current.b = darkness;
222 unsigned int mItemId;
223 unsigned int mNumberOfColumns;
225 unsigned int mColumnNumber;
228 struct DepthVisibilityConstraint
230 DepthVisibilityConstraint( unsigned int itemId, unsigned int numberOfColumns, float numberOfRows, unsigned int columnNumber )
232 mNumberOfColumns(numberOfColumns),
233 mNumberOfRows(numberOfRows),
234 mColumnNumber(columnNumber)
238 void operator()( bool& current, const Dali::PropertyInputContainer& inputs )
240 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
241 float row = ( layoutPosition - static_cast< float >( mColumnNumber ) ) / mNumberOfColumns;
243 current = ( row > -1.0f ) && ( row < mNumberOfRows );
246 unsigned int mItemId;
247 unsigned int mNumberOfColumns;
249 unsigned int mColumnNumber;
252 } // unnamed namespace
263 struct DepthLayout::Impl
266 : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS),
267 mNumberOfRows(DEFAULT_NUMBER_OF_ROWS),
268 mRowSpacing(DEFAULT_ROW_SPACING),
269 mTiltAngle(DEFAULT_TILT_ANGLE),
270 mItemTiltAngle(DEFAULT_ITEM_TILT_ANGLE),
271 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
272 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
273 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
277 unsigned int mNumberOfColumns;
278 unsigned int mNumberOfRows;
283 Radian mItemTiltAngle;
285 float mScrollSpeedFactor;
286 float mMaximumSwipeSpeed;
287 float mItemFlickAnimationDuration;
290 DepthLayoutPtr DepthLayout::New()
292 return DepthLayoutPtr(new DepthLayout());
295 DepthLayout::~DepthLayout()
300 void DepthLayout::SetNumberOfColumns(unsigned int columns)
302 mImpl->mNumberOfColumns = columns;
305 unsigned int DepthLayout::GetNumberOfColumns() const
307 return mImpl->mNumberOfColumns;
310 void DepthLayout::SetNumberOfRows(unsigned int rows)
312 mImpl->mNumberOfRows = rows;
315 unsigned int DepthLayout::GetNumberOfRows() const
317 return mImpl->mNumberOfRows;
320 void DepthLayout::SetRowSpacing(float spacing)
322 mImpl->mRowSpacing = spacing;
325 float DepthLayout::GetRowSpacing() const
327 return mImpl->mRowSpacing;
330 void DepthLayout::SetTiltAngle(Degree angle)
332 mImpl->mTiltAngle = Degree( Clamp( angle, -45.0f, 45.0f ) );
335 Degree DepthLayout::GetTiltAngle() const
337 return Degree( mImpl->mTiltAngle );
340 void DepthLayout::SetItemTiltAngle(Degree angle)
342 mImpl->mItemTiltAngle = angle;
345 Degree DepthLayout::GetItemTiltAngle() const
347 return Degree( mImpl->mItemTiltAngle );
350 void DepthLayout::SetScrollSpeedFactor(float scrollSpeed)
352 mImpl->mScrollSpeedFactor = scrollSpeed;
355 void DepthLayout::SetMaximumSwipeSpeed(float speed)
357 mImpl->mMaximumSwipeSpeed = speed;
360 void DepthLayout::SetItemFlickAnimationDuration(float durationSeconds)
362 mImpl->mItemFlickAnimationDuration = durationSeconds;
365 float DepthLayout::GetScrollSpeedFactor() const
367 return mImpl->mScrollSpeedFactor;
370 float DepthLayout::GetMaximumSwipeSpeed() const
372 return mImpl->mMaximumSwipeSpeed;
375 float DepthLayout::GetItemFlickAnimationDuration() const
377 return mImpl->mItemFlickAnimationDuration;
380 float DepthLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
382 return static_cast<float>(mImpl->mNumberOfColumns) - static_cast<float>(numberOfItems);
385 float DepthLayout::GetClosestAnchorPosition(float layoutPosition) const
387 float rowIndex = static_cast<float>(round(layoutPosition / mImpl->mNumberOfColumns));
388 return rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
391 float DepthLayout::GetItemScrollToPosition(unsigned int itemId) const
393 float rowIndex = static_cast<float>(itemId / mImpl->mNumberOfColumns);
394 return -rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
397 ItemRange DepthLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
399 float firstRow = -(firstItemPosition/mImpl->mNumberOfColumns);
400 float lastRow = firstRow + mImpl->mNumberOfRows * 0.5f;
402 unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, firstRow * mImpl->mNumberOfColumns));
403 unsigned int lastItem = static_cast<unsigned int>(std::max(0.0f, lastRow * mImpl->mNumberOfColumns));
405 return ItemRange(firstItem, lastItem+1);
408 unsigned int DepthLayout::GetReserveItemCount(Vector3 layoutSize) const
410 float itemsWithinLayout = (layoutSize.depth * mImpl->mNumberOfColumns) / (cosf(mImpl->mTiltAngle) * mImpl->mRowSpacing);
412 return static_cast<unsigned int>(itemsWithinLayout);
415 void DepthLayout::GetDefaultItemSize( unsigned int itemId, const Vector3& layoutSize, Vector3& itemSize ) const
418 itemSize.width = itemSize.height = itemSize.depth = ( IsVertical( GetOrientation() ) ? layoutSize.width : layoutSize.height ) / static_cast<float>( mImpl->mNumberOfColumns + 1 );
421 void DepthLayout::GetResizeAnimation(Animation& animation, Actor actor, Vector3 size, float durationSeconds) const
425 animation.AnimateTo( Property( actor, Actor::Property::SIZE ), size );
429 Degree DepthLayout::GetScrollDirection() const
431 Degree scrollDirection(0.0f);
432 ControlOrientation::Type orientation = GetOrientation();
434 if ( orientation == ControlOrientation::Up )
436 scrollDirection = Degree( 180.0f );
438 else if ( orientation == ControlOrientation::Left )
440 scrollDirection = Degree( 270.0f );
442 else if ( orientation == ControlOrientation::Down )
444 scrollDirection = Degree( 0.0f );
446 else // orientation == ControlOrientation::Right
448 scrollDirection = Degree( 90.0f );
451 return scrollDirection;
454 void DepthLayout::ApplyConstraints( Actor& actor, const int itemId, const Vector3& layoutSize, const Actor& itemViewActor )
456 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast( itemViewActor );
460 GetItemSize( itemId, layoutSize, itemSize );
462 ControlOrientation::Type orientation = GetOrientation();
464 // Position constraint
465 Constraint constraint;
466 DepthPositionConstraint depthPositionStruct( itemId,
467 mImpl->mNumberOfColumns,
468 itemId % mImpl->mNumberOfColumns,
470 -sinf( mImpl->mTiltAngle ) * mImpl->mRowSpacing,
471 cosf( mImpl->mTiltAngle ) * mImpl->mRowSpacing );
472 if ( orientation == ControlOrientation::Up )
474 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation0 );
476 else if ( orientation == ControlOrientation::Left )
478 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation90 );
480 else if ( orientation == ControlOrientation::Down )
482 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation180 );
484 else // orientation == ControlOrientation::Right
486 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation270 );
488 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
489 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
492 // Rotation constraint
493 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, DepthRotationConstraint( mImpl->mItemTiltAngle, orientation ) );
497 constraint = Constraint::New< Vector4 >( actor, Actor::Property::COLOR, DepthColorConstraint( itemId, mImpl->mNumberOfColumns, mImpl->mNumberOfRows*0.5f, itemId % mImpl->mNumberOfColumns ) );
498 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
499 constraint.SetRemoveAction( Dali::Constraint::Discard );
502 // Visibility constraint
503 constraint = Constraint::New< bool >( actor, Actor::Property::VISIBLE, DepthVisibilityConstraint( itemId, mImpl->mNumberOfColumns, mImpl->mNumberOfRows*0.5f, itemId % mImpl->mNumberOfColumns ) );
504 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
505 constraint.SetRemoveAction( Dali::Constraint::Discard );
510 Vector3 DepthLayout::GetItemPosition( int itemID, float currentLayoutPosition, const Vector3& layoutSize ) const
512 Vector3 itemPosition = Vector3::ZERO;
514 const float heightScale = -sinf( mImpl->mTiltAngle ) * mImpl->mRowSpacing;
515 const float depthScale = cosf( mImpl->mTiltAngle ) * mImpl->mRowSpacing;
518 GetItemSize( itemID, layoutSize, itemSize );
519 DepthPositionConstraint positionFunctor = DepthPositionConstraint( itemID,
520 mImpl->mNumberOfColumns,
521 itemID % mImpl->mNumberOfColumns,
525 ControlOrientation::Type orientation = GetOrientation();
526 if ( orientation == ControlOrientation::Up )
528 positionFunctor.Orientation0( itemPosition, currentLayoutPosition + itemID, layoutSize );
530 else if ( orientation == ControlOrientation::Left )
532 positionFunctor.Orientation90( itemPosition, currentLayoutPosition + itemID, layoutSize );
534 else if ( orientation == ControlOrientation::Down )
536 positionFunctor.Orientation180( itemPosition, currentLayoutPosition + itemID, layoutSize );
538 else // orientation == ControlOrientation::Right
540 positionFunctor.Orientation270( itemPosition, currentLayoutPosition + itemID, layoutSize );
546 DepthLayout::DepthLayout()
552 float DepthLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
554 float scrollTo = currentLayoutPosition;
555 float row = (currentLayoutPosition + itemID - static_cast<float>(itemID % mImpl->mNumberOfColumns)) / mImpl->mNumberOfColumns;
557 // Check whether item is not within viewable area
560 scrollTo = GetItemScrollToPosition(itemID);
562 else if(row > mImpl->mNumberOfRows * 0.5f - 1.0f)
564 scrollTo = GetItemScrollToPosition(itemID) + (mImpl->mNumberOfRows - 1.0f) * 0.5f * mImpl->mNumberOfColumns;
570 int DepthLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
574 case Toolkit::Control::KeyboardFocus::LEFT:
579 itemID = loopEnabled ? maxItems - 1 : 0;
583 case Toolkit::Control::KeyboardFocus::UP:
585 itemID += mImpl->mNumberOfColumns;
586 if( itemID >= maxItems )
588 itemID = loopEnabled ? 0 : itemID - mImpl->mNumberOfColumns;
592 case Toolkit::Control::KeyboardFocus::RIGHT:
595 if( itemID >= maxItems )
597 itemID = loopEnabled ? 0 : maxItems - 1;
601 case Toolkit::Control::KeyboardFocus::DOWN:
603 itemID -= mImpl->mNumberOfColumns;
606 itemID = loopEnabled ? itemID + maxItems : itemID + mImpl->mNumberOfColumns;
614 } // namespace Internal
616 } // namespace Toolkit