2 * Copyright (c) 2017 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>
28 #include <dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout-property.h>
31 using namespace Dali::Toolkit;
33 namespace // unnamed namespace
36 const unsigned int DEFAULT_NUMBER_OF_COLUMNS = 3;
37 const float DEFAULT_NUMBER_OF_ROWS = 26.0f;
38 const float DEFAULT_ROW_SPACING = 55.0f;
39 const float DEFAULT_BOTTOM_MARGIN_FACTOR = 0.2f;
40 const Radian DEFAULT_TILT_ANGLE ( Math::PI*0.15f );
41 const Radian DEFAULT_ITEM_TILT_ANGLE ( -Math::PI*0.025f );
42 const float DEFAULT_SCROLL_SPEED_FACTOR = 0.02f;
43 const float DEFAULT_MAXIMUM_SWIPE_SPEED = 50.0f;
44 const float DEFAULT_ITEM_FLICK_ANIMATION_DURATION = 0.03f;
46 inline float GetColumnPosition( unsigned int numberOfColumns, unsigned int columnNumber, const Vector3& itemSize, float layoutWidth )
48 // Share the available space between margins & column spacings
49 float availableSpace = std::max( 0.0f, ( layoutWidth - itemSize.width * numberOfColumns ) );
51 float leftMargin = availableSpace / numberOfColumns * 0.5f;
53 float columnPosition = leftMargin + itemSize.width * 0.5f + columnNumber * ( itemSize.width + availableSpace / numberOfColumns );
55 return columnPosition - layoutWidth * 0.5f;
58 struct DepthPositionConstraint
60 DepthPositionConstraint( unsigned int itemId,
61 unsigned int numberOfColumns,
62 unsigned int columnNumber,
63 const Vector3& itemSize,
66 : mItemSize( itemSize ),
68 mNumberOfColumns( numberOfColumns ),
69 mColumnNumber( columnNumber ),
70 mHeightScale( heightScale ),
71 mDepthScale( depthScale )
75 inline void Orientation0( Vector3& current, float layoutPosition, const Vector3& layoutSize )
77 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber );
79 current.x = GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.width );
80 current.y = rowLayoutPositon * mHeightScale + layoutSize.height * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.height - mItemSize.height * 0.5f;
81 current.z = -rowLayoutPositon * mDepthScale;
84 inline void Orientation90( Vector3& current, float layoutPosition, const Vector3& layoutSize )
86 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber ) + mNumberOfColumns * 0.5f;
88 current.x = rowLayoutPositon * mHeightScale + layoutSize.width * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.width - mItemSize.height * 0.5f;
89 current.y = -GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.height );
90 current.z = -rowLayoutPositon * mDepthScale;
93 inline void Orientation180( Vector3& current, float layoutPosition, const Vector3& layoutSize )
95 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber );
97 current.x = -GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.width );
98 current.y = -( rowLayoutPositon * mHeightScale + layoutSize.height * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.height - mItemSize.height * 0.5f );
99 current.z = -rowLayoutPositon * mDepthScale;
102 inline void Orientation270( Vector3& current, float layoutPosition, const Vector3& layoutSize )
104 float rowLayoutPositon = layoutPosition - static_cast< float >( mColumnNumber ) + mNumberOfColumns * 0.5f;
106 current.x = -( rowLayoutPositon * mHeightScale + layoutSize.width * 0.5f - DEFAULT_BOTTOM_MARGIN_FACTOR * layoutSize.width - mItemSize.height * 0.5f );
107 current.y = GetColumnPosition( mNumberOfColumns, mColumnNumber, mItemSize, layoutSize.height );
108 current.z = -rowLayoutPositon * mDepthScale;
111 void Orientation0( Vector3& current, const PropertyInputContainer& inputs )
113 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
114 const Vector3& layoutSize = inputs[1]->GetVector3();
115 Orientation0( current, layoutPosition, layoutSize );
118 void Orientation90( Vector3& current, const PropertyInputContainer& inputs )
120 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
121 const Vector3& layoutSize = inputs[1]->GetVector3();
122 Orientation90( current, layoutPosition, layoutSize );
125 void Orientation180( Vector3& current, const PropertyInputContainer& inputs )
127 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
128 const Vector3& layoutSize = inputs[1]->GetVector3();
129 Orientation180( current, layoutPosition, layoutSize );
132 void Orientation270( Vector3& current, const PropertyInputContainer& inputs )
134 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
135 const Vector3& layoutSize = inputs[1]->GetVector3();
136 Orientation270( current, layoutPosition, layoutSize );
140 unsigned int mItemId;
141 unsigned int mNumberOfColumns;
142 unsigned int mColumnNumber;
147 struct DepthRotationConstraint
149 DepthRotationConstraint( Radian angleRadians, ControlOrientation::Type orientation )
150 : mTiltAngle( angleRadians ),
153 if ( orientation == ControlOrientation::Up )
157 else if ( orientation == ControlOrientation::Left )
161 else if ( orientation == ControlOrientation::Down )
165 else // orientation == ControlOrientation::Right
171 void operator()( Quaternion& current, const PropertyInputContainer& /* inputs */ )
173 current = Quaternion( Radian( mMultiplier * Math::PI ), Vector3::ZAXIS ) * Quaternion( mTiltAngle, Vector3::XAXIS );
180 struct DepthColorConstraint
182 DepthColorConstraint( unsigned int itemId, unsigned int numberOfColumns, float numberOfRows, unsigned int columnNumber )
184 mNumberOfColumns( numberOfColumns ),
185 mNumberOfRows( numberOfRows ),
186 mColumnNumber( columnNumber )
190 void operator()( Vector4& current, const Dali::PropertyInputContainer& inputs )
192 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
193 float row = ( layoutPosition - static_cast<float>( mColumnNumber ) ) / mNumberOfColumns;
195 float darkness(1.0f);
200 darkness = alpha = std::max(0.0f, 1.0f + row);
204 if (row > mNumberOfRows)
210 darkness = 1.0f - ( 1.0f * (row / mNumberOfRows) );
213 if (row > (mNumberOfRows-1.0f))
215 alpha = std::max(0.0f, 1.0f - (row-(mNumberOfRows-1.0f)));
219 current.r = current.g = current.b = darkness;
223 unsigned int mItemId;
224 unsigned int mNumberOfColumns;
226 unsigned int mColumnNumber;
229 struct DepthVisibilityConstraint
231 DepthVisibilityConstraint( unsigned int itemId, unsigned int numberOfColumns, float numberOfRows, unsigned int columnNumber )
233 mNumberOfColumns(numberOfColumns),
234 mNumberOfRows(numberOfRows),
235 mColumnNumber(columnNumber)
239 void operator()( bool& current, const Dali::PropertyInputContainer& inputs )
241 float layoutPosition = inputs[0]->GetFloat() + static_cast< float >( mItemId );
242 float row = ( layoutPosition - static_cast< float >( mColumnNumber ) ) / mNumberOfColumns;
244 current = ( row > -1.0f ) && ( row < mNumberOfRows );
247 unsigned int mItemId;
248 unsigned int mNumberOfColumns;
250 unsigned int mColumnNumber;
253 } // unnamed namespace
264 struct DepthLayout::Impl
267 : mNumberOfColumns(DEFAULT_NUMBER_OF_COLUMNS),
268 mNumberOfRows(DEFAULT_NUMBER_OF_ROWS),
269 mRowSpacing(DEFAULT_ROW_SPACING),
270 mTiltAngle(DEFAULT_TILT_ANGLE),
271 mItemTiltAngle(DEFAULT_ITEM_TILT_ANGLE),
272 mScrollSpeedFactor(DEFAULT_SCROLL_SPEED_FACTOR),
273 mMaximumSwipeSpeed(DEFAULT_MAXIMUM_SWIPE_SPEED),
274 mItemFlickAnimationDuration(DEFAULT_ITEM_FLICK_ANIMATION_DURATION)
278 unsigned int mNumberOfColumns;
279 unsigned int mNumberOfRows;
284 Radian mItemTiltAngle;
286 float mScrollSpeedFactor;
287 float mMaximumSwipeSpeed;
288 float mItemFlickAnimationDuration;
291 DepthLayoutPtr DepthLayout::New()
293 return DepthLayoutPtr(new DepthLayout());
296 DepthLayout::~DepthLayout()
301 void DepthLayout::SetNumberOfColumns(unsigned int columns)
303 mImpl->mNumberOfColumns = columns;
306 unsigned int DepthLayout::GetNumberOfColumns() const
308 return mImpl->mNumberOfColumns;
311 void DepthLayout::SetNumberOfRows(unsigned int rows)
313 mImpl->mNumberOfRows = rows;
316 unsigned int DepthLayout::GetNumberOfRows() const
318 return mImpl->mNumberOfRows;
321 void DepthLayout::SetRowSpacing(float spacing)
323 mImpl->mRowSpacing = spacing;
326 float DepthLayout::GetRowSpacing() const
328 return mImpl->mRowSpacing;
331 void DepthLayout::SetTiltAngle(Degree angle)
333 mImpl->mTiltAngle = Degree( Clamp( angle, -45.0f, 45.0f ) );
336 Degree DepthLayout::GetTiltAngle() const
338 return Degree( mImpl->mTiltAngle );
341 void DepthLayout::SetItemTiltAngle(Degree angle)
343 mImpl->mItemTiltAngle = angle;
346 Degree DepthLayout::GetItemTiltAngle() const
348 return Degree( mImpl->mItemTiltAngle );
351 void DepthLayout::SetScrollSpeedFactor(float scrollSpeed)
353 mImpl->mScrollSpeedFactor = scrollSpeed;
356 void DepthLayout::SetMaximumSwipeSpeed(float speed)
358 mImpl->mMaximumSwipeSpeed = speed;
361 void DepthLayout::SetItemFlickAnimationDuration(float durationSeconds)
363 mImpl->mItemFlickAnimationDuration = durationSeconds;
366 float DepthLayout::GetScrollSpeedFactor() const
368 return mImpl->mScrollSpeedFactor;
371 float DepthLayout::GetMaximumSwipeSpeed() const
373 return mImpl->mMaximumSwipeSpeed;
376 float DepthLayout::GetItemFlickAnimationDuration() const
378 return mImpl->mItemFlickAnimationDuration;
381 float DepthLayout::GetMinimumLayoutPosition(unsigned int numberOfItems, Vector3 layoutSize) const
383 return static_cast<float>(mImpl->mNumberOfColumns) - static_cast<float>(numberOfItems);
386 float DepthLayout::GetClosestAnchorPosition(float layoutPosition) const
388 float rowIndex = static_cast<float>(round(layoutPosition / mImpl->mNumberOfColumns));
389 return rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
392 float DepthLayout::GetItemScrollToPosition(unsigned int itemId) const
394 float rowIndex = static_cast< float >( itemId ) / mImpl->mNumberOfColumns;
395 return -rowIndex * static_cast<float>(mImpl->mNumberOfColumns);
398 ItemRange DepthLayout::GetItemsWithinArea(float firstItemPosition, Vector3 layoutSize) const
400 float firstRow = -(firstItemPosition/mImpl->mNumberOfColumns);
401 float lastRow = firstRow + mImpl->mNumberOfRows * 0.5f;
403 unsigned int firstItem = static_cast<unsigned int>(std::max(0.0f, firstRow * mImpl->mNumberOfColumns));
404 unsigned int lastItem = static_cast<unsigned int>(std::max(0.0f, lastRow * mImpl->mNumberOfColumns));
406 return ItemRange(firstItem, lastItem+1);
409 unsigned int DepthLayout::GetReserveItemCount(Vector3 layoutSize) const
411 float itemsWithinLayout = (layoutSize.depth * mImpl->mNumberOfColumns) / (cosf(mImpl->mTiltAngle) * mImpl->mRowSpacing);
413 return static_cast<unsigned int>(itemsWithinLayout);
416 void DepthLayout::GetDefaultItemSize( unsigned int itemId, const Vector3& layoutSize, Vector3& itemSize ) const
419 itemSize.width = itemSize.height = itemSize.depth = ( IsVertical( GetOrientation() ) ? layoutSize.width : layoutSize.height ) / static_cast<float>( mImpl->mNumberOfColumns + 1 );
422 Degree DepthLayout::GetScrollDirection() const
424 Degree scrollDirection(0.0f);
425 ControlOrientation::Type orientation = GetOrientation();
427 if ( orientation == ControlOrientation::Up )
429 scrollDirection = Degree( 180.0f );
431 else if ( orientation == ControlOrientation::Left )
433 scrollDirection = Degree( 270.0f );
435 else if ( orientation == ControlOrientation::Down )
437 scrollDirection = Degree( 0.0f );
439 else // orientation == ControlOrientation::Right
441 scrollDirection = Degree( 90.0f );
444 return scrollDirection;
447 void DepthLayout::ApplyConstraints( Actor& actor, const int itemId, const Vector3& layoutSize, const Actor& itemViewActor )
450 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast( itemViewActor );
454 GetItemSize( itemId, layoutSize, itemSize );
456 ControlOrientation::Type orientation = GetOrientation();
458 // Position constraint
459 Constraint constraint;
460 DepthPositionConstraint depthPositionStruct( itemId,
461 mImpl->mNumberOfColumns,
462 itemId % mImpl->mNumberOfColumns,
464 -sinf( mImpl->mTiltAngle ) * mImpl->mRowSpacing,
465 cosf( mImpl->mTiltAngle ) * mImpl->mRowSpacing );
466 if ( orientation == ControlOrientation::Up )
468 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation0 );
470 else if ( orientation == ControlOrientation::Left )
472 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation90 );
474 else if ( orientation == ControlOrientation::Down )
476 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation180 );
478 else // orientation == ControlOrientation::Right
480 constraint = Constraint::New< Vector3 >( actor, Actor::Property::POSITION, depthPositionStruct, &DepthPositionConstraint::Orientation270 );
482 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
483 constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
486 // Rotation constraint
487 constraint = Constraint::New< Quaternion >( actor, Actor::Property::ORIENTATION, DepthRotationConstraint( mImpl->mItemTiltAngle, orientation ) );
491 constraint = Constraint::New< Vector4 >( actor, Actor::Property::COLOR, DepthColorConstraint( itemId, mImpl->mNumberOfColumns, mImpl->mNumberOfRows*0.5f, itemId % mImpl->mNumberOfColumns ) );
492 constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_POSITION ) );
493 constraint.SetRemoveAction( Dali::Constraint::Discard );
496 // Visibility constraint
497 constraint = Constraint::New< bool >( actor, Actor::Property::VISIBLE, DepthVisibilityConstraint( 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 );
504 void DepthLayout::SetDepthLayoutProperties(const Property::Map& properties)
506 // Set any properties specified for DepthLayout.
507 for( unsigned int idx = 0, mapCount = properties.Count(); idx < mapCount; ++idx )
509 KeyValuePair propertyPair = properties.GetKeyValue( idx );
510 switch(DefaultItemLayoutProperty::Property(propertyPair.first.indexKey))
512 case DefaultItemLayoutProperty::DEPTH_COLUMN_NUMBER:
514 SetNumberOfColumns(propertyPair.second.Get<int>());
517 case DefaultItemLayoutProperty::DEPTH_ROW_NUMBER:
519 SetNumberOfRows(propertyPair.second.Get<int>());
522 case DefaultItemLayoutProperty::DEPTH_ROW_SPACING:
524 SetRowSpacing(propertyPair.second.Get<float>());
527 case DefaultItemLayoutProperty::DEPTH_MAXIMUM_SWIPE_SPEED:
529 SetMaximumSwipeSpeed(propertyPair.second.Get<float>());
532 case DefaultItemLayoutProperty::DEPTH_SCROLL_SPEED_FACTOR:
534 SetScrollSpeedFactor(propertyPair.second.Get<float>());
537 case DefaultItemLayoutProperty::DEPTH_TILT_ANGLE:
539 SetTiltAngle(Degree(Radian(propertyPair.second.Get<float>())));
542 case DefaultItemLayoutProperty::DEPTH_ITEM_TILT_ANGLE:
544 SetItemTiltAngle(Degree(Radian(propertyPair.second.Get<float>())));
547 case DefaultItemLayoutProperty::DEPTH_ITEM_FLICK_ANIMATION_DURATION:
549 SetItemFlickAnimationDuration(propertyPair.second.Get<float>());
560 Vector3 DepthLayout::GetItemPosition( int itemID, float currentLayoutPosition, const Vector3& layoutSize ) const
562 Vector3 itemPosition = Vector3::ZERO;
564 const float heightScale = -sinf( mImpl->mTiltAngle ) * mImpl->mRowSpacing;
565 const float depthScale = cosf( mImpl->mTiltAngle ) * mImpl->mRowSpacing;
568 GetItemSize( itemID, layoutSize, itemSize );
569 DepthPositionConstraint positionFunctor = DepthPositionConstraint( itemID,
570 mImpl->mNumberOfColumns,
571 itemID % mImpl->mNumberOfColumns,
575 ControlOrientation::Type orientation = GetOrientation();
576 if ( orientation == ControlOrientation::Up )
578 positionFunctor.Orientation0( itemPosition, currentLayoutPosition + itemID, layoutSize );
580 else if ( orientation == ControlOrientation::Left )
582 positionFunctor.Orientation90( itemPosition, currentLayoutPosition + itemID, layoutSize );
584 else if ( orientation == ControlOrientation::Down )
586 positionFunctor.Orientation180( itemPosition, currentLayoutPosition + itemID, layoutSize );
588 else // orientation == ControlOrientation::Right
590 positionFunctor.Orientation270( itemPosition, currentLayoutPosition + itemID, layoutSize );
596 DepthLayout::DepthLayout()
602 float DepthLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
604 float scrollTo = currentLayoutPosition;
605 float row = (currentLayoutPosition + itemID - static_cast<float>(itemID % mImpl->mNumberOfColumns)) / mImpl->mNumberOfColumns;
607 // Check whether item is not within viewable area
610 scrollTo = GetItemScrollToPosition(itemID);
612 else if(row > mImpl->mNumberOfRows * 0.5f - 1.0f)
614 scrollTo = GetItemScrollToPosition(itemID) + (mImpl->mNumberOfRows - 1.0f) * 0.5f * mImpl->mNumberOfColumns;
620 int DepthLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
624 case Toolkit::Control::KeyboardFocus::LEFT:
629 itemID = loopEnabled ? maxItems - 1 : 0;
633 case Toolkit::Control::KeyboardFocus::UP:
635 itemID += mImpl->mNumberOfColumns;
636 if( itemID >= maxItems )
638 itemID = loopEnabled ? 0 : itemID - mImpl->mNumberOfColumns;
642 case Toolkit::Control::KeyboardFocus::RIGHT:
645 if( itemID >= maxItems )
647 itemID = loopEnabled ? 0 : maxItems - 1;
651 case Toolkit::Control::KeyboardFocus::DOWN:
653 itemID -= mImpl->mNumberOfColumns;
656 itemID = loopEnabled ? itemID + maxItems : itemID + mImpl->mNumberOfColumns;
668 } // namespace Internal
670 } // namespace Toolkit