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