f70a5f1c5fc134153b28d1086ebfd9da9a51c8c2
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / devel-api / layouting / layout-item-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 #include <dali/integration-api/debug.h>
18
19 #include <dali/public-api/animation/animation.h>
20 #include <dali/public-api/object/type-registry-helper.h>
21 #include <dali-toolkit/public-api/controls/control.h>
22 #include <dali-toolkit/devel-api/layouting/layout-item-impl.h>
23 #include <dali-toolkit/devel-api/layouting/layout-group-impl.h>
24 #include <dali-toolkit/internal/layouting/layout-item-data-impl.h>
25
26 namespace
27 {
28
29 #if defined(DEBUG_ENABLED)
30 Debug::Filter* gLayoutFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_LAYOUT" );
31 #endif
32
33 const char* WIDTH_SPECIFICATION_NAME( "widthSpecification" );
34 const char* HEIGHT_SPECIFICATION_NAME( "heightSpecification" );
35
36 const float DEFAULT_TRANSITION_DURATION( 0.5f );
37 }
38
39 namespace Dali
40 {
41 namespace Toolkit
42 {
43 namespace Internal
44 {
45
46 LayoutItem::LayoutItem()
47 : mImpl( new LayoutItem::Impl() ),
48   mSlotDelegate( this )
49 {
50 }
51
52 LayoutItem::~LayoutItem()
53 {
54   // An object with a unique_ptr to an opaque structure must define it's destructor in the translation unit
55   // where the opaque structure is defined. It cannot use the default method in the header file.
56 }
57
58 LayoutItemPtr LayoutItem::New( Handle& owner )
59 {
60   LayoutItemPtr layoutPtr = new LayoutItem();
61   return layoutPtr;
62 }
63
64 void LayoutItem::Initialize( Handle& owner, const std::string& containerType )
65 {
66   mImpl->mOwner = &(owner.GetBaseObject());
67   RegisterChildProperties( containerType );
68   OnInitialize(); // Ensure direct deriving class gets initialized
69   RequestLayout();
70 }
71
72 Handle LayoutItem::GetOwner() const
73 {
74   return Handle::DownCast(BaseHandle(mImpl->mOwner));
75 }
76
77 void LayoutItem::Unparent()
78 {
79   // Enable directly derived types to first remove children
80   OnUnparent();
81
82   // Remove myself from parent
83   LayoutParent* parent = GetParent();
84   if( parent )
85   {
86     parent->Remove( *this );
87   }
88
89   // Remove parent reference
90   SetParent(nullptr);
91
92   // Last, clear owner
93   mImpl->mOwner = NULL;
94 }
95
96 void LayoutItem::SetAnimateLayout( bool animateLayout )
97 {
98   auto owner = GetOwner();
99   auto actor = Actor::DownCast(owner);
100
101   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetAnimateLayout animateLayout(%s) owner(%s)\n", (animateLayout)?"true":"false",
102                  ( ( Actor::DownCast( owner) ) ? Actor::DownCast(owner).GetName().c_str() : "Invalid Actor" ) );
103
104   mImpl->mAnimated = animateLayout;
105
106   OnAnimationStateChanged( animateLayout );
107 }
108
109 bool LayoutItem::IsLayoutAnimated() const
110 {
111   return mImpl->mAnimated;
112 }
113
114 void LayoutItem::RegisterChildProperties( const std::string& containerType )
115 {
116   // Call on derived types
117   auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
118   if( typeInfo )
119   {
120     Property::IndexContainer indices;
121     typeInfo.GetChildPropertyIndices( indices );
122
123     if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) ==
124         indices.End() )
125     {
126       ChildPropertyRegistration( typeInfo.GetName(), WIDTH_SPECIFICATION_NAME,
127                                  Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, Property::INTEGER );
128
129       ChildPropertyRegistration( typeInfo.GetName(), HEIGHT_SPECIFICATION_NAME,
130                                  Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, Property::INTEGER );
131     }
132
133     OnRegisterChildProperties( containerType );
134   }
135 }
136
137 void LayoutItem::OnRegisterChildProperties( const std::string& containerType )
138 {
139 }
140
141
142 void LayoutItem::Measure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
143 {
144   DALI_LOG_TRACE_METHOD( gLayoutFilter );
145
146   const bool forceLayout = mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
147
148   const bool specChanged =
149     ( widthMeasureSpec  != mImpl->mOldWidthMeasureSpec ) ||
150     ( heightMeasureSpec != mImpl->mOldHeightMeasureSpec );
151
152   const bool isSpecExactly =
153     ( widthMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY ) &&
154     ( heightMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY );
155
156   const bool matchesSpecSize =
157     ( GetMeasuredWidth() == widthMeasureSpec.GetSize() ) &&
158     ( GetMeasuredHeight() == heightMeasureSpec.GetSize() );
159
160   const bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize );
161
162   DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::Measure("<<widthMeasureSpec<<", "<<heightMeasureSpec<<") Owner:"
163                                                   <<Actor::DownCast(GetOwner()).GetName() <<"  forceLayout="<<forceLayout
164                                                   <<", specChanged="<<specChanged<<", isSpecExactly="<<isSpecExactly
165                                                   <<", matchesSpecSize="<<matchesSpecSize
166                                                   <<", needsLayout="<<needsLayout <<(forceLayout||needsLayout?"  Remeasuring":"  NoChange"));
167
168   if( forceLayout || needsLayout )
169   {
170     mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET );
171
172     // measure ourselves, this should set the measured dimension flag back
173 #if defined(DEBUG_ENABLED)
174     std::ostringstream o;
175     o<<widthMeasureSpec<<","<<heightMeasureSpec;
176     DALI_LOG_INFO( gLayoutFilter, Debug::General, "LayoutItem::Measure Calling %s OnMeasure( %s )\n", Actor::DownCast(GetOwner()).GetName().c_str(), o.str().c_str());
177 #endif
178     OnMeasure( widthMeasureSpec, heightMeasureSpec );
179     mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_MEASURE_NEEDED_BEFORE_LAYOUT );
180
181     // flag not set, setMeasuredDimension() was not invoked, we raise an exception to warn the developer
182     DALI_ASSERT_ALWAYS( mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET ) &&
183                         "Layout's OnMeasure() Measured dimension flag not set" );
184     mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_LAYOUT_REQUIRED );
185   }
186
187   mImpl->mOldWidthMeasureSpec = widthMeasureSpec;
188   mImpl->mOldHeightMeasureSpec = heightMeasureSpec;
189 }
190
191 void LayoutItem::Layout( LayoutLength l, LayoutLength t, LayoutLength r, LayoutLength b )
192 {
193   DALI_LOG_TRACE_METHOD( gLayoutFilter );
194
195   if( mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_MEASURE_NEEDED_BEFORE_LAYOUT ) )
196   {
197     OnMeasure( mImpl->mOldWidthMeasureSpec, mImpl->mOldHeightMeasureSpec );
198     mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_MEASURE_NEEDED_BEFORE_LAYOUT );
199   }
200
201   bool changed = SetFrame( l, t, r, b );
202
203   if( changed || mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_LAYOUT_REQUIRED ) )
204   {
205     OnLayout( changed, l, t, r, b );
206     mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_LAYOUT_REQUIRED );
207   }
208
209   mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
210   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_IS_LAID_OUT );
211 }
212
213 LayoutLength LayoutItem::GetMinimumWidth() const
214 {
215   return mImpl->mMinimumSize.GetWidth();
216 }
217
218 LayoutLength LayoutItem::GetMinimumHeight() const
219 {
220   return mImpl->mMinimumSize.GetHeight();
221 }
222
223 void LayoutItem::SetMinimumWidth( LayoutLength minimumWidth )
224 {
225   mImpl->mMinimumSize.SetWidth( minimumWidth );
226   RequestLayout();
227 }
228
229 void LayoutItem::SetMinimumHeight( LayoutLength minimumHeight )
230 {
231   mImpl->mMinimumSize.SetHeight( minimumHeight );
232   RequestLayout();
233 }
234
235 Extents LayoutItem::GetPadding() const
236 {
237   Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
238   if( control )
239   {
240     Extents padding = control.GetProperty<Extents>( Toolkit::Control::Property::PADDING );
241
242     DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::Padding for %s : (%d,%d,%d,%d) \n",
243                    control.GetName().c_str(),
244                    padding.start, padding.end, padding.top, padding.bottom
245                  );
246     return padding;
247   }
248   else
249   {
250     return Extents();
251   }
252 }
253
254 Extents LayoutItem::GetMargin() const
255 {
256   Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
257   if ( control )
258   {
259     return control.GetProperty<Extents>( Toolkit::Control::Property::MARGIN );
260   }
261   else
262   {
263     return Extents();
264   }
265 }
266
267 LayoutLength LayoutItem::GetDefaultSize( LayoutLength size, MeasureSpec measureSpec )
268 {
269   LayoutLength result = size;
270   auto specMode = measureSpec.GetMode();
271   auto specSize = measureSpec.GetSize();
272
273   DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::GetDefaultSize MeasureSpec("<<measureSpec<< ") size:" << size << "\n" );
274
275   switch (specMode)
276   {
277     case MeasureSpec::Mode::UNSPECIFIED:
278     {
279       result = size;
280       break;
281     }
282     case MeasureSpec::Mode::AT_MOST:
283     {
284       // Ensure the default size does not exceed the spec size unless the default size is 0.
285       // Another container could provide a default size of 0.
286       LayoutLength tmp = specSize;
287
288       // Do not set size to 0, use specSize in this case as could be a legacy container
289       if( size < tmp && size > LayoutLength( 0 ) )
290       {
291         result = size;
292       }
293       else
294       {
295         result = specSize;
296       }
297       break;
298     }
299     case MeasureSpec::Mode::EXACTLY:
300     {
301       result = specSize;
302       break;
303     }
304   }
305   DALI_LOG_STREAM( gLayoutFilter, Debug::General, "LayoutItem::GetDefaultSize setting default size:" << result << "\n" );
306   return result;
307 }
308
309 void LayoutItem::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
310 {
311   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::OnMeasure\n");
312
313   // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
314   SetMeasuredDimensions( GetDefaultSize( GetSuggestedMinimumWidth(), widthMeasureSpec ),
315                          GetDefaultSize( GetSuggestedMinimumHeight(), heightMeasureSpec ) );
316 }
317
318 void LayoutItem::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
319 {
320 }
321
322 void LayoutItem::SetParent( LayoutParent* parent )
323 {
324   mImpl->mLayoutParent = parent;
325   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_SET_FRAME );
326 }
327
328 LayoutParent* LayoutItem::GetParent()
329 {
330   return mImpl->mLayoutParent;
331 }
332
333 void LayoutItem::RequestLayout()
334 {
335   Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
336   if ( control )
337   {
338     DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::RequestLayout %s\n", control.GetName().c_str());
339   }
340   // @todo Enforce failure if called in Measure/Layout passes.
341   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
342   Toolkit::LayoutController layoutController = Toolkit::LayoutController::Get();
343   layoutController.RequestLayout( Toolkit::LayoutItem(this) );
344 }
345
346 bool LayoutItem::IsLayoutRequested() const
347 {
348   return mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
349 }
350
351 void LayoutItem::SetLayoutRequested()
352 {
353   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
354 }
355
356 bool LayoutItem::IsResizePolicyRequired() const
357 {
358   return mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_USE_RESIZE_POLICY );
359 }
360
361 void LayoutItem::SetResizePolicyRequired( bool resizePolicyRequired )
362 {
363   if( resizePolicyRequired )
364   {
365     mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_USE_RESIZE_POLICY );
366   }
367   else
368   {
369     mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_USE_RESIZE_POLICY );
370   }
371 }
372
373 void LayoutItem::SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
374 {
375
376   DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetMeasuredDimensions width(" << measuredWidth.GetSize() << ") height(" << measuredHeight.GetSize() << ") Control:" <<
377                         ( ( Actor::DownCast( GetOwner()) ) ? Actor::DownCast(GetOwner()).GetName().c_str() : "Invalid Actor" ) << "\n" );
378
379   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET );
380   mImpl->mMeasuredWidth = measuredWidth;
381   mImpl->mMeasuredHeight = measuredHeight;
382 }
383
384 LayoutLength LayoutItem::GetMeasuredWidth() const
385 {
386   // Get the size portion of the measured width
387   return  mImpl->mMeasuredWidth.GetSize();
388 }
389
390 LayoutLength LayoutItem::GetMeasuredHeight() const
391 {
392   return  mImpl->mMeasuredHeight.GetSize();
393 }
394
395 MeasuredSize LayoutItem::GetMeasuredWidthAndState() const
396 {
397   return mImpl->mMeasuredWidth;
398 }
399
400 MeasuredSize LayoutItem::GetMeasuredHeightAndState() const
401 {
402   return mImpl->mMeasuredHeight;
403 }
404
405 LayoutLength LayoutItem::GetSuggestedMinimumWidth() const
406 {
407   auto owner = GetOwner();
408   auto actor = Actor::DownCast(owner);
409   auto naturalSize = actor ? actor.GetNaturalSize() : Vector3::ZERO;
410
411   return std::max( mImpl->mMinimumSize.GetWidth(), LayoutLength( naturalSize.width ) );
412 }
413
414 LayoutLength LayoutItem::GetSuggestedMinimumHeight() const
415 {
416   auto owner = GetOwner();
417   auto actor = Actor::DownCast(owner);
418   auto naturalSize = actor ? actor.GetNaturalSize() : Vector3::ZERO;
419
420   return std::max( mImpl->mMinimumSize.GetHeight(), LayoutLength( naturalSize.height ) );
421 }
422
423 MeasuredSize LayoutItem::ResolveSizeAndState( LayoutLength size, MeasureSpec measureSpec, MeasuredSize::State childMeasuredState )
424 {
425   auto specMode = measureSpec.GetMode();
426   LayoutLength specSize = measureSpec.GetSize();
427   MeasuredSize result;
428
429   switch( specMode )
430   {
431     case MeasureSpec::Mode::AT_MOST:
432     {
433       if (specSize < size)
434       {
435         result = MeasuredSize( specSize, MeasuredSize::MEASURED_SIZE_TOO_SMALL );
436       }
437       else
438       {
439         result.SetSize( size );
440       }
441       break;
442     }
443
444     case MeasureSpec::Mode::EXACTLY:
445     {
446       result.SetSize( specSize );
447       break;
448     }
449
450     case MeasureSpec::Mode::UNSPECIFIED:
451     default:
452     {
453       result.SetSize( size );
454       break;
455     }
456   }
457
458   result.SetState( childMeasuredState );
459   return result;
460 }
461
462
463 bool LayoutItem::SetFrame( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
464 {
465   bool changed = false;
466
467   DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame enter(" << left << ", " << top << ", " << right << ", " << bottom << ")\n" );
468
469   if( mImpl->mLeft != left || mImpl->mRight != right || mImpl->mTop != top || mImpl->mBottom != bottom || mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_SET_FRAME ) )
470   {
471     changed = true;
472
473     LayoutLength oldWidth = mImpl->mRight - mImpl->mLeft;
474     LayoutLength oldHeight = mImpl->mBottom - mImpl->mTop;
475     LayoutLength newWidth = right - left;
476     LayoutLength newHeight = bottom - top;
477     bool sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
478
479     mImpl->mLeft = left;
480     mImpl->mTop = top;
481     mImpl->mRight = right;
482     mImpl->mBottom = bottom;
483
484     mImpl->ClearPrivateFlag( Impl::PRIVATE_FLAG_FORCE_SET_FRAME );
485
486
487     // Reflect up to parent control
488     auto owner = GetOwner();
489     auto actor = Actor::DownCast(owner);
490     if( actor )
491     {
492       DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame owner(" << left << ", " << top << ", " << right << ", " << bottom << ")\n" );
493
494       if( mImpl->mAnimated )
495       {
496         auto animation = Animation::New( 0.5f );
497         animation.AnimateTo( Property( actor, Actor::Property::POSITION_X ), left.AsInteger() );
498         animation.AnimateTo( Property( actor, Actor::Property::POSITION_Y ), top.AsInteger() );
499
500         animation.AnimateTo( Property( actor, Actor::Property::SIZE_WIDTH ), newWidth.AsInteger() );
501         animation.AnimateTo( Property( actor, Actor::Property::SIZE_HEIGHT ), newHeight.AsInteger() );
502
503         animation.FinishedSignal().Connect( mSlotDelegate, &LayoutItem::OnLayoutAnimationFinished );
504         animation.Play();
505       }
506       else
507       {
508         // @todo Collate into list of Property & Property::Value pairs.
509         actor.SetX( left.AsInteger() );
510         actor.SetY( top.AsInteger() );
511         actor.SetProperty( Actor::Property::SIZE_WIDTH, newWidth.AsInteger() );
512         actor.SetProperty( Actor::Property::SIZE_HEIGHT, newHeight.AsInteger() );
513       }
514     }
515
516     if( sizeChanged )
517     {
518       SizeChange( LayoutSize( newWidth, newHeight ), LayoutSize( oldWidth, oldHeight ) );
519     }
520   }
521
522   DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame  exit(" << left << ", " << top << ", " << right << ", " << bottom << ")\n" );
523
524   return changed;
525 }
526
527 void LayoutItem::OnLayoutAnimationFinished( Animation& animation )
528 {
529   auto owner = GetOwner();
530   auto actor = Actor::DownCast(owner);
531   if( actor )
532   {
533     actor.SetSize( Vector3( mImpl->mRight.AsInteger() - mImpl->mLeft.AsInteger(), mImpl->mBottom.AsInteger() - mImpl->mTop.AsInteger(), 0.0f ) );
534   }
535 }
536
537 void LayoutItem::SizeChange( LayoutSize newSize, LayoutSize oldSize)
538 {
539   OnSizeChanged( newSize, oldSize );
540 }
541
542
543 void LayoutItem::OnSizeChanged( LayoutSize newSize, LayoutSize oldSize )
544 {
545 }
546
547 void LayoutItem::OnInitialize()
548 {
549 }
550
551
552 } // namespace Internal
553 } // namespace Toolkit
554 } // namespace Dali