Adding Progress notification to Animation 12/132812/14
authorAgnelo Vaz <agnelo.vaz@samsung.com>
Wed, 7 Jun 2017 18:06:17 +0000 (19:06 +0100)
committerAgnelo Vaz <agnelo.vaz@samsung.com>
Fri, 9 Jun 2017 16:42:31 +0000 (17:42 +0100)
Change-Id: Iff153ceebfc89938e43a6ce50a0e0fe962e6ef81

13 files changed:
automated-tests/src/dali/utc-Dali-Animation.cpp
dali/devel-api/animation/animation-devel.cpp [new file with mode: 0644]
dali/devel-api/animation/animation-devel.h [new file with mode: 0644]
dali/devel-api/file.list
dali/internal/event/animation/animation-impl.cpp
dali/internal/event/animation/animation-impl.h
dali/internal/event/animation/animation-playlist.cpp
dali/internal/event/animation/animation-playlist.h
dali/internal/event/common/complete-notification-interface.h
dali/internal/update/animation/scene-graph-animation.cpp
dali/internal/update/animation/scene-graph-animation.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h

index 01180b8ad27dc004e29c7eb65a3c0d44b37e17e8..a11ca7856cd90c73ee62bfdc4930186869d59353 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/public-api/dali-core.h>
 #include <dali/devel-api/actors/actor-devel.h>
 #include <dali-test-suite-utils.h>
+#include <dali/devel-api/animation/animation-devel.h>
 
 using std::max;
 using namespace Dali;
@@ -89,6 +90,55 @@ struct AnimationFinishCheck
   bool& mSignalReceived; // owned by individual tests
 };
 
+// Functor to test whether a Progress signal is emitted
+struct AnimationProgressCheck
+{
+  AnimationProgressCheck(bool& signalReceived, std::string name = " ")
+  : mSignalReceived(signalReceived),
+    mName( name )
+  {
+  }
+
+  void operator()(Animation& animation)
+  {
+    mSignalReceived = true;
+  }
+
+  void Reset()
+  {
+    mSignalReceived = false;
+  }
+
+  void CheckSignalReceived()
+  {
+    if (!mSignalReceived)
+    {
+      tet_printf("Expected Progress reached signal was not received %s \n", mName.c_str() );
+      tet_result(TET_FAIL);
+    }
+    else
+    {
+      tet_result(TET_PASS);
+    }
+  }
+
+  void CheckSignalNotReceived()
+  {
+    if (mSignalReceived)
+    {
+      tet_printf("Unexpected Progress reached signal was received %s \n", mName.c_str());
+      tet_result(TET_FAIL);
+    }
+    else
+    {
+      tet_result(TET_PASS);
+    }
+  }
+
+  bool& mSignalReceived; // owned by individual tests
+  std::string mName;
+};
+
 } // anon namespace
 
 int UtcDaliAnimationConstructorP(void)
@@ -11210,3 +11260,323 @@ int UtcDaliAnimationAnimateBetweenVector2P(void)
 
   END_TEST;
 }
