API to allow Actor to insert child at a specific index 60/26460/12
authorKevin Butchart <k.butchart@partner.samsung.com>
Fri, 22 Aug 2014 17:11:37 +0000 (18:11 +0100)
committerKevin Butchart <k.butchart@partner.samsung.com>
Fri, 12 Sep 2014 14:56:26 +0000 (15:56 +0100)
Change-Id: I5ae0582bc50e3d31d8eb9d62d4662ba85af5b896

automated-tests/src/dali/utc-Dali-Actor.cpp
dali/internal/event/actors/actor-impl.cpp
dali/internal/event/actors/actor-impl.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/internal/update/nodes/node.cpp
dali/internal/update/nodes/node.h
dali/public-api/actors/actor.cpp
dali/public-api/actors/actor.h

index d810d07..94f37c2 100644 (file)
@@ -405,6 +405,33 @@ int UtcDaliActorAdd(void)
   END_TEST;
 }
 
+int UtcDaliActorInsert(void)
+{
+  tet_infoline("Testing Actor::Insert");
+  TestApplication application;
+
+  Actor parent = Actor::New();
+  Stage::GetCurrent().Add( parent );
+  Actor first = Actor::New();
+  Actor second = Actor::New();
+  Actor third = Actor::New();
+
+  parent.Insert(1, first); // test insert beyond range
+  DALI_TEST_EQUALS( parent.GetChildCount(), 1u, TEST_LOCATION );
+  parent.Insert(0, second);
+  DALI_TEST_EQUALS( parent.GetChildCount(), 2u, TEST_LOCATION );
+  parent.Insert(1, third);
+
+  DALI_TEST_EQUALS( parent.GetChildCount(), 3u, TEST_LOCATION );
+
+  DALI_TEST_CHECK(parent.GetChildAt(0) == first);
+  DALI_TEST_CHECK(parent.GetChildAt(1) == second);
+  DALI_TEST_CHECK(parent.GetChildAt(2) == third);
+
+  END_TEST;
+}
+
+
 int UtcDaliActorRemove01(void)
 {
   tet_infoline("Testing Actor::Remove");
index cdb8105..9675844 100644 (file)
@@ -331,6 +331,46 @@ void Actor::Add(Actor& child)
   }
 }
 
+void Actor::Insert(unsigned int index, Actor& child)
+{
+  DALI_ASSERT_ALWAYS( this != &child && "Cannot add actor to itself" );
+  DALI_ASSERT_ALWAYS( !child.IsRoot() && "Cannot add root actor" );
+
+  if( !mChildren )
+  {
+    mChildren = new ActorContainer;
+  }
+
+  Actor* const oldParent( child.mParent );
+
+  // since an explicit position has been given, always insert, even if already a child
+  if( oldParent )
+  {
+    oldParent->Remove( child ); // This causes OnChildRemove callback
+  }
+
+  // Guard against Add() during previous OnChildRemove callback
+  if ( !child.mParent )
+  {
+    // Do this first, since user callbacks from within SetParent() may need to remove child
+    if (index < child.GetChildCount())
+    {
+      ActorIter it = mChildren->begin();
+      std::advance(it, index);
+      mChildren->insert(it, Dali::Actor(&child));
+    }
+    else
+    {
+      mChildren->push_back(Dali::Actor(&child));
+    }
+    // SetParent asserts that child can be added
+    child.SetParent(this, index);
+
+    // Notification for derived classes
+    OnChildAdd(child);
+  }
+}
+
 void Actor::Remove(Actor& child)
 {
   DALI_ASSERT_ALWAYS( this != &child && "Cannot remove actor from itself" );
@@ -2022,14 +2062,14 @@ Actor::~Actor()
   delete mAnchorPoint;
 }
 
-void Actor::ConnectToStage( Stage& stage )
+void Actor::ConnectToStage( Stage& stage, int index )
 {
   // This container is used instead of walking the Actor hierachy.
   // It protects us when the Actor hierachy is modified during OnStageConnectionExternal callbacks.
   ActorContainer connectionList;
 
   // This stage is atomic i.e. not interrupted by user callbacks
-  RecursiveConnectToStage( stage, connectionList );
+  RecursiveConnectToStage( stage, connectionList, index );
 
   // Notify applications about the newly connected actors.
   const ActorIter endIter = connectionList.end();
@@ -2040,13 +2080,13 @@ void Actor::ConnectToStage( Stage& stage )
   }
 }
 
