2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/event/events/touch-event-processor.h>
21 #if defined(DEBUG_ENABLED)
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/integration-api/trace.h>
29 #include <dali/internal/event/actors/actor-impl.h>
30 #include <dali/internal/event/actors/layer-impl.h>
31 #include <dali/internal/event/common/scene-impl.h>
32 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
33 #include <dali/internal/event/events/multi-point-event-util.h>
34 #include <dali/internal/event/events/touch-event-impl.h>
35 #include <dali/internal/event/render-tasks/render-task-impl.h>
36 #include <dali/public-api/events/touch-event.h>
37 #include <dali/public-api/math/vector2.h>
38 #include <dali/public-api/signals/callback.h>
46 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TOUCH_PROCESSOR");
49 #endif // defined(DEBUG_ENABLED)
51 const char* TOUCH_POINT_STATE[6] =
61 bool ShouldEmitInterceptTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
63 PointState::Type state = event.GetState(0);
64 return actorImpl.GetInterceptTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
67 bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
69 PointState::Type state = event.GetState(0);
70 return actorImpl.GetTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
74 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
76 Dali::Actor interceptedActor;
80 Dali::Actor parent = actor.GetParent();
83 // Recursively deliver events to the actor and its parents for intercept touch event.
84 interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
89 bool intercepted = false;
90 Actor& actorImpl(GetImplementation(actor));
91 if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
93 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
94 intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
97 interceptedActor = Dali::Actor(&actorImpl);
102 return interceptedActor;
107 Dali::Actor EmitGeoInterceptTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, std::list<Dali::Internal::Actor*>& interceptActorList, const Dali::TouchEvent& touchEvent, ActorObserver& lastConsumedActor)
109 interceptActorList.clear();
110 Dali::Actor interceptedActor;
111 for(auto&& actor : actorLists)
113 // If there is a consumed actor, the intercept is sent only up to the moment before the consumed actor.
114 if(lastConsumedActor.GetActor() == actor)
118 interceptActorList.push_back(actor);
119 if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
121 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
122 if(actor->EmitInterceptTouchEventSignal(touchEvent))
124 interceptedActor = Dali::Actor(actor);
129 return interceptedActor;
133 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
135 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
137 Dali::Actor consumedActor;
141 Dali::Actor oldParent(actor.GetParent());
143 Actor& actorImpl(GetImplementation(actor));
145 bool consumed(false);
147 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
148 if(ShouldEmitTouchEvent(actorImpl, touchEvent))
150 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
151 consumed = actorImpl.EmitTouchEventSignal(touchEvent);
156 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
157 consumedActor = Dali::Actor(&actorImpl);
161 // The actor may have been removed/reparented during the signal callbacks.
162 Dali::Actor parent = actor.GetParent();
165 (parent == oldParent))
167 // One of the actor's parents may consumed the event and they should be set as the consumed actor.
168 consumedActor = EmitTouchSignals(parent, touchEvent);
173 return consumedActor;
177 * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
179 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
181 Dali::Actor consumedActor;
183 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
184 for (; rIter != actorLists.rend(); rIter++)
186 Actor* actorImpl(*rIter);
187 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
188 if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
190 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
191 if(actorImpl->EmitTouchEventSignal(touchEvent))
193 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
194 consumedActor = Dali::Actor(actorImpl);
199 return consumedActor;
202 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
204 TouchEventPtr touchEvent(new TouchEvent(time));
205 Dali::TouchEvent touchEventHandle(touchEvent.Get());
207 touchEvent->AddPoint(point);
208 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
210 return EmitTouchSignals(actor, touchEventHandle);
213 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
215 TouchEventPtr touchEvent(new TouchEvent(time));
216 Dali::TouchEvent touchEventHandle(touchEvent.Get());
218 touchEvent->AddPoint(point);
219 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
221 return EmitGeoTouchSignals(actorLists, touchEventHandle);
225 * Changes the state of the primary point to leave and emits the touch signals
227 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
229 Dali::Actor consumingActor;
233 TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
234 touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
236 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
238 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
239 Vector2 localPosition;
240 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
242 primaryPoint.SetLocalPosition(localPosition);
243 primaryPoint.SetHitActor(Dali::Actor(actor));
244 primaryPoint.SetState(state);
248 std::list<Dali::Internal::Actor*> actorLists;
249 actorLists.push_back(actor);
250 consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
254 consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
258 return consumingActor;
262 * @brief Parses the primary touch point by performing a hit-test if necessary
264 * @param[out] hitTestResults The hit test results are put into this variable
265 * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
266 * @param[in] lastRenderTask The last render task member
267 * @param[in] currentPoint The current point information
268 * @param[in] scene The scene that this touch is related to
269 * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
271 void ParsePrimaryTouchPoint(
272 HitTestAlgorithm::Results& hitTestResults,
273 ActorObserver& capturingTouchActorObserver,
274 ActorObserver& ownTouchActorObserver,
275 const RenderTaskPtr& lastRenderTask,
276 const Integration::Point& currentPoint,
277 const Internal::Scene& scene,
278 std::list<Dali::Internal::Actor*>& actorLists)
280 Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
282 // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
283 if(capturingTouchActor && lastRenderTask)
285 hitTestResults.actor = Dali::Actor(capturingTouchActor);
286 hitTestResults.renderTask = lastRenderTask;
287 const Vector2& screenPosition = currentPoint.GetScreenPosition();
288 capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
292 Actor* ownTouchActor = ownTouchActorObserver.GetActor();
293 HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
295 if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
297 bool isGeometry = scene.IsGeometryHittestEnabled();
298 // If we've just started touch, then check whether the actor has requested to capture all touch events
299 Actor* hitActor = &GetImplementation(hitTestResults.actor);
300 if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
302 capturingTouchActorObserver.SetActor(hitActor);
304 if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
306 ownTouchActorObserver.SetActor(hitActor);
310 actorLists = hitTestResults.actorLists;
316 } // unnamed namespace
318 TouchEventProcessor::TouchEventProcessor(Scene& scene)
320 mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
321 mLastConsumedActor(),
322 mCapturingTouchActor(),
324 mTouchDownConsumedActor(),
325 mInterceptedTouchActor(),
327 mLastPrimaryPointState(PointState::FINISHED)
329 DALI_LOG_TRACE_METHOD(gLogFilter);
332 TouchEventProcessor::~TouchEventProcessor()
334 DALI_LOG_TRACE_METHOD(gLogFilter);
337 void TouchEventProcessor::Clear()
339 mLastPrimaryHitActor.SetActor(nullptr);
340 mLastConsumedActor.SetActor(nullptr);
341 mCapturingTouchActor.SetActor(nullptr);
342 mOwnTouchActor.SetActor(nullptr);
343 mInterceptedTouchActor.SetActor(nullptr);
344 mLastRenderTask.Reset();
345 mLastPrimaryPointState = PointState::FINISHED;
348 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
350 DALI_LOG_TRACE_METHOD(gLogFilter);
351 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
353 PRINT_HIERARCHY(gLogFilter);
355 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
357 bool isGeometry = mScene.IsGeometryHittestEnabled();
359 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
360 // and emit the stage signal as well.
362 if(event.points[0].GetState() == PointState::INTERRUPTED)
364 Dali::Actor consumingActor;
365 Integration::Point currentPoint(event.points[0]);
369 // Since the geometry way only receives touch events from the consumed actor,
370 // it first searches for whether there is a consumed actor and then sends the event
371 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
372 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
373 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
374 if(touchConsumedActor)
376 Dali::Actor touchConsumedActorHandle(touchConsumedActor);
377 currentPoint.SetHitActor(touchConsumedActorHandle);
378 std::list<Dali::Internal::Actor*> actorLists;
379 actorLists.push_back(touchConsumedActor);
380 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
382 else if(touchDownConsumedActor)
384 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
385 currentPoint.SetHitActor(touchDownConsumedActorHandle);
386 std::list<Dali::Internal::Actor*> actorLists;
387 actorLists.push_back(touchDownConsumedActor);
388 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
390 else if(lastPrimaryHitActor)
392 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
393 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
395 GeoAllocAndEmitTouchSignals(mCandidateActorLists, event.time, currentPoint, mLastRenderTask);
400 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
401 if(lastPrimaryHitActor)
403 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
404 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
406 consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask);
409 // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
410 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
411 if(lastConsumedActor &&
412 lastConsumedActor != lastPrimaryHitActor &&
413 lastConsumedActor != consumingActor)
415 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
416 currentPoint.SetHitActor(lastConsumedActorHandle);
417 AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask);
420 // Tell the touch-down consuming actor as well, if required
421 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
422 if(touchDownConsumedActor &&
423 touchDownConsumedActor != lastPrimaryHitActor &&
424 touchDownConsumedActor != lastConsumedActor &&
425 touchDownConsumedActor != consumingActor)
427 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
429 currentPoint.SetHitActor(touchDownConsumedActorHandle);
430 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask);
435 mTouchDownConsumedActor.SetActor(nullptr);
437 currentPoint.SetHitActor(Dali::Actor());
439 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
440 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
442 touchEventImpl->AddPoint(currentPoint);
444 mScene.EmitTouchedSignal(touchEventHandle);
445 return false; // No need for hit testing & already an interrupted event so just return false
449 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
450 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
452 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
453 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
455 RenderTaskPtr currentRenderTask;
456 bool firstPointParsed = false;
458 for(auto&& currentPoint : event.points)
460 HitTestAlgorithm::Results hitTestResults;
461 hitTestResults.point = currentPoint;
462 hitTestResults.eventTime = event.time;
463 if(!firstPointParsed)
465 firstPointParsed = true;
466 ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
468 // Only set the currentRenderTask for the primary hit actor.
469 currentRenderTask = hitTestResults.renderTask;
470 touchEventImpl->SetRenderTask(Dali::RenderTask(currentRenderTask.Get()));
474 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, isGeometry);
477 Integration::Point newPoint(currentPoint);
478 newPoint.SetHitActor(hitTestResults.actor);
479 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
481 touchEventImpl->AddPoint(newPoint);
483 DALI_LOG_INFO(gLogFilter, Debug::General, " State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n", 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);
486 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
488 bool consumed = false;
490 // Emit the touch signal
491 Dali::Actor consumedActor;
493 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
494 Dali::Actor primaryHitActor = primaryPoint.GetHitActor();
495 PointState::Type primaryPointState = primaryPoint.GetState();
497 if(currentRenderTask)
501 Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
502 if(interceptedTouchActor)
504 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
505 if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
507 RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
508 consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
510 else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
512 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
513 std::list<Dali::Internal::Actor*> interceptActorLists = mInterceptedActorLists;
514 consumedActor = EmitGeoTouchSignals(interceptActorLists, touchEventHandle);
519 Dali::Actor interceptedActor;
520 // Let's find out if there is an intercept actor.
521 interceptedActor = EmitGeoInterceptTouchSignals(mCandidateActorLists, mInterceptedActorLists, touchEventHandle, mLastConsumedActor);
524 mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
526 // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
527 if(mLastConsumedActor.GetActor() &&
528 mLastConsumedActor.GetActor() != interceptedActor &&
530 mLastPrimaryPointState != PointState::FINISHED)
532 EmitTouchSignals(mLastConsumedActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
533 mTouchDownConsumedActor.SetActor(nullptr);
534 mLastConsumedActor.SetActor(nullptr);
536 else if(mLastPrimaryHitActor.GetActor() &&
537 mLastPrimaryHitActor.GetActor() != interceptedActor &&
539 mLastPrimaryPointState != PointState::FINISHED)
541 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = mCandidateActorLists.rbegin();
542 for (; rIter != mCandidateActorLists.rend(); rIter++)
544 Actor* actorImpl(*rIter);
545 EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
550 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
551 if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
553 RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
554 consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
558 // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
559 consumedActor = EmitGeoTouchSignals(interceptedActor ? mInterceptedActorLists : mCandidateActorLists, touchEventHandle);
565 Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
566 if(interceptedTouchActor)
568 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
569 consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
573 // Emit the intercept touch signal
574 Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
577 mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
578 // If the child is being touched, INTERRUPTED is sent.
579 if(mLastPrimaryHitActor.GetActor() &&
580 mLastPrimaryHitActor.GetActor() != interceptedActor &&
582 mLastPrimaryPointState != PointState::FINISHED)
584 EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
585 mTouchDownConsumedActor.SetActor(nullptr);
587 consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
591 consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
596 consumed = consumedActor ? true : false;
598 if(primaryPointState == PointState::MOTION)
600 DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n", primaryHitActor ? reinterpret_cast<void*>(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y, isGeometry);
601 DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) id(%d), name(%s), state(%s), isGeo : %d \n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], isGeometry);
605 DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n", primaryHitActor ? reinterpret_cast<void*>(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y, isGeometry);
606 DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s), isGeo : %d \n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], isGeometry);
610 if((primaryPointState == PointState::DOWN) &&
611 (touchEventImpl->GetPointCount() == 1) &&
612 (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
614 mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
617 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
618 // hit actor. Also process the last consumed actor in the same manner.
619 Actor* lastPrimaryHitActor(nullptr);
620 if(mInterceptedTouchActor.GetActor())
622 lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
626 lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
628 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
629 if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
633 Dali::Actor leaveEventConsumer;
634 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
638 if(lastPrimaryHitActor)
640 if(!lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(lastPrimaryHitActor))
642 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
643 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
644 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
645 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
649 consumed |= leaveEventConsumer ? true : false;
651 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
652 // consumed actor's listeners may need to be informed (through a leave event).
653 // Further checks here to ensure we do not signal the same actor twice for the same event.
654 if(lastConsumedActor &&
655 lastConsumedActor != lastPrimaryHitActor &&
656 lastConsumedActor != leaveEventConsumer)
658 if(!lastConsumedActor->IsHittable() || !IsActuallySensitive(lastConsumedActor))
660 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
661 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
662 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
663 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
669 if(lastPrimaryHitActor &&
670 lastPrimaryHitActor != primaryHitActor &&
671 lastPrimaryHitActor != consumedActor)
673 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
675 if(lastPrimaryHitActor->GetLeaveRequired())
677 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
678 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
683 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
684 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
685 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
686 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
690 consumed |= leaveEventConsumer ? true : false;
692 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
693 // consumed actor's listeners may need to be informed (through a leave event).
694 // Further checks here to ensure we do not signal the same actor twice for the same event.
695 if(lastConsumedActor &&
696 lastConsumedActor != consumedActor &&
697 lastConsumedActor != lastPrimaryHitActor &&
698 lastConsumedActor != primaryHitActor &&
699 lastConsumedActor != leaveEventConsumer)
701 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
703 if(lastConsumedActor->GetLeaveRequired())
705 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
706 EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
711 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
712 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
713 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
714 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
721 // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
722 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
723 if(primaryPointState == PointState::UP)
729 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
730 if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
732 mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
734 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
735 if(consumedActor && GetImplementation(consumedActor).OnScene())
737 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
743 if(lastConsumedActor && !lastConsumedActor->OnScene())
745 mLastConsumedActor.SetActor(nullptr);
750 mLastConsumedActor.SetActor(nullptr);
754 mLastRenderTask = currentRenderTask;
755 mLastPrimaryPointState = primaryPointState;
763 // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
764 // emit the stage touched event if required.
766 if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
768 switch(primaryPointState)
772 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
773 if(touchDownConsumedActor &&
774 touchDownConsumedActor != consumedActor &&
775 touchDownConsumedActor != lastPrimaryHitActor &&
776 touchDownConsumedActor != lastConsumedActor)
778 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
780 Integration::Point currentPoint = touchEventImpl->GetPoint(0);
781 currentPoint.SetHitActor(touchDownConsumedActorHandle);
782 currentPoint.SetState(PointState::INTERRUPTED);
786 std::list<Dali::Internal::Actor*> actorLists;
787 actorLists.push_back(touchDownConsumedActor);
788 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
792 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
796 mTouchDownConsumedActor.SetActor(nullptr);
797 mInterceptedTouchActor.SetActor(nullptr);
802 case PointState::DOWN:
804 mScene.EmitTouchedSignal(touchEventHandle);
808 case PointState::MOTION:
809 case PointState::LEAVE:
810 case PointState::STATIONARY:
811 case PointState::INTERRUPTED:
822 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
824 if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
826 Dali::Actor actorHandle(actor);
827 Integration::Point point;
828 point.SetState(PointState::INTERRUPTED);
829 point.SetHitActor(actorHandle);
830 if(actor == mLastConsumedActor.GetActor())
832 std::list<Dali::Internal::Actor*> actorLists;
833 actorLists.push_back(mLastConsumedActor.GetActor());
834 GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
836 else if(!mLastConsumedActor.GetActor())
838 GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
840 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
841 mLastConsumedActor.SetActor(nullptr);
842 mLastRenderTask.Reset();
843 mLastPrimaryPointState = PointState::FINISHED;
847 if(actor == mLastPrimaryHitActor.GetActor())
849 Dali::Actor actorHandle(actor);
850 Integration::Point point;
851 point.SetState(PointState::INTERRUPTED);
852 point.SetHitActor(actorHandle);
854 TouchEventPtr touchEventImpl(new TouchEvent);
855 touchEventImpl->AddPoint(point);
856 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
858 Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
859 if(mLastConsumedActor.GetActor() != eventConsumer)
861 EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
864 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
865 mLastConsumedActor.SetActor(nullptr);
866 mLastRenderTask.Reset();
867 mLastPrimaryPointState = PointState::FINISHED;
873 } // namespace Internal