+
+int UtcDaliAnimationProgressCallbackP(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  Animation animation = Animation::New(0.0f);
+
+  //Set duration
+  float durationSeconds(1.0f);
+  animation.SetDuration(durationSeconds);
+
+  bool finishedSignalReceived(false);
+  bool progressSignalReceived(false);
+
+  AnimationFinishCheck finishCheck(finishedSignalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  AnimationProgressCheck progressCheck(progressSignalReceived);
+  DevelAnimation::ProgressReachedSignal( animation ).Connect( &application, progressCheck);
+  application.SendNotification();
+
+  Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  tet_infoline( "Animation Progress notification set to 30%" );
+  DevelAnimation::SetProgressNotification( animation, 0.3f );
+
+  application.SendNotification();
+  application.Render( );
+
+  DALI_TEST_EQUALS( 0.3f, DevelAnimation::GetProgressNotification( animation ), TEST_LOCATION );
+
+  progressCheck.CheckSignalNotReceived();
+
+  // Start the animation from 10% progress
+  animation.SetCurrentProgress( 0.1f );
+  animation.Play();
+
+  tet_infoline( "Animation Playing from 10%" );
+
+  application.SendNotification();
+  application.Render(0); // start animation
+  application.Render(durationSeconds*100.0f ); // 20% progress
+
+  tet_infoline( "Animation at 20%" );
+
+  progressCheck.CheckSignalNotReceived();
+
+  application.SendNotification();
+  application.Render(durationSeconds*200.0f ); // 40% progress
+  application.SendNotification();
+  tet_infoline( "Animation at 40%" );
+  DALI_TEST_EQUALS( 0.4f, animation.GetCurrentProgress(), TEST_LOCATION );
+
+  progressCheck.CheckSignalReceived();
+
+  tet_infoline( "Progress check reset" );
+  progressCheck.Reset();
+
+  application.Render(durationSeconds*100.0f ); // 50% progress
+  tet_infoline( "Animation at 50%" );
+  application.SendNotification();
+
+  DALI_TEST_EQUALS( 0.5f, animation.GetCurrentProgress(), TEST_LOCATION );
+
+  progressCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*100.0f)/* 60% progress */);
+  application.SendNotification();
+
+  tet_infoline( "Animation at 60%" );
+
+  finishCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 80% progress */);
+  application.SendNotification();
+  DALI_TEST_EQUALS( 0.8f, animation.GetCurrentProgress(), TEST_LOCATION );
+  tet_infoline( "Animation at 80%" );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f) + 1u/*just beyond the animation duration*/);
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  tet_infoline( "Animation finished" );
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliAnimationProgressSignalConnectionWithoutProgressMarkerP(void)
+{
+  TestApplication application;
+
+  tet_infoline( "Connect to ProgressReachedSignal but do not set a required Progress marker" );
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  Animation animation = Animation::New(0.0f);
+
+  //Set duration
+  float durationSeconds(1.0f);
+  animation.SetDuration(durationSeconds);
+
+  bool finishedSignalReceived(false);
+  bool progressSignalReceived(false);
+
+  AnimationFinishCheck finishCheck(finishedSignalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  AnimationProgressCheck progressCheck( progressSignalReceived );
+  DevelAnimation::ProgressReachedSignal( animation ).Connect( &application, progressCheck);
+  application.SendNotification();
+
+  Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  progressCheck.CheckSignalNotReceived();
+
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(0); // start animation
+  application.Render(durationSeconds*100.0f ); // 10% progress
+  application.SendNotification();
+
+  tet_infoline( "Ensure after animation has started playing that ProgressReachedSignal not emitted" );
+  progressCheck.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*900.0f) + 1u/*just beyond the animation duration*/);
+
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  tet_infoline( "Animation finished" );
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliAnimationMultipleProgressSignalsP(void)
+{
+  tet_infoline( "Multiple animations with different progress markers" );
+
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  Animation animationAlpha = Animation::New(0.0f);
+  Animation animationBeta = Animation::New(0.0f);
+
+  //Set duration
+  float durationSeconds(1.0f);
+  animationAlpha.SetDuration(durationSeconds);
+  animationBeta.SetDuration(durationSeconds);
+
+  bool progressSignalReceivedAlpha(false);
+  bool progressSignalReceivedBeta(false);
+
+  AnimationProgressCheck progressCheckAlpha(progressSignalReceivedAlpha, "animation:Alpha");
+  AnimationProgressCheck progressCheckBeta(progressSignalReceivedBeta, "animation:Beta" );
+
+  DevelAnimation::ProgressReachedSignal( animationAlpha ).Connect( &application, progressCheckAlpha );
+  DevelAnimation::ProgressReachedSignal( animationBeta ).Connect( &application, progressCheckBeta);
+  application.SendNotification();
+
+  Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+  animationAlpha.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+  animationBeta.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  tet_infoline( "AnimationAlpha Progress notification set to 30%" );
+  DevelAnimation::SetProgressNotification( animationAlpha, 0.3f );
+
+  tet_infoline( "AnimationBeta Progress notification set to 50%" );
+  DevelAnimation::SetProgressNotification( animationBeta, 0.5f );
+
+  application.SendNotification();
+  application.Render( );
+
+  progressCheckAlpha.CheckSignalNotReceived();
+  progressCheckBeta.CheckSignalNotReceived();
+
+  // Start the animations from 10% progress
+  animationAlpha.SetCurrentProgress( 0.1f );
+  animationBeta.SetCurrentProgress( 0.1f );
+  animationAlpha.Play();
+  animationBeta.Play();
+
+  tet_infoline( "Animation Playing from 10%" );
+
+  application.SendNotification();
+  application.Render(0); // start animation
+  application.Render(durationSeconds*100.0f ); // 20% progress
+
+  tet_infoline( "Animation at 20% - No signals to be received" );
+
+  progressCheckAlpha.CheckSignalNotReceived();
+  progressCheckBeta.CheckSignalNotReceived();
+
+  application.SendNotification();
+  application.Render(durationSeconds*200.0f ); // 40% progress
+  application.SendNotification();
+  tet_infoline( "Animation at 40% - Alpha signal should be received" );
+  DALI_TEST_EQUALS( 0.4f, animationAlpha.GetCurrentProgress(), TEST_LOCATION );
+
+  progressCheckAlpha.CheckSignalReceived();
+  progressCheckBeta.CheckSignalNotReceived();
+
+  tet_infoline( "Progress check reset" );
+  progressCheckAlpha.Reset();
+  progressCheckBeta.Reset();
+
+  application.Render(durationSeconds*100.0f ); // 50% progress
+  tet_infoline( "Animation at 50% - Beta should receive signal, Alpha should not" );
+  application.SendNotification();
+
+  DALI_TEST_EQUALS( 0.5f, animationBeta.GetCurrentProgress(), TEST_LOCATION );
+
+  progressCheckAlpha.CheckSignalNotReceived();
+  progressCheckBeta.CheckSignalReceived();
+  tet_infoline( "Progress check reset" );
+  progressCheckAlpha.Reset();
+  progressCheckBeta.Reset();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*100.0f)/* 60% progress */);
+  application.SendNotification();
+
+  tet_infoline( "Animation at 60%" );
+
+  progressCheckAlpha.CheckSignalNotReceived();
+  progressCheckBeta.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 80% progress */);
+  application.SendNotification();
+  tet_infoline( "Animation at 80%" );
+
+  progressCheckAlpha.CheckSignalNotReceived();
+  progressCheckBeta.CheckSignalNotReceived();
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f) + 1u/*just beyond the animation duration*/);
+  // We did expect the animation to finish
+  tet_infoline( "Animation finished" );
+
+  END_TEST;
+}
+
+
+int UtcDaliAnimationProgressCallbackLongDurationP(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  Animation animation = Animation::New(0.0f);
+
+  //Set duration
+  float durationSeconds(5.0f);
+  animation.SetDuration(durationSeconds);
+
+  bool finishedSignalReceived(false);
+  bool progressSignalReceived(false);
+
+  AnimationFinishCheck finishCheck(finishedSignalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  AnimationProgressCheck progressCheck(progressSignalReceived);
+  DevelAnimation::ProgressReachedSignal( animation ).Connect( &application, progressCheck);
+  application.SendNotification();
+
+  Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+  animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR);
+
+  tet_infoline( "Animation Progress notification set to 50%" );
+  DevelAnimation::SetProgressNotification( animation, 0.5f );
+
+  application.SendNotification();
+  application.Render( );
+
+  progressCheck.CheckSignalNotReceived();
+
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(0); // start animation
+  application.Render(durationSeconds*0.25*1000.0f ); // 25% progress
+  DALI_TEST_EQUALS( 0.25f, animation.GetCurrentProgress(), TEST_LOCATION );
+
+  tet_infoline( "Animation at 25%" );
+
+  progressCheck.CheckSignalNotReceived();
+
+  application.SendNotification();
+  application.Render(durationSeconds*0.25*1000.0f ); // 50% progress
+  application.SendNotification();
+  tet_infoline( "Animation at 50%" );
+  DALI_TEST_EQUALS( 0.5f, animation.GetCurrentProgress(), TEST_LOCATION );
+
+  progressCheck.CheckSignalReceived();
+
+  tet_infoline( "Progress check reset" );
+  progressCheck.Reset();
+
+  application.Render(durationSeconds*0.25*1000.0f ); // 75% progress
+  tet_infoline( "Animation at 75%" );
+  application.SendNotification();
+
+  DALI_TEST_EQUALS( 0.75f, animation.GetCurrentProgress(), TEST_LOCATION );
+
+  progressCheck.CheckSignalNotReceived();
+
+  END_TEST;
+}
diff --git a/dali/devel-api/animation/animation-devel.cpp b/dali/devel-api/animation/animation-devel.cpp
new file mode 100644 (file)
index 0000000..0840ae7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/animation/animation-devel.h>
+#include <dali/internal/event/animation/animation-impl.h>
+
+namespace Dali
+{
+
+namespace DevelAnimation
+{
+
+void SetProgressNotification( Animation animation, float progress )
+{
+  GetImplementation(animation).SetProgressNotification( progress );
+}
+
+float GetProgressNotification( Animation animation )
+{
+  return GetImplementation(animation).GetProgressNotification();
+}
+
+Animation::AnimationSignalType& ProgressReachedSignal( Animation animation )
+{
+  return GetImplementation( animation ).ProgressReachedSignal();
+}
+
+} // namespace DevelAnimation
+
+} // namespace Dali
diff --git a/dali/devel-api/animation/animation-devel.h b/dali/devel-api/animation/animation-devel.h
new file mode 100644 (file)
index 0000000..b106f6f
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef DALI_ANIMATION_DEVEL_H
+#define DALI_ANIMATION_DEVEL_H
+
+/*
+ * Copyright (c) 2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/public-api/animation/animation.h>
+
+namespace Dali
+{
+
+namespace DevelAnimation
+{
+
+/**
+ * @brief Set progress percentage marker to trigger ProgressHasBeenReachedSignal
+ *
+ * @param[in] animation the animation object to perform this operation on
+ * @param[in] progress the progress percentage to trigger the signal at, e.g .3 for 30%.
+ */
+DALI_IMPORT_API void SetProgressNotification( Animation animation, float progress );
+
+/**
+ * @brief Get progress percentage marker that has been set to trigger ProgressHasBeenReachedSignal
+ *
+ * @param[in] animation the animation object to perform this operation on
+ * @return the percentage to trigger at eg 0.3 for 30%
+ */
+DALI_IMPORT_API float GetProgressNotification( Animation animation );
+
+/**
+ * @brief Connects to this signal to be notified when an Animation's animations have reached set progress.
+ *
+ * @return A signal object to connect with
+ *
+ */
+DALI_IMPORT_API Animation::AnimationSignalType& ProgressReachedSignal( Animation animation );
+
+} // namespace DevelAnimation
+
+} // namespace Dali
+
+#endif // DALI_ANIMATION_DEVEL_H
index b44c3f9fac93df1e7986b51246605d03a964cc49..5a779ad099520d8bc1e1eda1a800f1ce32a6a415 100644 (file)
@@ -2,6 +2,7 @@
 devel_api_src_files = \
   $(devel_api_src_dir)/actors/actor-devel.cpp \
   $(devel_api_src_dir)/animation/animation-data.cpp \
