[dali_2.3.27] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / control / control-data-impl.cpp
index b967d14..a67cd34 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
 #include <dali/devel-api/scripting/scripting.h>
 #include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
+#include <dali/public-api/math/math-utils.h>
 #include <dali/public-api/object/object-registry.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <cstring>
@@ -44,6 +45,7 @@
 #include <dali-toolkit/internal/styling/style-manager-impl.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali-toolkit/public-api/align-enumerations.h>
 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
 #include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 
 namespace
 {
-const std::string READING_INFO_TYPE_NAME           = "name";
-const std::string READING_INFO_TYPE_ROLE           = "role";
-const std::string READING_INFO_TYPE_DESCRIPTION    = "description";
-const std::string READING_INFO_TYPE_STATE          = "state";
-const std::string READING_INFO_TYPE_ATTRIBUTE_NAME = "reading_info_type";
-const std::string READING_INFO_TYPE_SEPARATOR      = "|";
+const char* READING_INFO_TYPE_NAME           = "name";
+const char* READING_INFO_TYPE_ROLE           = "role";
+const char* READING_INFO_TYPE_DESCRIPTION    = "description";
+const char* READING_INFO_TYPE_STATE          = "state";
+const char* READING_INFO_TYPE_ATTRIBUTE_NAME = "reading_info_type";
+const char* READING_INFO_TYPE_SEPARATOR      = "|";
 } // namespace
 
 namespace Dali
@@ -75,6 +77,7 @@ const Scripting::StringEnum ControlStateTable[] = {
   {"DISABLED", Toolkit::DevelControl::DISABLED},
 };
 const unsigned int ControlStateTableCount = sizeof(ControlStateTable) / sizeof(ControlStateTable[0]);
