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