+  $(devel_api_src_dir)/animation/animation-devel.cpp \
   $(devel_api_src_dir)/animation/path-constrainer.cpp \
   $(devel_api_src_dir)/common/hash.cpp \
   $(devel_api_src_dir)/common/stage-devel.cpp \
@@ -27,7 +28,8 @@ devel_api_core_actors_header_files = \
 
 devel_api_core_animation_header_files = \
   $(devel_api_src_dir)/animation/animation-data.h \
-  $(devel_api_src_dir)/animation/path-constrainer.h
+  $(devel_api_src_dir)/animation/path-constrainer.h \
+  $(devel_api_src_dir)/animation/animation-devel.h
 
 devel_api_core_common_header_files = \
   $(devel_api_src_dir)/common/hash.h \
index a57f936670265faffcc0196920749dcb09746ce7..a985f71b484904fe53fdae22057a23bad7577d87 100644 (file)
@@ -128,7 +128,8 @@ Animation::Animation( EventThreadServices& eventThreadServices, AnimationPlaylis
   mEndAction( endAction ),
   mDisconnectAction( disconnectAction ),
   mDefaultAlpha( defaultAlpha ),
-  mState(Dali::Animation::STOPPED)
+  mState(Dali::Animation::STOPPED),
+  mProgressReachedMarker( 0.0f )
 {
 }
 
@@ -191,6 +192,17 @@ void Animation::SetDuration(float seconds)
   SetDurationMessage( mEventThreadServices, *mAnimation, seconds );
 }
 
