Rename LayoutBase class to LayoutItem.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / layouting / layout-group-impl.cpp
1 /*
2  * Copyright (c) 2018 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 // CLASS HEADER
18 #include <dali/public-api/object/type-registry-helper.h>
19 #include <dali/devel-api/actors/actor-devel.h>
20 #include <dali/devel-api/object/handle-devel.h>
21 #include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
22 #include <dali-toolkit/internal/layouting/layout-group-data-impl.h>
23 #include <dali-toolkit/public-api/controls/control-impl.h>
24 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
25
26
27 namespace
28 {
29 const char* MARGIN_SPECIFICATION_NAME( "marginSpec" );
30 }
31
32 namespace Dali
33 {
34 namespace Toolkit
35 {
36 namespace Internal
37 {
38
39 LayoutGroup::LayoutGroup()
40 : mImpl( new LayoutGroup::Impl() ),
41   mSlotDelegate(this)
42 {
43 }
44
45 LayoutGroup::~LayoutGroup()
46 {
47   // An object with a unique_ptr to an opaque structure must define it's destructor in the translation unit
48   // where the opaque structure is defined. It cannot use the default method in the header file.
49 }
50
51 Toolkit::LayoutGroup::LayoutId LayoutGroup::Add( LayoutItem& child )
52 {
53   LayoutParent* oldParent = child.GetParent();
54   if( oldParent )
55   {
56     LayoutGroupPtr parentGroup( dynamic_cast< LayoutGroup* >( oldParent ) );
57     if( parentGroup )
58     {
59       parentGroup->Remove( child );
60     }
61   }
62
63   Impl::ChildLayout childLayout;
64   childLayout.layoutId = mImpl->mNextLayoutId++;
65   childLayout.child = &child;
66   mImpl->mChildren.emplace_back( childLayout );
67
68   auto owner = child.GetOwner();
69
70   // If the owner does not have any LayoutItem child properties, add them
71   if( ! DevelHandle::DoesCustomPropertyExist( owner, Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) )
72   {
73     // Set default properties for LayoutGroup and LayoutItem.
74     // Deriving classes can override OnChildAdd() to add their own default properties
75     GenerateDefaultChildPropertyValues( owner );
76   }
77
78   // Inform deriving classes that this child has been added
79   OnChildAdd( *childLayout.child.Get() );
80
81   // Now listen to future changes to the child properties.
82   DevelHandle::PropertySetSignal(owner).Connect( this, &LayoutGroup::OnSetChildProperties );
83
84   RequestLayout();
85
86   return childLayout.layoutId;
87 }
88
89 void LayoutGroup::Remove( Toolkit::LayoutGroup::LayoutId childId )
90 {
91   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
92   {
93     if( iter->layoutId == childId )
94     {
95       OnChildRemove( *iter->child.Get() );
96       mImpl->mChildren.erase(iter);
97       break;
98     }
99   }
100   RequestLayout();
101 }
102
103 void LayoutGroup::Remove( LayoutItem& child )
104 {
105   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
106   {
107     if( iter->child.Get() == &child )
108     {
109       OnChildRemove( *iter->child.Get() );
110       mImpl->mChildren.erase(iter);
111       break;
112     }
113   }
114   RequestLayout();
115 }
116
117 void LayoutGroup::RemoveAll()
118 {
119   for( auto iter = mImpl->mChildren.begin() ; iter != mImpl->mChildren.end() ; ++iter )
120   {
121     OnChildRemove( *iter->child.Get() );
122     iter = mImpl->mChildren.erase(iter);
123   }
124 }
125
126 unsigned int LayoutGroup::GetChildCount() const
127 {
128   return mImpl->mChildren.size();
129 }
130
131 LayoutItemPtr LayoutGroup::GetChildAt( unsigned int index ) const
132 {
133   DALI_ASSERT_ALWAYS( index < mImpl->mChildren.size() );
134   return mImpl->mChildren[ index ].child;
135 }
136
137 LayoutItemPtr LayoutGroup::GetChild( Toolkit::LayoutGroup::LayoutId childId ) const
138 {
139   for( auto&& childLayout : mImpl->mChildren )
140   {
141     if( childLayout.layoutId == childId )
142     {
143       return childLayout.child;
144     }
145   }
146   return NULL;
147 }
148
149 Toolkit::LayoutGroup::LayoutId LayoutGroup::GetChildId( LayoutItem& child ) const
150 {
151   for( auto&& childLayout : mImpl->mChildren )
152   {
153     if( childLayout.child.Get() == &child )
154     {
155       return childLayout.layoutId;
156     }
157   }
158   return Toolkit::LayoutGroup::UNKNOWN_ID;
159 }
160
161 void LayoutGroup::OnChildAdd( LayoutItem& child )
162 {
163 }
164
165 void LayoutGroup::OnChildRemove( LayoutItem& child )
166 {
167 }
168
169 void LayoutGroup::DoInitialize()
170 {
171 }
172
173 void LayoutGroup::DoRegisterChildProperties( const std::string& containerType )
174 {
175 }
176
177 void LayoutGroup::OnSetChildProperties( Handle& handle, Property::Index index, Property::Value value )
178 {
179   if ( ( index >= CHILD_PROPERTY_REGISTRATION_START_INDEX ) && ( index <= CHILD_PROPERTY_REGISTRATION_MAX_INDEX ) )
180   {
181     // If any child properties are set, must perform relayout
182     RequestLayout();
183   }
184 }
185
186 void LayoutGroup::GenerateDefaultChildPropertyValues( Handle child )
187 {
188   child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION,
189                      Toolkit::ChildLayoutData::WRAP_CONTENT );
190   child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION,
191                      Toolkit::ChildLayoutData::WRAP_CONTENT );
192   child.SetProperty( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Extents() );
193 }
194
195 void LayoutGroup::MeasureChildren( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
196 {
197   for( auto&& child : mImpl->mChildren )
198   {
199     //if( (child.mViewFlags & Impl::VISIBILITY_MASK) != Impl::GONE ) // Use owner visibility/enabled/ready
200     {
201       MeasureChild( child.child, widthMeasureSpec, heightMeasureSpec );
202     }
203   }
204 }
205
206 void LayoutGroup::MeasureChild( LayoutItemPtr child,
207                                 MeasureSpec parentWidthMeasureSpec,
208                                 MeasureSpec parentHeightMeasureSpec )
209 {
210   auto childOwner = child->GetOwner();
211   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
212   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
213
214   auto padding = GetPadding();
215
216   const MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
217                                                                  padding.start + padding.end,
218                                                                  desiredWidth);
219   const MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
220                                                                   padding.top + padding.bottom,
221                                                                   desiredHeight);
222
223   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
224 }
225
226 void LayoutGroup::MeasureChildWithMargins( LayoutItemPtr child,
227                                            MeasureSpec parentWidthMeasureSpec, LayoutLength widthUsed,
228                                            MeasureSpec parentHeightMeasureSpec, LayoutLength heightUsed)
229 {
230   auto childOwner = child->GetOwner();
231   auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
232   auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
233   auto desiredMargin = childOwner.GetProperty<Extents>( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION );
234   auto padding = GetPadding();
235
236   MeasureSpec childWidthMeasureSpec = GetChildMeasureSpec( parentWidthMeasureSpec,
237                                                            padding.start + padding.end +
238                                                            desiredMargin.start + desiredMargin.end +
239                                                            widthUsed, desiredWidth );
240
241   MeasureSpec childHeightMeasureSpec = GetChildMeasureSpec( parentHeightMeasureSpec,
242                                                             padding.top + padding.bottom +
243                                                             desiredMargin.top + desiredMargin.end +
244                                                             heightUsed, desiredHeight );
245
246   child->Measure( childWidthMeasureSpec, childHeightMeasureSpec );
247 }
248
249
250 MeasureSpec LayoutGroup::GetChildMeasureSpec(
251   MeasureSpec  measureSpec,
252   LayoutLength padding,
253   LayoutLength childDimension )
254 {
255   auto specMode = measureSpec.GetMode();
256   LayoutLength specSize = measureSpec.GetSize();
257
258   auto size = std::max( LayoutLength(0), specSize - padding );
259
260   MeasureSpec::IntType resultSize = 0;
261   MeasureSpec::Mode resultMode = MeasureSpec::Mode::UNSPECIFIED;
262
263   switch( specMode )
264   {
265     // Parent has imposed an exact size on us
266     case MeasureSpec::Mode::EXACTLY:
267     {
268
269       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
270       {
271         // Child wants to be our size. So be it.
272         resultSize = size;
273         resultMode = MeasureSpec::Mode::EXACTLY;
274       }
275       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
276       {
277         // Child wants to determine its own size. It can't be
278         // bigger than us.
279         resultSize = size;
280         resultMode = MeasureSpec::Mode::AT_MOST;
281       }
282       else
283       {
284         resultSize = childDimension;
285         resultMode = MeasureSpec::Mode::EXACTLY;
286       }
287
288       break;
289     }
290
291       // Parent has imposed a maximum size on us
292     case MeasureSpec::Mode::AT_MOST:
293     {
294       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
295       {
296         // Child wants to be our size, but our size is not fixed.
297         // Constrain child to not be bigger than us.
298         resultSize = size;
299         resultMode = MeasureSpec::Mode::AT_MOST;
300       }
301       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
302       {
303         // Child wants to determine its own size. It can't be
304         // bigger than us.
305         resultSize = size;
306         resultMode = MeasureSpec::Mode::AT_MOST;
307       }
308       else
309       {
310         // Child wants a specific size... so be it
311         resultSize = childDimension;
312         resultMode = MeasureSpec::Mode::EXACTLY;
313       }
314
315       break;
316     }
317
318       // Parent asked to see how big we want to be
319     case MeasureSpec::Mode::UNSPECIFIED:
320     {
321       if (childDimension == Toolkit::ChildLayoutData::MATCH_PARENT)
322       {
323         // Child wants to be our size... find out how big it should be
324         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
325         resultMode = MeasureSpec::Mode::UNSPECIFIED;
326       }
327       else if (childDimension == Toolkit::ChildLayoutData::WRAP_CONTENT)
328       {
329         // Child wants to determine its own size.... find out how big
330         // it should be
331         resultSize = LayoutItem::Impl::sUseZeroUnspecifiedMeasureSpec ? LayoutLength(0) : size;
332         resultMode = MeasureSpec::Mode::UNSPECIFIED;
333       }
334       else
335       {
336         // Child wants a specific size... let him have it
337         resultSize = childDimension;
338         resultMode = MeasureSpec::Mode::EXACTLY;
339       }
340       break;
341     }
342   }
343
344   //noinspection ResourceType
345   return MeasureSpec( resultSize, resultMode );
346 }
347
348
349 void LayoutGroup::OnInitialize()
350 {
351   auto control = Toolkit::Control::DownCast( GetOwner() );
352
353   if( control )
354   {
355     // Take ownership of existing children
356     for( unsigned int childIndex = 0 ; childIndex < control.GetChildCount(); ++childIndex )
357     {
358       ChildAddedToOwner( control.GetChildAt( childIndex ) );
359     }
360
361     DevelActor::ChildAddedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildAddedToOwner );
362     DevelActor::ChildRemovedSignal( control ).Connect( mSlotDelegate, &LayoutGroup::ChildRemovedFromOwner );
363     DevelHandle::PropertySetSignal( control ).Connect( mSlotDelegate, &LayoutGroup::OnOwnerPropertySet );
364   }
365 }
366
367 void LayoutGroup::OnRegisterChildProperties( const std::string& containerType )
368 {
369   auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
370   if( typeInfo )
371   {
372     Property::IndexContainer indices;
373     typeInfo.GetChildPropertyIndices( indices );
374
375     if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION ) ==
376         indices.End() )
377     {
378       ChildPropertyRegistration( typeInfo.GetName(), MARGIN_SPECIFICATION_NAME, Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Property::EXTENTS );
379     }
380   }
381
382   DoRegisterChildProperties( containerType );
383 }
384
385 void LayoutGroup::OnUnparent()
386 {
387   RemoveAll();
388 }
389
390 void LayoutGroup::ChildAddedToOwner( Actor child )
391 {
392   LayoutItemPtr childLayout;
393   Toolkit::Control control = Toolkit::Control::DownCast( child );
394
395   if( control ) // Can only support adding Controls, not Actors to layout
396   {
397     Internal::Control& childControlImpl = GetImplementation( control );
398     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
399     childLayout = childControlDataImpl.GetLayout();
400
401     if( ! childLayout )
402     {
403       // If the child doesn't already have a layout, then create a LayoutItem for it.
404       childLayout = LayoutItem::New( control );
405       childLayout->SetAnimateLayout( IsLayoutAnimated() ); // @todo this essentially forces animation inheritance. Bad?
406
407       auto desiredSize = control.GetNaturalSize();
408       childControlDataImpl.SetLayout( *childLayout.Get() );
409
410       // HBoxLayout will apply default layout data for this object
411       child.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, LayoutLength::IntType( desiredSize.width ) );
412       child.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, LayoutLength::IntType( desiredSize.height ) );
413       child.SetProperty( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION, Extents() );
414     }
415
416     Add( *childLayout.Get() );
417   }
418 }
419
420 void LayoutGroup::ChildRemovedFromOwner( Actor child )
421 {
422   Toolkit::Control control = Toolkit::Control::DownCast( child );
423   if( control )
424   {
425     Internal::Control& childControlImpl = GetImplementation( control );
426     Internal::Control::Impl& childControlDataImpl = Internal::Control::Impl::Get( childControlImpl );
427     auto childLayout = childControlDataImpl.GetLayout();
428     if( childLayout )
429     {
430       Remove( *childLayout.Get() );
431     }
432   }
433 }
434
435 void LayoutGroup::OnOwnerPropertySet( Handle& handle, Property::Index index, Property::Value value )
436 {
437   auto actor = Actor::DownCast( handle );
438   if( actor && index == Actor::Property::LAYOUT_DIRECTION )
439   {
440     RequestLayout();
441   }
442 }
443
444
445 } // namespace Internal
446 } // namespace Toolkit
447 } // namespace Dali