Turned off Layout logging by default
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / hbox-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/hbox-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/public-api/actors/actor.h>
25 #include <dali-toolkit/devel-api/layouting/layout-item.h>
26 #include <dali-toolkit/public-api/controls/control-impl.h>
27 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
28
29 namespace
30 {
31 #if defined(DEBUG_ENABLED)
32 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
33 #endif
34 }
35
36 namespace Dali
37 {
38 namespace Toolkit
39 {
40 namespace Internal
41 {
42
43 HboxLayoutPtr HboxLayout::New()
44 {
45   HboxLayoutPtr layout( new HboxLayout() );
46   return layout;
47 }
48
49 HboxLayout::HboxLayout()
50 : LayoutGroup(),
51   mCellPadding( 0, 0 ),
52   mTotalLength( 0 )
53 {
54 }
55
56 HboxLayout::~HboxLayout()
57 {
58 }
59
60 void HboxLayout::DoInitialize()
61 {
62 }
63
64 void HboxLayout::DoRegisterChildProperties( const std::string& containerType )
65 {
66   auto typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( containerType );
67   if( typeInfo )
68   {
69     Property::IndexContainer indices;
70     typeInfo.GetChildPropertyIndices( indices );
71
72     if( std::find( indices.Begin(), indices.End(), Toolkit::HboxLayout::ChildProperty::WEIGHT ) == indices.End() )
73     {
74       ChildPropertyRegistration( typeInfo.GetName(), "weight",
75                                  Toolkit::HboxLayout::ChildProperty::WEIGHT, Property::FLOAT );
76     }
77   }
78 }
79
80 void HboxLayout::OnChildAdd( LayoutItem& child )
81 {
82   auto owner = child.GetOwner();
83   owner.SetProperty( Toolkit::HboxLayout::ChildProperty::WEIGHT, 1.0f );
84 }
85
86 void HboxLayout::SetCellPadding( LayoutSize size )
87 {
88   mCellPadding = size;
89 }
90
91 LayoutSize HboxLayout::GetCellPadding()
92 {
93   return mCellPadding;
94 }
95
96 void HboxLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
97 {
98 #if defined(DEBUG_ENABLED)
99   auto actor = Actor::DownCast(GetOwner());
100
101   std::ostringstream oss;
102   oss << "HBoxLayout::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   DALI_LOG_INFO( gLogFilter, Debug::Concise, "HboxLayout::OnMeasure widthSize(%d) \n", widthMeasureSpec.GetSize());
112
113   auto widthMode = widthMeasureSpec.GetMode();
114   auto heightMode = heightMeasureSpec.GetMode();
115   bool isExactly = (widthMode == MeasureSpec::Mode::EXACTLY);
116   bool matchHeight = false;
117   bool allFillParent = true;
118   LayoutLength maxHeight = 0;
119   LayoutLength alternativeMaxHeight = 0;
120   struct
121   {
122     MeasuredSize::State widthState;
123     MeasuredSize::State heightState;
124   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
125
126   // measure children, and determine if further resolution is required
127   for( unsigned int i=0; i<GetChildCount(); ++i )
128   {
129     auto childLayout = GetChildAt( i );
130     if( childLayout )
131     {
132       auto childOwner = childLayout->GetOwner();
133       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
134
135       MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
136       auto childWidth = childLayout->GetMeasuredWidth();
137       auto childMargin = childLayout->GetMargin();
138
139       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "HboxLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
140
141       auto length = childWidth + LayoutLength::IntType(childMargin.start + childMargin.end);
142
143       auto cellPadding = i<GetChildCount()-1 ? mCellPadding.width: 0;
144
145       if( isExactly )
146       {
147         mTotalLength += length;
148       }
149       else
150       {
151         auto totalLength = mTotalLength;
152         mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
153       }
154
155       bool matchHeightLocally = false;
156       if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
157       {
158         // Will have to re-measure at least this child when we know exact height.
159         matchHeight = true;
160         matchHeightLocally = true;
161       }
162
163       auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
164       auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
165
166       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
167       {
168         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
169       }
170       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
171       {
172         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
173       }
174
175       maxHeight = std::max( maxHeight, childHeight );
176       allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
177       alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
178     }
179   }
180
181   Extents padding = GetPadding();
182   mTotalLength += padding.start + padding.end;
183   auto widthSize = mTotalLength;
184   widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
185   MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
186   widthSize = widthSizeAndState.GetSize();
187
188   if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
189   {
190     maxHeight = alternativeMaxHeight;
191   }
192   maxHeight += padding.top + padding.bottom;
193   maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
194
195   widthSizeAndState.SetState( childState.widthState );
196
197   SetMeasuredDimensions( widthSizeAndState,
198                          ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
199
200   if( matchHeight )
201   {
202     ForceUniformHeight( GetChildCount(), widthMeasureSpec );
203   }
204 }
205
206 void HboxLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
207 {
208   // Pretend that the linear layout has an exact size. This is the measured height of
209   // ourselves. The measured height should be the max height of the children, changed
210   // to accommodate the heightMeasureSpec from the parent
211   auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
212   for (int i = 0; i < count; ++i)
213   {
214     LayoutItemPtr childLayout = GetChildAt(i);
215     if( childLayout != nullptr )
216     {
217       auto childOwner = childLayout->GetOwner();
218       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
219       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
220
221       if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
222       {
223         // Temporarily force children to reuse their old measured width
224         int oldWidth = desiredWidth;
225         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
226
227         // Remeasure with new dimensions
228         MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
229
230         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
231       }
232     }
233   }
234 }
235
236 void HboxLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
237 {
238   auto owner = GetOwner();
239   auto actor = Actor::DownCast(owner);
240   bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
241
242   Extents padding = GetPadding();
243
244   LayoutLength childTop( 0 );
245   LayoutLength childLeft( padding.start );
246
247   // Where bottom of child should go
248   auto height = bottom - top;
249
250   // Space available for child
251   auto childSpace = height - padding.top - padding.bottom;
252
253   auto count = GetChildCount();
254
255   int start = 0;
256   int dir = 1;
257
258   // In case of RTL, start drawing from the last child.
259   // @todo re-work to draw the first child from the right edge, and move leftwards.
260   // (Should have an alignment also)
261   if( isLayoutRtl ) {
262     start = count - 1;
263     dir = -1;
264   }
265
266   for( unsigned int i = 0; i < count; i++)
267   {
268     int childIndex = start + dir * i;
269     LayoutItemPtr childLayout = GetChildAt( childIndex );
270     if( childLayout != nullptr )
271     {
272       auto childWidth = childLayout->GetMeasuredWidth();
273       auto childHeight = childLayout->GetMeasuredHeight();
274
275       auto childMargin = childLayout->GetMargin();
276
277       childTop = LayoutLength(padding.top) + ((childSpace - childHeight) / 2) + childMargin.top - childMargin.bottom;
278
279       childLeft += childMargin.start;
280       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
281       childLeft += childWidth + childMargin.end + mCellPadding.width;
282     }
283   }
284 }
285
286 } // namespace Internal
287 } // namespace Toolkit
288 } // namespace Dali