+void Animation::SetProgressNotification( float progress )
+{
+  // mAnimation is being used in a separate thread; queue a message to set the value
+  mProgressReachedMarker = progress;
+}
+
+float Animation::GetProgressNotification()
+{
+  return mProgressReachedMarker;
+}
+
 float Animation::GetDuration() const
 {
   // This is not animatable; the cached value is up-to-date.
@@ -265,6 +277,8 @@ void Animation::Play()
 
   NotifyObjects();
 
+  SendFinalProgressNotificationMessage();
+
   // mAnimation is being used in a separate thread; queue a Play message
   PlayAnimationMessage( mEventThreadServices, *mAnimation );
 }
@@ -280,6 +294,8 @@ void Animation::PlayFrom( float progress )
 
     NotifyObjects();
 
+    SendFinalProgressNotificationMessage();
+
     // mAnimation is being used in a separate thread; queue a Play message
     PlayAnimationFromMessage( mEventThreadServices, *mAnimation, progress );
   }
@@ -770,6 +786,11 @@ Dali::Animation::AnimationSignalType& Animation::FinishedSignal()
   return mFinishedSignal;
 }
 
+Dali::Animation::AnimationSignalType& Animation::ProgressReachedSignal()
+{
+  return mProgressReachedSignal;
+}
+
 void Animation::EmitSignalFinish()
 {
   if ( !mFinishedSignal.Empty() )
@@ -779,6 +800,15 @@ void Animation::EmitSignalFinish()
   }
 }
 
