a58f78f8205af9d63f25b1e2d9b59e876b5eef29
[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 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
29
30 namespace Dali
31 {
32
33 namespace Toolkit
34 {
35
36 namespace Internal
37 {
38
39 namespace
40 {
41
42 //Type Registration
43 BaseHandle Create()
44 {
45   return Toolkit::Alignment::New();
46 }
47
48 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Alignment, Toolkit::Control, Create )
49 DALI_TYPE_REGISTRATION_END()
50
51 /**
52  * @param padding The padding value
53  * @param horizontalAlignment The horizontal alignment.
54  * @param verticalAlignment The vertical alignment.
55  * @param currentSize of the object
56  * @param parentSize
57  */
58 inline Vector3 GetPosition( const Toolkit::Alignment::Padding& padding, Toolkit::Alignment::Type horizontalAlignment, Toolkit::Alignment::Type verticalAlignment,
59                             const Vector2& currentSize, const Vector2& parentSize )
60 {
61   Vector3 position( 0.f, 0.f, 0.f );
62
63   switch( horizontalAlignment )
64   {
65     case Dali::Toolkit::Alignment::HORIZONTAL_LEFT:
66     {
67       position.x += padding.left;
68       break;
69     }
70     case Dali::Toolkit::Alignment::HORIZONTAL_RIGHT:
71     {
72       position.x -= padding.right;
73       break;
74     }
75     case Dali::Toolkit::Alignment::HORIZONTAL_CENTER: // FALLTHROUGH
76     default: // use center as default
77     {
78       if( currentSize.width + padding.left + padding.right >= parentSize.width )
79       {
80         position.x += 0.5f * ( padding.left - padding.right );
81       }
82       break;
83     }
84   }
85
86   switch( verticalAlignment )
87   {
88     case Dali::Toolkit::Alignment::VERTICAL_TOP:
89     {
90       position.y += padding.top;
91       break;
92     }
93     case Dali::Toolkit::Alignment::VERTICAL_BOTTOM:
94     {
95       position.y -= padding.bottom;
96       break;
97     }
98     case Dali::Toolkit::Alignment::VERTICAL_CENTER: // FALLTHROUGH
99     default: // use center as default
100     {
101       if( currentSize.height + padding.top + padding.bottom >= parentSize.height )
102       {
103         position.y += 0.5f * ( padding.top - padding.bottom );
104       }
105       break;
106     }
107   }
108
109   return position;
110 }
111
112 } // namespace
113
114 Toolkit::Alignment Alignment::New( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
115 {
116   // Create the implementation, temporarily owned on stack
117   IntrusivePtr< Alignment > internalAlignment = new Alignment( horizontal, vertical );
118
119   // Pass ownership to Toolkit::Alignment
120   Toolkit::Alignment alignment( *internalAlignment );
121
122   // Second-phase init of the implementation
123   // This can only be done after the CustomActor connection has been made...
124   internalAlignment->Initialize();
125
126   return alignment;
127 }
128
129 void Alignment::SetAlignmentType( Toolkit::Alignment::Type type )
130 {
131   // Horizontal Alignment
132   if( type & Toolkit::Alignment::HORIZONTAL_RIGHT )
133   {
134     mHorizontal = Toolkit::Alignment::HORIZONTAL_RIGHT;
135   }
136   if( type & Toolkit::Alignment::HORIZONTAL_LEFT )
137   {
138     mHorizontal = Toolkit::Alignment::HORIZONTAL_LEFT;
139   }
140   if( type & Toolkit::Alignment::HORIZONTAL_CENTER )
141   {
142     mHorizontal = Toolkit::Alignment::HORIZONTAL_CENTER;
143   }
144
145   // Vertical Alignment
146   if( type & Toolkit::Alignment::VERTICAL_BOTTOM )
147   {
148     mVertical = Toolkit::Alignment::VERTICAL_BOTTOM;
149   }
150   if( type & Toolkit::Alignment::VERTICAL_TOP )
151   {
152     mVertical = Toolkit::Alignment::VERTICAL_TOP;
153   }
154   if( type & Toolkit::Alignment::VERTICAL_CENTER )
155   {
156     mVertical = Toolkit::Alignment::VERTICAL_CENTER;
157   }
158
159   RelayoutRequest();
160 }
161
162 Toolkit::Alignment::Type Alignment::GetAlignmentType() const
163 {
164   return Toolkit::Alignment::Type( mHorizontal | mVertical );
165 }
166
167 void Alignment::SetScaling( Toolkit::Alignment::Scaling scaling )
168 {
169   mScaling = scaling;
170
171   RelayoutRequest();
172 }
173
174 Toolkit::Alignment::Scaling Alignment::GetScaling() const
175 {
176   return mScaling;
177 }
178
179 void Alignment::SetPadding( const Toolkit::Alignment::Padding& padding )
180 {
181   DALI_ASSERT_ALWAYS( ( padding.left >= 0.f ) && ( padding.top >= 0.f ) && ( padding.right >= 0.f ) && ( padding.bottom >= 0.f ) );
182
183   mPadding = padding;
184
185   RelayoutRequest();
186 }
187
188 const Toolkit::Alignment::Padding& Alignment::GetPadding() const
189 {
190   return mPadding;
191 }
192
193 void Alignment::OnRelayout( const Vector2& size, RelayoutContainer& container )
194 {
195   // lay out the actors
196   Vector3 anchorPointAndParentOrigin  = Vector3::ZERO;
197   anchorPointAndParentOrigin.z = 0.5f;
198   // anchorPoint.x is initialized to 0.0, which is HORIZONTAL_LEFT
199   if( Toolkit::Alignment::HORIZONTAL_CENTER == mHorizontal )
200   {
201     anchorPointAndParentOrigin.x = 0.5f;
202   }
203   else if( Toolkit::Alignment::HORIZONTAL_RIGHT == mHorizontal )
204   {
205     anchorPointAndParentOrigin.x = 1.0f;
206   }
207   // anchorPoint.y is initialized to 0.0, which is VERTICAL_TOP
208   if( Toolkit::Alignment::VERTICAL_CENTER == mVertical )
209   {
210     anchorPointAndParentOrigin.y = 0.5f;
211   }
212   else if( Toolkit::Alignment::VERTICAL_BOTTOM == mVertical )
213   {
214     anchorPointAndParentOrigin.y = 1.0f;
215   }
216
217   for( unsigned int i = 0, childCount = Self().GetChildCount(); i < childCount; ++i )
218   {
219     Actor child = Self().GetChildAt(i);
220
221     child.SetProperty( Actor::Property::ANCHOR_POINT, anchorPointAndParentOrigin );
222     child.SetProperty( Actor::Property::PARENT_ORIGIN, anchorPointAndParentOrigin );
223
224     Vector2 currentChildSize( child.GetTargetSize().GetVectorXY() );
225     if( currentChildSize == Vector2::ZERO )
226     {
227       currentChildSize = child.GetNaturalSize();
228     }
229
230     bool renegotiate = true;
231     Vector2 newChildSize;
232     newChildSize.width  = size.width - ( mPadding.left + mPadding.right );
233     newChildSize.height = size.height- (  mPadding.top + mPadding.bottom );
234
235     // prevent ridiculous sizes if parent is really small or if we don't have a proper size for the actor
236     if( ( newChildSize.width > Math::MACHINE_EPSILON_1000 ) && ( newChildSize.height > Math::MACHINE_EPSILON_1000 ) &&
237         ( currentChildSize.width > Math::MACHINE_EPSILON_1000 ) && ( currentChildSize.height > Math::MACHINE_EPSILON_1000 ) )
238     {
239       // no point trying to squeeze actors into too small size
240       switch( mScaling )
241       {
242         case Toolkit::Alignment::SCALE_NONE:
243         {
244           // Nothing to do
245           renegotiate = false;
246           break;
247         }
248         case Toolkit::Alignment::SCALE_TO_FILL:
249         {
250           // Nothing to do, newChildSize is already full size minus padding
251           break;
252         }
253         case Toolkit::Alignment::SCALE_TO_FIT_KEEP_ASPECT:
254         {
255           newChildSize = currentChildSize * std::min( ( newChildSize.width / currentChildSize.width ), ( newChildSize.height / currentChildSize.height ) );
256           break;
257         }
258         case Toolkit::Alignment::SCALE_TO_FILL_KEEP_ASPECT:
259         {
260           newChildSize = currentChildSize * std::max( ( newChildSize.width / currentChildSize.width ), ( newChildSize.height / currentChildSize.height ) );
261           break;
262         }
263         case Toolkit::Alignment::SHRINK_TO_FIT:
264         {
265           newChildSize = Vector2( std::min( newChildSize.width, currentChildSize.width ), std::min( newChildSize.height, currentChildSize.height ) );
266           break;
267         }
268         case Toolkit::Alignment::SHRINK_TO_FIT_KEEP_ASPECT:
269         {
270           // check source size vs target size to see if we need to shrink
271           float widthScale = ( newChildSize.width < currentChildSize.width ) ? (newChildSize.width / currentChildSize.width) : 1.f;
272           float heightScale = ( newChildSize.height < currentChildSize.height ) ? (newChildSize.height / currentChildSize.height) : 1.0f;
273           // use smaller of the scales
274           float scale = std::min( widthScale, heightScale );
275           // check if we need to scale
276           if( scale < 1.0f )
277           {
278             // scale natural size to fit inside
279             newChildSize *= scale;
280           }
281           break;
282         }
283       }
284     }
285
286     child.SetProperty( Actor::Property::POSITION, GetPosition( mPadding, mHorizontal, mVertical , newChildSize, currentChildSize ) );
287
288     if( renegotiate )
289     {
290       container.Add( child, newChildSize );
291     }
292   }
293 }
294
295 Alignment::Alignment( Toolkit::Alignment::Type horizontal, Toolkit::Alignment::Type vertical )
296 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
297   mHorizontal( horizontal ),
298   mVertical( vertical ),
299   mScaling( Toolkit::Alignment::SCALE_NONE ),
300   mPadding( 0.f, 0.f, 0.f, 0.f )
301 {
302   DevelControl::SetAccessibilityConstructor( Self(), []( Dali::Actor actor ) {
303     return std::unique_ptr< Dali::Accessibility::Accessible >(
304       new Control::Impl::AccessibleImpl( actor, Dali::Accessibility::Role::FILLER ) );
305   } );
306 }
307
308 Alignment::~Alignment()
309 {
310 }
311
312 } // namespace Internal
313
314 } // namespace Toolkit
315
316 } // namespace Dali