[dali_1.0.29] Merge branch 'tizen'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / alignment / alignment-impl.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
20 #include "alignment-impl.h"
21
22 // EXTERNAL INCLUDES
23 #include <dali/public-api/object/type-registry.h>
24
25 // INTERNAL INCLUDES
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace
37 {
38
39 //Type Registration
40 BaseHandle Create()
41 {
42   return Toolkit::Alignment::New();
43 }
44
45 TypeRegistration mType( typeid(Toolkit::Alignment), typeid(Toolkit::Control), Create );
46
47 struct ScaleToFillConstraint
48 {
49   /**
50    * @param padding to be added.
51    */
52   ScaleToFillConstraint( const Toolkit::Alignment::Padding& padding )
53   : mPadding( padding )
54   {}
55
56   /**
57    * Called by render thread
58    */
59   Vector3 operator()( const Vector3& currentSize,
60                       const PropertyInput& parentSizeProperty )
61   {
62     const Vector3& parentSize( parentSizeProperty.GetVector3() );
63     return GetSize( currentSize, parentSize );
64   }
65
66   inline Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize )
67   {
68     const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right );
69     const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom );
70
71     // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor
72     if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 ) )
73     {
74       // no point trying to squeeze actors into this small size
75       return Vector3::ZERO;
76     }
77     return  Vector3( parentSizeWidth, parentSizeHeight, parentSize.depth );
78   }
79
80   const Toolkit::Alignment::Padding mPadding;
81 };
82
83 struct ScaleToFitKeepAspectConstraint
84 {
85   /**
86    * @param padding to be added.
87    */
88   ScaleToFitKeepAspectConstraint( const Toolkit::Alignment::Padding& padding )
89   : mPadding( padding ),
90     mSizeStored( false ),
91     mOriginalSize()
92   {}
93
94   /**
95    * Called by render thread
96    */
97   Vector3 operator()( const Vector3& currentSize,
98                       const PropertyInput& parentSizeProperty )
99   {
100     const Vector3& parentSize( parentSizeProperty.GetVector3() );
101     return GetSize( currentSize, parentSize );
102   }
103
104   inline Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize )
105   {
106     if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) )
107     {
108       mOriginalSize = currentSize;
109       mSizeStored = true;
110     }
111
112     const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right );
113     const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom );
114
115     // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor
116     if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )||
117         ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) )
118     {
119       // no point trying to squeeze actors into this small size
120       return Vector3::ZERO;
121     }
122
123     return mOriginalSize * std::min( ( parentSizeWidth / mOriginalSize.width ), ( parentSizeHeight / mOriginalSize.height ) );
124   }
125
126   const Toolkit::Alignment::Padding mPadding;
127         bool                        mSizeStored;
128         Vector3                     mOriginalSize;
129 };
130
131 struct ScaleToFillKeepAspectConstraint
132 {
133   /**
134    * @param padding to be added.
135    */
136   ScaleToFillKeepAspectConstraint( const Toolkit::Alignment::Padding& padding )
137   : mPadding( padding ),
138     mSizeStored( false ),
139     mOriginalSize()
140   { }
141
142   /**
143    * Called by render thread
144    */
145   Vector3 operator()( const Vector3& currentSize,
146                       const PropertyInput& parentSizeProperty )
147   {
148     const Vector3& parentSize( parentSizeProperty.GetVector3() );
149     return GetSize( currentSize, parentSize );
150   }
151
152   Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize )
153   {
154     if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) )
155     {
156       mOriginalSize = currentSize;
157       mSizeStored = true;
158     }
159
160     const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right );
161     const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom );
162
163     // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor
164     if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )||
165         ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) )
166     {
167       // no point trying to squeeze actors into this small size
168       return Vector3::ZERO;
169     }
170
171     return mOriginalSize * std::max( ( parentSizeWidth / mOriginalSize.width ), ( parentSizeHeight / mOriginalSize.height ) );
172   }
173
174   const Toolkit::Alignment::Padding mPadding;
175         bool                        mSizeStored;
176         Vector3                     mOriginalSize;
177 };
178
179 struct ShrinkToFitConstraint
180 {
181   /**
182    * @param padding to be added.
183    */
184   ShrinkToFitConstraint( const Toolkit::Alignment::Padding& padding )
185   : mPadding( padding ),
186     mSizeStored( false ),
187     mOriginalSize()
188   {}
189
190   /**
191    * Called by render thread
192    */
193   Vector3 operator()( const Vector3& currentSize,
194                       const PropertyInput& parentSizeProperty )
195   {
196     const Vector3& parentSize( parentSizeProperty.GetVector3() );
197     return GetSize( currentSize, parentSize );
198   }
199
200   Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize )
201   {
202     if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) )
203     {
204       mOriginalSize = currentSize;
205       mSizeStored = true;
206     }
207
208     const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right );
209     const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom );
210
211     // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor
212     if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )||
213         ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) )
214     {
215       // no point trying to squeeze actors into this small size
216       return Vector3::ZERO;
217     }
218
219     return Vector3( std::min( parentSizeWidth, mOriginalSize.width ), std::min( parentSizeHeight, mOriginalSize.height ), std::min( parentSize.depth, mOriginalSize.depth ) );
220   }
221
222   const Toolkit::Alignment::Padding mPadding;
223         bool                        mSizeStored;
224         Vector3                     mOriginalSize;
225 };
226
227 /**
228  * Constraint that uses naturalSize if it fits inside parent and parent size if not. It also adds some padding pixels
229  */
230 struct ShrinkToFitKeepAspectConstraint
231 {
232   /**
233    * @param padding to be added.
234    */
235   ShrinkToFitKeepAspectConstraint( const Toolkit::Alignment::Padding& padding )
236   : mPadding( padding ),
237     mSizeStored( false ),
238     mOriginalSize()
239   {}
240
241   /**
242    * Called by render thread
243    */
244   Vector3 operator()( const Vector3& currentSize,
245                       const PropertyInput& parentSizeProperty )
246   {
247     const Vector3& parentSize( parentSizeProperty.GetVector3() );
248     return GetSize( currentSize, parentSize );
249   }
250
251   inline Vector3 GetSize( const Vector3& currentSize, const Vector3& parentSize )
252   {
253     if( ( !mSizeStored ) && ( Vector3::ZERO != currentSize ) )
254     {
255       mOriginalSize = currentSize;
256       mSizeStored = true;
257     }
258
259     const float parentSizeWidth = parentSize.width - ( mPadding.left + mPadding.right );
260     const float parentSizeHeight = parentSize.height - ( mPadding.top + mPadding.bottom );
261
262     // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor
263     if( ( parentSizeWidth < Math::MACHINE_EPSILON_1000 ) || ( parentSizeHeight < Math::MACHINE_EPSILON_1000 )||
264         ( mOriginalSize.width < Math::MACHINE_EPSILON_1000 ) || ( mOriginalSize.height < Math::MACHINE_EPSILON_1000 ) )
265     {
266       // no point trying to squeeze actors into this small size
267       return Vector3::ZERO;
268     }
269
270     return Vector3( ShrinkInside( Vector2( parentSizeWidth, parentSizeHeight ), Vector2( mOriginalSize ) ) );
271   }
272
273   const Toolkit::Alignment::Padding mPadding;
274         bool                        mSizeStored;
275         Vector3                     mOriginalSize;
276 };
277
278 /**
279  * Constraint that modifies the contained actor taking into account the padding value.
280  */
281 struct PositionConstraint
282 {
283   /**
284    * @param padding The padding value
285    * @param horizontalAlignment The horizontal alignment.
286    * @param verticalAlignment The vertical alignment.
287    */
288   PositionConstraint( const Toolkit::Alignment::Padding& padding, Toolkit::Alignment::Type horizontalAlignment, Toolkit::Alignment::Type verticalAlignment )
289   : mPadding( padding ),
290     mHorizontalAlignment( horizontalAlignment ),
291     mVerticalAlignment( verticalAlignment )
292   {}
293
294   /**
295    * Called by render thread.
296    */
297   Vector3 operator()( const Vector3& currentPosition,
298                       const PropertyInput& currentSizeProperty,
299                       const PropertyInput& parentSizeProperty )
300   {
301     const Vector3& currentSize( currentSizeProperty.GetVector3() );
302     const Vector3& parentSize( parentSizeProperty.GetVector3() );
303
304     Vector3 position( 0.f, 0.f, 0.f );
305
306     switch( mHorizontalAlignment )
307     {
308       case Dali::Toolkit::Alignment::HorizontalLeft:
309       {
310         position.x += mPadding.left;
311         break;
312       }
313       case Dali::Toolkit::Alignment::HorizontalCenter:
314       {
315         if( currentSize.width + mPadding.left + mPadding.right >= parentSize.width )
316         {
317           position.x += 0.5f * ( mPadding.left - mPadding.right );
318         }
319         break;
320       }
321       case Dali::Toolkit::Alignment::HorizontalRight:
322       {
323         position.x -= mPadding.right;
324         break;
325       }
326       default:
327       {
328         DALI_ASSERT_ALWAYS( !"Wrong horizontal alignment value" );
329         break;
330       }
331     }
332
333     switch( mVerticalAlignment )
334     {
335       case Dali::Toolkit::Alignment::VerticalTop:
336       {
337         position.y += mPadding.top;
338         break;
339       }
340       case Dali::Toolkit::Alignment::VerticalCenter:
341       {
342         if( currentSize.height + mPadding.top + mPadding.bottom >= parentSize.height )
343         {
344           position.y += 0.5f * ( mPadding.top - mPadding.bottom );
345         }
346         break;
347       }
348       case Dali::Toolkit::Alignment::VerticalBottom:
349       {
350         position.y -= mPadding.bottom;
351         break;
352       }
353       default:
354       {
355         DALI_ASSERT_ALWAYS( !"Wrong vertical alignment value" );
356         break;
357       }
358     }
359
360     return position;
361   }
362
363   const Toolkit::Alignment::Padding mPadding;
364   const Toolkit::Alignment::Type mHorizontalAlignment;
365   const Toolkit::Alignment::Type mVerticalAlignment;
366 };
367
368 void SetPositionConstraint( Actor actor, const Toolkit::Alignment::Padding& padding, Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
369 {
370   Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
371                                                     LocalSource( Actor::SIZE ),
372                                                     ParentSource( Actor::SIZE ),
373                                                     PositionConstraint( padding, horizontal, vertical ) );
374   actor.ApplyConstraint( constraint );
375 }
376 } // namespace
377
378 Toolkit::Alignment Alignment::New( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
379 {
380   // Create the implementation, temporarily owned on stack
381   IntrusivePtr< Alignment > internalAlignment = new Alignment( horizontal, vertical );
382
383   // Pass ownership to Toolkit::View
384   Toolkit::Alignment alignment( *internalAlignment );
385
386   // Second-phase init of the implementation
387   // This can only be done after the CustomActor connection has been made...
388   internalAlignment->Initialize();
389
390   return alignment;
391 }
392
393 void Alignment::SetAlignmentType( Toolkit::Alignment::Type type )
394 {
395   // Horizontal Alignment
396   if( type & Toolkit::Alignment::HorizontalRight )
397   {
398     mHorizontal = Toolkit::Alignment::HorizontalRight;
399   }
400   if( type & Toolkit::Alignment::HorizontalLeft )
401   {
402     mHorizontal = Toolkit::Alignment::HorizontalLeft;
403   }
404   if( type & Toolkit::Alignment::HorizontalCenter )
405   {
406     mHorizontal = Toolkit::Alignment::HorizontalCenter;
407   }
408
409   // Vertical Alignment
410   if( type & Toolkit::Alignment::VerticalBottom )
411   {
412     mVertical = Toolkit::Alignment::VerticalBottom;
413   }
414   if( type & Toolkit::Alignment::VerticalTop )
415   {
416     mVertical = Toolkit::Alignment::VerticalTop;
417   }
418   if( type & Toolkit::Alignment::VerticalCenter )
419   {
420     mVertical = Toolkit::Alignment::VerticalCenter;
421   }
422
423   RelayoutRequest();
424 }
425
426 Toolkit::Alignment::Type Alignment::GetAlignmentType() const
427 {
428   return Toolkit::Alignment::Type( mHorizontal | mVertical );
429 }
430
431 void Alignment::SetScaling( Toolkit::Alignment::Scaling scaling )
432 {
433   mScaling = scaling;
434
435   RelayoutRequest();
436 }
437
438 Toolkit::Alignment::Scaling Alignment::GetScaling() const
439 {
440   return mScaling;
441 }
442
443 void Alignment::SetPadding( const Toolkit::Alignment::Padding& padding )
444 {
445   DALI_ASSERT_ALWAYS( ( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f ) );
446
447   mPadding = padding;
448
449   RelayoutRequest();
450 }
451
452 const Toolkit::Alignment::Padding& Alignment::GetPadding() const
453 {
454   return mPadding;
455 }
456
457 void Alignment::OnRelayout( const Vector2& size, ActorSizeContainer& container )
458 {
459   // lay out the actors
460   Vector3 anchorPointAndParentOrigin  = Vector3::ZERO;
461   anchorPointAndParentOrigin.z = 0.5f;
462   // anchorPoint.x is initialized to 0.0, which is HorizontalLeft
463   if( Toolkit::Alignment::HorizontalCenter == mHorizontal )
464   {
465     anchorPointAndParentOrigin.x = 0.5f;
466   }
467   else if( Toolkit::Alignment::HorizontalRight == mHorizontal )
468   {
469     anchorPointAndParentOrigin.x = 1.0f;
470   }
471   // anchorPoint.y is initialized to 0.0, which is VerticalTop
472   if( Toolkit::Alignment::VerticalCenter == mVertical )
473   {
474     anchorPointAndParentOrigin.y = 0.5f;
475   }
476   else if( Toolkit::Alignment::VerticalBottom == mVertical )
477   {
478     anchorPointAndParentOrigin.y = 1.0f;
479   }
480
481   unsigned int childCount = Self().GetChildCount();
482   for( unsigned int i=0; i<childCount; ++i )
483   {
484     Actor actor = Self().GetChildAt(i);
485
486     actor.SetAnchorPoint( anchorPointAndParentOrigin );
487     actor.SetParentOrigin( anchorPointAndParentOrigin );
488
489     if( Toolkit::Alignment::ScaleNone != mScaling )
490     {
491       actor.RemoveConstraints();
492     }
493
494     Vector3 actorSize ( actor.GetCurrentSize() );
495     Toolkit::Control control( Toolkit::Control::DownCast( actor ) );
496     if ( actorSize == Vector3::ZERO && control )
497     {
498       actorSize = control.GetNaturalSize();
499     }
500
501     Vector2 childSize;
502
503     switch( mScaling )
504     {
505       case Toolkit::Alignment::ScaleNone:
506       {
507         // Nothing to do but needed just to not to jump to the default.
508         childSize = size;
509         break;
510       }
511       case Toolkit::Alignment::ScaleToFill:
512       {
513         ScaleToFillConstraint constraint( mPadding );
514         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
515         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
516         break;
517       }
518       case Toolkit::Alignment::ScaleToFitKeepAspect:
519       {
520         ScaleToFitKeepAspectConstraint constraint( mPadding );
521         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
522         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
523         break;
524       }
525       case Toolkit::Alignment::ScaleToFillKeepAspect:
526       {
527         ScaleToFillKeepAspectConstraint constraint( mPadding );
528         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
529         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
530         break;
531       }
532       case Toolkit::Alignment::ShrinkToFit:
533       {
534         ShrinkToFitConstraint constraint( mPadding );
535         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
536         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
537         break;
538       }
539       case Toolkit::Alignment::ShrinkToFitKeepAspect:
540       {
541         ShrinkToFitKeepAspectConstraint constraint( mPadding );
542         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
543         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
544         break;
545       }
546       default:
547       {
548         DALI_ASSERT_ALWAYS( !"Invalid Alignment::mGeometryScaling value" );
549         break;
550       }
551     }
552
553     Relayout( actor, childSize, container );
554   }
555 }
556
557 Alignment::Alignment( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
558 : Control( CONTROL_BEHAVIOUR_NONE ),
559   mHorizontal( horizontal ),
560   mVertical( vertical ),
561   mScaling( Toolkit::Alignment::ScaleNone ),
562   mPadding( 0.f, 0.f, 0.f, 0.f )
563 {
564 }
565
566 Alignment::~Alignment()
567 {
568 }
569
570 } // namespace Internal
571
572 } // namespace Toolkit
573
574 } // namespace Dali