+void Animation::EmitSignalProgressReached()
+{
+  if ( !mProgressReachedSignal.Empty() )
+  {
+    Dali::Animation handle( this );
+    mProgressReachedSignal.Emit( handle );
+  }
+}
+
 bool Animation::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
 {
   bool connected( true );
@@ -1000,6 +1030,16 @@ void Animation::NotifyObjects()
   }
 }
 
+
+void Animation::SendFinalProgressNotificationMessage()
+{
+  if ( mProgressReachedMarker > 0.0f )
+  {
+    float progressMarkerSeconds = mDurationSeconds * mProgressReachedMarker;
+    SetProgressNotificationMessage( mEventThreadServices, *mAnimation, progressMarkerSeconds );
+  }
+}
+
 } // namespace Internal
 
 } // namespace Dali
index c329a511631e7a5b90530d342426fb3c58dd1624..6135cc745956b7794f25fa3c1d96497d80a9a0ff 100644 (file)
@@ -83,6 +83,16 @@ public:
    */
   void SetDuration(float seconds);
 
+  /**
+   * @copydoc Dali::DevelAnimation::SetProgressNotification()
+   */
+  void SetProgressNotification( float progress );
+
+  /**
+   * @copydoc Dali::DevelAnimation::GetProgressNotification()
+   */
+  float GetProgressNotification();
+
   /**
    * @copydoc Dali::Animation::GetDuration()
    */
@@ -191,11 +201,21 @@ public:
    */
   Dali::Animation::AnimationSignalType& FinishedSignal();
 
+  /**
+   * @copydoc Dali::DevelAnimation::ProgressHasBeenReachedSignal()
+   */
+  Dali::Animation::AnimationSignalType& ProgressReachedSignal();
+
   /**
    * Emit the Finished signal
    */
   void EmitSignalFinish();
 
+  /**
+   * Emit the ProgressReached signal
+   */
+  void EmitSignalProgressReached();
+
   /**
    * Connects a callback function with the object's signals.
    * @param[in] object The object providing the signal.
@@ -480,6 +500,11 @@ private:
    */
   void NotifyObjects();
 
+  /**
+   * Sends message to SceneGraph with final progress value
+   */
+  void SendFinalProgressNotificationMessage();
+
 private:
 
   const SceneGraph::Animation* mAnimation;
@@ -489,6 +514,8 @@ private:
 
   Dali::Animation::AnimationSignalType mFinishedSignal;
 
+  Dali::Animation::AnimationSignalType mProgressReachedSignal;
+
   typedef OwnerContainer< AnimatorConnectorBase* > AnimatorConnectorContainer;
   AnimatorConnectorContainer mConnectors; ///< Owned by the Animation
 
