Merge "DALi Version 1.3.26" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / vbox-layout-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-toolkit/internal/layouting/vbox-layout-impl.h>
19
20 //EXTERNAL HEADERS
21 //INTERNAL HEADERS
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/common/extents.h>
24 #include <dali/devel-api/actors/actor-devel.h>
25 #include <dali/devel-api/object/handle-devel.h>
26 #include <dali-toolkit/devel-api/layouting/layout-item.h>
27 #include <dali-toolkit/public-api/controls/control-impl.h>
28 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
29
30
31 #if defined(DEBUG_ENABLED)
32 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::Concise, false, "LOG_LAYOUT" );
33 #endif
34
35 namespace Dali
36 {
37 namespace Toolkit
38 {
39 namespace Internal
40 {
41
42 VboxLayoutPtr VboxLayout::New()
43 {
44   VboxLayoutPtr layout( new VboxLayout() );
45   return layout;
46 }
47
48 VboxLayout::VboxLayout()
49 : LayoutGroup(),
50   mCellPadding( 0, 0 ),
51   mTotalLength( 0 )
52 {
53 }
54
55 VboxLayout::~VboxLayout()
56 {
57 }
58
59 void VboxLayout::DoInitialize()
60 {
61 }
62
63 void VboxLayout::DoRegisterChildProperties( const std::string& containerType )
64 {
65   auto typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( containerType );
66   if( typeInfo )
67   {
68     Property::IndexContainer indices;
69     typeInfo.GetChildPropertyIndices( indices );
70
71     if( std::find( indices.Begin(), indices.End(), Toolkit::VboxLayout::ChildProperty::WEIGHT ) ==
72         indices.End() )
73     {
74       ChildPropertyRegistration( typeInfo.GetName(), "weight", Toolkit::VboxLayout::ChildProperty::WEIGHT, Property::FLOAT );
75     }
76   }
77 }
78
79 void VboxLayout::OnChildAdd( LayoutItem& child )
80 {
81   auto owner = child.GetOwner();
82   owner.SetProperty( Toolkit::VboxLayout::ChildProperty::WEIGHT, 1.0f );
83 }
84
85 void VboxLayout::SetCellPadding( LayoutSize size )
86 {
87   mCellPadding = size;
88 }
89
90 LayoutSize VboxLayout::GetCellPadding()
91 {
92   return mCellPadding;
93 }
94
95
96 void VboxLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
97 {
98 #if defined(DEBUG_ENABLED)
99   auto actor = Actor::DownCast(GetOwner());
100
101   std::ostringstream oss;
102   oss << "VboxLayout::OnMeasure  ";
103   if( actor )
104   {
105     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
106   }
107   oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
108   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
109 #endif
110
111   auto widthMode = widthMeasureSpec.GetMode();
112
113   bool matchWidth = false;
114   bool allFillParent = true;
115   LayoutLength maxWidth = 0;
116   LayoutLength alternativeMaxWidth = 0;
117
118   struct
119   {
120     MeasuredSize::State widthState;
121     MeasuredSize::State heightState;
122   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
123
124   // measure children, and determine if further resolution is required
125   for( unsigned int i=0; i<GetChildCount(); ++i )
126   {
127     auto childLayout = GetChildAt( i );
128     if( childLayout )
129     {
130       auto childOwner = childLayout->GetOwner();
131       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
132
133       MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, heightMeasureSpec, 0 );
134       auto childHeight = childLayout->GetMeasuredHeight();
135       auto childMargin = childOwner.GetProperty<Extents>( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION );
136       auto length = childHeight + LayoutLength::IntType(childMargin.top + childMargin.bottom );
137
138       auto cellPadding = i<GetChildCount()-1 ? mCellPadding.height : 0;
139       auto totalLength = mTotalLength;
140       mTotalLength = std::max( totalLength, totalLength + length + cellPadding);
141
142       bool matchWidthLocally = false;
143       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
144       {
145         // Will have to re-measure at least this child when we know exact height.
146         matchWidth = true;
147         matchWidthLocally = true;
148       }
149
150       auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
151       auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
152
153       // was combineMeasuredStates()
154       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
155       {
156         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
157       }
158       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
159       {
160         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
161       }
162
163       maxWidth = std::max( maxWidth, childWidth );
164       allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
165       alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
166     }
167   }
168
169   Extents padding = GetPadding();
170   mTotalLength += padding.top + padding.bottom;
171   auto heightSize = mTotalLength;
172   heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
173   MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
174   heightSize = heightSizeAndState.GetSize();
175
176   if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
177   {
178     maxWidth = alternativeMaxWidth;
179   }
180   maxWidth += padding.start + padding.end;
181   maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
182
183   heightSizeAndState.SetState( childState.heightState );
184
185   SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
186                          heightSizeAndState );
187
188   if( matchWidth )
189   {
190     ForceUniformWidth( GetChildCount(), heightMeasureSpec );
191   }
192 }
193
194 void VboxLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
195 {
196   // Pretend that the linear layout has an exact size. This is the measured height of
197   // ourselves. The measured height should be the max height of the children, changed
198   // to accommodate the heightMeasureSpec from the parent
199   auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
200   for (int i = 0; i < count; ++i)
201   {
202     LayoutItemPtr childLayout = GetChildAt(i);
203     if( childLayout != nullptr )
204     {
205       auto childOwner = childLayout->GetOwner();
206       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
207       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
208
209       if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
210       {
211         // Temporarily force children to reuse their old measured height
212         int oldHeight = desiredHeight;
213         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
214
215         // Remeasure with new dimensions
216         MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
217
218         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
219       }
220     }
221   }
222 }
223
224 void VboxLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
225 {
226   auto owner = GetOwner();
227   Extents padding = GetPadding();
228
229   LayoutLength childTop( 0 );
230   LayoutLength childLeft( padding.start );
231
232   // Where bottom of child should go
233   auto width = right - left;
234
235   // Space available for child
236   auto childSpace = width - padding.start - padding.end;
237   auto count = GetChildCount();
238
239   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
240   {
241     LayoutItemPtr childLayout = GetChildAt( childIndex );
242     if( childLayout != nullptr )
243     {
244       auto childWidth = childLayout->GetMeasuredWidth();
245       auto childHeight = childLayout->GetMeasuredHeight();
246
247       auto childOwner = childLayout->GetOwner();
248       auto childMargin = childOwner.GetProperty<Extents>( Toolkit::LayoutGroup::ChildProperty::MARGIN_SPECIFICATION );
249
250       childTop += childMargin.top;
251       childLeft = ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
252
253       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
254       childTop += childHeight + childMargin.bottom + mCellPadding.height;
255     }
256   }
257 }
258
259 } // namespace Internal
260 } // namespace Toolkit
261 } // namespace Dali