Property refactor in dali-toolkit: Toolkit changes
[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 #include "alignment-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/property-input.h>
23 #include <dali/public-api/object/type-registry.h>
24 #include <dali/public-api/object/type-registry-helper.h>
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 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Alignment, Toolkit::Control, Create )
45 DALI_TYPE_REGISTRATION_END()
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     return GetPosition( currentSize, parentSize );
305   }
306
307   inline Vector3 GetPosition( const Vector3& currentSize, const Vector3& parentSize )
308   {
309     Vector3 position( 0.f, 0.f, 0.f );
310
311     switch( mHorizontalAlignment )
312     {
313     case Dali::Toolkit::Alignment::HorizontalLeft:
314     {
315       position.x += mPadding.left;
316       break;
317     }
318     case Dali::Toolkit::Alignment::HorizontalCenter:
319     {
320       if( currentSize.width + mPadding.left + mPadding.right >= parentSize.width )
321       {
322         position.x += 0.5f * ( mPadding.left - mPadding.right );
323       }
324       break;
325     }
326     case Dali::Toolkit::Alignment::HorizontalRight:
327     {
328       position.x -= mPadding.right;
329       break;
330     }
331     default:
332     {
333       DALI_ASSERT_ALWAYS( !"Wrong horizontal alignment value" );
334       break;
335     }
336     }
337
338     switch( mVerticalAlignment )
339     {
340     case Dali::Toolkit::Alignment::VerticalTop:
341     {
342       position.y += mPadding.top;
343       break;
344     }
345     case Dali::Toolkit::Alignment::VerticalCenter:
346     {
347       if( currentSize.height + mPadding.top + mPadding.bottom >= parentSize.height )
348       {
349         position.y += 0.5f * ( mPadding.top - mPadding.bottom );
350       }
351       break;
352     }
353     case Dali::Toolkit::Alignment::VerticalBottom:
354     {
355       position.y -= mPadding.bottom;
356       break;
357     }
358     default:
359     {
360       DALI_ASSERT_ALWAYS( !"Wrong vertical alignment value" );
361       break;
362     }
363     }
364
365     return position;
366   }
367
368   const Toolkit::Alignment::Padding mPadding;
369   const Toolkit::Alignment::Type mHorizontalAlignment;
370   const Toolkit::Alignment::Type mVerticalAlignment;
371 };
372 } // namespace
373
374 Toolkit::Alignment Alignment::New( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
375 {
376   // Create the implementation, temporarily owned on stack
377   IntrusivePtr< Alignment > internalAlignment = new Alignment( horizontal, vertical );
378
379   // Pass ownership to Toolkit::View
380   Toolkit::Alignment alignment( *internalAlignment );
381
382   // Second-phase init of the implementation
383   // This can only be done after the CustomActor connection has been made...
384   internalAlignment->Initialize();
385
386   return alignment;
387 }
388
389 void Alignment::SetAlignmentType( Toolkit::Alignment::Type type )
390 {
391   // Horizontal Alignment
392   if( type & Toolkit::Alignment::HorizontalRight )
393   {
394     mHorizontal = Toolkit::Alignment::HorizontalRight;
395   }
396   if( type & Toolkit::Alignment::HorizontalLeft )
397   {
398     mHorizontal = Toolkit::Alignment::HorizontalLeft;
399   }
400   if( type & Toolkit::Alignment::HorizontalCenter )
401   {
402     mHorizontal = Toolkit::Alignment::HorizontalCenter;
403   }
404
405   // Vertical Alignment
406   if( type & Toolkit::Alignment::VerticalBottom )
407   {
408     mVertical = Toolkit::Alignment::VerticalBottom;
409   }
410   if( type & Toolkit::Alignment::VerticalTop )
411   {
412     mVertical = Toolkit::Alignment::VerticalTop;
413   }
414   if( type & Toolkit::Alignment::VerticalCenter )
415   {
416     mVertical = Toolkit::Alignment::VerticalCenter;
417   }
418
419   RelayoutRequest();
420 }
421
422 Toolkit::Alignment::Type Alignment::GetAlignmentType() const
423 {
424   return Toolkit::Alignment::Type( mHorizontal | mVertical );
425 }
426
427 void Alignment::SetScaling( Toolkit::Alignment::Scaling scaling )
428 {
429   mScaling = scaling;
430
431   RelayoutRequest();
432 }
433
434 Toolkit::Alignment::Scaling Alignment::GetScaling() const
435 {
436   return mScaling;
437 }
438
439 void Alignment::SetPadding( const Toolkit::Alignment::Padding& padding )
440 {
441   DALI_ASSERT_ALWAYS( ( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f ) );
442
443   mPadding = padding;
444
445   RelayoutRequest();
446 }
447
448 const Toolkit::Alignment::Padding& Alignment::GetPadding() const
449 {
450   return mPadding;
451 }
452
453 void Alignment::OnRelayout( const Vector2& size, ActorSizeContainer& container )
454 {
455   // lay out the actors
456   Vector3 anchorPointAndParentOrigin  = Vector3::ZERO;
457   anchorPointAndParentOrigin.z = 0.5f;
458   // anchorPoint.x is initialized to 0.0, which is HorizontalLeft
459   if( Toolkit::Alignment::HorizontalCenter == mHorizontal )
460   {
461     anchorPointAndParentOrigin.x = 0.5f;
462   }
463   else if( Toolkit::Alignment::HorizontalRight == mHorizontal )
464   {
465     anchorPointAndParentOrigin.x = 1.0f;
466   }
467   // anchorPoint.y is initialized to 0.0, which is VerticalTop
468   if( Toolkit::Alignment::VerticalCenter == mVertical )
469   {
470     anchorPointAndParentOrigin.y = 0.5f;
471   }
472   else if( Toolkit::Alignment::VerticalBottom == mVertical )
473   {
474     anchorPointAndParentOrigin.y = 1.0f;
475   }
476
477   unsigned int childCount = Self().GetChildCount();
478   for( unsigned int i=0; i<childCount; ++i )
479   {
480     Actor actor = Self().GetChildAt(i);
481
482     actor.SetAnchorPoint( anchorPointAndParentOrigin );
483     actor.SetParentOrigin( anchorPointAndParentOrigin );
484
485     Vector3 actorSize ( actor.GetSize() );
486     Toolkit::Control control( Toolkit::Control::DownCast( actor ) );
487     if ( actorSize == Vector3::ZERO && control )
488     {
489       actorSize = control.GetNaturalSize();
490     }
491
492     Vector3 childSize;
493
494     switch( mScaling )
495     {
496       case Toolkit::Alignment::ScaleNone:
497       {
498         // Nothing to do but needed just to not to jump to the default.
499         childSize = actorSize;
500         break;
501       }
502       case Toolkit::Alignment::ScaleToFill:
503       {
504         ScaleToFillConstraint constraint( mPadding );
505         childSize = constraint.GetSize( actorSize, Vector3(size) ) ;
506         break;
507       }
508       case Toolkit::Alignment::ScaleToFitKeepAspect:
509       {
510         ScaleToFitKeepAspectConstraint constraint( mPadding );
511         childSize = constraint.GetSize( actorSize, Vector3(size) ) ;
512         break;
513       }
514       case Toolkit::Alignment::ScaleToFillKeepAspect:
515       {
516         ScaleToFillKeepAspectConstraint constraint( mPadding );
517         childSize = constraint.GetSize( actorSize, Vector3(size) );
518         break;
519       }
520       case Toolkit::Alignment::ShrinkToFit:
521       {
522         ShrinkToFitConstraint constraint( mPadding );
523         childSize = constraint.GetSize( actorSize, Vector3(size) );
524         break;
525       }
526       case Toolkit::Alignment::ShrinkToFitKeepAspect:
527       {
528         ShrinkToFitKeepAspectConstraint constraint( mPadding );
529         childSize = constraint.GetSize( actorSize, Vector3(size) );
530         break;
531       }
532       default:
533       {
534         DALI_ASSERT_ALWAYS( !"Invalid Alignment::mGeometryScaling value" );
535         break;
536       }
537     }
538
539     PositionConstraint positionConstraint(mPadding, mHorizontal, mVertical);
540     actor.SetPosition( positionConstraint.GetPosition(childSize, actorSize) );
541
542     if( !control )
543     {
544       actor.SetScale(childSize / actorSize);
545     }
546
547     Relayout( actor, Vector2(childSize), container );
548   }
549 }
550
551 Alignment::Alignment( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
552 : Control( CONTROL_BEHAVIOUR_NONE ),
553   mHorizontal( horizontal ),
554   mVertical( vertical ),
555   mScaling( Toolkit::Alignment::ScaleNone ),
556   mPadding( 0.f, 0.f, 0.f, 0.f )
557 {
558 }
559
560 Alignment::~Alignment()
561 {
562 }
563
564 } // namespace Internal
565
566 } // namespace Toolkit
567
568 } // namespace Dali