@@ -506,6 +533,7 @@ private:
   EndAction mDisconnectAction;
   AlphaFunction mDefaultAlpha;
   Dali::Animation::State mState;
+  float mProgressReachedMarker;
 };
 
 } // namespace Internal
index d210a6e52ca85ca4593effffab4e4cde46ecbd0f..55c9b910816d5e871ad037ae9beaf3fba5ae51f3 100644 (file)
@@ -104,6 +104,29 @@ void AnimationPlaylist::NotifyCompleted()
   }
 }
 
+void AnimationPlaylist::NotifyProgressReached( const SceneGraph::Animation* sceneGraphAnimation )
+{
+  std::vector< Dali::Animation > notifyProgressAnimations; // Will own animations until all emits have been done
+
+  for ( Dali::Vector< Animation* >::Iterator iter = mAnimations.Begin(); iter != mAnimations.End(); ++iter )
+  {
+    Animation* animation = *iter;
+
+    if ( ( animation->GetSceneObject() ) == sceneGraphAnimation )
+    {
+      // Store handles to animations that need signals emitted in the case of an animation being cleared in-between emits
+      notifyProgressAnimations.push_back(  Dali::Animation( animation ) );
+    }
+  }
+
+  for ( std::vector< Dali::Animation >::iterator iter = notifyProgressAnimations.begin(); iter != notifyProgressAnimations.end(); ++iter )
+  {
+    Dali::Animation& handle = *iter;
+
+    GetImplementation(handle).EmitSignalProgressReached();
+  }
+}
+
 } // namespace Internal
 
 } // namespace Dali
index 504bc5f740a040e09416956bc4419e5412d2cc37..c27bdb0884bfb90fc9a4b1945dca066af1bf7c85 100644 (file)
@@ -22,7 +22,9 @@
 #include <dali/public-api/animation/animation.h>
 #include <dali/public-api/common/dali-vector.h>
 #include <dali/public-api/common/vector-wrapper.h>
+#include <dali/internal/common/message.h>
 #include <dali/internal/event/common/complete-notification-interface.h>
+#include <dali/internal/update/animation/scene-graph-animation.h>
 
 namespace Dali
 {
@@ -73,6 +75,12 @@ public:
    */
   void OnClear( Animation& animation );
 
+  /**
+   * @brief Notify that an animation has reached a progress marker
+   * @param[in] sceneGraphAnimation scene graph animation that has reached progress
+   */
+  void NotifyProgressReached( const SceneGraph::Animation* sceneGraphAnimation );
+
 private:
 
   /**
@@ -100,6 +108,16 @@ private:
 
 };
 
+/**
+ * Called when an animation reaches a progress marker
+ *
+ * Note animationPlaylist is of type CompleteNotificationInterface because of updateManager only knowing about the interface not actual playlist
+ */
+inline MessageBase* NotifyProgressReachedMessage( CompleteNotificationInterface& animationPlaylist, const SceneGraph::Animation* animation )
+{
+  return new MessageValue1< AnimationPlaylist, const SceneGraph::Animation* >( static_cast<AnimationPlaylist*>(&animationPlaylist), &AnimationPlaylist::NotifyProgressReached, animation );
+}
+
 } // namespace Internal
 
 } // namespace Dali
index af43f2512eff1da263d515829237caf8719ff98b..ab33a5a123a27b34b77f476c013c22a124154121 100644 (file)
@@ -28,7 +28,7 @@ namespace Internal
 
 /**
  * Provides notifications to the event-thread regarding the changes in previous update(s).
- * For example after an animation finished, or after resources were loaded.
+ * For example after an animation finished
  */
 class CompleteNotificationInterface
 {
index e1e027e464282581c477c347a7a5d2bfe3f969e9..ccd089469442809d135214cb6791695a0498963e 100644 (file)
@@ -74,7 +74,9 @@ Animation::Animation( float durationSeconds, float speedFactor, const Vector2& p
   mPlayedCount(0),
   mLoopCount(loopCount),
   mCurrentLoop(0),
-  mPlayRange( playRange )
+  mPlayRange( playRange ),
+  mProgressMarker(0.0f),
+  mProgressReachedSignalRequired( false )
 {
 }
 
@@ -92,6 +94,15 @@ void Animation::SetDuration(float durationSeconds)
   mDurationSeconds = durationSeconds;
 }
 
+void Animation::SetProgressNotification( float progress )
+{
+  mProgressMarker = progress;
+  if ( mProgressMarker > 0.0f )
+  {
+    mProgressReachedSignalRequired = true;
+  }
+}
+
 void Animation::SetLoopCount(int loopCount)
 {
   mLoopCount = loopCount;
@@ -256,7 +267,7 @@ void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
   mAnimators.PushBack( animator.Release() );
 }
 
-void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
+void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
 {
   looped = false;
   finished = false;
@@ -271,6 +282,13 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
   if (mState == Playing)
   {
     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
+
+    if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
+    {
+      // The application should be notified by NotificationManager, in another thread
+      progressReached = true;
+      mProgressReachedSignalRequired = false;
+    }
   }
 
   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
@@ -280,7 +298,7 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
     // loop forever
     WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
 
-    UpdateAnimators(bufferIndex, false, false);
+    UpdateAnimators(bufferIndex, false, false );
 
     // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
   }
@@ -293,11 +311,12 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
 
     WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
 
-    UpdateAnimators(bufferIndex, false, false);
+    UpdateAnimators(bufferIndex, false, false );
 
     if(looped)
     {
       ++mCurrentLoop;
+      mProgressReachedSignalRequired = mProgressMarker > 0.0f;
       // don't increment mPlayedCount until the finished final loop
     }
   }
@@ -309,7 +328,7 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
                  ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
 
     // update with bake if finished
-    UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
+    UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished );
 
     if(finished)
     {
@@ -323,6 +342,7 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
         DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
       }
 
+      mProgressReachedSignalRequired = mProgressMarker > 0.0f;
       mElapsedSeconds = playRangeSeconds.x;
       mState = Stopped;
     }
index 2b01ff850ba3ed920d907eafea013ec50dde919e..4f2095eadbd40d5acfa5b18e30f1f9c34266de7c 100644 (file)
@@ -91,6 +91,12 @@ public:
    */
   void SetDuration(float durationSeconds);
 
+  /**
+   * Set the progress marker to trigger notification
+   * @param[in] progress percent of progress to trigger notification, 0.0f < progress <= 1.0f
+   */
+  void SetProgressNotification( float progress );
+
   /**
    * Retrieve the duration of the animation.
    * @return The duration in seconds.
@@ -274,8 +280,9 @@ public:
    * @param[in] elapsedSeconds The time elapsed since the previous frame.
    * @param[out] looped True if the animation looped
    * @param[out] finished True if the animation has finished.
+   * @param[out] progressReached True if progress marker reached
    */
-  void Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished );
+  void Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached );
 
 
 protected:
@@ -331,6 +338,10 @@ protected:
   int mCurrentLoop;              // Current loop number
 
   Vector2 mPlayRange;
+
+  float mProgressMarker;                // Progress marker to trigger a notification
+  bool mProgressReachedSignalRequired;  // Flag to indicate the progress marker was hit
+
   AnimatorContainer mAnimators;
 };
 
@@ -355,6 +366,18 @@ inline void SetDurationMessage( EventThreadServices& eventThreadServices, const
   new (slot) LocalType( &animation, &Animation::SetDuration, durationSeconds );
 }
 
