Updates after Handle/Constrainable merge
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / controls / scrollable / item-view / item-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 // CLASS HEADER
19 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-layout.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/animation/active-constraint.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/public-api/animation/time-period.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-view.h>
28
29 namespace
30 {
31
32   // Functors which wrap constraint functions with stored item IDs
33   struct WrappedQuaternionConstraint
34   {
35     WrappedQuaternionConstraint(Dali::Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId)
36     :mWrapMe(wrapMe),
37      mItemId(itemId)
38     {
39     }
40
41     Dali::Quaternion operator()(const Dali::Quaternion& current, const Dali::PropertyInput& layoutPosition, const Dali::PropertyInput& scrollSpeed, const Dali::PropertyInput& layoutSize)
42     {
43       float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
44
45       return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
46     }
47
48     Dali::Toolkit::ItemLayout::QuaternionFunction mWrapMe;
49     unsigned int mItemId;
50   };
51
52   struct WrappedVector3Constraint
53   {
54     WrappedVector3Constraint(Dali::Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId)
55     : mWrapMe(wrapMe),
56       mItemId(itemId)
57     {
58     }
59
60     Dali::Vector3 operator()(const Dali::Vector3& current, const Dali::PropertyInput& layoutPosition, const Dali::PropertyInput& scrollSpeed, const Dali::PropertyInput& layoutSize)
61     {
62       float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
63
64       return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
65     }
66
67     Dali::Toolkit::ItemLayout::Vector3Function mWrapMe;
68     unsigned int mItemId;
69   };
70
71   struct WrappedVector4Constraint
72   {
73     WrappedVector4Constraint(Dali::Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId)
74     : mWrapMe(wrapMe),
75       mItemId(itemId)
76     {
77     }
78
79     Dali::Vector4 operator()(const Dali::Vector4& current, const Dali::PropertyInput& layoutPosition, const Dali::PropertyInput& scrollSpeed, const Dali::PropertyInput& layoutSize)
80     {
81       float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
82
83       return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
84     }
85
86     Dali::Toolkit::ItemLayout::Vector4Function mWrapMe;
87     unsigned int mItemId;
88   };
89
90   struct WrappedBoolConstraint
91   {
92     WrappedBoolConstraint(Dali::Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId)
93     : mWrapMe(wrapMe),
94       mItemId(itemId)
95     {
96     }
97
98     bool operator()(const bool& current, const Dali::PropertyInput& layoutPosition, const Dali::PropertyInput& scrollSpeed, const Dali::PropertyInput& layoutSize)
99     {
100       float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
101
102       return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
103     }
104
105     Dali::Toolkit::ItemLayout::BoolFunction mWrapMe;
106     unsigned int mItemId;
107   };
108
109 }  //Unnamed namespace
110
111 namespace Dali
112 {
113
114 namespace Toolkit
115 {
116
117 ItemLayout::ItemLayout()
118 : mOrientation(ControlOrientation::Up),
119   mAlphaFunction(Dali::Constraint::DEFAULT_ALPHA_FUNCTION)
120 {
121 }
122
123 ItemLayout::~ItemLayout()
124 {
125 }
126
127 void ItemLayout::SetOrientation(ControlOrientation::Type orientation)
128 {
129   mOrientation = orientation;
130 }
131
132 ControlOrientation::Type ItemLayout::GetOrientation() const
133 {
134   return mOrientation;
135 }
136
137 float ItemLayout::GetClosestOnScreenLayoutPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize)
138 {
139   Vector3 itemPosition = GetItemPosition( itemID, currentLayoutPosition, layoutSize );
140   Vector3 itemSize;
141   GetItemSize(itemID, layoutSize, itemSize);
142   Vector3 onScreenArea = (layoutSize - itemSize) * 0.5f;
143   if (itemPosition.x < -onScreenArea.x
144       || itemPosition.x > onScreenArea.x
145       || itemPosition.y < -onScreenArea.y
146       || itemPosition.y > onScreenArea.y)
147   {
148     // item not within viewable area
149     // safest thing to do here since we have no idea how the implementation will work is to return the scroll to position
150     return GetItemScrollToPosition(itemID);
151   }
152   return currentLayoutPosition;
153 }
154
155 void ItemLayout::GetXAxisScrollHint(Vector2& scrollHint) const
156 {
157   scrollHint = Vector2::ZERO;
158   Radian scrollAngle(GetScrollDirection());
159   Vector2 scrollDirection(sinf(scrollAngle), cosf(scrollAngle));
160   switch(mOrientation)
161   {
162     case ControlOrientation::Up:
163     {
164       if(fabsf(scrollDirection.y) < Math::MACHINE_EPSILON_1)
165       {
166         // we probably want x scrolling
167         if(scrollDirection.x > 0.0f)
168         {
169           // normal positive scrolling
170           scrollHint = Vector2::XAXIS;
171         }
172         else
173         {
174           scrollHint = -Vector2::XAXIS;
175         }
176       }
177       break;
178     }
179     case ControlOrientation::Down:
180     {
181       if(fabsf(scrollDirection.y) < Math::MACHINE_EPSILON_1)
182       {
183         // we probably want x scrolling
184         if(scrollDirection.x > 0.0f)
185         {
186           // normal positive scrolling
187           scrollHint = -Vector2::XAXIS;
188         }
189         else
190         {
191           scrollHint = Vector2::XAXIS;
192         }
193       }
194       break;
195     }
196     case ControlOrientation::Left:
197     {
198       // we probably want x scrolling
199       if(scrollDirection.x > 0.0f)
200       {
201         // normal positive scrolling
202         scrollHint = Vector2::XAXIS;
203       }
204       else
205       {
206         scrollHint = -Vector2::XAXIS;
207       }
208       break;
209     }
210     case ControlOrientation::Right:
211     {
212       // we probably want x scrolling
213       if(scrollDirection.x > 0.0f)
214       {
215         // normal positive scrolling
216         scrollHint = -Vector2::XAXIS;
217       }
218       else
219       {
220         scrollHint = Vector2::XAXIS;
221       }
222       break;
223     }
224   }
225 }
226
227 void ItemLayout::GetYAxisScrollHint(Vector2& scrollHint) const
228 {
229   scrollHint = Vector2::ZERO;
230   Radian scrollAngle(GetScrollDirection());
231   Vector2 scrollDirection(sinf(scrollAngle), cosf(scrollAngle));
232   switch(mOrientation)
233   {
234     case ControlOrientation::Up:
235     {
236       // we probably want x scrolling
237       if(scrollDirection.y > 0.0f)
238       {
239         // normal positive scrolling
240         scrollHint = Vector2::YAXIS;
241       }
242       else
243       {
244         scrollHint = -Vector2::YAXIS;
245       }
246       break;
247     }
248     case ControlOrientation::Down:
249     {
250       // we probably want x scrolling
251       if(scrollDirection.y > 0.0f)
252       {
253         // normal positive scrolling
254         scrollHint = -Vector2::YAXIS;
255       }
256       else
257       {
258         scrollHint = Vector2::YAXIS;
259       }
260       break;
261     }
262     case ControlOrientation::Left:
263     {
264       if(fabsf(scrollDirection.x) < Math::MACHINE_EPSILON_1)
265       {
266         // we probably want x scrolling
267         if(scrollDirection.y > 0.0f)
268         {
269           // normal positive scrolling
270           scrollHint = -Vector2::YAXIS;
271         }
272         else
273         {
274           scrollHint = Vector2::YAXIS;
275         }
276       }
277       break;
278     }
279     case ControlOrientation::Right:
280     {
281       if(fabsf(scrollDirection.x) < Math::MACHINE_EPSILON_1)
282       {
283         // we probably want x scrolling
284         if(scrollDirection.y > 0.0f)
285         {
286           // normal positive scrolling
287           scrollHint = Vector2::YAXIS;
288         }
289         else
290         {
291           scrollHint = -Vector2::YAXIS;
292         }
293       }
294       break;
295     }
296   }
297 }
298
299 int ItemLayout::GetNextFocusItemID(int itemID, int maxItems, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
300 {
301   switch( direction )
302   {
303     case Control::Left:
304     case Control::Up:
305     {
306       itemID--;
307       if( itemID < 0 )
308       {
309         itemID = loopEnabled ? maxItems - 1 : 0;
310       }
311       break;
312     }
313     case Control::Right:
314     case Control::Down:
315     {
316       itemID++;
317       if( itemID >= maxItems )
318       {
319         itemID = loopEnabled ? 0 : maxItems - 1;
320       }
321       break;
322     }
323   }
324   return itemID;
325 }
326
327 float ItemLayout::GetFlickSpeedFactor() const
328 {
329   // By default, the speed factor while dragging and swiping is the same.
330   return GetScrollSpeedFactor();
331 }
332
333 void ItemLayout::ApplyConstraints( Actor& actor, const int itemId, const float durationSeconds, Handle scrollPositionObject, const Actor& itemViewActor )
334 {
335   // This just implements the default behaviour of constraint application.
336   // Custom layouts can override this function to apply their custom constraints.
337   Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast( itemViewActor );
338   if(itemView && scrollPositionObject)
339   {
340     Property::Index scrollSpeedProperty = itemView.GetPropertyIndex("item-view-scroll-speed");
341     Property::Index scrollPositionProperty = scrollPositionObject.GetPropertyIndex("scroll-position");
342
343     ItemLayout::Vector3Function positionConstraint;
344     if (GetPositionConstraint(itemId, positionConstraint))
345     {
346       WrappedVector3Constraint wrapped(positionConstraint, itemId);
347       Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
348                                                         Source( scrollPositionObject, scrollPositionProperty ),
349                                                         ParentSource( scrollSpeedProperty ),
350                                                         ParentSource( Actor::SIZE ),
351                                                         wrapped );
352       constraint.SetApplyTime(durationSeconds);
353       constraint.SetAlphaFunction(mAlphaFunction);
354       actor.ApplyConstraint(constraint);
355     }
356
357     ItemLayout::QuaternionFunction rotationConstraint;
358     if (GetRotationConstraint(itemId, rotationConstraint))
359     {
360       WrappedQuaternionConstraint wrapped(rotationConstraint, itemId);
361
362       Constraint constraint = Constraint::New<Quaternion>( Actor::ROTATION,
363                                                            Source( scrollPositionObject, scrollPositionProperty ),
364                                                            ParentSource( scrollSpeedProperty ),
365                                                            ParentSource( Actor::SIZE ),
366                                                            wrapped );
367       constraint.SetApplyTime(durationSeconds);
368       constraint.SetAlphaFunction(mAlphaFunction);
369
370       actor.ApplyConstraint(constraint);
371     }
372
373     ItemLayout::Vector3Function scaleConstraint;
374     if (GetScaleConstraint(itemId, scaleConstraint))
375     {
376       WrappedVector3Constraint wrapped(scaleConstraint, itemId);
377
378       Constraint constraint = Constraint::New<Vector3>( Actor::SCALE,
379                                                         Source( scrollPositionObject, scrollPositionProperty ),
380                                                         ParentSource( scrollSpeedProperty ),
381                                                         ParentSource( Actor::SIZE ),
382                                                         wrapped );
383       constraint.SetApplyTime(durationSeconds);
384       constraint.SetAlphaFunction(mAlphaFunction);
385
386       actor.ApplyConstraint(constraint);
387     }
388
389     ItemLayout::Vector4Function colorConstraint;
390     if (GetColorConstraint(itemId, colorConstraint))
391     {
392       WrappedVector4Constraint wrapped(colorConstraint, itemId);
393
394       Constraint constraint = Constraint::New<Vector4>( Actor::COLOR,
395                                                         Source( scrollPositionObject, scrollPositionProperty ),
396                                                         ParentSource( scrollSpeedProperty ),
397                                                         ParentSource( Actor::SIZE ),
398                                                         wrapped );
399
400       constraint.SetApplyTime(durationSeconds);
401       constraint.SetAlphaFunction(mAlphaFunction);
402       constraint.SetRemoveAction(Dali::Constraint::Discard);
403
404       actor.ApplyConstraint(constraint);
405     }
406
407     ItemLayout::BoolFunction visibilityConstraint;
408     if (GetVisibilityConstraint(itemId, visibilityConstraint))
409     {
410       WrappedBoolConstraint wrapped(visibilityConstraint, itemId);
411
412       Constraint constraint = Constraint::New<bool>( Actor::VISIBLE,
413                                                      Source( scrollPositionObject, scrollPositionProperty ),
414                                                      ParentSource( scrollSpeedProperty ),
415                                                      ParentSource( Actor::SIZE ),
416                                                      wrapped );
417
418       constraint.SetApplyTime(durationSeconds);
419       constraint.SetAlphaFunction(mAlphaFunction);
420
421       // Release visibility constraints the same time as the color constraint
422       constraint.SetRemoveAction(Dali::Constraint::Discard);
423
424       actor.ApplyConstraint(constraint);
425     }
426   }
427 }
428
429 Vector3 ItemLayout::GetItemPosition(int itemID, float currentLayoutPosition, const Vector3& layoutSize) const
430 {
431   Vector3 itemPosition = Vector3::ZERO;
432
433   ItemLayout::Vector3Function positionConstraint;
434   if (GetPositionConstraint(itemID, positionConstraint))
435   {
436     itemPosition = positionConstraint(Vector3::ZERO, currentLayoutPosition + itemID, 0.0f, layoutSize);
437   }
438
439   return itemPosition;
440 }
441
442 void ItemLayout::SetAlphaFunction(AlphaFunction func)
443 {
444   mAlphaFunction = func;
445 }
446
447 AlphaFunction ItemLayout::GetAlphaFunction() const
448 {
449   return mAlphaFunction;
450 }
451
452
453 } // namespace Toolkit
454
455 } // namespace Dali