+const Vector4      FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
 namespace
 {
@@ -128,6 +131,38 @@ bool FindVisual(std::string visualName, const RegisteredVisualContainer& visuals
   return false;
 }
 
+/**
+ *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
+ */
+bool FindVisual(const Toolkit::Visual::Base findVisual, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter)
+{
+  for(iter = visuals.Begin(); iter != visuals.End(); iter++)
+  {
+    Toolkit::Visual::Base visual = (*iter)->visual;
+    if(visual && visual == findVisual)
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ *  Finds internal visual in given array, returning true if found along with the iterator for that visual as a out parameter
+ */
+bool FindVisual(const Visual::Base& findInternalVisual, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter)
+{
+  for(iter = visuals.Begin(); iter != visuals.End(); iter++)
+  {
+    Visual::Base& visual = Toolkit::GetImplementation((*iter)->visual);
+    if((&visual == &findInternalVisual))
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
 void FindChangableVisuals(Dictionary<Property::Map>& stateVisualsToAdd,
                           Dictionary<Property::Map>& stateVisualsToChange,
                           DictionaryKeys&            stateVisualsToRemove)
@@ -200,6 +235,23 @@ void MoveVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVisual
 }
 
 /**
+ * Discard visual from source to visual factory.
+ */
+void DiscardVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVisualContainer& source)
+{
+  Toolkit::Visual::Base visual = (*sourceIter)->visual;
+  if(visual)
+  {
+    if(Stage::IsInstalled())
+    {
+      Toolkit::VisualFactory::Get().DiscardVisual(visual);
+    }
+  }
+
+  source.Erase(sourceIter);
+}
+
+/**
  * Performs actions as requested using the action name.
  * @param[in] object The object on which to perform the action.
  * @param[in] actionName The action to perform.
@@ -504,6 +556,7 @@ Control::Impl::Impl(Control& controlImpl)
   mStartingPinchScale(nullptr),
   mMargin(0, 0, 0, 0),
   mPadding(0, 0, 0, 0),
+  mSize(0, 0),
   mKeyEventSignal(),
   mKeyInputFocusGainedSignal(),
   mKeyInputFocusLostSignal(),
@@ -523,8 +576,9 @@ Control::Impl::Impl(Control& controlImpl)
   mIsKeyboardNavigationSupported(false),
   mIsKeyboardFocusGroup(false),
   mIsEmittingResourceReadySignal(false),
-  mNeedToEmitResourceReady(false),
-  mDispatchKeyEvents(true)
+  mIdleCallbackRegistered(false),
+  mDispatchKeyEvents(true),
+  mProcessorRegistered(false)
 {
   Dali::Accessibility::Accessible::RegisterExternalAccessibleGetter(&ExternalAccessibleGetter);
 }
@@ -544,6 +598,12 @@ Control::Impl::~Impl()
   // All gesture detectors will be destroyed so no need to disconnect.
   delete mStartingPinchScale;
 
+  if(mProcessorRegistered && Adaptor::IsAvailable())
+  {
+    // Unregister the processor from the adaptor
+    Adaptor::Get().UnregisterProcessorOnce(*this, true);
+  }
+
   if(mIdleCallback && Adaptor::IsAvailable())
   {
     // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
@@ -553,68 +613,75 @@ Control::Impl::~Impl()
 
 Control::Impl& Control::Impl::Get(Internal::Control& internalControl)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   return *internalControl.mImpl;
 }
 
 const Control::Impl& Control::Impl::Get(const Internal::Control& internalControl)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   return *internalControl.mImpl;
 }
 
 void Control::Impl::CheckHighlightedObjectGeometry()
 {
-  auto accessible     = GetAccessibleObject();
-  auto lastPosition   = accessible->GetLastPosition();
-  auto accessibleRect = accessible->GetExtents(Dali::Accessibility::CoordinateType::WINDOW);
-  auto rect           = GetShowingGeometry(accessibleRect, accessible);
-
-  switch(mAccessibilityLastScreenRelativeMoveType)
+  auto accessible = GetAccessibleObject();
+  if(DALI_LIKELY(accessible))
   {
-    case Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE:
-    {
-      if(IsShowingGeometryOnScreen(rect))
-      {
-        mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE;
-      }
-      break;
-    }
-    case Dali::Accessibility::ScreenRelativeMoveType::INSIDE:
+    auto lastPosition   = accessible->GetLastPosition();
+    auto accessibleRect = accessible->GetExtents(Dali::Accessibility::CoordinateType::WINDOW);
+    auto rect           = GetShowingGeometry(accessibleRect, accessible);
+
+    switch(mAccessibilityLastScreenRelativeMoveType)
     {
-      if(rect.width < 0 && accessibleRect.x != lastPosition.x)
+      case Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE:
       {
-        mAccessibilityLastScreenRelativeMoveType = (accessibleRect.x < lastPosition.x) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT;
+        if(IsShowingGeometryOnScreen(rect))
+        {
+          mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE;
+        }
+        break;
       }
-      if(rect.height < 0 && accessibleRect.y != lastPosition.y)
+      case Dali::Accessibility::ScreenRelativeMoveType::INSIDE:
       {
-        mAccessibilityLastScreenRelativeMoveType = (accessibleRect.y < lastPosition.y) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT;
+        if(rect.width < 0 && !Dali::Equals(accessibleRect.x, lastPosition.x))
+        {
+          mAccessibilityLastScreenRelativeMoveType = (accessibleRect.x < lastPosition.x) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT;
+        }
+        if(rect.height < 0 && !Dali::Equals(accessibleRect.y, lastPosition.y))
+        {
+          mAccessibilityLastScreenRelativeMoveType = (accessibleRect.y < lastPosition.y) ? Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT : Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT;
+        }
+        // notify AT-clients on outgoing moves only
+        if(mAccessibilityLastScreenRelativeMoveType != Dali::Accessibility::ScreenRelativeMoveType::INSIDE)
+        {
+          accessible->EmitMovedOutOfScreen(mAccessibilityLastScreenRelativeMoveType);
+        }
+        break;
       }
-      // notify AT-clients on outgoing moves only
-      if(mAccessibilityLastScreenRelativeMoveType != Dali::Accessibility::ScreenRelativeMoveType::INSIDE)
+      case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT:
+      case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT:
       {
-        accessible->EmitMovedOutOfScreen(mAccessibilityLastScreenRelativeMoveType);
+        if(IsShowingGeometryOnScreen(rect))
+        {
+          mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE;
+        }
+        else
+        {
+          mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE;
+        }
+        break;
       }
-      break;
-    }
-    case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_TOP_LEFT:
-    case Dali::Accessibility::ScreenRelativeMoveType::OUTGOING_BOTTOM_RIGHT:
-    {
-      if(IsShowingGeometryOnScreen(rect))
+      default:
       {
-        mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::INSIDE;
-      }
-      else
-      {
-        mAccessibilityLastScreenRelativeMoveType = Dali::Accessibility::ScreenRelativeMoveType::OUTSIDE;
+        break;
       }
-      break;
     }
-    default:
-    {
-      break;
-    }
-  }
 
-  accessible->SetLastPosition(Vector2(accessibleRect.x, accessibleRect.y));
+    accessible->SetLastPosition(Vector2(accessibleRect.x, accessibleRect.y));
+  }
 }
 
 void Control::Impl::RegisterAccessibilityPositionPropertyNotification()
@@ -639,6 +706,49 @@ void Control::Impl::UnregisterAccessibilityPositionPropertyNotification()
   mIsAccessibilityPositionPropertyNotificationSet = false;
 }
 
+void Control::Impl::RegisterAccessibilityPropertySetSignal()
+{
+  if(mIsAccessibilityPropertySetSignalRegistered)
+  {
+    return;
+  }
+  mControlImpl.Self().PropertySetSignal().Connect(this, &Control::Impl::OnAccessibilityPropertySet);
+  mIsAccessibilityPropertySetSignalRegistered = true;
+}
+
+void Control::Impl::UnregisterAccessibilityPropertySetSignal()
+{
+  if(!mIsAccessibilityPropertySetSignalRegistered)
+  {
+    return;
+  }
+  mControlImpl.Self().PropertySetSignal().Disconnect(this, &Control::Impl::OnAccessibilityPropertySet);
+  mIsAccessibilityPropertySetSignalRegistered = false;
+}
+
+void Control::Impl::OnAccessibilityPropertySet(Dali::Handle& handle, Dali::Property::Index index, const Dali::Property::Value& value)
+{
+  auto* accessible = GetAccessibleObject();
+  if(DALI_LIKELY(accessible))
+  {
+    if(mAccessibilityGetNameSignal.Empty())
+    {
+      if(index == DevelControl::Property::ACCESSIBILITY_NAME || (mAccessibilityName.empty() && index == accessible->GetNamePropertyIndex()))
+      {
+        accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::NAME);
+      }
+    }
+
+    if(mAccessibilityGetDescriptionSignal.Empty())
+    {
+      if(index == DevelControl::Property::ACCESSIBILITY_DESCRIPTION || (mAccessibilityDescription.empty() && index == accessible->GetDescriptionPropertyIndex()))
+      {
+        accessible->Emit(Dali::Accessibility::ObjectPropertyChangeEvent::DESCRIPTION);
+      }
+    }
+  }
+}
+
 // Gesture Detection Methods
 void Control::Impl::PinchDetected(Actor actor, const PinchGesture& pinch)
 {
@@ -682,6 +792,8 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
 
 void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base& visual, VisualState::Type enabled, DepthIndexValue::Type depthIndexValueSet, int depthIndex)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   DALI_LOG_INFO(gLogFilter, Debug::Concise, "RegisterVisual:%d \n", index);
 
   bool  visualReplaced(false);
@@ -696,6 +808,10 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
     requiredDepthIndex = depthIndex;
   }
 
+  // Change the depth index value automatically if the visual has DepthIndex to AUTO_INDEX
+  // or if RegisterVisual set DepthIndex to AUTO_INDEX.
+  const bool requiredDepthIndexChanged = (requiredDepthIndex == DepthIndex::AUTO_INDEX);
+
   // Visual replacement, existing visual should only be removed from stage when replacement ready.
   if(!mVisuals.Empty())
   {
@@ -737,11 +853,11 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
           mVisuals.Erase(registeredVisualsiter);
         }
 
-        // If we've not set the depth-index value and the new visual does not have a depth index applied to it, then use the previously set depth-index for this index
-        if((depthIndexValueSet == DepthIndexValue::NOT_SET) &&
-           (visual.GetDepthIndex() == 0))
+        // If the visual have a depth index as AUTO_INDEX and the new visual does not have a depth index applied to it, then use the previously set depth-index for this index
+        if(requiredDepthIndexChanged)
         {
           requiredDepthIndex = currentDepthIndex;
+          DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Use replaced visual index. VisualDepthIndex AUTO_INDEX set as: %d\n", requiredDepthIndex);
         }
       }
 
@@ -764,12 +880,10 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
 
   if(!visualReplaced) // New registration entry
   {
-    // If we've not set the depth-index value, we have more than one visual and the visual does not have a depth index, then set it to be the highest
-    if((depthIndexValueSet == DepthIndexValue::NOT_SET) &&
-       (mVisuals.Size() > 0) &&
-       (visual.GetDepthIndex() == 0))
+    // If we have more than one visual and the visual have a depth index as AUTO_INDEX, then set it to be the highest
+    if((mVisuals.Size() > 0) && requiredDepthIndexChanged)
     {
-      int maxDepthIndex = std::numeric_limits<int>::min();
+      int maxDepthIndex = static_cast<int>(DepthIndex::CONTENT) - 1; // Start at DepthIndex::CONTENT if maxDepth index belongs to a background or no visuals have been added yet.
 
       RegisteredVisualContainer::ConstIterator       iter;
       const RegisteredVisualContainer::ConstIterator endIter = mVisuals.End();
@@ -781,13 +895,20 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
           maxDepthIndex = visualDepthIndex;
         }
       }
-      ++maxDepthIndex;                                 // Add one to the current maximum depth index so that our added visual appears on top
-      requiredDepthIndex = std::max(0, maxDepthIndex); // Start at zero if maxDepth index belongs to a background
+      requiredDepthIndex = ++maxDepthIndex; // Add one to the current maximum depth index so that our added visual appears on top.
+      DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Use top of all visuals. VisualDepthIndex AUTO_INDEX set as: %d\n", requiredDepthIndex);
     }
   }
 
   if(visual)
   {
+    // If required depth index still DepthIndex::AUTO_INDEX, Make it as DepthIndex::CONTENT now
+    if(requiredDepthIndex == static_cast<int>(DepthIndex::AUTO_INDEX))
+    {
+      requiredDepthIndex = static_cast<int>(DepthIndex::CONTENT);
+      DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Some strange cases. VisualDepthIndex AUTO_INDEX set as: %d\n", requiredDepthIndex);
+    }
+
     // Set determined depth index
     visual.SetDepthIndex(requiredDepthIndex);
 
@@ -804,7 +925,7 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
     {
       visualImpl.SetOnScene(self);
     }
-    else if(visualImpl.IsResourceReady()) // When not being staged, check if visual already 'ResourceReady' before it was Registered. ( Resource may have been loaded already )
+    else if(enabled && visualImpl.IsResourceReady()) // When not being staged, check if visual already 'ResourceReady' before it was Registered. ( Resource may have been loaded already )
     {
       ResourceReady(visualImpl);
     }
@@ -815,6 +936,8 @@ void Control::Impl::RegisterVisual(Property::Index index, Toolkit::Visual::Base&
 
 void Control::Impl::UnregisterVisual(Property::Index index)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   RegisteredVisualContainer::Iterator iter;
   if(FindVisual(index, mVisuals, iter))
   {
@@ -832,8 +955,9 @@ void Control::Impl::UnregisterVisual(Property::Index index)
     Actor self(mControlImpl.Self());
     Toolkit::GetImplementation((*iter)->visual).SetOffScene(self);
     (*iter)->pending = false;
-    (*iter)->visual.Reset();
-    mRemoveVisuals.Erase(iter);
+
+    // Discard removed visual. It will be destroyed at next Idle time.
+    DiscardVisual(iter, mRemoveVisuals);
   }
 }
 
@@ -893,6 +1017,23 @@ bool Control::Impl::IsVisualEnabled(Property::Index index) const
   return false;
 }
 
+void Control::Impl::EnableReadyTransitionOverriden(Toolkit::Visual::Base& visual, bool enable)
+{
+  DALI_LOG_INFO(gLogFilter, Debug::General, "Control::EnableReadyTransitionOverriden(%p, %s)\n", visual, enable ? "T" : "F");
+
+  RegisteredVisualContainer::Iterator iter;
+  if(FindVisual(visual, mVisuals, iter))
+  {
+    if((*iter)->overideReadyTransition == enable)
+    {
+      DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::EnableReadyTransitionOverriden Visual %s(%p) already %s\n", (*iter)->visual.GetName().c_str(), visual, enable ? "enabled" : "disabled");
+      return;
+    }
+
+    (*iter)->overideReadyTransition = enable;
+  }
+}
+
 void Control::Impl::StopObservingVisual(Toolkit::Visual::Base& visual)
 {
   Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
@@ -909,6 +1050,17 @@ void Control::Impl::StartObservingVisual(Toolkit::Visual::Base& visual)
   visualImpl.AddEventObserver(*this);
 }
 
+void Control::Impl::ResourceReady()
+{
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
+  // Emit signal if all enabled visuals registered by the control are ready or there are no visuals.
+  if(IsResourceReady())
+  {
+    EmitResourceReadySignal();
+  }
+}
+
 // Called by a Visual when it's resource is ready
 void Control::Impl::ResourceReady(Visual::Base& object)
 {
@@ -916,41 +1068,38 @@ void Control::Impl::ResourceReady(Visual::Base& object)
 
   Actor self = mControlImpl.Self();
 
+  RegisteredVisualContainer::Iterator registeredIter;
+
   // A resource is ready, find resource in the registered visuals container and get its index
-  for(auto registeredIter = mVisuals.Begin(), end = mVisuals.End(); registeredIter != end; ++registeredIter)
+  if(!FindVisual(object, mVisuals, registeredIter))
   {
-    Internal::Visual::Base& registeredVisualImpl = Toolkit::GetImplementation((*registeredIter)->visual);
+    return;
+  }
 
-    if(&object == &registeredVisualImpl)
+  RegisteredVisualContainer::Iterator visualToRemoveIter;
+  // Find visual with the same index in the removal container
+  // Set if off stage as it's replacement is now ready.
+  // Remove if from removal list as now removed from stage.
+  // Set Pending flag on the ready visual to false as now ready.
+  if(FindVisual((*registeredIter)->index, mRemoveVisuals, visualToRemoveIter))
+  {
+    (*registeredIter)->pending = false;
+    if(!((*visualToRemoveIter)->overideReadyTransition))
     {
-      RegisteredVisualContainer::Iterator visualToRemoveIter;
-      // Find visual with the same index in the removal container
-      // Set if off stage as it's replacement is now ready.
-      // Remove if from removal list as now removed from stage.
-      // Set Pending flag on the ready visual to false as now ready.
-      if(FindVisual((*registeredIter)->index, mRemoveVisuals, visualToRemoveIter))
-      {
-        (*registeredIter)->pending = false;
-        Toolkit::GetImplementation((*visualToRemoveIter)->visual).SetOffScene(self);
-        mRemoveVisuals.Erase(visualToRemoveIter);
-      }
-      break;
+      Toolkit::GetImplementation((*visualToRemoveIter)->visual).SetOffScene(self);
     }
+
+    // Discard removed visual. It will be destroyed at next Idle time.
+    DiscardVisual(visualToRemoveIter, mRemoveVisuals);
   }
 
   // A visual is ready so control may need relayouting if staged
-  if(self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
-  {
-    mControlImpl.RelayoutRequest();
-  }
+  RelayoutRequest(object);
 
-  // Emit signal if all enabled visuals registered by the control are ready.
-  if(IsResourceReady())
+  // Called by a Visual when it's resource is ready
+  if(((*registeredIter)->enabled))
   {
-    // Reset the flag
-    mNeedToEmitResourceReady = false;
-
-    EmitResourceReadySignal();
+    ResourceReady();
   }
 }
 
@@ -970,7 +1119,10 @@ void Control::Impl::NotifyVisualEvent(Visual::Base& object, Property::Index sign
 
 void Control::Impl::RelayoutRequest(Visual::Base& object)
 {
-  mControlImpl.RelayoutRequest();
+  if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+  {
+    mControlImpl.RelayoutRequest();
+  }
 }
 
 bool Control::Impl::IsResourceReady() const
@@ -1019,7 +1171,6 @@ void Control::Impl::AddTransitions(Dali::Animation&               animation,
     TransitionData::Animator* animator = (*iter);
 
     Toolkit::Visual::Base visual = GetVisualByName(mVisuals, animator->objectName);
-
     if(visual)
     {
 #if defined(DEBUG_ENABLED)
@@ -1096,6 +1247,15 @@ void Control::Impl::DoAction(Dali::Property::Index visualIndex, Dali::Property::
   }
 }
 
+void Control::Impl::DoActionExtension(Dali::Property::Index visualIndex, Dali::Property::Index actionId, Dali::Any attributes)
+{
+  RegisteredVisualContainer::Iterator iter;
+  if(FindVisual(visualIndex, mVisuals, iter))
+  {
+    Toolkit::GetImplementation((*iter)->visual).DoActionExtension(actionId, attributes);
+  }
+}
+
 void Control::Impl::AppendAccessibilityAttribute(const std::string& key, const std::string value)
 {
   Property::Value* checkedValue = mAccessibilityAttributes.Find(key);
@@ -1111,6 +1271,8 @@ void Control::Impl::AppendAccessibilityAttribute(const std::string& key, const s
 
 void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   Toolkit::Control control = Toolkit::Control::DownCast(BaseHandle(object));
 
   if(control)
@@ -1369,10 +1531,13 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
           controlImpl.mImpl->mAccessibilityHidden = hidden;
 
           auto* accessible = controlImpl.GetAccessibleObject();
-          auto* parent     = dynamic_cast<Dali::Accessibility::ActorAccessible*>(accessible->GetParent());
-          if(parent)
+          if(DALI_LIKELY(accessible))
           {
-            parent->OnChildrenChanged();
+            auto* parent = dynamic_cast<Dali::Accessibility::ActorAccessible*>(accessible->GetParent());
+            if(parent)
+            {
+              parent->OnChildrenChanged();
+            }
           }
         }
         break;
@@ -1411,6 +1576,8 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
 
 Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index index)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   Property::Value value;
 
   Toolkit::Control control = Toolkit::Control::DownCast(BaseHandle(object));
@@ -1782,6 +1949,8 @@ void Control::Impl::RecreateChangedVisuals(Dictionary<Property::Map>& stateVisua
 
 void Control::Impl::ReplaceStateVisualsAndProperties(const StylePtr oldState, const StylePtr newState, const std::string& subState)
 {
+  DALI_ASSERT_ALWAYS(Stage::IsCoreThread() && "Core is not installed. Might call this API from worker thread?");
+
   // Collect all old visual names
   DictionaryKeys stateVisualsToRemove;
   if(oldState)
@@ -1919,17 +2088,25 @@ void Control::Impl::OnSceneDisconnection()
 
   // Visuals pending replacement can now be taken out of the removal list and set off scene
   // Iterate through all replacement visuals and add to a move queue then set off scene
-  for(auto removalIter = mRemoveVisuals.Begin(), end = mRemoveVisuals.End(); removalIter != end; removalIter++)
+
+  if(!mRemoveVisuals.Empty())
   {
-    Toolkit::GetImplementation((*removalIter)->visual).SetOffScene(self);
+    std::reverse(mRemoveVisuals.Begin(), mRemoveVisuals.End());
+
+    while(!mRemoveVisuals.Empty())
+    {
+      auto removalIter = mRemoveVisuals.End() - 1u;
+      Toolkit::GetImplementation((*removalIter)->visual).SetOffScene(self);
+
+      // Discard removed visual. It will be destroyed at next Idle time.
+      DiscardVisual(removalIter, mRemoveVisuals);
+    }
   }
 
   for(auto replacedIter = mVisuals.Begin(), end = mVisuals.End(); replacedIter != end; replacedIter++)
   {
     (*replacedIter)->pending = false;
   }
-
-  mRemoveVisuals.Clear();
 }
 
 void Control::Impl::SetMargin(Extents margin)
@@ -2006,7 +2183,7 @@ Dali::Property Control::Impl::GetVisualProperty(Dali::Property::Index index, Dal
   if(visual)
   {
     Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
-    return visualImpl.GetPropertyObject(visualPropertyKey);
+    return visualImpl.GetPropertyObject(std::move(visualPropertyKey));
   }
 
   Handle handle;
@@ -2064,53 +2241,61 @@ void Control::Impl::EmitResourceReadySignal()
     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
+    // get called again recursively. If so, mIdleCallbackRegistered 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)
+    mIsEmittingResourceReadySignal = false;
+  }
+  else
+  {
+    if(!mIdleCallbackRegistered)
     {
+      mIdleCallbackRegistered = true;
+
       // 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);
+        if(DALI_UNLIKELY(!Adaptor::Get().AddIdle(mIdleCallback, true)))
+        {
+          DALI_LOG_ERROR("Fail to add idle callback for control resource ready. Skip this callback.\n");
+          mIdleCallback           = nullptr;
+          mIdleCallbackRegistered = false;
+        }
       }
     }
-
-    mIsEmittingResourceReadySignal = false;
-  }
-  else
-  {
-    mNeedToEmitResourceReady = true;
   }
 }
 
-void Control::Impl::OnIdleCallback()
+bool Control::Impl::OnIdleCallback()
 {
-  if(mNeedToEmitResourceReady)
+  // Reset the flag
+  mIdleCallbackRegistered = false;
+
+  // A visual is ready so control may need relayouting if staged
+  if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
   {
-    // Reset the flag
-    mNeedToEmitResourceReady = false;
+    mControlImpl.RelayoutRequest();
+  }
 
-    // A visual is ready so control may need relayouting if staged
-    if(mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
-    {
-      mControlImpl.RelayoutRequest();
-    }
+  EmitResourceReadySignal();
 
-    EmitResourceReadySignal();
+  if(!mIdleCallbackRegistered)
+  {
+    // Set the pointer to null as the callback manager deletes the callback after execute it.
+    mIdleCallback = nullptr;
   }
 
-  // Set the pointer to null as the callback manager deletes the callback after execute it.
-  mIdleCallback = nullptr;
+  // Repeat idle if mIdleCallbackRegistered become true one more time.
+  return mIdleCallbackRegistered;
 }
 
 Toolkit::DevelControl::ControlAccessible* Control::Impl::GetAccessibleObject()
 {
-  if(!mAccessibleObject)
+  if(mAccessibleCreatable && !mAccessibleObject)
   {
     mAccessibleObject.reset(mControlImpl.CreateAccessibleObject());
   }
@@ -2118,6 +2303,220 @@ Toolkit::DevelControl::ControlAccessible* Control::Impl::GetAccessibleObject()
   return mAccessibleObject.get();
 }
 
+bool Control::Impl::IsAccessibleCreated() const
+{
+  return !!mAccessibleObject;
+}
+
+void Control::Impl::EnableCreateAccessible(bool enable)
+{
+  mAccessibleCreatable = enable;
+}
+
+bool Control::Impl::IsCreateAccessibleEnabled() const
+{
+  return mAccessibleCreatable;
+}
+
+void Control::Impl::ApplyFittingMode(const Vector2& size)
+{
+  Actor self = mControlImpl.Self();
+  for(RegisteredVisualContainer::Iterator iter = mVisuals.Begin(); iter != mVisuals.End(); iter++)
+  {
+    // Check whether the visual is empty and enabled
+    if((*iter)->visual && (*iter)->enabled)
+    {
+      Internal::Visual::Base& visualImpl = Toolkit::GetImplementation((*iter)->visual);
+
+      // If the current visual is using the transform property map, fittingMode will not be applied.
+      if(visualImpl.IsIgnoreFittingMode())
+      {
+        continue;
+      }
+
+      Visual::FittingMode fittingMode  = visualImpl.GetFittingMode();
+      Property::Map       transformMap = Property::Map();
+
+      // If the fittingMode is DONT_CARE, we don't need to apply fittingMode, just Set empty transformMap
+      if(fittingMode == Visual::FittingMode::DONT_CARE)
+      {
+        if(visualImpl.GetType() != Toolkit::Visual::Type::TEXT)
+        {
+          ((*iter)->visual).SetTransformAndSize(transformMap, size);
+        }
+        continue;
+      }
+
+      Extents padding = self.GetProperty<Extents>(Toolkit::Control::Property::PADDING);
+
+      bool zeroPadding = (padding == Extents());
+
+      Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>(
+        self.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+      if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
+      {
+        std::swap(padding.start, padding.end);
+      }
+
+      // remove padding from the size to know how much is left for the visual
+      Vector2 finalSize   = size - Vector2(padding.start + padding.end, padding.top + padding.bottom);
+      Vector2 finalOffset = Vector2(padding.start, padding.top);
+
+      // Reset PIXEL_AREA after using OVER_FIT_KEEP_ASPECT_RATIO
+      if(visualImpl.IsPixelAreaSetForFittingMode())
+      {
+        visualImpl.SetPixelAreaForFittingMode(FULL_TEXTURE_RECT);
+      }
+
+      if((!zeroPadding) || // If padding is not zero
+         (fittingMode != Visual::FittingMode::FILL))
+      {
+        visualImpl.SetTransformMapUsageForFittingMode(true);
+
+        Vector2 naturalSize;
+        // NaturalSize will not be used for FILL fitting mode, which is default.
+        // Skip GetNaturalSize
+        if(fittingMode != Visual::FittingMode::FILL)
+        {
+          ((*iter)->visual).GetNaturalSize(naturalSize);
+        }
+
+        // If FittingMode use FIT_WIDTH or FIT_HEIGTH, it need to change proper fittingMode
+        if(fittingMode == Visual::FittingMode::FIT_WIDTH || fittingMode == Visual::FittingMode::FIT_HEIGHT)
+        {
+          const float widthRatio  = !Dali::EqualsZero(naturalSize.width) ? (finalSize.width / naturalSize.width) : 0.0f;
+          const float heightRatio = !Dali::EqualsZero(naturalSize.height) ? (finalSize.height / naturalSize.height) : 0.0f;
+          if(widthRatio < heightRatio)
+          {
+            // Final size has taller form than natural size.
+            fittingMode = (fittingMode == Visual::FittingMode::FIT_WIDTH) ? Visual::FittingMode::FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO;
+          }
+          else
+          {
+            // Final size has wider form than natural size.
+            fittingMode = (fittingMode == Visual::FittingMode::FIT_WIDTH) ? Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::FIT_KEEP_ASPECT_RATIO;
+          }
+        }
+
+        // Calculate size for fittingMode
+        switch(fittingMode)
+        {
+          case Visual::FittingMode::FIT_KEEP_ASPECT_RATIO:
+          {
+            auto availableVisualSize = finalSize;
+
+            // scale to fit the padded area
+            finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0),
+                                               (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0));
+
+            // calculate final offset within the padded area
+            finalOffset += (availableVisualSize - finalSize) * .5f;
+
+            // populate the transform map
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
+            break;
+          }
+          case Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO:
+          {
+            auto availableVisualSize = finalSize;
+            finalSize                = naturalSize * std::max((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
+                                               (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
+
+            auto originalOffset = finalOffset;
+
+            if(!visualImpl.IsPixelAreaSetForFittingMode() && !Dali::EqualsZero(finalSize.width) && !Dali::EqualsZero(finalSize.height))
+            {
+              float   x           = abs((availableVisualSize.width - finalSize.width) / finalSize.width) * .5f;
+              float   y           = abs((availableVisualSize.height - finalSize.height) / finalSize.height) * .5f;
+              float   widthRatio  = 1.f - abs((availableVisualSize.width - finalSize.width) / finalSize.width);
+              float   heightRatio = 1.f - abs((availableVisualSize.height - finalSize.height) / finalSize.height);
+              Vector4 pixelArea   = Vector4(x, y, widthRatio, heightRatio);
+              visualImpl.SetPixelAreaForFittingMode(pixelArea);
+            }
+
+            // populate the transform map
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, originalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, availableVisualSize);
+            break;
+          }
+          case Visual::FittingMode::CENTER:
+          {
+            auto availableVisualSize = finalSize;
+            if(availableVisualSize.width > naturalSize.width && availableVisualSize.height > naturalSize.height)
+            {
+              finalSize = naturalSize;
+            }
+            else
+            {
+              finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
+                                                 (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
+            }
+
+            finalOffset += (availableVisualSize - finalSize) * .5f;
+
+            // populate the transform map
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
+            break;
+          }
+          case Visual::FittingMode::FILL:
+          {
+            transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
+              .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
+            break;
+          }
+          case Visual::FittingMode::FIT_WIDTH:
+          case Visual::FittingMode::FIT_HEIGHT:
+          case Visual::FittingMode::DONT_CARE:
+          {
+            // This FittingMode already converted
+            break;
+          }
+        }
+
+        // Set extra value for applying transformMap
+        transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
+                         Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
+          .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
+          .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN)
+          .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
+               Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE));
+      }
+      else if(visualImpl.IsTransformMapSetForFittingMode() && zeroPadding) // Reset offset to zero only if padding applied previously
+      {
+        visualImpl.SetTransformMapUsageForFittingMode(false);
+
+        // Reset the transform map
+        transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, Vector2::ZERO)
+          .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
+               Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE))
+          .Add(Toolkit::Visual::Transform::Property::SIZE, Vector2::ONE)
+          .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
+               Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE));
+      }
+
+      ((*iter)->visual).SetTransformAndSize(transformMap, size);
+    }
+  }
+}
+
+void Control::Impl::RegisterProcessorOnce()
+{
+  if(!mProcessorRegistered)
+  {
+    Adaptor::Get().RegisterProcessorOnce(*this, true);
+    mProcessorRegistered = true;
+  }
+}
+
+void Control::Impl::Process(bool postProcessor)
+{
+  // Call ApplyFittingMode
+  ApplyFittingMode(mSize);
+  mProcessorRegistered = false;
+}
+
 } // namespace Internal
 
 } // namespace Toolkit