-void Actor::RecursiveConnectToStage( Stage& stage, ActorContainer& connectionList )
+void Actor::RecursiveConnectToStage( Stage& stage, ActorContainer& connectionList, int index )
 {
   DALI_ASSERT_ALWAYS( !OnStage() );
 
   mIsOnStage = true;
 
-  ConnectToSceneGraph();
+  ConnectToSceneGraph(index);
 
   // Notification for internal derived classes
   OnStageConnectionInternal();
@@ -2072,7 +2112,7 @@ void Actor::RecursiveConnectToStage( Stage& stage, ActorContainer& connectionLis
  * The child must connect its Node to the parent's Node.
  * This is resursive; the child calls ConnectToStage() for its children.
  */
-void Actor::ConnectToSceneGraph()
+void Actor::ConnectToSceneGraph(int index)
 {
   DALI_ASSERT_DEBUG( mNode != NULL);
   DALI_ASSERT_DEBUG( mParent != NULL);
@@ -2081,7 +2121,7 @@ void Actor::ConnectToSceneGraph()
   if( NULL != mNode )
   {
     // Reparent Node in next Update
-    ConnectNodeMessage( mStage->GetUpdateManager(), *(mParent->mNode), *mNode );
+    ConnectNodeMessage( mStage->GetUpdateManager(), *(mParent->mNode), *mNode, index );
   }
 
   // Notify attachment
@@ -3275,7 +3315,7 @@ int Actor::GetPropertyComponentIndex( Property::Index index ) const
   return componentIndex;
 }
 
-void Actor::SetParent(Actor* parent)
+void Actor::SetParent(Actor* parent, int index)
 {
   if( parent )
   {
@@ -3289,7 +3329,7 @@ void Actor::SetParent(Actor* parent)
       StagePtr stage = parent->mStage;
 
       // Instruct each actor to create a corresponding node in the scene graph
-      ConnectToStage(*stage);
+      ConnectToStage(*stage, index);
     }
   }
   else // parent being set to NULL
index b2f75a8..0b60100 100644 (file)
@@ -163,6 +163,16 @@ public:
   void Add(Actor& child);
 
   /**
+   * Inserts a child Actor to this Actor's child list
+   * @pre The child actor is not the same as the parent actor.
+   * @pre The child actor does not already have a parent.
+   * @param [in] index in childlist to insert child at
+   * @param [in] child The child.
+   * @post The child will be referenced by its parent.
+   */
+  void Insert(unsigned int index, Actor& child);
+
+  /**
    * Removes a child Actor from this Actor.
    * @param [in] child The child.
    * @post The child will be unreferenced.
@@ -1026,21 +1036,24 @@ protected:
   /**
    * Called on a child during Add() when the parent actor is connected to the Stage.
    * @param[in] stage The stage.
+   * @param[in] index If set, it is only used for positioning the actor within the parent's child list.
    */
-  void ConnectToStage(Stage& stage);
+  void ConnectToStage(Stage& stage, int index = -1);
 
   /**
    * Helper for ConnectToStage, to recursively connect a tree of actors.
    * This is atomic i.e. not interrupted by user callbacks.
    * @param[in] stage The stage.
+   * @param[in] index If set, it is only used for positioning the actor within the parent's child list.
    * @param[out] connectionList On return, the list of connected actors which require notification.
    */
-  void RecursiveConnectToStage( Stage& stage, ActorContainer& connectionList );
+  void RecursiveConnectToStage( Stage& stage, ActorContainer& connectionList, int index = -1 );
 
   /**
    * Connect the Node associated with this Actor to the scene-graph.
+   * @param[in] index If set, it is only used for positioning the actor within the parent's child list.
    */
-  void ConnectToSceneGraph();
+  void ConnectToSceneGraph(int index = -1);
 
   /**
    * Helper for ConnectToStage, to notify a connected actor through the public API.
@@ -1176,8 +1189,9 @@ private:
   /**
    * Set the actors parent.
    * @param[in] parent The new parent.
+   * @param[in] index If set, it is only used for positioning the actor within the parent's child list.
    */
-  void SetParent(Actor* parent);
+  void SetParent(Actor* parent, int index = -1);
 
   /**
    * Helper to create a Node for this Actor.
index a3f8b6e..4a37f8c 100644 (file)
@@ -369,7 +369,7 @@ void UpdateManager::AddNode( Node* node )
   mImpl->activeDisconnectedNodes.insert( node ); // Takes ownership of node
 }
 
-void UpdateManager::ConnectNode( Node* parent, Node* node )
+void UpdateManager::ConnectNode( Node* parent, Node* node, int index )
 {
   DALI_ASSERT_ALWAYS( NULL != parent );
   DALI_ASSERT_ALWAYS( NULL != node );
@@ -386,7 +386,7 @@ void UpdateManager::ConnectNode( Node* parent, Node* node )
 
   node->SetActive( true );
 
-  parent->ConnectChild( node );
+  parent->ConnectChild( node, index );
 }
 
 void UpdateManager::DisconnectNode( Node* node )
index 8fca6f0..80e80eb 100644 (file)
@@ -177,7 +177,7 @@ public:
    * @param[in] node The new parent node.
    * @param[in] node The node to connect.
    */
-  void ConnectNode( Node* parent, Node* node );
+  void ConnectNode( Node* parent, Node* node, int index );
 
   /**
    * Disconnect a Node from the scene-graph.
@@ -528,19 +528,19 @@ inline void AddNodeMessage( UpdateManager& manager, Node& node )
   new (slot) LocalType( &manager, &UpdateManager::AddNode, &node );
 }
 
-inline void ConnectNodeMessage( UpdateManager& manager, const Node& constParent, const Node& constChild )
+inline void ConnectNodeMessage( UpdateManager& manager, const Node& constParent, const Node& constChild, int index )
 {
   // Update thread can edit the object
   Node& parent = const_cast< Node& >( constParent );
   Node& child = const_cast< Node& >( constChild );
 
-  typedef MessageValue2< UpdateManager, Node*, Node* > LocalType;
+  typedef MessageValue3< UpdateManager, Node*, Node*, int > LocalType;
 
   // Reserve some memory inside the message queue
   unsigned int* slot = manager.GetEventToUpdate().ReserveMessageSlot( sizeof( LocalType ) );
 
   // Construct message in the message queue memory; note that delete should not be called on the return value
-  new (slot) LocalType( &manager, &UpdateManager::ConnectNode, &parent, &child );
+  new (slot) LocalType( &manager, &UpdateManager::ConnectNode, &parent, &child, index );
 }
 
 inline void DisconnectNodeMessage( UpdateManager& manager, const Node& constNode )
index 0dd87c1..45004b5 100644 (file)
@@ -107,7 +107,7 @@ void Node::SetRoot(bool isRoot)
   mIsRoot = isRoot;
 }
 
-void Node::ConnectChild( Node* childNode )
+void Node::ConnectChild( Node* childNode, int index )
 {
   DALI_ASSERT_ALWAYS( this != childNode );
   DALI_ASSERT_ALWAYS( IsRoot() || NULL != mParent ); // Parent should be connected first
@@ -118,7 +118,14 @@ void Node::ConnectChild( Node* childNode )
   // Everything should be reinherited when reconnected to scene-graph
   childNode->SetAllDirtyFlags();
 
-  mChildren.PushBack( childNode );
+  if (index == -1)
+  {
+    mChildren.PushBack( childNode );
+  }
+  else
+  {
+    mChildren.Insert(mChildren.Begin()+index, childNode);
+  }
 }
 
 void Node::DisconnectChild( BufferIndex updateBufferIndex, Node& childNode, std::set<Node*>& connectedNodes,  std::set<Node*>& disconnectedNodes )
index c28e2ea..ce2125a 100644 (file)
@@ -224,8 +224,10 @@ public:
    * @pre The childNode does not already have a parent.
    * @pre The childNode is not a root node.
    * @param[in] childNode The child to add.
+   * @param[in] index to insert at, if not supplied or -1 it will be appended
+   *
    */
-  void ConnectChild( Node* childNode );
+  void ConnectChild( Node* childNode, int index = -1);
 
   /**
    * Disconnect a child (& its children) from the scene-graph.
index 6bb0eb5..6469997 100644 (file)
@@ -135,6 +135,11 @@ void Actor::Add(Actor actor)
   GetImplementation(*this).Add(GetImplementation(actor));
 }
 
+void Actor::Insert(unsigned int index, Actor actor)
+{
+  GetImplementation(*this).Insert(index, GetImplementation(actor));
+}
+
 void Actor::Remove(Actor actor)
 {
   GetImplementation(*this).Remove(GetImplementation(actor));
index 1634e82..da29650 100644 (file)
@@ -417,6 +417,25 @@ public:
   void Add(Actor child);
 
   /**
+   * @brief Inserts a child Actor to this actor's list of children at the given index
+   *
+   * NOTE! if the child already has a parent, it will be removed from old parent
+   * and reparented to this actor. This may change childs position, color,
+   * scale etc as it now inherits them from this actor
+   * @pre This Actor (the parent) has been initialized.
+   * @pre The child actor has been initialized.
+   * @pre The child actor is not the same as the parent actor.
+   * @pre The actor is not the Root actor
+   * @param [in] index of actor to insert before
+   * @param [in] child The child.
+   * @post The child will be referenced by its parent. This means that the child will be kept alive,
+   * even if the handle passed into this method is reset or destroyed.
+   * @post If the index is greater than the current child count, it will be ignored and added at the end.
+   * @post This may invalidate ActorContainer iterators.
+   */
+  void Insert(unsigned int index, Actor child);
+
+  /**
    * @brief Removes a child Actor from this Actor.
    *
    * If the actor was not a child of this actor, this is a no-op.