Fix grid layout defaults
[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   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetAnimateLayout animateLayout(%s)\n", (animateLayout)?"true":"false" );
99
100   auto owner = GetOwner();
101   auto actor = Actor::DownCast(owner);
102
103   if( actor )
104   {
105       DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetAnimateLayout animateLayout(%s) owner(%s)\n", (animateLayout)?"true":"false", actor.GetName().c_str() );
106   }
107
108   mImpl->mAnimated = animateLayout;
109
110   OnAnimationStateChanged( animateLayout );
111 }
112
113 bool LayoutItem::IsLayoutAnimated() const
114 {
115   return mImpl->mAnimated;
116 }
117
118 void LayoutItem::RegisterChildProperties( const std::string& containerType )
119 {
120   // Call on derived types
121   auto typeInfo = TypeRegistry::Get().GetTypeInfo( containerType );
122   if( typeInfo )
123   {
124     Property::IndexContainer indices;
125     typeInfo.GetChildPropertyIndices( indices );
126
127     if( std::find( indices.Begin(), indices.End(), Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION ) ==
128         indices.End() )
129     {
130       ChildPropertyRegistration( typeInfo.GetName(), WIDTH_SPECIFICATION_NAME,
131                                  Toolkit::LayoutItem::ChildProperty::WIDTH_SPECIFICATION, Property::INTEGER );
132
133       ChildPropertyRegistration( typeInfo.GetName(), HEIGHT_SPECIFICATION_NAME,
134                                  Toolkit::LayoutItem::ChildProperty::HEIGHT_SPECIFICATION, Property::INTEGER );
135     }
136
137     OnRegisterChildProperties( containerType );
138   }
139 }
140
141 void LayoutItem::OnRegisterChildProperties( const std::string& containerType )
142 {
143 }
144
145
146 void LayoutItem::Measure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec )
147 {
148   DALI_LOG_TRACE_METHOD( gLayoutFilter );
149
150   const bool forceLayout = mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
151
152   const bool specChanged =
153     ( widthMeasureSpec  != mImpl->mOldWidthMeasureSpec ) ||
154     ( heightMeasureSpec != mImpl->mOldHeightMeasureSpec );
155
156   const bool isSpecExactly =
157     ( widthMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY ) &&
158     ( heightMeasureSpec.GetMode() == MeasureSpec::Mode::EXACTLY );
159
160   const bool matchesSpecSize =
161     ( GetMeasuredWidth() == widthMeasureSpec.GetSize() ) &&
162     ( GetMeasuredHeight() == heightMeasureSpec.GetSize() );
163
164   const bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize );
165
166   DALI_LOG_STREAM( gLayoutFilter, Debug::Verbose, "LayoutItem::Measure("<<widthMeasureSpec<<", "<<heightMeasureSpec<<") Owner:"<<Actor::DownCast(GetOwner()).GetName() <<"  forceLayout="<<forceLayout<<", specChanged="<<specChanged<<", isSpecExactly="<<isSpecExactly<<", matchesSpecSize="<<matchesSpecSize<<", needsLayout="<<needsLayout <<std::endl <<(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::Concise, "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() mension()" );
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   switch (specMode)
274   {
275     case MeasureSpec::Mode::UNSPECIFIED:
276     {
277       result = size;
278       break;
279     }
280     case MeasureSpec::Mode::AT_MOST:
281     {
282       LayoutLength tmp = specSize;
283       if( size < tmp )
284       {
285         result = size;
286       }
287       else
288       {
289         result = specSize;
290       }
291       break;
292     }
293     case MeasureSpec::Mode::EXACTLY:
294     {
295       result = specSize;
296       break;
297     }
298   }
299   return result;
300 }
301
302 void LayoutItem::OnMeasure( MeasureSpec widthMeasureSpec, MeasureSpec heightMeasureSpec)
303 {
304   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::OnMeasure\n");
305
306   SetMeasuredDimensions( GetDefaultSize( GetSuggestedMinimumWidth(), widthMeasureSpec ),
307                          GetDefaultSize( GetSuggestedMinimumHeight(), heightMeasureSpec ) );
308 }
309
310 void LayoutItem::OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
311 {
312 }
313
314 void LayoutItem::SetParent( LayoutParent* parent )
315 {
316   mImpl->mLayoutParent = parent;
317 }
318
319 LayoutParent* LayoutItem::GetParent()
320 {
321   return mImpl->mLayoutParent;
322 }
323
324 void LayoutItem::RequestLayout()
325 {
326   Toolkit::Control control = Toolkit::Control::DownCast( mImpl->mOwner );
327   if ( control )
328   {
329     DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::RequestLayout %s\n", control.GetName().c_str());
330   }
331   // @todo Enforce failure if called in Measure/Layout passes.
332   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
333   Toolkit::LayoutController layoutController = Toolkit::LayoutController::Get();
334   layoutController.RequestLayout( Toolkit::LayoutItem(this) );
335 }
336
337 bool LayoutItem::IsLayoutRequested() const
338 {
339   return mImpl->GetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
340 }
341
342 void LayoutItem::SetLayoutRequested()
343 {
344   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_FORCE_LAYOUT );
345 }
346
347 void LayoutItem::SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
348 {
349   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetMeasuredDimensions width(%d) height(%d) \n",
350                                                  MeasureSpec::IntType( measuredWidth.GetSize() ),
351                                                  MeasureSpec::IntType( measuredHeight.GetSize() )
352                );
353
354   mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_MEASURED_DIMENSION_SET );
355   mImpl->mMeasuredWidth = measuredWidth;
356   mImpl->mMeasuredHeight = measuredHeight;
357 }
358
359 LayoutLength LayoutItem::GetMeasuredWidth() const
360 {
361   // Get the size portion of the measured width
362   return  mImpl->mMeasuredWidth.GetSize();
363 }
364
365 LayoutLength LayoutItem::GetMeasuredHeight() const
366 {
367   return  mImpl->mMeasuredHeight.GetSize();
368 }
369
370 MeasuredSize LayoutItem::GetMeasuredWidthAndState() const
371 {
372   return mImpl->mMeasuredWidth;
373 }
374
375 MeasuredSize LayoutItem::GetMeasuredHeightAndState() const
376 {
377   return mImpl->mMeasuredHeight;
378 }
379
380 LayoutLength LayoutItem::GetSuggestedMinimumWidth() const
381 {
382   auto owner = GetOwner();
383   auto actor = Actor::DownCast(owner);
384   auto naturalSize = actor ? actor.GetNaturalSize() : Vector3::ZERO;
385
386   return std::max( mImpl->mMinimumSize.GetWidth(), LayoutLength::IntType( naturalSize.width ) );
387 }
388
389 LayoutLength LayoutItem::GetSuggestedMinimumHeight() const
390 {
391   auto owner = GetOwner();
392   auto actor = Actor::DownCast(owner);
393   auto naturalSize = actor ? actor.GetNaturalSize() : Vector3::ZERO;
394
395   return std::max( mImpl->mMinimumSize.GetHeight(), LayoutLength::IntType(naturalSize.height) );
396 }
397
398 MeasuredSize LayoutItem::ResolveSizeAndState( LayoutLength size, MeasureSpec measureSpec, MeasuredSize::State childMeasuredState )
399 {
400   auto specMode = measureSpec.GetMode();
401   LayoutLength specSize = measureSpec.GetSize();
402   MeasuredSize result;
403
404   switch( specMode )
405   {
406     case MeasureSpec::Mode::AT_MOST:
407     {
408       if (specSize < size)
409       {
410         result = MeasuredSize( specSize, MeasuredSize::MEASURED_SIZE_TOO_SMALL );
411       }
412       else
413       {
414         result.SetSize( size );
415       }
416       break;
417     }
418
419     case MeasureSpec::Mode::EXACTLY:
420     {
421       result.SetSize( specSize );
422       break;
423     }
424
425     case MeasureSpec::Mode::UNSPECIFIED:
426     default:
427     {
428       result.SetSize( size );
429       break;
430     }
431   }
432
433   result.SetState( childMeasuredState );
434   return result;
435 }
436
437
438 bool LayoutItem::SetFrame( LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
439 {
440   bool changed = false;
441
442   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame enter(%d, %d, %d, %d)\n", left.mValue, top.mValue, right.mValue, bottom.mValue );
443
444   if( mImpl->mLeft != left || mImpl->mRight != right || mImpl->mTop != top || mImpl->mBottom != bottom )
445   {
446     changed = true;
447
448     auto oldWidth = mImpl->mRight - mImpl->mLeft;
449     auto oldHeight = mImpl->mBottom - mImpl->mTop;
450     auto newWidth = right - left;
451     auto newHeight = bottom - top;
452     bool sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
453
454     mImpl->mLeft = left;
455     mImpl->mTop = top;
456     mImpl->mRight = right;
457     mImpl->mBottom = bottom;
458
459     mImpl->SetPrivateFlag( Impl::PRIVATE_FLAG_HAS_BOUNDS );
460
461
462     // Reflect up to parent control
463     auto owner = GetOwner();
464     auto actor = Actor::DownCast(owner);
465     if( actor )
466     {
467       DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame owner(%s) (%d, %d, %d, %d)\n",  actor.GetName().c_str(),
468                                     left.mValue, top.mValue, right.mValue, bottom.mValue );
469       if( mImpl->mAnimated )
470       {
471         auto animation = Animation::New( 0.5f );
472         animation.AnimateTo( Property( actor, Actor::Property::POSITION ),
473                              Vector3( float(left.mValue), float(top.mValue), 0.0f ) );
474         animation.AnimateTo( Property( actor, Actor::Property::SIZE ),
475                              Vector3( right-left, bottom-top, 0.0f ) );
476         animation.FinishedSignal().Connect( mSlotDelegate, &LayoutItem::OnLayoutAnimationFinished );
477         animation.Play();
478       }
479       else
480       {
481         // @todo Collate into list of Property & Property::Value pairs.
482         actor.SetPosition( Vector3( float(left.mValue), float(top.mValue), 0.0f ) );
483         actor.SetSize( Vector3( right-left, bottom-top, 0.0f ) );
484       }
485     }
486
487     if( sizeChanged )
488     {
489       SizeChange( LayoutSize( newWidth, newHeight ), LayoutSize( oldWidth, oldHeight ) );
490     }
491   }
492
493   DALI_LOG_INFO( gLayoutFilter, Debug::Verbose, "LayoutItem::SetFrame  exit(%d, %d, %d, %d)\n", left.mValue, top.mValue, right.mValue, bottom.mValue );
494
495   return changed;
496 }
497
498 void LayoutItem::OnLayoutAnimationFinished( Animation& animation )
499 {
500   auto owner = GetOwner();
501   auto actor = Actor::DownCast(owner);
502   if( actor )
503   {
504     actor.SetSize( Vector3( mImpl->mRight-mImpl->mLeft, mImpl->mBottom-mImpl->mTop, 0.0f ) );
505   }
506 }
507
508 void LayoutItem::SizeChange( LayoutSize newSize, LayoutSize oldSize)
509 {
510   OnSizeChanged( newSize, oldSize );
511 }
512
513
514 void LayoutItem::OnSizeChanged( LayoutSize newSize, LayoutSize oldSize )
515 {
516 }
517
518 void LayoutItem::OnInitialize()
519 {
520 }
521
522
523 } // namespace Internal
524 } // namespace Toolkit
525 } // namespace Dali