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