Alignment: To use default copy constructor in the constraints
[platform/core/uifw/dali-toolkit.git] / base / 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   bool horizontalSet(false);
397
398   if( type & Toolkit::Alignment::HorizontalLeft )
399   {
400     mHorizontal = Toolkit::Alignment::HorizontalLeft;
401     horizontalSet = true;
402   }
403   if( type & Toolkit::Alignment::HorizontalCenter )
404   {
405     DALI_ASSERT_ALWAYS(!horizontalSet);
406     mHorizontal = Toolkit::Alignment::HorizontalCenter;
407     horizontalSet = true;
408   }
409   if( type & Toolkit::Alignment::HorizontalRight )
410   {
411     DALI_ASSERT_ALWAYS(!horizontalSet);
412     mHorizontal = Toolkit::Alignment::HorizontalRight;
413   }
414
415   // Vertical Alignment
416   bool verticalSet(false);
417
418   if( type & Toolkit::Alignment::VerticalTop )
419   {
420     mVertical = Toolkit::Alignment::VerticalTop;
421     verticalSet = true;
422   }
423   if( type & Toolkit::Alignment::VerticalCenter )
424   {
425     DALI_ASSERT_ALWAYS(!verticalSet);
426     mVertical = Toolkit::Alignment::VerticalCenter;
427     verticalSet = true;
428   }
429   if( type & Toolkit::Alignment::VerticalBottom )
430   {
431     DALI_ASSERT_ALWAYS(!verticalSet);
432     mVertical = Toolkit::Alignment::VerticalBottom;
433   }
434
435   RelayoutRequest();
436 }
437
438 Toolkit::Alignment::Type Alignment::GetAlignmentType() const
439 {
440   return Toolkit::Alignment::Type( mHorizontal | mVertical );
441 }
442
443 void Alignment::SetScaling( Toolkit::Alignment::Scaling scaling )
444 {
445   mScaling = scaling;
446
447   RelayoutRequest();
448 }
449
450 Toolkit::Alignment::Scaling Alignment::GetScaling() const
451 {
452   return mScaling;
453 }
454
455 void Alignment::SetPadding( const Toolkit::Alignment::Padding& padding )
456 {
457   DALI_ASSERT_ALWAYS( ( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f ) );
458
459   mPadding = padding;
460
461   RelayoutRequest();
462 }
463
464 const Toolkit::Alignment::Padding& Alignment::GetPadding() const
465 {
466   return mPadding;
467 }
468
469 void Alignment::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
470 {
471   // lay out the actors
472   Vector3 anchorPointAndParentOrigin  = Vector3::ZERO;
473   anchorPointAndParentOrigin.z = 0.5f;
474   // anchorPoint.x is initialized to 0.0, which is HorizontalLeft
475   if( Toolkit::Alignment::HorizontalCenter == mHorizontal )
476   {
477     anchorPointAndParentOrigin.x = 0.5f;
478   }
479   else if( Toolkit::Alignment::HorizontalRight == mHorizontal )
480   {
481     anchorPointAndParentOrigin.x = 1.0f;
482   }
483   // anchorPoint.y is initialized to 0.0, which is VerticalTop
484   if( Toolkit::Alignment::VerticalCenter == mVertical )
485   {
486     anchorPointAndParentOrigin.y = 0.5f;
487   }
488   else if( Toolkit::Alignment::VerticalBottom == mVertical )
489   {
490     anchorPointAndParentOrigin.y = 1.0f;
491   }
492
493   unsigned int childCount = Self().GetChildCount();
494   for( unsigned int i=0; i<childCount; ++i )
495   {
496     Actor actor = Self().GetChildAt(i);
497
498     actor.SetAnchorPoint( anchorPointAndParentOrigin );
499     actor.SetParentOrigin( anchorPointAndParentOrigin );
500
501     if( Toolkit::Alignment::ScaleNone != mScaling )
502     {
503       actor.RemoveConstraints();
504     }
505
506     Vector3 actorSize ( actor.GetCurrentSize() );
507     Toolkit::Control control( Toolkit::Control::DownCast( actor ) );
508     if ( actorSize == Vector3::ZERO && control )
509     {
510       actorSize = control.GetNaturalSize();
511     }
512
513     Vector2 childSize;
514
515     switch( mScaling )
516     {
517       case Toolkit::Alignment::ScaleNone:
518       {
519         // Nothing to do but needed just to not to jump to the default.
520         childSize = size;
521         break;
522       }
523       case Toolkit::Alignment::ScaleToFill:
524       {
525         ScaleToFillConstraint constraint( mPadding );
526         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
527         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
528         break;
529       }
530       case Toolkit::Alignment::ScaleToFitKeepAspect:
531       {
532         ScaleToFitKeepAspectConstraint constraint( mPadding );
533         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
534         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
535         break;
536       }
537       case Toolkit::Alignment::ScaleToFillKeepAspect:
538       {
539         ScaleToFillKeepAspectConstraint constraint( mPadding );
540         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
541         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
542         break;
543       }
544       case Toolkit::Alignment::ShrinkToFit:
545       {
546         ShrinkToFitConstraint constraint( mPadding );
547         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
548         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
549         break;
550       }
551       case Toolkit::Alignment::ShrinkToFitKeepAspect:
552       {
553         ShrinkToFitKeepAspectConstraint constraint( mPadding );
554         childSize = Vector2( constraint.GetSize( actorSize, Vector3(size) ) );
555         SetPositionConstraint( actor, mPadding, mHorizontal, mVertical );
556         break;
557       }
558       default:
559       {
560         DALI_ASSERT_ALWAYS( !"Invalid Alignment::mGeometryScaling value" );
561         break;
562       }
563     }
564
565     Relayout( actor, childSize, container );
566   }
567 }
568
569 Alignment::Alignment( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
570 : Control( CONTROL_BEHAVIOUR_NONE ),
571   mHorizontal( horizontal ),
572   mVertical( vertical ),
573   mScaling( Toolkit::Alignment::ScaleNone ),
574   mPadding( 0.f, 0.f, 0.f, 0.f )
575 {
576 }
577
578 Alignment::~Alignment()
579 {
580 }
581
582 } // namespace Internal
583
584 } // namespace Toolkit
585
586 } // namespace Dali