From: Adeel Kazmi Date: Fri, 3 Jul 2020 17:03:35 +0000 (+0100) Subject: Add property which allows touch-down actor to continuously receive touch X-Git-Tag: dali_1.9.22~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=159651c6769bc334709493b5f9a0916e46b72977;p=platform%2Fcore%2Fuifw%2Fdali-core.git Add property which allows touch-down actor to continuously receive touch Change-Id: Ie1c1cb38a0a8c3b3dc16ef711c65893d4b841df9 --- diff --git a/automated-tests/src/dali/utc-Dali-Actor.cpp b/automated-tests/src/dali/utc-Dali-Actor.cpp index f9451d2..676eba1 100644 --- a/automated-tests/src/dali/utc-Dali-Actor.cpp +++ b/automated-tests/src/dali/utc-Dali-Actor.cpp @@ -7864,3 +7864,37 @@ int utcDaliActorPartialUpdateActorsWithSizeHint(void) END_TEST; } +int UtcDaliActorCaptureAllTouchAfterStartPropertyP(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + DALI_TEST_EQUALS(actor.GetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START).Get(), false, TEST_LOCATION); + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, true); + DALI_TEST_EQUALS(actor.GetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START).Get(), true, TEST_LOCATION); + END_TEST; +} + +int UtcDaliActorCaptureAllTouchAfterStartPropertyN(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + + // Make sure setting invalid types does not cause a crash + try + { + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, 1.0f); + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Vector2::ONE); + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Vector3::ONE); + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Vector4::ONE); + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Property::Map()); + actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Property::Array()); + tet_result(TET_PASS); + } + catch(...) + { + tet_result(TET_FAIL); + } + END_TEST; +} diff --git a/automated-tests/src/dali/utc-Dali-TouchDataProcessing.cpp b/automated-tests/src/dali/utc-Dali-TouchDataProcessing.cpp index 4e96d66..95db95e 100644 --- a/automated-tests/src/dali/utc-Dali-TouchDataProcessing.cpp +++ b/automated-tests/src/dali/utc-Dali-TouchDataProcessing.cpp @@ -22,6 +22,7 @@ #include #include #include +#include using namespace Dali; @@ -2034,3 +2035,56 @@ int UtcDaliTouchDataGetMouseButtonNagative(void) END_TEST; } +int UtcDaliTouchDataCapturePropertySet(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetProperty( Actor::Property::SIZE, Vector2( 100.0f, 100.0f ) ); + actor.SetProperty( Actor::Property::ANCHOR_POINT,AnchorPoint::TOP_LEFT); + application.GetScene().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchDataFunctor functor( data ); + actor.TouchSignal().Connect( &application, functor ); + + // Emit a down signal + application.ProcessEvent( GenerateSingleTouch( PointState::STARTED, Vector2( 10.0f, 10.0f ) ) ); + DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION ); + data.Reset(); + + // Now motion outside of actor, we should not receive the event + application.ProcessEvent( GenerateSingleTouch( PointState::MOTION, Vector2( 110.0f, 110.0f ) ) ); + DALI_TEST_EQUALS( false, data.functorCalled, TEST_LOCATION ); + data.Reset(); + + // Up event, should receive an interrupted + application.ProcessEvent( GenerateSingleTouch( PointState::FINISHED, Vector2( 110.0f, 110.0f ) ) ); + DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION ); + DALI_TEST_EQUALS( data.touchData.GetPoint(0).state, PointState::INTERRUPTED, TEST_LOCATION ); + + // Now set the capture property + actor.SetProperty( DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, true); + + // Emit a down signal + application.ProcessEvent( GenerateSingleTouch( PointState::STARTED, Vector2( 10.0f, 10.0f ) ) ); + DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION ); + data.Reset(); + + // Now motion outside of actor, we now SHOULD receive the event + application.ProcessEvent( GenerateSingleTouch( PointState::MOTION, Vector2( 110.0f, 110.0f ) ) ); + DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION ); + data.Reset(); + + // Up event, we should receive it again, but as ended rather than interrupted + application.ProcessEvent( GenerateSingleTouch( PointState::FINISHED, Vector2( 110.0f, 110.0f ) ) ); + DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION ); + DALI_TEST_EQUALS( data.touchData.GetPoint(0).state, PointState::FINISHED, TEST_LOCATION ); + + END_TEST; +} diff --git a/dali/devel-api/actors/actor-devel.h b/dali/devel-api/actors/actor-devel.h index 9eccc32..9a82366 100644 --- a/dali/devel-api/actors/actor-devel.h +++ b/dali/devel-api/actors/actor-devel.h @@ -106,14 +106,21 @@ enum Type * @note Raise, Lower, RaiseToTop, LowerToBottom, RaiseAbove and LowerBelow will override the * sibling order. The values set by this Property will likely change. */ - SIBLING_ORDER = KEYBOARD_FOCUSABLE + 1, + SIBLING_ORDER, /** * @brief Sets the update size hint of the actor. * @details Name "updateSizeHint", type Property::VECTOR2. * @note Overrides the size used for the actor damaged area calculation. Affected by the actor model view matrix. */ - UPDATE_SIZE_HINT = SIBLING_ORDER + 1, + UPDATE_SIZE_HINT, + + /** + * @brief If this actor receives a touch-start event, then all following touch events are sent to this actor until a touch-end. + * @details Name "captureAllTouchAfterStart", type Property::BOOLEAN + * @note Default is false, i.e. actor under touch event will receive the touch even if touch started on this actor + */ + CAPTURE_ALL_TOUCH_AFTER_START }; } // namespace Property diff --git a/dali/internal/event/actors/actor-impl.cpp b/dali/internal/event/actors/actor-impl.cpp index 65e9ee2..58577dc 100644 --- a/dali/internal/event/actors/actor-impl.cpp +++ b/dali/internal/event/actors/actor-impl.cpp @@ -220,6 +220,7 @@ DALI_PROPERTY( "connectedToScene", BOOLEAN, false, false, false, Dali: DALI_PROPERTY( "keyboardFocusable", BOOLEAN, true, false, false, Dali::Actor::Property::KEYBOARD_FOCUSABLE ) DALI_PROPERTY( "siblingOrder", INTEGER, true, false, false, Dali::DevelActor::Property::SIBLING_ORDER ) DALI_PROPERTY( "updateSizeHint", VECTOR2, true, false, false, Dali::DevelActor::Property::UPDATE_SIZE_HINT ) +DALI_PROPERTY( "captureAllTouchAfterStart", VECTOR2, true, false, false, Dali::DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START ) DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX, ActorDefaultProperties ) // Signals @@ -2807,6 +2808,16 @@ void Actor::SetDefaultProperty( Property::Index index, const Property::Value& pr break; } + case Dali::DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START: + { + bool boolValue = false; + if ( property.Get( boolValue ) ) + { + mCaptureAllTouchAfterStart = boolValue; + } + break; + } + default: { // this can happen in the case of a non-animatable default property so just do nothing @@ -3939,6 +3950,12 @@ bool Actor::GetCachedPropertyValue( Property::Index index, Property::Value& valu break; } + case Dali::DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START: + { + value = mCaptureAllTouchAfterStart; + break; + } + default: { // Must be a scene-graph only property diff --git a/dali/internal/event/actors/actor-impl.h b/dali/internal/event/actors/actor-impl.h old mode 100755 new mode 100644 index 4e57518..0adfa39 --- a/dali/internal/event/actors/actor-impl.h +++ b/dali/internal/event/actors/actor-impl.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_ACTOR_H /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 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. @@ -1374,6 +1374,15 @@ public: */ bool IsHittable() const; + /** + * Query whether the actor captures all touch after it starts even if touch leaves its boundary. + * @return true, if it captures all touch after start + */ + bool CapturesAllTouchAfterStart() const + { + return mCaptureAllTouchAfterStart; + } + // Gestures /** @@ -2017,6 +2026,7 @@ protected: bool mPositionUsesAnchorPoint : 1; ///< Cached: Whether the position uses the anchor point or not. bool mVisible : 1; ///< Cached: Whether the actor is visible or not. bool mInheritLayoutDirection : 1; ///< Whether the actor inherits the layout direction from parent. + bool mCaptureAllTouchAfterStart : 1; ///< Whether the actor should capture all touch after touch starts even if the motion moves outside of the actor area. LayoutDirection::Type mLayoutDirection : 2; ///< Layout direction, Left to Right or Right to Left. DrawMode::Type mDrawMode : 3; ///< Cached: How the actor and its children should be drawn ColorMode mColorMode : 3; ///< Cached: Determines whether mWorldColor is inherited diff --git a/dali/internal/event/events/touch-event-processor.cpp b/dali/internal/event/events/touch-event-processor.cpp index 97c15a2..cc08da8 100644 --- a/dali/internal/event/events/touch-event-processor.cpp +++ b/dali/internal/event/events/touch-event-processor.cpp @@ -147,12 +147,55 @@ Dali::Actor EmitTouchSignals( Actor* actor, RenderTask& renderTask, const TouchE return consumingActor; } +/** + * @brief Parses the primary touch point by performing a hit-test if necessary + * + * @param[out] hitTestResults The hit test results are put into this variable + * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member + * @param[in] lastRenderTask The last render task member + * @param[in] currentPoint The current point information + * @param[in] scene The scene that this touch is related to + */ +void ParsePrimaryTouchPoint( + HitTestAlgorithm::Results& hitTestResults, + ActorObserver& capturingTouchActorObserver, + const RenderTaskPtr& lastRenderTask, + const Integration::Point& currentPoint, + const Internal::Scene& scene ) +{ + Actor* capturingTouchActor = capturingTouchActorObserver.GetActor(); + + // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it + if( capturingTouchActor && lastRenderTask ) + { + hitTestResults.actor = Dali::Actor( capturingTouchActor ); + hitTestResults.renderTask = lastRenderTask; + const Vector2& screenPosition = currentPoint.GetScreenPosition(); + capturingTouchActor->ScreenToLocal( *lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y ); + } + else + { + HitTestAlgorithm::HitTest( scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults ); + + if( currentPoint.GetState() == PointState::STARTED && hitTestResults.actor ) + { + // If we've just started touch, then check whether the actor has requested to capture all touch events + Actor* hitActor = &GetImplementation( hitTestResults.actor ); + if( hitActor->CapturesAllTouchAfterStart() ) + { + capturingTouchActorObserver.SetActor( hitActor ); + } + } + } +} + } // unnamed namespace TouchEventProcessor::TouchEventProcessor( Scene& scene ) : mScene( scene ), mLastPrimaryHitActor( MakeCallback( this, &TouchEventProcessor::OnObservedActorDisconnected ) ), mLastConsumedActor(), + mCapturingTouchActor(), mTouchDownConsumedActor(), mLastRenderTask() { @@ -212,9 +255,10 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even AllocAndEmitTouchSignals( event.time, touchDownConsumedActorHandle, currentPoint ); } - mLastPrimaryHitActor.SetActor( NULL ); - mLastConsumedActor.SetActor( NULL ); - mTouchDownConsumedActor.SetActor( NULL ); + mLastPrimaryHitActor.SetActor( nullptr ); + mLastConsumedActor.SetActor( nullptr ); + mCapturingTouchActor.SetActor( nullptr ); + mTouchDownConsumedActor.SetActor( nullptr ); mLastRenderTask.Reset(); currentPoint.SetHitActor( Dali::Actor() ); @@ -239,13 +283,25 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even DALI_LOG_INFO( gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount() ); RenderTaskPtr currentRenderTask; + bool firstPointParsed = false; - for ( Integration::PointContainerConstIterator iter = event.points.begin(), beginIter = event.points.begin(), endIter = event.points.end(); iter != endIter; ++iter ) + for ( auto&& currentPoint : event.points ) { HitTestAlgorithm::Results hitTestResults; - HitTestAlgorithm::HitTest( mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), iter->GetScreenPosition(), hitTestResults ); + if( !firstPointParsed ) + { + firstPointParsed = true; + ParsePrimaryTouchPoint( hitTestResults, mCapturingTouchActor, mLastRenderTask, currentPoint, mScene ); + + // Only set the currentRenderTask for the primary hit actor. + currentRenderTask = hitTestResults.renderTask; + } + else + { + HitTestAlgorithm::HitTest( mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults ); + } - Integration::Point newPoint( *iter ); + Integration::Point newPoint( currentPoint ); newPoint.SetHitActor( hitTestResults.actor ); newPoint.SetLocalPosition( hitTestResults.actorCoordinates ); @@ -253,16 +309,11 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even touchData->AddPoint( newPoint ); DALI_LOG_INFO( gLogFilter, Debug::General, " State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n", - TOUCH_POINT_STATE[iter->GetState()], iter->GetScreenPosition().x, iter->GetScreenPosition().y, + TOUCH_POINT_STATE[currentPoint.GetState()], currentPoint.GetScreenPosition().x, currentPoint.GetScreenPosition().y, ( hitTestResults.actor ? reinterpret_cast< void* >( &hitTestResults.actor.GetBaseObject() ) : NULL ), ( hitTestResults.actor ? hitTestResults.actor.GetProperty< std::string >( Dali::Actor::Property::NAME ).c_str() : "" ), hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y ); - // Only set the currentRenderTask for the primary hit actor. - if ( iter == beginIter && hitTestResults.renderTask ) - { - currentRenderTask = hitTestResults.renderTask; - } } // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached. @@ -354,8 +405,9 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even if ( primaryPointState == PointState::UP ) { - mLastPrimaryHitActor.SetActor( NULL ); - mLastConsumedActor.SetActor( NULL ); + mLastPrimaryHitActor.SetActor( nullptr ); + mLastConsumedActor.SetActor( nullptr ); + mCapturingTouchActor.SetActor( nullptr ); mLastRenderTask.Reset(); } else @@ -379,8 +431,9 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even } else { - mLastPrimaryHitActor.SetActor( NULL ); - mLastConsumedActor.SetActor( NULL ); + mLastPrimaryHitActor.SetActor( nullptr ); + mLastConsumedActor.SetActor( nullptr ); + mCapturingTouchActor.SetActor( nullptr ); mLastRenderTask.Reset(); } } diff --git a/dali/internal/event/events/touch-event-processor.h b/dali/internal/event/events/touch-event-processor.h index 61edcca..9306e6f 100644 --- a/dali/internal/event/events/touch-event-processor.h +++ b/dali/internal/event/events/touch-event-processor.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_TOUCH_EVENT_PROCESSOR_H /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 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. @@ -91,6 +91,7 @@ private: ActorObserver mLastPrimaryHitActor; ///< Stores the last primary point hit actor ActorObserver mLastConsumedActor; ///< Stores the last consumed actor + ActorObserver mCapturingTouchActor; ///< Stored the actor that captures touch ActorObserver mTouchDownConsumedActor; ///< Stores the touch-down consumed actor RenderTaskPtr mLastRenderTask; ///< The RenderTask used for the last hit actor };