Merge "DALi Version 2.2.45" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / events / touch-event-processor.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/touch-event-processor.h>
20
21 #if defined(DEBUG_ENABLED)
22 #include <sstream>
23 #endif
24
25 // INTERNAL INCLUDES
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>
39
40 namespace Dali
41 {
42 namespace Internal
43 {
44 namespace
45 {
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)
50
51 const char* TOUCH_POINT_STATE[6] =
52   {
53     "DOWN",
54     "UP",
55     "MOTION",
56     "LEAVE",
57     "STATIONARY",
58     "INTERRUPTED",
59 };
60
61 bool ShouldEmitInterceptTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
62 {
63   PointState::Type state = event.GetState(0);
64   return actorImpl.GetInterceptTouchRequired() && (state!= PointState::MOTION || actorImpl.IsDispatchTouchMotion());
65 }
66
67 bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
68 {
69   PointState::Type state = event.GetState(0);
70   return actorImpl.GetTouchRequired() && (state!= PointState::MOTION || actorImpl.IsDispatchTouchMotion());
71 }
72
73 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
74 {
75   Dali::Actor interceptedActor;
76
77   if(actor)
78   {
79     Dali::Actor parent = actor.GetParent();
80     if(parent)
81     {
82       // Recursively deliver events to the actor and its parents for intercept touch event.
83       interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
84     }
85
86     if(!interceptedActor)
87     {
88       bool   intercepted = false;
89       Actor& actorImpl(GetImplementation(actor));
90       if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
91       {
92         DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
93         intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
94         if(intercepted)
95         {
96           interceptedActor = Dali::Actor(&actorImpl);
97         }
98       }
99     }
100   }
101
102   return interceptedActor;
103 }
104
105 /**
106  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
107  */
108 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
109 {
110   Dali::Actor consumedActor;
111
112   if(actor)
113   {
114     Dali::Actor oldParent(actor.GetParent());
115
116     Actor& actorImpl(GetImplementation(actor));
117
118     bool consumed(false);
119
120     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
121     if(ShouldEmitTouchEvent(actorImpl, touchEvent))
122     {
123       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
124       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
125     }
126
127     if(consumed)
128     {
129       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
130       consumedActor = Dali::Actor(&actorImpl);
131     }
132     else
133     {
134       // The actor may have been removed/reparented during the signal callbacks.
135       Dali::Actor parent = actor.GetParent();
136
137       if(parent &&
138          (parent == oldParent))
139       {
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);
142       }
143     }
144   }
145
146   return consumedActor;
147 }
148
149 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
150 {
151   TouchEventPtr    touchEvent(new TouchEvent(time));
152   Dali::TouchEvent touchEventHandle(touchEvent.Get());
153
154   touchEvent->AddPoint(point);
155
156   return EmitTouchSignals(actor, touchEventHandle);
157 }
158
159 /**
160  * Changes the state of the primary point to leave and emits the touch signals
161  */
162 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state)
163 {
164   Dali::Actor consumingActor;
165
166   if(actor)
167   {
168     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
169
170     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
171
172     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
173     Vector2        localPosition;
174     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
175
176     primaryPoint.SetLocalPosition(localPosition);
177     primaryPoint.SetHitActor(Dali::Actor(actor));
178     primaryPoint.SetState(state);
179
180     consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
181   }
182
183   return consumingActor;
184 }
185
186 /**
187  * @brief Parses the primary touch point by performing a hit-test if necessary
188  *
189  * @param[out] hitTestResults The hit test results are put into this variable
190  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
191  * @param[in] lastRenderTask The last render task member
192  * @param[in] currentPoint The current point information
193  * @param[in] scene The scene that this touch is related to
194  */
195 void ParsePrimaryTouchPoint(
196   HitTestAlgorithm::Results& hitTestResults,
197   ActorObserver&             capturingTouchActorObserver,
198   ActorObserver&             ownTouchActorObserver,
199   const RenderTaskPtr&       lastRenderTask,
200   const Integration::Point&  currentPoint,
201   const Internal::Scene&     scene)
202 {
203   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
204
205   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
206   if(capturingTouchActor && lastRenderTask)
207   {
208     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
209     hitTestResults.renderTask     = lastRenderTask;
210     const Vector2& screenPosition = currentPoint.GetScreenPosition();
211     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
212   }
213   else
214   {
215     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
216     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor);
217
218     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
219     {
220       // If we've just started touch, then check whether the actor has requested to capture all touch events
221       Actor* hitActor = &GetImplementation(hitTestResults.actor);
222       if(hitActor->CapturesAllTouchAfterStart())
223       {
224         capturingTouchActorObserver.SetActor(hitActor);
225       }
226       if(hitActor->IsAllowedOnlyOwnTouch())
227       {
228         ownTouchActorObserver.SetActor(hitActor);
229       }
230     }
231   }
232 }
233
234 } // unnamed namespace
235
236 TouchEventProcessor::TouchEventProcessor(Scene& scene)
237 : mScene(scene),
238   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
239   mLastConsumedActor(),
240   mCapturingTouchActor(),
241   mOwnTouchActor(),
242   mTouchDownConsumedActor(),
243   mInterceptedTouchActor(),
244   mLastRenderTask(),
245   mLastPrimaryPointState(PointState::FINISHED)
246 {
247   DALI_LOG_TRACE_METHOD(gLogFilter);
248 }
249
250 TouchEventProcessor::~TouchEventProcessor()
251 {
252   DALI_LOG_TRACE_METHOD(gLogFilter);
253 }
254
255 void TouchEventProcessor::Clear()
256 {
257   mLastPrimaryHitActor.SetActor(nullptr);
258   mLastConsumedActor.SetActor(nullptr);
259   mCapturingTouchActor.SetActor(nullptr);
260   mOwnTouchActor.SetActor(nullptr);
261   mInterceptedTouchActor.SetActor(nullptr);
262   mLastRenderTask.Reset();
263   mLastPrimaryPointState = PointState::FINISHED;
264 }
265
266 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
267 {
268   DALI_LOG_TRACE_METHOD(gLogFilter);
269   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
270
271   PRINT_HIERARCHY(gLogFilter);
272
273   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
274
275   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
276   //    and emit the stage signal as well.
277
278   if(event.points[0].GetState() == PointState::INTERRUPTED)
279   {
280     Dali::Actor        consumingActor;
281     Integration::Point currentPoint(event.points[0]);
282
283     Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
284     if(lastPrimaryHitActor)
285     {
286       Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
287       currentPoint.SetHitActor(lastPrimaryHitActorHandle);
288
289       consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
290     }
291
292     // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
293     Actor* lastConsumedActor(mLastConsumedActor.GetActor());
294     if(lastConsumedActor &&
295        lastConsumedActor != lastPrimaryHitActor &&
296        lastConsumedActor != consumingActor)
297     {
298       Dali::Actor lastConsumedActorHandle(lastConsumedActor);
299       currentPoint.SetHitActor(lastConsumedActorHandle);
300       AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint);
301     }
302
303     // Tell the touch-down consuming actor as well, if required
304     Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
305     if(touchDownConsumedActor &&
306        touchDownConsumedActor != lastPrimaryHitActor &&
307        touchDownConsumedActor != lastConsumedActor &&
308        touchDownConsumedActor != consumingActor)
309     {
310       Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
311
312       currentPoint.SetHitActor(touchDownConsumedActorHandle);
313       AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint);
314     }
315
316     Clear();
317     mTouchDownConsumedActor.SetActor(nullptr);
318
319     currentPoint.SetHitActor(Dali::Actor());
320
321     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
322     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
323
324     touchEventImpl->AddPoint(currentPoint);
325
326     mScene.EmitTouchedSignal(touchEventHandle);
327     return false; // No need for hit testing & already an interrupted event so just return false
328   }
329
330   // 2) Hit Testing.
331   TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
332   Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
333
334   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
335   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
336
337   RenderTaskPtr currentRenderTask;
338   bool          firstPointParsed = false;
339
340   for(auto&& currentPoint : event.points)
341   {
342     HitTestAlgorithm::Results hitTestResults;
343     hitTestResults.point     = currentPoint;
344     hitTestResults.eventTime = event.time;
345     if(!firstPointParsed)
346     {
347       firstPointParsed = true;
348       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene);
349
350       // Only set the currentRenderTask for the primary hit actor.
351       currentRenderTask = hitTestResults.renderTask;
352     }
353     else
354     {
355       HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults);
356     }
357
358     Integration::Point newPoint(currentPoint);
359     newPoint.SetHitActor(hitTestResults.actor);
360     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
361
362     touchEventImpl->AddPoint(newPoint);
363
364     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);
365   }
366
367   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
368
369   bool consumed = false;
370
371   // Emit the touch signal
372   Dali::Actor consumedActor;
373
374   Integration::Point& primaryPoint      = touchEventImpl->GetPoint(0);
375   Dali::Actor         primaryHitActor   = primaryPoint.GetHitActor();
376   PointState::Type    primaryPointState = primaryPoint.GetState();
377
378   if(currentRenderTask)
379   {
380     Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
381     if(interceptedTouchActor)
382     {
383       Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
384       consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
385     }
386     else
387     {
388       // Emit the intercept touch signal
389       Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
390       if(interceptedActor)
391       {
392         mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
393         // If the child is being touched, INTERRUPTED is sent.
394         if(mLastPrimaryHitActor.GetActor() &&
395           mLastPrimaryHitActor.GetActor() != interceptedActor &&
396           mLastRenderTask &&
397           mLastPrimaryPointState != PointState::FINISHED)
398         {
399           EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED);
400           mTouchDownConsumedActor.SetActor(nullptr);
401         }
402         consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
403       }
404       else
405       {
406         consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
407       }
408     }
409     consumed = consumedActor ? true : false;
410
411     if(primaryPointState == PointState::MOTION)
412     {
413       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);
414       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]);
415     }
416     else
417     {
418       DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s)\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]);
419       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]);
420     }
421   }
422
423   if((primaryPointState == PointState::DOWN) &&
424      (touchEventImpl->GetPointCount() == 1) &&
425      (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
426   {
427     mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
428   }
429
430   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
431   //    hit actor.  Also process the last consumed actor in the same manner.
432   Actor* lastPrimaryHitActor(nullptr);
433   if(mInterceptedTouchActor.GetActor())
434   {
435     lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
436   }
437   else
438   {
439     lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
440   }
441   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
442   if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
443   {
444     if(mLastRenderTask)
445     {
446       Dali::Actor leaveEventConsumer;
447       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
448
449       if(lastPrimaryHitActor &&
450          lastPrimaryHitActor != primaryHitActor &&
451          lastPrimaryHitActor != consumedActor)
452       {
453         if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
454         {
455           if(lastPrimaryHitActor->GetLeaveRequired())
456           {
457             DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
458             leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
459           }
460         }
461         else
462         {
463           // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
464           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
465           DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
466           leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
467         }
468       }
469
470       consumed |= leaveEventConsumer ? true : false;
471
472       // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
473       // consumed actor's listeners may need to be informed (through a leave event).
474       // Further checks here to ensure we do not signal the same actor twice for the same event.
475       if(lastConsumedActor &&
476          lastConsumedActor != consumedActor &&
477          lastConsumedActor != lastPrimaryHitActor &&
478          lastConsumedActor != primaryHitActor &&
479          lastConsumedActor != leaveEventConsumer)
480       {
481         if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
482         {
483           if(lastConsumedActor->GetLeaveRequired())
484           {
485             DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
486             EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
487           }
488         }
489         else
490         {
491           // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
492           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
493           DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
494           EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
495         }
496       }
497     }
498   }
499
500   // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
501   //    time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
502   if(primaryPointState == PointState::UP)
503   {
504     Clear();
505   }
506   else
507   {
508     // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
509     if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
510     {
511       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
512
513       // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
514       if(consumedActor && GetImplementation(consumedActor).OnScene())
515       {
516         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
517       }
518       else
519       {
520         mLastConsumedActor.SetActor(nullptr);
521       }
522
523       mLastRenderTask        = currentRenderTask;
524       mLastPrimaryPointState = primaryPointState;
525     }
526     else
527     {
528       Clear();
529     }
530   }
531
532   // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
533   //    emit the stage touched event if required.
534
535   if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
536   {
537     switch(primaryPointState)
538     {
539       case PointState::UP:
540       {
541         Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
542         if(touchDownConsumedActor &&
543            touchDownConsumedActor != consumedActor &&
544            touchDownConsumedActor != lastPrimaryHitActor &&
545            touchDownConsumedActor != lastConsumedActor)
546         {
547           Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
548
549           Integration::Point currentPoint = touchEventImpl->GetPoint(0);
550           currentPoint.SetHitActor(touchDownConsumedActorHandle);
551           currentPoint.SetState(PointState::INTERRUPTED);
552
553           AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint);
554         }
555
556         mTouchDownConsumedActor.SetActor(nullptr);
557         mInterceptedTouchActor.SetActor(nullptr);
558
559         DALI_FALLTHROUGH;
560       }
561
562       case PointState::DOWN:
563       {
564         mScene.EmitTouchedSignal(touchEventHandle);
565         break;
566       }
567
568       case PointState::MOTION:
569       case PointState::LEAVE:
570       case PointState::STATIONARY:
571       case PointState::INTERRUPTED:
572       {
573         // Ignore
574         break;
575       }
576     }
577   }
578
579   return consumed;
580 }
581
582 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
583 {
584   if(actor == mLastPrimaryHitActor.GetActor())
585   {
586     Dali::Actor actorHandle(actor);
587
588     Integration::Point point;
589     point.SetState(PointState::INTERRUPTED);
590     point.SetHitActor(actorHandle);
591
592     TouchEventPtr touchEventImpl(new TouchEvent);
593     touchEventImpl->AddPoint(point);
594     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
595
596     Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
597
598     if(mLastConsumedActor.GetActor() != eventConsumer)
599     {
600       EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
601     }
602
603     // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
604
605     mLastConsumedActor.SetActor(nullptr);
606     mLastRenderTask.Reset();
607     mLastPrimaryPointState = PointState::FINISHED;
608   }
609 }
610
611 } // namespace Internal
612
613 } // namespace Dali