+inline void SetProgressNotificationMessage( EventThreadServices& eventThreadServices, const Animation& animation, float progress )
+{
+  typedef MessageValue1< Animation, float > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventThreadServices.ReserveMessageSlot( sizeof( LocalType ) );
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new (slot) LocalType( &animation, &Animation::SetProgressNotification, progress );
+}
+
+
 inline void SetLoopingMessage( EventThreadServices& eventThreadServices, const Animation& animation, int loopCount )
 {
   typedef MessageValue1< Animation, int > LocalType;
index 743aa8fe14db3ab12f465763f09f87e23aafc9c7..569bc55c49dbc3e972a16418f002d52274960eb0 100644 (file)
@@ -35,6 +35,7 @@
 #include <dali/internal/event/common/property-notification-impl.h>
 #include <dali/internal/event/common/property-notifier.h>
 #include <dali/internal/event/effects/shader-factory.h>
+#include <dali/internal/event/animation/animation-playlist.h>
 
 #include <dali/internal/update/animation/scene-graph-animator.h>
 #include <dali/internal/update/animation/scene-graph-animation.h>
@@ -161,7 +162,7 @@ typedef OwnerContainer< PropertyOwner* >       CustomObjectOwner;
 struct UpdateManager::Impl
 {
   Impl( NotificationManager& notificationManager,
-        CompleteNotificationInterface& animationFinishedNotifier,
+        CompleteNotificationInterface& animationPlaylist,
         PropertyNotifier& propertyNotifier,
         DiscardQueue& discardQueue,
         RenderController& renderController,
@@ -172,7 +173,7 @@ struct UpdateManager::Impl
   : renderMessageDispatcher( renderManager, renderQueue, sceneGraphBuffers ),
     notificationManager( notificationManager ),
     transformManager(),
-    animationFinishedNotifier( animationFinishedNotifier ),
+    animationPlaylist( animationPlaylist ),
     propertyNotifier( propertyNotifier ),
     shaderSaver( NULL ),
     discardQueue( discardQueue ),
@@ -254,7 +255,7 @@ struct UpdateManager::Impl
   RenderMessageDispatcher             renderMessageDispatcher;       ///< Used for passing messages to the render-thread
   NotificationManager&                notificationManager;           ///< Queues notification messages for the event-thread.
   TransformManager                    transformManager;              ///< Used to update the transformation matrices of the nodes
-  CompleteNotificationInterface&      animationFinishedNotifier;     ///< Provides notification to applications when animations are finished.
+  CompleteNotificationInterface&      animationPlaylist;             ///< Holds handles to all the animations
   PropertyNotifier&                   propertyNotifier;              ///< Provides notification to applications when properties are modified.
   ShaderSaver*                        shaderSaver;                   ///< Saves shader binaries.
   DiscardQueue&                       discardQueue;                  ///< Nodes are added here when disconnected from the scene-graph.
@@ -660,12 +661,19 @@ void UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
   AnimationContainer &animations = mImpl->animations;
   AnimationIter iter = animations.Begin();
   bool animationLooped = false;
+
   while ( iter != animations.End() )
   {
     Animation* animation = *iter;
     bool finished = false;
     bool looped = false;
-    animation->Update( bufferIndex, elapsedSeconds, looped, finished );
+    bool progressMarkerReached = false;
+    animation->Update( bufferIndex, elapsedSeconds, looped, finished, progressMarkerReached );
+
+    if ( progressMarkerReached )
+    {
+      mImpl->notificationManager.QueueMessage( Internal::NotifyProgressReachedMessage( mImpl->animationPlaylist, animation ) );
+    }
 
     mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || finished;
     animationLooped = animationLooped || looped;
@@ -685,7 +693,7 @@ void UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
   if ( mImpl->animationFinishedDuringUpdate || animationLooped )
   {
     // The application should be notified by NotificationManager, in another thread
-    mImpl->notificationManager.QueueCompleteNotification( &mImpl->animationFinishedNotifier );
+    mImpl->notificationManager.QueueCompleteNotification( &mImpl->animationPlaylist );
   }
 }
 
index 704b25c7fd9eeb21cc280638de2fbf125f439bec..d49c1113342c7e6930e8dd6348c1c74b39899b7d 100644 (file)
@@ -119,7 +119,7 @@ public:
   /**
    * Construct a new UpdateManager.
    * @param[in] notificationManager This should be notified when animations have finished.
-   * @param[in] animationFinishedNotifier The CompleteNotificationInterface that handles animation completions
+   * @param[in] animationPlaylist The CompleteNotificationInterface that handles the animations
    * @param[in] propertyNotifier The PropertyNotifier
    * @param[in] discardQueue Nodes are added here when disconnected from the scene-graph.
    * @param[in] controller After messages are flushed, we request a render from the RenderController.
@@ -128,7 +128,7 @@ public:
    * @param[in] renderTaskProcessor Handles RenderTasks and RenderInstrucitons.
    */
   UpdateManager( NotificationManager& notificationManager,
-                 CompleteNotificationInterface& animationFinishedNotifier,
+                 CompleteNotificationInterface& animationPlaylist,
                  PropertyNotifier& propertyNotifier,
                  DiscardQueue& discardQueue,
                  Integration::RenderController& controller,