[Tizen] Add codes for Dali Windows Backend
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / layouting / linear-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 "linear-layout-impl.h"
19
20 //PUBLIC INCLUDES
21 #include <dali/integration-api/debug.h>
22 #include <dali/public-api/common/extents.h>
23 #include <dali/public-api/actors/actor.h>
24
25 //INTERNAL INCLUDES
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 namespace
31 {
32 #if defined(DEBUG_ENABLED)
33 static Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
34 #endif
35 }
36
37 namespace Dali
38 {
39 namespace Toolkit
40 {
41 namespace Internal
42 {
43
44 const int HORIZONTAL_ALIGNMENT_MASK  = ( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL | Dali::Toolkit::LinearLayout::Alignment::END );
45 const int VERTICAL_ALIGNMENT_MASK = ( Dali::Toolkit::LinearLayout::Alignment::TOP | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL | Dali::Toolkit::LinearLayout::Alignment::BOTTOM );
46
47 LinearLayoutPtr LinearLayout::New()
48 {
49   LinearLayoutPtr layout( new LinearLayout() );
50   return layout;
51 }
52
53 LinearLayout::LinearLayout()
54 : LayoutGroup(),
55   mCellPadding( 0, 0 ),
56   mOrientation( Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL ),
57   mAlignment( Dali::Toolkit::LinearLayout::Alignment::BEGIN | Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL ),
58   mTotalLength( 0 )
59 {
60 }
61
62 LinearLayout::~LinearLayout()
63 {
64 }
65
66 void LinearLayout::SetCellPadding( LayoutSize size )
67 {
68   if ( mCellPadding != size )
69   {
70     mCellPadding = size;
71     RequestLayout();
72   }
73 }
74
75 LayoutSize LinearLayout::GetCellPadding() const
76 {
77   return mCellPadding;
78 }
79
80 void LinearLayout::SetOrientation( Dali::Toolkit::LinearLayout::Orientation orientation )
81 {
82   if ( mOrientation != orientation )
83   {
84     mOrientation = orientation;
85     RequestLayout();
86   }
87 }
88
89 Dali::Toolkit::LinearLayout::Orientation LinearLayout::GetOrientation() const
90 {
91   return mOrientation;
92 }
93
94 void LinearLayout::SetAlignment( unsigned int alignment )
95 {
96   if ( mAlignment != alignment )
97   {
98     mAlignment = alignment;
99     RequestLayout();
100   }
101 }
102
103 unsigned int LinearLayout::GetAlignment() const
104 {
105   return mAlignment;
106 }
107
108 void LinearLayout::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
109 {
110 #if defined(DEBUG_ENABLED)
111   auto actor = Actor::DownCast(GetOwner());
112
113   std::ostringstream oss;
114   oss << "LinearLayout::OnMeasure  ";
115   if( actor )
116   {
117     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
118   }
119   oss << "widthMeasureSpec:" << widthMeasureSpec << " heightMeasureSpec:" << heightMeasureSpec << std::endl;
120   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
121 #endif
122
123   if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
124   {
125     MeasureHorizontal( widthMeasureSpec, heightMeasureSpec );
126   }
127   else
128   {
129     MeasureVertical( widthMeasureSpec, heightMeasureSpec );
130   }
131 }
132
133 void LinearLayout::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
134 {
135 #if defined(DEBUG_ENABLED)
136   auto actor = Actor::DownCast(GetOwner());
137
138   std::ostringstream oss;
139   oss << "LinearLayout::OnLayout  ";
140   if( actor )
141   {
142     oss << "Actor Id:" << actor.GetId() << " Name:" << actor.GetName() << "  ";
143   }
144   oss << "left:" << left << " top:" << top << " right:" << right << " bottom:" << bottom << std::endl;
145   DALI_LOG_INFO( gLogFilter, Debug::Concise, oss.str().c_str() );
146 #endif
147
148   if( mOrientation == Dali::Toolkit::LinearLayout::Orientation::HORIZONTAL )
149   {
150     LayoutHorizontal( left, top, right, bottom );
151   }
152   else
153   {
154     LayoutVertical( left, top, right, bottom );
155   }
156 }
157
158 void LinearLayout::MeasureHorizontal( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
159 {
160   auto widthMode = widthMeasureSpec.GetMode();
161   auto heightMode = heightMeasureSpec.GetMode();
162   bool isExactly = (widthMode == MeasureSpec::Mode::EXACTLY);
163   bool matchHeight = false;
164   bool allFillParent = true;
165   LayoutLength maxHeight = 0;
166   LayoutLength alternativeMaxHeight = 0;
167   struct
168   {
169     MeasuredSize::State widthState;
170     MeasuredSize::State heightState;
171   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
172
173   // Reset total length
174   mTotalLength = 0;
175
176   // measure children, and determine if further resolution is required
177   for( unsigned int i=0; i<GetChildCount(); ++i )
178   {
179     auto childLayout = GetChildAt( i );
180     if( childLayout )
181     {
182       auto childOwner = childLayout->GetOwner();
183       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
184
185       MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
186       auto childWidth = childLayout->GetMeasuredWidth();
187       auto childMargin = childLayout->GetMargin();
188
189       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "LinearLayout::OnMeasure childWidth(%d)\n", MeasureSpec::IntType( childWidth ) );
190
191       auto length = childWidth + LayoutLength::IntType(childMargin.start + childMargin.end);
192
193       auto cellPadding = i<GetChildCount()-1 ? mCellPadding.width: 0;
194
195       if( isExactly )
196       {
197         mTotalLength += length;
198       }
199       else
200       {
201         auto totalLength = mTotalLength;
202         mTotalLength = std::max( totalLength, totalLength + length + cellPadding );
203       }
204
205       bool matchHeightLocally = false;
206       if( heightMode != MeasureSpec::Mode::EXACTLY && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
207       {
208         // Will have to re-measure at least this child when we know exact height.
209         matchHeight = true;
210         matchHeightLocally = true;
211       }
212
213       auto marginHeight = LayoutLength( childMargin.top + childMargin.bottom );
214       auto childHeight = childLayout->GetMeasuredHeight() + marginHeight;
215
216       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
217       {
218         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
219       }
220       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
221       {
222         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
223       }
224
225       maxHeight = std::max( maxHeight, childHeight );
226       allFillParent = ( allFillParent && desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT );
227       alternativeMaxHeight = std::max( alternativeMaxHeight, matchHeightLocally ? marginHeight : childHeight );
228     }
229   }
230
231   Extents padding = GetPadding();
232   mTotalLength += padding.start + padding.end;
233   auto widthSize = mTotalLength;
234   widthSize = std::max( widthSize, GetSuggestedMinimumWidth() );
235   MeasuredSize widthSizeAndState = ResolveSizeAndState( widthSize, widthMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
236   widthSize = widthSizeAndState.GetSize();
237
238   if( !allFillParent && heightMode != MeasureSpec::Mode::EXACTLY )
239   {
240     maxHeight = alternativeMaxHeight;
241   }
242   maxHeight += padding.top + padding.bottom;
243   maxHeight = std::max( maxHeight, GetSuggestedMinimumHeight() );
244
245   widthSizeAndState.SetState( childState.widthState );
246
247   SetMeasuredDimensions( widthSizeAndState,
248                          ResolveSizeAndState( maxHeight, heightMeasureSpec, childState.heightState ) );
249
250   if( matchHeight )
251   {
252     ForceUniformHeight( GetChildCount(), widthMeasureSpec );
253   }
254 }
255
256 void LinearLayout::ForceUniformHeight( int count, MeasureSpec widthMeasureSpec )
257 {
258   // Pretend that the linear layout has an exact size. This is the measured height of
259   // ourselves. The measured height should be the max height of the children, changed
260   // to accommodate the heightMeasureSpec from the parent
261   auto uniformMeasureSpec = MeasureSpec( GetMeasuredHeight(), MeasureSpec::Mode::EXACTLY );
262   for (int i = 0; i < count; ++i)
263   {
264     LayoutItemPtr childLayout = GetChildAt(i);
265     if( childLayout != nullptr )
266     {
267       auto childOwner = childLayout->GetOwner();
268       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
269       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
270
271       if( desiredHeight == Toolkit::ChildLayoutData::MATCH_PARENT )
272       {
273         // Temporarily force children to reuse their old measured width
274         int oldWidth = desiredWidth;
275         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, childLayout->GetMeasuredWidth().mValue );
276
277         // Remeasure with new dimensions
278         MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, uniformMeasureSpec, 0);
279
280         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, oldWidth );
281       }
282     }
283   }
284 }
285
286 void LinearLayout::LayoutHorizontal( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
287 {
288   auto owner = GetOwner();
289   auto actor = Actor::DownCast(owner);
290   bool isLayoutRtl = actor ? actor.GetProperty<bool>( Actor::Property::LAYOUT_DIRECTION ) : false;
291
292   Extents padding = GetPadding();
293
294   LayoutLength childTop( padding.top );
295   LayoutLength childLeft( padding.start );
296
297   // Where bottom of child should go
298   auto height = bottom - top;
299
300   // Space available for child
301   auto childSpace = height - (int)padding.top - (int)padding.bottom;
302
303   auto count = GetChildCount();
304
305   switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
306   {
307     case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
308     default:
309     {
310       // mTotalLength contains the padding already
311       // In case of RTL map BEGIN alignment to the right edge
312       if ( isLayoutRtl ) {
313         childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
314       }
315       else {
316         childLeft = LayoutLength( padding.start );
317       }
318       break;
319     }
320     case Dali::Toolkit::LinearLayout::Alignment::END:
321     {
322       // mTotalLength contains the padding already
323       // In case of RTL map END alignment to the left edge
324       if ( isLayoutRtl ) {
325         childLeft = LayoutLength( padding.start );
326       }
327       else {
328         childLeft = LayoutLength( padding.start ) + right - left - mTotalLength;
329       }
330       break;
331     }
332     case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
333     {
334       // mTotalLength contains the padding already
335       childLeft = LayoutLength( padding.start ) + ( right - left - mTotalLength ) / 2;
336       break;
337     }
338   }
339
340   int start = 0;
341   int dir = 1;
342
343   // In case of RTL, start drawing from the last child.
344   if( isLayoutRtl ) {
345     start = count - 1;
346     dir = -1;
347   }
348
349   for( unsigned int i = 0; i < count; i++)
350   {
351     int childIndex = start + dir * i;
352     LayoutItemPtr childLayout = GetChildAt( childIndex );
353     if( childLayout != nullptr )
354     {
355       auto childWidth = childLayout->GetMeasuredWidth();
356       auto childHeight = childLayout->GetMeasuredHeight();
357       auto childMargin = childLayout->GetMargin();
358
359       switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
360       {
361         case Dali::Toolkit::LinearLayout::Alignment::TOP:
362         {
363           childTop = LayoutLength( padding.top ) + (int)childMargin.top;
364           break;
365         }
366         case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
367         {
368           childTop = height - (int)padding.bottom - childHeight - (int)childMargin.bottom;
369           break;
370         }
371         case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
372         default:
373         {
374           childTop = LayoutLength( padding.top ) + (int)( ( childSpace - childHeight ) / 2 ) + (int)childMargin.top - (int)childMargin.bottom;
375           break;
376         }
377       }
378
379       childLeft += childMargin.start;
380       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
381       childLeft += childWidth + (int)childMargin.end + mCellPadding.width;
382     }
383   }
384 }
385
386 void LinearLayout::MeasureVertical( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
387 {
388   auto widthMode = widthMeasureSpec.GetMode();
389
390   bool matchWidth = false;
391   bool allFillParent = true;
392   LayoutLength maxWidth = 0;
393   LayoutLength alternativeMaxWidth = 0;
394
395   struct
396   {
397     MeasuredSize::State widthState;
398     MeasuredSize::State heightState;
399   } childState = { MeasuredSize::State::MEASURED_SIZE_OK, MeasuredSize::State::MEASURED_SIZE_OK };
400
401   // Reset total length
402   mTotalLength = 0;
403
404   // measure children, and determine if further resolution is required
405   for( unsigned int i=0; i<GetChildCount(); ++i )
406   {
407     auto childLayout = GetChildAt( i );
408     if( childLayout )
409     {
410       auto childOwner = childLayout->GetOwner();
411       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
412
413       MeasureChildWithMargins( childLayout, widthMeasureSpec, 0, heightMeasureSpec, 0 );
414       auto childHeight = childLayout->GetMeasuredHeight();
415       auto childMargin = childLayout->GetMargin();
416       auto length = childHeight + LayoutLength::IntType(childMargin.top + childMargin.bottom );
417
418       auto cellPadding = i<GetChildCount()-1 ? mCellPadding.height : 0;
419       auto totalLength = mTotalLength;
420       mTotalLength = std::max( totalLength, totalLength + length + cellPadding);
421
422       bool matchWidthLocally = false;
423       if( widthMode != MeasureSpec::Mode::EXACTLY && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
424       {
425         // Will have to re-measure at least this child when we know exact height.
426         matchWidth = true;
427         matchWidthLocally = true;
428       }
429
430       auto marginWidth = LayoutLength( childMargin.start + childMargin.end );
431       auto childWidth = childLayout->GetMeasuredWidth() + marginWidth;
432
433       // was combineMeasuredStates()
434       if( childLayout->GetMeasuredWidthAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
435       {
436         childState.widthState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
437       }
438       if( childLayout->GetMeasuredHeightAndState().GetState() == MeasuredSize::State::MEASURED_SIZE_TOO_SMALL )
439       {
440         childState.heightState = MeasuredSize::State::MEASURED_SIZE_TOO_SMALL;
441       }
442
443       maxWidth = std::max( maxWidth, childWidth );
444       allFillParent = ( allFillParent && desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT );
445       alternativeMaxWidth = std::max( alternativeMaxWidth, matchWidthLocally ? marginWidth : childWidth );
446     }
447   }
448   Extents padding = GetPadding();
449   mTotalLength += padding.top + padding.bottom;
450   auto heightSize = mTotalLength;
451   heightSize = std::max( heightSize, GetSuggestedMinimumHeight() );
452   MeasuredSize heightSizeAndState = ResolveSizeAndState( heightSize, heightMeasureSpec, MeasuredSize::State::MEASURED_SIZE_OK);
453   heightSize = heightSizeAndState.GetSize();
454
455   if( !allFillParent && widthMode != MeasureSpec::Mode::EXACTLY )
456   {
457     maxWidth = alternativeMaxWidth;
458   }
459   maxWidth += padding.start + padding.end;
460   maxWidth = std::max( maxWidth, GetSuggestedMinimumWidth() );
461
462   heightSizeAndState.SetState( childState.heightState );
463
464   SetMeasuredDimensions( ResolveSizeAndState( maxWidth, widthMeasureSpec, childState.widthState ),
465                          heightSizeAndState );
466
467   if( matchWidth )
468   {
469     ForceUniformWidth( GetChildCount(), heightMeasureSpec );
470   }
471 }
472
473 void LinearLayout::ForceUniformWidth( int count, MeasureSpec heightMeasureSpec )
474 {
475   // Pretend that the linear layout has an exact size.
476   auto uniformMeasureSpec = MeasureSpec( GetMeasuredWidth(), MeasureSpec::Mode::EXACTLY );
477   for (int i = 0; i < count; ++i)
478   {
479     LayoutItemPtr childLayout = GetChildAt(i);
480     if( childLayout != nullptr )
481     {
482       auto childOwner = childLayout->GetOwner();
483       auto desiredWidth = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION );
484       auto desiredHeight = childOwner.GetProperty<int>( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION );
485
486       if( desiredWidth == Toolkit::ChildLayoutData::MATCH_PARENT )
487       {
488         // Temporarily force children to reuse their old measured height
489         int oldHeight = desiredHeight;
490         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, childLayout->GetMeasuredHeight().mValue );
491
492         // Remeasure with new dimensions
493         MeasureChildWithMargins( childLayout, uniformMeasureSpec, 0, heightMeasureSpec, 0 );
494
495         childOwner.SetProperty( Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, oldHeight );
496       }
497     }
498   }
499 }
500
501 void LinearLayout::LayoutVertical( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
502 {
503   Extents padding = GetPadding();
504
505   LayoutLength childTop( padding.top );
506   LayoutLength childLeft( padding.start );
507
508   // Where end of child should go
509   auto width = right - left;
510
511   // Space available for child
512   auto childSpace = width - (int)padding.start - (int)padding.end;
513   auto count = GetChildCount();
514
515   switch ( mAlignment & VERTICAL_ALIGNMENT_MASK )
516   {
517     case Dali::Toolkit::LinearLayout::Alignment::TOP:
518     {
519       // mTotalLength contains the padding already
520       childTop = LayoutLength( padding.top );
521       break;
522     }
523     case Dali::Toolkit::LinearLayout::Alignment::BOTTOM:
524     {
525       // mTotalLength contains the padding already
526       childTop = LayoutLength( padding.top ) + bottom - top - mTotalLength;
527       break;
528     }
529     case Dali::Toolkit::LinearLayout::Alignment::CENTER_VERTICAL:
530     default:
531     {
532       // mTotalLength contains the padding already
533       childTop = LayoutLength( padding.top ) + ( bottom - top - mTotalLength ) / 2;
534       break;
535     }
536   }
537
538   for( unsigned int childIndex = 0; childIndex < count; childIndex++)
539   {
540     LayoutItemPtr childLayout = GetChildAt( childIndex );
541     if( childLayout != nullptr )
542     {
543       auto childWidth = childLayout->GetMeasuredWidth();
544       auto childHeight = childLayout->GetMeasuredHeight();
545       auto childMargin = childLayout->GetMargin();
546
547       childTop += childMargin.top;
548       switch ( mAlignment & HORIZONTAL_ALIGNMENT_MASK )
549       {
550         case Dali::Toolkit::LinearLayout::Alignment::BEGIN:
551         default:
552         {
553           childLeft = LayoutLength( padding.start ) + childMargin.start;
554           break;
555         }
556         case Dali::Toolkit::LinearLayout::Alignment::END:
557         {
558           childLeft = width - padding.end - childWidth - childMargin.end;
559           break;
560         }
561         case Dali::Toolkit::LinearLayout::Alignment::CENTER_HORIZONTAL:
562         {
563           childLeft = LayoutLength( padding.start ) + ( childSpace - childWidth ) / 2 + childMargin.start - childMargin.end;
564           break;
565         }
566       }
567       childLayout->Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight );
568       childTop += childHeight + (int)childMargin.bottom + mCellPadding.height;
569     }
570   }
571 }
572
573 } // namespace Internal
574 } // namespace Toolkit
575 } // namespace Dali