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