[dali_2.2.48] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / control / control-data-impl.cpp
index 23f9204..548a4c9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 
 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
@@ -129,6 +129,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)
@@ -201,6 +233,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.
@@ -524,7 +573,7 @@ Control::Impl::Impl(Control& controlImpl)
   mIsKeyboardNavigationSupported(false),
   mIsKeyboardFocusGroup(false),
   mIsEmittingResourceReadySignal(false),
-  mNeedToEmitResourceReady(false),
+  mIdleCallbackRegistered(false),
   mDispatchKeyEvents(true)
 {
   Dali::Accessibility::Accessible::RegisterExternalAccessibleGetter(&ExternalAccessibleGetter);
@@ -554,11 +603,15 @@ 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;
 }
 
@@ -683,6 +736,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);
@@ -805,7 +860,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);
     }
@@ -816,6 +871,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))
   {
@@ -833,8 +890,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);
   }
 }
 
@@ -894,6 +952,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);
@@ -910,20 +985,13 @@ void Control::Impl::StartObservingVisual(Toolkit::Visual::Base& visual)
   visualImpl.AddEventObserver(*this);
 }
 
-void Control::Impl::ResourceReady(bool relayoutRequest)
+void Control::Impl::ResourceReady()
 {
-  Actor self = mControlImpl.Self();
-  // A visual is ready so control may need relayouting if staged
-  if(relayoutRequest && self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
-  {
-    mControlImpl.RelayoutRequest();
-  }
+  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())
   {
-    // Reset the flag
-    mNeedToEmitResourceReady = false;
     EmitResourceReadySignal();
   }
 }
@@ -935,30 +1003,39 @@ 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
+  RelayoutRequest(object);
+
   // Called by a Visual when it's resource is ready
-  ResourceReady(true);
+  if(((*registeredIter)->enabled))
+  {
+    ResourceReady();
+  }
 }
 
 void Control::Impl::NotifyVisualEvent(Visual::Base& object, Property::Index signalId)
@@ -977,7 +1054,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
@@ -1127,6 +1207,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)
@@ -1427,6 +1509,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));
@@ -1798,6 +1882,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)
@@ -1935,17 +2021,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)
@@ -2080,48 +2174,51 @@ 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);
+        Adaptor::Get().AddIdle(mIdleCallback, true);
       }
     }
-
-    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()