Ensure not to emit the resource ready signal during the callback
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / control / control-data-impl.cpp
index ab65570..368ab0a 100755 (executable)
@@ -26,6 +26,7 @@
 #include <dali/devel-api/scripting/scripting.h>
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/object/type-registry-helper.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <cstring>
 #include <limits>
 
@@ -229,22 +230,22 @@ static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tra
     }
     else if( 0 == strcmp( signalName.c_str(), SIGNAL_TAPPED ) )
     {
-      controlImpl.EnableGestureDetection( Gesture::Tap );
+      controlImpl.EnableGestureDetection( GestureType::TAP );
       controlImpl.GetTapGestureDetector().DetectedSignal().Connect( tracker, functor );
     }
     else if( 0 == strcmp( signalName.c_str(), SIGNAL_PANNED ) )
     {
-      controlImpl.EnableGestureDetection( Gesture::Pan );
+      controlImpl.EnableGestureDetection( GestureType::PAN );
       controlImpl.GetPanGestureDetector().DetectedSignal().Connect( tracker, functor );
     }
     else if( 0 == strcmp( signalName.c_str(), SIGNAL_PINCHED ) )
     {
-      controlImpl.EnableGestureDetection( Gesture::Pinch );
+      controlImpl.EnableGestureDetection( GestureType::PINCH );
       controlImpl.GetPinchGestureDetector().DetectedSignal().Connect( tracker, functor );
     }
     else if( 0 == strcmp( signalName.c_str(), SIGNAL_LONG_PRESSED ) )
     {
-      controlImpl.EnableGestureDetection( Gesture::LongPress );
+      controlImpl.EnableGestureDetection( GestureType::LONG_PRESS );
       controlImpl.GetLongPressGestureDetector().DetectedSignal().Connect( tracker, functor );
     }
   }
@@ -322,7 +323,7 @@ Control::Impl::Impl( Control& controlImpl )
   mDownFocusableActorId( -1 ),
   mStyleName(""),
   mBackgroundColor(Color::TRANSPARENT),
-  mStartingPinchScale( NULL ),
+  mStartingPinchScale(nullptr),
   mMargin( 0, 0, 0, 0 ),
   mPadding( 0, 0, 0, 0 ),
   mKeyEventSignal(),
@@ -336,9 +337,12 @@ Control::Impl::Impl( Control& controlImpl )
   mLongPressGestureDetector(),
   mTooltip( NULL ),
   mInputMethodContext(),
+  mIdleCallback(nullptr),
   mFlags( Control::ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
   mIsKeyboardNavigationSupported( false ),
-  mIsKeyboardFocusGroup( false )
+  mIsKeyboardFocusGroup( false ),
+  mIsEmittingResourceReadySignal(false),
+  mNeedToEmitResourceReady(false)
 {
 }
 
@@ -346,6 +350,12 @@ Control::Impl::~Impl()
 {
   // All gesture detectors will be destroyed so no need to disconnect.
   delete mStartingPinchScale;
+
+  if(mIdleCallback && Adaptor::IsAvailable())
+  {
+    // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
+    Adaptor::Get().RemoveIdle(mIdleCallback);
+  }
 }
 
 Control::Impl& Control::Impl::Get( Internal::Control& internalControl )
@@ -670,8 +680,10 @@ void Control::Impl::ResourceReady( Visual::Base& object)
   // Emit signal if all enabled visuals registered by the control are ready.
   if( IsResourceReady() )
   {
-    Dali::Toolkit::Control handle( mControlImpl.GetOwner() );
-    mResourceReadySignal.Emit( handle );
+    // Reset the flag
+    mNeedToEmitResourceReady = false;
+
+    EmitResourceReadySignal();
   }
 }
 
@@ -759,7 +771,7 @@ void Control::Impl::AddTransitions( Dali::Animation& animation,
       Actor child = mControlImpl.Self().FindChildByName( animator->objectName );
       if( child )
       {
-        Property::Index propertyIndex = DevelHandle::GetPropertyIndex( child, animator->propertyKey );
+        Property::Index propertyIndex = child.GetPropertyIndex( animator->propertyKey );
         if( propertyIndex != Property::INVALID_INDEX )
         {
           if( animator->animate == false )
@@ -835,7 +847,7 @@ void Control::Impl::SetProperty( BaseObject* object, Property::Index index, cons
       {
         bool withTransitions=true;
         const Property::Value* valuePtr=&value;
-        Property::Map* map = value.GetMap();
+        const Property::Map* map = value.GetMap();
         if(map)
         {
           Property::Value* value2 = map->Find("withTransitions");
@@ -1448,6 +1460,58 @@ void Control::Impl::ClearShadow()
    mControlImpl.RelayoutRequest();
 }
 
+void Control::Impl::EmitResourceReadySignal()
+{
+  if(!mIsEmittingResourceReadySignal)
+  {
+    // Guard against calls to emit the signal during the callback
+    mIsEmittingResourceReadySignal = true;
+
+    // If the signal handler changes visual, it may become ready during this call & therefore this method will
+    // get called again recursively. If so, mNeedToEmitResourceReady is set below, and we act on it after that secondary
+    // invocation has completed by notifying in an Idle callback to prevent further recursion.
+    Dali::Toolkit::Control handle(mControlImpl.GetOwner());
+    mResourceReadySignal.Emit(handle);
+
+    if(mNeedToEmitResourceReady)
+    {
+      // Add idler to emit the signal again
+      if(!mIdleCallback)
+      {
+        // The callback manager takes the ownership of the callback object.
+        mIdleCallback = MakeCallback( this, &Control::Impl::OnIdleCallback);
+        Adaptor::Get().AddIdle(mIdleCallback, false);
+      }
+    }
+
+    mIsEmittingResourceReadySignal = false;
+  }
+  else
+  {
+    mNeedToEmitResourceReady = true;
+  }
+}
+
+void Control::Impl::OnIdleCallback()
+{
+  if(mNeedToEmitResourceReady)
+  {
+    // Reset the flag
+    mNeedToEmitResourceReady = false;
+
+    // A visual is ready so control may need relayouting if staged
+    if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+    {
+      mControlImpl.RelayoutRequest();
+    }
+
+    EmitResourceReadySignal();
+  }
+
+  // Set the pointer to null as the callback manager deletes the callback after execute it.
+  mIdleCallback = nullptr;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit