From: Eunki, Hong Date: Tue, 4 Jun 2024 02:26:51 +0000 (+0900) Subject: [Tizen] Make InternalState for animation. So ensure Stop() Play() make the playstate... X-Git-Tag: accepted/tizen/8.0/unified/20240612.161755~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F80%2F312080%2F1;p=platform%2Fcore%2Fuifw%2Fdali-core.git [Tizen] Make InternalState for animation. So ensure Stop() Play() make the playstate is Playing Since there is no way to determine that Animation::HasFinished() function called due to we call Stop() API, or Animation finished normally. To seperate the state, let we make new internal state : STOPPING, PLAYING_DURING_STOPPING, PAUSED_DURING_STOPPING STOPPING will be changed as STOPPED after Animation::HasFinished() function called. And also, make CLEARED state so we can easly control Clear() function call. Change-Id: I2acccfaaa034dd1edc956e4a23b0726b5d1b65e7 Signed-off-by: Eunki, Hong --- diff --git a/automated-tests/src/dali/utc-Dali-Animation.cpp b/automated-tests/src/dali/utc-Dali-Animation.cpp index 7293256..55ca82f 100644 --- a/automated-tests/src/dali/utc-Dali-Animation.cpp +++ b/automated-tests/src/dali/utc-Dali-Animation.cpp @@ -14354,13 +14354,13 @@ int UtcDaliAnimationClearPropertyValue02(void) animation1.Play(); application.SendNotification(); - application.Render(static_cast(durationSeconds * 1000.0f) - 1u /*just less than the animation duration*/); + application.Render(static_cast(durationSeconds * 1000.0f) - 100u /*just less than the animation duration*/); // The event side property should be set the current value immediately DALI_TEST_EQUALS(actor.GetProperty(Actor::Property::POSITION).Get(), targetPosition1, VECTOR3_EPSILON, TEST_LOCATION); application.SendNotification(); - application.Render(2u /*just beyond the animation duration*/); + application.Render(200u /*just beyond the animation duration*/); // Build a new animation Animation animation2 = Animation::New(durationSeconds); @@ -14368,7 +14368,7 @@ int UtcDaliAnimationClearPropertyValue02(void) animation2.Play(); application.SendNotification(); - application.Render(static_cast(durationSeconds * 1000.0f) - 1u /*just less than the animation duration*/); + application.Render(static_cast(durationSeconds * 1000.0f) - 100u /*just less than the animation duration*/); // The event side property should be set the current value immediately DALI_TEST_EQUALS(actor.GetProperty(Actor::Property::POSITION).Get(), targetPosition2, VECTOR3_EPSILON, TEST_LOCATION); @@ -14377,7 +14377,7 @@ int UtcDaliAnimationClearPropertyValue02(void) animation1.Clear(); application.SendNotification(); - application.Render(static_cast(durationSeconds * 1000.0f) - 1u /*just less than the animation duration*/); + application.Render(static_cast(durationSeconds * 1000.0f) - 100u /*just less than the animation duration*/); // The property should not be changed. DALI_TEST_EQUALS(actor.GetProperty(Actor::Property::POSITION).Get(), targetPosition2, VECTOR3_EPSILON, TEST_LOCATION); @@ -16241,6 +16241,7 @@ struct AnimationClearCheck }; } // namespace + int UtcDaliAnimationClearDuringAnimationFinished(void) { tet_infoline("UtcDaliAnimationClearDuringAnimationFinished"); @@ -16307,4 +16308,220 @@ int UtcDaliAnimationClearDuringAnimationFinished(void) finish3Check.CheckSignalNotReceived(); END_TEST; +} + +int UtcDaliAnimationPlayAfterStopGetState(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + application.GetScene().Add(actor); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + Vector3 initialPosition(0.0f, 0.0f, 0.0f); + Vector3 targetPosition(100.0f, 100.0f, 100.0f); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition, AlphaFunction::LINEAR); + + Vector3 fiftyPercentProgress = (initialPosition + targetPosition) * 0.5f; + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + + // Stop and Play. + { + tet_printf("Play, than Stop and Play immediately. Check the current value and animation state\n"); + // Start the animation. + animation.Play(); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + application.SendNotification(); + application.Render(500); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + // Stop, and Play immediately + animation.Stop(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + animation.Play(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + application.SendNotification(); + + // Re-play the animation. So the position value changed after Render execute. + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + application.Render(0); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + application.SendNotification(); + + // expect finished signal recieved due to Stop API. + finishCheck.CheckSignalReceived(); + finishCheck.Reset(); + + // Even if finished signal recieved, animation state should be playing for now. + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + + application.Render(500); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + + application.SendNotification(); + application.Render(550); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), targetPosition, TEST_LOCATION); + + // Still Playing since animation finished signal not comming yet. + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + + // expect finished signal recieved due to Stop API. + application.SendNotification(); + finishCheck.CheckSignalReceived(); + + // And now animation state is stopped. + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + + // Reset test status + finishCheck.Reset(); + application.SendNotification(); + application.Render(0); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + application.SendNotification(); + application.Render(0); + } + + // Stop and Pause. + { + tet_printf("Play, than Stop and Pause immediately. Check the current value and animation state\n"); + // Start the animation. + animation.Play(); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + application.SendNotification(); + application.Render(500); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + // Stop, and Pause immediately + animation.Stop(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + animation.Pause(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PAUSED, TEST_LOCATION); + + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + application.SendNotification(); + + // Animation is stopped. So the position value not be changed after Render execute. + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + application.Render(0); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + application.SendNotification(); + + // expect finished signal recieved due to Stop API. + finishCheck.CheckSignalReceived(); + finishCheck.Reset(); + + // Even if finished signal recieved, animation state should be paused for now. + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PAUSED, TEST_LOCATION); + + application.Render(500); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PAUSED, TEST_LOCATION); + + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + + // Reset test status + finishCheck.Reset(); + application.SendNotification(); + application.Render(0); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + application.SendNotification(); + application.Render(0); + } + + // Stop and Play and Stop. + { + tet_printf("Play, than Stop / Play / Stop immediately. Check the current value and animation state\n"); + // Start the animation. + animation.Play(); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + application.SendNotification(); + application.Render(500); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + // Stop, and Play and Stop immediately + animation.Stop(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + animation.Play(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::PLAYING, TEST_LOCATION); + animation.Stop(); + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + + finishCheck.CheckSignalNotReceived(); + application.SendNotification(); + + // Animation is stopped. So the position value not be changed after Render execute. + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), fiftyPercentProgress, TEST_LOCATION); + application.Render(0); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + application.SendNotification(); + + // expect finished signal recieved due to Stop API. + finishCheck.CheckSignalReceived(); + finishCheck.Reset(); + + // Animation state should be stopped for now. + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + + application.Render(500); + DALI_TEST_EQUALS(actor.GetCurrentProperty(Actor::Property::POSITION), initialPosition, TEST_LOCATION); + + DALI_TEST_EQUALS(animation.GetState(), Dali::Animation::State::STOPPED, TEST_LOCATION); + + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + + // Reset test status + finishCheck.Reset(); + application.SendNotification(); + application.Render(0); + actor.SetProperty(Actor::Property::POSITION, initialPosition); + application.SendNotification(); + application.Render(0); + } + + END_TEST; } \ No newline at end of file diff --git a/dali/internal/event/animation/animation-impl.cpp b/dali/internal/event/animation/animation-impl.cpp index f359180..1adfcf3 100644 --- a/dali/internal/event/animation/animation-impl.cpp +++ b/dali/internal/event/animation/animation-impl.cpp @@ -133,6 +133,93 @@ void ValidateParameters(Property::Type propertyType, Property::Type destinationT DALI_ASSERT_ALWAYS(period.durationSeconds >= 0 && "Duration must be >=0"); } +/** + * @brief Converts the internal state to the target state. + * @param[in, out] currentState The current state of the animation. + * @param[in] targetState The target state of the animation. + * @return True if the animation state has been changed. + */ +bool InternalStateConverter(Internal::Animation::InternalState& currentState, Dali::Animation::State targetState) +{ + bool changed = false; + switch(targetState) + { + case Dali::Animation::PLAYING: + { + switch(currentState) + { + case Internal::Animation::CLEARED: + case Internal::Animation::STOPPED: + case Internal::Animation::PAUSED: + { + currentState = Internal::Animation::InternalState::PLAYING; + changed = true; + break; + } + case Internal::Animation::STOPPING: + case Internal::Animation::PAUSED_DURING_STOPPING: + { + currentState = Internal::Animation::InternalState::PLAYING_DURING_STOPPING; + changed = true; + break; + } + default: + { + break; + } + } + break; + } + case Dali::Animation::PAUSED: + { + switch(currentState) + { + case Internal::Animation::CLEARED: + case Internal::Animation::STOPPED: + case Internal::Animation::PLAYING: + { + currentState = Internal::Animation::InternalState::PAUSED; + changed = true; + break; + } + case Internal::Animation::STOPPING: + case Internal::Animation::PLAYING_DURING_STOPPING: + { + currentState = Internal::Animation::InternalState::PAUSED_DURING_STOPPING; + changed = true; + break; + } + default: + { + break; + } + } + break; + } + case Dali::Animation::STOPPED: + { + switch(currentState) + { + case Internal::Animation::PLAYING: + case Internal::Animation::PLAYING_DURING_STOPPING: + case Internal::Animation::PAUSED: + case Internal::Animation::PAUSED_DURING_STOPPING: + { + currentState = Internal::Animation::InternalState::STOPPING; + changed = true; + break; + } + default: + { + break; + } + } + break; + } + } + return changed; +} + } // anonymous namespace AnimationPtr Animation::New(float durationSeconds) @@ -317,12 +404,10 @@ Dali::Animation::EndAction Animation::GetDisconnectAction() const void Animation::Play() { - mPlayCalled = true; - // Update the current playlist mPlaylist.OnPlay(*this); - mState = Dali::Animation::PLAYING; + InternalStateConverter(mState, Dali::Animation::PLAYING); NotifyObjects(Notify::USE_TARGET_VALUE); @@ -336,12 +421,10 @@ void Animation::PlayFrom(float progress) { if(progress >= mPlayRange.x && progress <= mPlayRange.y) { - mPlayCalled = true; - // Update the current playlist mPlaylist.OnPlay(*this); - mState = Dali::Animation::PLAYING; + InternalStateConverter(mState, Dali::Animation::PLAYING); NotifyObjects(Notify::USE_TARGET_VALUE); @@ -354,8 +437,6 @@ void Animation::PlayFrom(float progress) void Animation::PlayAfter(float delaySeconds) { - mPlayCalled = true; - // The negative delay means play immediately. delaySeconds = std::max(0.f, delaySeconds); @@ -364,7 +445,7 @@ void Animation::PlayAfter(float delaySeconds) // Update the current playlist mPlaylist.OnPlay(*this); - mState = Dali::Animation::PLAYING; + InternalStateConverter(mState, Dali::Animation::PLAYING); NotifyObjects(Notify::USE_TARGET_VALUE); @@ -376,10 +457,8 @@ void Animation::PlayAfter(float delaySeconds) void Animation::Pause() { - if(mState != Dali::Animation::PAUSED) + if(InternalStateConverter(mState, Dali::Animation::PAUSED)) { - mState = Dali::Animation::PAUSED; - // mAnimation is being used in a separate thread; queue a Pause message PauseAnimationMessage(mEventThreadServices, *mAnimation); @@ -390,15 +469,36 @@ void Animation::Pause() Dali::Animation::State Animation::GetState() const { - return mState; + Dali::Animation::State state = Dali::Animation::State::STOPPED; + switch(mState) + { + case Dali::Internal::Animation::InternalState::STOPPED: + case Dali::Internal::Animation::InternalState::CLEARED: + case Dali::Internal::Animation::InternalState::STOPPING: + { + state = Dali::Animation::State::STOPPED; + break; + } + case Dali::Internal::Animation::InternalState::PLAYING: + case Dali::Internal::Animation::InternalState::PLAYING_DURING_STOPPING: + { + state = Dali::Animation::State::PLAYING; + break; + } + case Dali::Internal::Animation::InternalState::PAUSED: + case Dali::Internal::Animation::InternalState::PAUSED_DURING_STOPPING: + { + state = Dali::Animation::State::PAUSED; + break; + } + } + return state; } void Animation::Stop() { - if(mState != Dali::Animation::STOPPED) + if(InternalStateConverter(mState, Dali::Animation::STOPPED)) { - mState = Dali::Animation::STOPPED; - // mAnimation is being used in a separate thread; queue a Stop message StopAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation); @@ -414,14 +514,14 @@ void Animation::Clear() { DALI_ASSERT_DEBUG(mAnimation); - if(mConnectors.Empty() && mState == Dali::Animation::STOPPED && !mPlayCalled) + if(mConnectors.Empty() && mState == Dali::Internal::Animation::CLEARED) { // Animation is empty. Fast-out return; } // Only notify the objects with the current values if the end action is set to BAKE - if(mEndAction == EndAction::BAKE && mState != Dali::Animation::STOPPED) + if(mEndAction == EndAction::BAKE && GetState() != Dali::Animation::STOPPED) { NotifyObjects(Notify::USE_CURRENT_VALUE); } @@ -438,8 +538,7 @@ void Animation::Clear() // Reset the notification count and relative values, since the new scene-object has never been played mNotificationCount = 0; - mState = Dali::Animation::STOPPED; - mPlayCalled = false; + mState = Dali::Internal::Animation::CLEARED; // Update the current playlist mPlaylist.OnClear(*this, true); @@ -844,9 +943,32 @@ bool Animation::HasFinished() // Note that only one signal is emitted, if the animation has been played repeatedly mNotificationCount = playedCount; - hasFinished = true; - - mState = Dali::Animation::STOPPED; + switch(mState) + { + case Internal::Animation::InternalState::PLAYING: + case Internal::Animation::InternalState::STOPPING: + { + mState = Dali::Internal::Animation::STOPPED; + hasFinished = true; + break; + } + case Internal::Animation::InternalState::PLAYING_DURING_STOPPING: + { + mState = Dali::Internal::Animation::PLAYING; + hasFinished = true; + break; + } + case Internal::Animation::InternalState::PAUSED_DURING_STOPPING: + { + mState = Dali::Internal::Animation::PAUSED; + hasFinished = true; + break; + } + default: + { + break; + } + } } return hasFinished; diff --git a/dali/internal/event/animation/animation-impl.h b/dali/internal/event/animation/animation-impl.h index ab23d10..08271b2 100644 --- a/dali/internal/event/animation/animation-impl.h +++ b/dali/internal/event/animation/animation-impl.h @@ -66,6 +66,18 @@ public: BETWEEN ///< Animating BETWEEN key-frames }; + enum InternalState : uint8_t + { + STOPPED = Dali::Animation::State::STOPPED, ///< @copydoc Dali::Animation::State::STOPPED + PLAYING = Dali::Animation::State::PLAYING, ///< @copydoc Dali::Animation::State::PLAYING + PAUSED = Dali::Animation::State::PAUSED, ///< @copydoc Dali::Animation::State::PAUSED + + CLEARED, ///< Animation is cleared. + STOPPING, ///< Stopping animation. It will be STOPPED when animation finisehd signal called. + PLAYING_DURING_STOPPING, ///< Play called during stopping. It will be PLAYING when animation finisehd signal called. + PAUSED_DURING_STOPPING, ///< Pause called during stopping. It will be PAUSED when animation finisehd signal called. + }; + using EndAction = Dali::Animation::EndAction; using Interpolation = Dali::Animation::Interpolation; @@ -569,21 +581,20 @@ private: uint32_t mAnimationId{0u}; - AlphaFunction mDefaultAlpha; - Vector2 mPlayRange{0.0f, 1.0f}; - float mBlendPoint{0.0f}; - float mDurationSeconds; - float mSpeedFactor{1.0f}; - int32_t mNotificationCount{0}; ///< Keep track of how many Finished signals have been emitted. - int32_t mLoopCount{1}; - float mProgressReachedMarker{0.0f}; - float mDelaySeconds{0.0f}; - EndAction mEndAction; - EndAction mDisconnectAction; - Dali::Animation::State mState{Dali::Animation::STOPPED}; - bool mAutoReverseEnabled{false}; ///< Flag to identify that the looping mode is auto reverse. - bool mConnectorTargetValuesSortRequired{false}; ///< Flag to whether we need to sort mConnectorTargetValues or not - bool mPlayCalled{false}; ///< Flag to whether we call Play at least 1 time after create, or clear. + AlphaFunction mDefaultAlpha; + Vector2 mPlayRange{0.0f, 1.0f}; + float mBlendPoint{0.0f}; + float mDurationSeconds; + float mSpeedFactor{1.0f}; + int32_t mNotificationCount{0}; ///< Keep track of how many Finished signals have been emitted. + int32_t mLoopCount{1}; + float mProgressReachedMarker{0.0f}; + float mDelaySeconds{0.0f}; + EndAction mEndAction; + EndAction mDisconnectAction; + InternalState mState{InternalState::CLEARED}; + bool mAutoReverseEnabled{false}; ///< Flag to identify that the looping mode is auto reverse. + bool mConnectorTargetValuesSortRequired{false}; ///< Flag to whether we need to sort mConnectorTargetValues or not }; } // namespace Internal