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());
73 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
75 Dali::Actor interceptedActor;
79 Dali::Actor parent = actor.GetParent();
82 // Recursively deliver events to the actor and its parents for intercept touch event.
83 interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
88 bool intercepted = false;
89 Actor& actorImpl(GetImplementation(actor));
90 if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
92 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
93 intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
96 interceptedActor = Dali::Actor(&actorImpl);
102 return interceptedActor;
106 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
108 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
110 Dali::Actor consumedActor;
114 Dali::Actor oldParent(actor.GetParent());
116 Actor& actorImpl(GetImplementation(actor));
118 bool consumed(false);
120 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
121 if(ShouldEmitTouchEvent(actorImpl, touchEvent))
123 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
124 consumed = actorImpl.EmitTouchEventSignal(touchEvent);
129 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
130 consumedActor = Dali::Actor(&actorImpl);
134 // The actor may have been removed/reparented during the signal callbacks.
135 Dali::Actor parent = actor.GetParent();
138 (parent == oldParent))
140 // One of the actor's parents may consumed the event and they should be set as the consumed actor.
141 consumedActor = EmitTouchSignals(parent, touchEvent);
146 return consumedActor;
149 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
151 TouchEventPtr touchEvent(new TouchEvent(time));
152 Dali::TouchEvent touchEventHandle(touchEvent.Get());
154 touchEvent->AddPoint(point);
155 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
157 return EmitTouchSignals(actor, touchEventHandle);
161 * Changes the state of the primary point to leave and emits the touch signals
163 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state)
165 Dali::Actor consumingActor;
169 TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
170 touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
172 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
174 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
175 Vector2 localPosition;
176 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
178 primaryPoint.SetLocalPosition(localPosition);
179 primaryPoint.SetHitActor(Dali::Actor(actor));
180 primaryPoint.SetState(state);
182 consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
185 return consumingActor;
189 * @brief Parses the primary touch point by performing a hit-test if necessary
191 * @param[out] hitTestResults The hit test results are put into this variable
192 * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
193 * @param[in] lastRenderTask The last render task member
194 * @param[in] currentPoint The current point information
195 * @param[in] scene The scene that this touch is related to
197 void ParsePrimaryTouchPoint(
198 HitTestAlgorithm::Results& hitTestResults,
199 ActorObserver& capturingTouchActorObserver,
200 ActorObserver& ownTouchActorObserver,
201 const RenderTaskPtr& lastRenderTask,
202 const Integration::Point& currentPoint,
203 const Internal::Scene& scene)
205 Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
207 // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
208 if(capturingTouchActor && lastRenderTask)
210 hitTestResults.actor = Dali::Actor(capturingTouchActor);
211 hitTestResults.renderTask = lastRenderTask;
212 const Vector2& screenPosition = currentPoint.GetScreenPosition();
213 capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
217 Actor* ownTouchActor = ownTouchActorObserver.GetActor();
218 HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor);
220 if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
222 // If we've just started touch, then check whether the actor has requested to capture all touch events
223 Actor* hitActor = &GetImplementation(hitTestResults.actor);
224 if(hitActor->CapturesAllTouchAfterStart())
226 capturingTouchActorObserver.SetActor(hitActor);
228 if(hitActor->IsAllowedOnlyOwnTouch())
230 ownTouchActorObserver.SetActor(hitActor);
236 } // unnamed namespace
238 TouchEventProcessor::TouchEventProcessor(Scene& scene)
240 mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
241 mLastConsumedActor(),
242 mCapturingTouchActor(),
244 mTouchDownConsumedActor(),
245 mInterceptedTouchActor(),
247 mLastPrimaryPointState(PointState::FINISHED)
249 DALI_LOG_TRACE_METHOD(gLogFilter);
252 TouchEventProcessor::~TouchEventProcessor()
254 DALI_LOG_TRACE_METHOD(gLogFilter);
257 void TouchEventProcessor::Clear()
259 mLastPrimaryHitActor.SetActor(nullptr);
260 mLastConsumedActor.SetActor(nullptr);
261 mCapturingTouchActor.SetActor(nullptr);
262 mOwnTouchActor.SetActor(nullptr);
263 mInterceptedTouchActor.SetActor(nullptr);
264 mLastRenderTask.Reset();
265 mLastPrimaryPointState = PointState::FINISHED;
268 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
270 DALI_LOG_TRACE_METHOD(gLogFilter);
271 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
273 PRINT_HIERARCHY(gLogFilter);
275 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
277 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
278 // and emit the stage signal as well.
280 if(event.points[0].GetState() == PointState::INTERRUPTED)
282 Dali::Actor consumingActor;
283 Integration::Point currentPoint(event.points[0]);
285 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
286 if(lastPrimaryHitActor)
288 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
289 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
291 consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask);
294 // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
295 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
296 if(lastConsumedActor &&
297 lastConsumedActor != lastPrimaryHitActor &&
298 lastConsumedActor != consumingActor)
300 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
301 currentPoint.SetHitActor(lastConsumedActorHandle);
302 AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask);
305 // Tell the touch-down consuming actor as well, if required
306 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
307 if(touchDownConsumedActor &&
308 touchDownConsumedActor != lastPrimaryHitActor &&
309 touchDownConsumedActor != lastConsumedActor &&
310 touchDownConsumedActor != consumingActor)
312 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
314 currentPoint.SetHitActor(touchDownConsumedActorHandle);
315 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask);
319 mTouchDownConsumedActor.SetActor(nullptr);
321 currentPoint.SetHitActor(Dali::Actor());
323 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
324 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
326 touchEventImpl->AddPoint(currentPoint);
328 mScene.EmitTouchedSignal(touchEventHandle);
329 return false; // No need for hit testing & already an interrupted event so just return false
333 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
334 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
336 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
337 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
339 RenderTaskPtr currentRenderTask;
340 bool firstPointParsed = false;
342 for(auto&& currentPoint : event.points)
344 HitTestAlgorithm::Results hitTestResults;
345 hitTestResults.point = currentPoint;
346 hitTestResults.eventTime = event.time;
347 if(!firstPointParsed)
349 firstPointParsed = true;
350 ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene);
352 // Only set the currentRenderTask for the primary hit actor.
353 currentRenderTask = hitTestResults.renderTask;
354 touchEventImpl->SetRenderTask(Dali::RenderTask(currentRenderTask.Get()));
358 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults);
361 Integration::Point newPoint(currentPoint);
362 newPoint.SetHitActor(hitTestResults.actor);
363 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
365 touchEventImpl->AddPoint(newPoint);
367 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);
370 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
372 bool consumed = false;
374 // Emit the touch signal
375 Dali::Actor consumedActor;
377 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
378 Dali::Actor primaryHitActor = primaryPoint.GetHitActor();
379 PointState::Type primaryPointState = primaryPoint.GetState();
381 if(currentRenderTask)
383 Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
384 if(interceptedTouchActor)
386 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
387 consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
391 // Emit the intercept touch signal
392 Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
395 mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
396 // If the child is being touched, INTERRUPTED is sent.
397 if(mLastPrimaryHitActor.GetActor() &&
398 mLastPrimaryHitActor.GetActor() != interceptedActor &&
400 mLastPrimaryPointState != PointState::FINISHED)
402 EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED);
403 mTouchDownConsumedActor.SetActor(nullptr);
405 consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
409 consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
412 consumed = consumedActor ? true : false;
414 if(primaryPointState == PointState::MOTION)
416 DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f)\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);
417 DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) id(%d), name(%s), state(%s)\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]);
421 DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f)\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);
422 DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s)\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]);
426 if((primaryPointState == PointState::DOWN) &&
427 (touchEventImpl->GetPointCount() == 1) &&
428 (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
430 mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
433 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
434 // hit actor. Also process the last consumed actor in the same manner.
435 Actor* lastPrimaryHitActor(nullptr);
436 if(mInterceptedTouchActor.GetActor())
438 lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
442 lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
444 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
445 if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
449 Dali::Actor leaveEventConsumer;
450 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
452 if(lastPrimaryHitActor &&
453 lastPrimaryHitActor != primaryHitActor &&
454 lastPrimaryHitActor != consumedActor)
456 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
458 if(lastPrimaryHitActor->GetLeaveRequired())
460 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
461 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
466 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
467 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
468 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
469 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
473 consumed |= leaveEventConsumer ? true : false;
475 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
476 // consumed actor's listeners may need to be informed (through a leave event).
477 // Further checks here to ensure we do not signal the same actor twice for the same event.
478 if(lastConsumedActor &&
479 lastConsumedActor != consumedActor &&
480 lastConsumedActor != lastPrimaryHitActor &&
481 lastConsumedActor != primaryHitActor &&
482 lastConsumedActor != leaveEventConsumer)
484 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
486 if(lastConsumedActor->GetLeaveRequired())
488 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
489 EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
494 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
495 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
496 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
497 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
503 // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
504 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
505 if(primaryPointState == PointState::UP)
511 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
512 if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
514 mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
516 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
517 if(consumedActor && GetImplementation(consumedActor).OnScene())
519 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
523 mLastConsumedActor.SetActor(nullptr);
526 mLastRenderTask = currentRenderTask;
527 mLastPrimaryPointState = primaryPointState;
535 // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
536 // emit the stage touched event if required.
538 if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
540 switch(primaryPointState)
544 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
545 if(touchDownConsumedActor &&
546 touchDownConsumedActor != consumedActor &&
547 touchDownConsumedActor != lastPrimaryHitActor &&
548 touchDownConsumedActor != lastConsumedActor)
550 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
552 Integration::Point currentPoint = touchEventImpl->GetPoint(0);
553 currentPoint.SetHitActor(touchDownConsumedActorHandle);
554 currentPoint.SetState(PointState::INTERRUPTED);
556 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
559 mTouchDownConsumedActor.SetActor(nullptr);
560 mInterceptedTouchActor.SetActor(nullptr);
565 case PointState::DOWN:
567 mScene.EmitTouchedSignal(touchEventHandle);
571 case PointState::MOTION:
572 case PointState::LEAVE:
573 case PointState::STATIONARY:
574 case PointState::INTERRUPTED:
585 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
587 if(actor == mLastPrimaryHitActor.GetActor())
589 Dali::Actor actorHandle(actor);
591 Integration::Point point;
592 point.SetState(PointState::INTERRUPTED);
593 point.SetHitActor(actorHandle);
595 TouchEventPtr touchEventImpl(new TouchEvent);
596 touchEventImpl->AddPoint(point);
597 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
599 Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
601 if(mLastConsumedActor.GetActor() != eventConsumer)
603 EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
606 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
608 mLastConsumedActor.SetActor(nullptr);
609 mLastRenderTask.Reset();
610 mLastPrimaryPointState = PointState::FINISHED;
614 } // namespace Internal