Touch and Hover event propagrated by geometry way.(2)
[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 // child -> parent
74 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
75 {
76   Dali::Actor interceptedActor;
77
78   if(actor)
79   {
80     Dali::Actor parent = actor.GetParent();
81     if(parent)
82     {
83       // Recursively deliver events to the actor and its parents for intercept touch event.
84       interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
85     }
86
87     if(!interceptedActor)
88     {
89       bool   intercepted = false;
90       Actor& actorImpl(GetImplementation(actor));
91       if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
92       {
93         DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
94         intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
95         if(intercepted)
96         {
97           interceptedActor = Dali::Actor(&actorImpl);
98         }
99       }
100     }
101   }
102   return interceptedActor;
103 }
104
105 // geometry
106 // child -> below
107 Dali::Actor EmitGeoInterceptTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, std::list<Dali::Internal::Actor*>& interceptActorList, const Dali::TouchEvent& touchEvent)
108 {
109   interceptActorList.clear();
110   Dali::Actor interceptedActor;
111   for(auto&& actor : actorLists)
112   {
113     interceptActorList.push_back(actor);
114     if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
115     {
116       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
117       if(actor->EmitInterceptTouchEventSignal(touchEvent))
118       {
119         interceptedActor = Dali::Actor(actor);
120         break;
121       }
122     }
123   }
124   return interceptedActor;
125 }
126
127 /**
128  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
129  */
130 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
131 {
132   Dali::Actor consumedActor;
133
134   if(actor)
135   {
136     Dali::Actor oldParent(actor.GetParent());
137
138     Actor& actorImpl(GetImplementation(actor));
139
140     bool consumed(false);
141
142     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
143     if(ShouldEmitTouchEvent(actorImpl, touchEvent))
144     {
145       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
146       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
147     }
148
149     if(consumed)
150     {
151       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
152       consumedActor = Dali::Actor(&actorImpl);
153     }
154     else
155     {
156       // The actor may have been removed/reparented during the signal callbacks.
157       Dali::Actor parent = actor.GetParent();
158
159       if(parent &&
160          (parent == oldParent))
161       {
162         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
163         consumedActor = EmitTouchSignals(parent, touchEvent);
164       }
165     }
166   }
167
168   return consumedActor;
169 }
170
171 /**
172  *  Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
173  */
174 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
175 {
176   Dali::Actor consumedActor;
177
178   std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
179   for (; rIter != actorLists.rend(); rIter++)
180   {
181     Actor* actorImpl(*rIter);
182     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
183     if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
184     {
185       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
186       if(actorImpl->EmitTouchEventSignal(touchEvent))
187       {
188         // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
189         consumedActor = Dali::Actor(actorImpl);
190         break;
191       }
192     }
193   }
194   return consumedActor;
195 }
196
197 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
198 {
199   TouchEventPtr    touchEvent(new TouchEvent(time));
200   Dali::TouchEvent touchEventHandle(touchEvent.Get());
201
202   touchEvent->AddPoint(point);
203   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
204
205   return EmitTouchSignals(actor, touchEventHandle);
206 }
207
208 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
209 {
210   TouchEventPtr    touchEvent(new TouchEvent(time));
211   Dali::TouchEvent touchEventHandle(touchEvent.Get());
212
213   touchEvent->AddPoint(point);
214   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
215
216   return EmitGeoTouchSignals(actorLists, touchEventHandle);
217 }
218
219 /**
220  * Changes the state of the primary point to leave and emits the touch signals
221  */
222 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
223 {
224   Dali::Actor consumingActor;
225
226   if(actor)
227   {
228     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
229     touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
230
231     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
232
233     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
234     Vector2        localPosition;
235     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
236
237     primaryPoint.SetLocalPosition(localPosition);
238     primaryPoint.SetHitActor(Dali::Actor(actor));
239     primaryPoint.SetState(state);
240
241     if(isGeometry)
242     {
243       std::list<Dali::Internal::Actor*> actorLists;
244       actorLists.push_back(actor);
245       consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
246     }
247     else
248     {
249       consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
250     }
251   }
252
253   return consumingActor;
254 }
255
256 /**
257  * @brief Parses the primary touch point by performing a hit-test if necessary
258  *
259  * @param[out] hitTestResults The hit test results are put into this variable
260  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
261  * @param[in] lastRenderTask The last render task member
262  * @param[in] currentPoint The current point information
263  * @param[in] scene The scene that this touch is related to
264  * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
265  */
266 void ParsePrimaryTouchPoint(
267   HitTestAlgorithm::Results& hitTestResults,
268   ActorObserver&             capturingTouchActorObserver,
269   ActorObserver&             ownTouchActorObserver,
270   const RenderTaskPtr&       lastRenderTask,
271   const Integration::Point&  currentPoint,
272   const Internal::Scene&     scene,
273   std::list<Dali::Internal::Actor*>& actorLists)
274 {
275   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
276
277   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
278   if(capturingTouchActor && lastRenderTask)
279   {
280     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
281     hitTestResults.renderTask     = lastRenderTask;
282     const Vector2& screenPosition = currentPoint.GetScreenPosition();
283     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
284   }
285   else
286   {
287     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
288     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
289
290     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
291     {
292       bool isGeometry = scene.IsGeometryHittestEnabled();
293       // If we've just started touch, then check whether the actor has requested to capture all touch events
294       Actor* hitActor = &GetImplementation(hitTestResults.actor);
295       if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
296       {
297         capturingTouchActorObserver.SetActor(hitActor);
298       }
299       if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
300       {
301         ownTouchActorObserver.SetActor(hitActor);
302       }
303       if(isGeometry)
304       {
305         actorLists = hitTestResults.actorLists;
306       }
307     }
308   }
309 }
310
311 } // unnamed namespace
312
313 TouchEventProcessor::TouchEventProcessor(Scene& scene)
314 : mScene(scene),
315   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
316   mLastConsumedActor(),
317   mCapturingTouchActor(),
318   mOwnTouchActor(),
319   mTouchDownConsumedActor(),
320   mInterceptedTouchActor(),
321   mLastRenderTask(),
322   mLastPrimaryPointState(PointState::FINISHED)
323 {
324   DALI_LOG_TRACE_METHOD(gLogFilter);
325 }
326
327 TouchEventProcessor::~TouchEventProcessor()
328 {
329   DALI_LOG_TRACE_METHOD(gLogFilter);
330 }
331
332 void TouchEventProcessor::Clear()
333 {
334   mLastPrimaryHitActor.SetActor(nullptr);
335   mLastConsumedActor.SetActor(nullptr);
336   mCapturingTouchActor.SetActor(nullptr);
337   mOwnTouchActor.SetActor(nullptr);
338   mInterceptedTouchActor.SetActor(nullptr);
339   mLastRenderTask.Reset();
340   mLastPrimaryPointState = PointState::FINISHED;
341 }
342
343 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
344 {
345   DALI_LOG_TRACE_METHOD(gLogFilter);
346   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
347
348   PRINT_HIERARCHY(gLogFilter);
349
350   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
351
352   bool isGeometry = mScene.IsGeometryHittestEnabled();
353
354   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
355   //    and emit the stage signal as well.
356
357   if(event.points[0].GetState() == PointState::INTERRUPTED)
358   {
359     Dali::Actor        consumingActor;
360     Integration::Point currentPoint(event.points[0]);
361
362     if(isGeometry)
363     {
364       // Since the geometry way only receives touch events from the consumed actor,
365       // it first searches for whether there is a consumed actor and then sends the event
366       Actor* touchConsumedActor(mLastConsumedActor.GetActor());
367       Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
368       Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
369       if(touchConsumedActor)
370       {
371         Dali::Actor touchConsumedActorHandle(touchConsumedActor);
372         currentPoint.SetHitActor(touchConsumedActorHandle);
373         std::list<Dali::Internal::Actor*> actorLists;
374         actorLists.push_back(touchConsumedActor);
375         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
376       }
377       else if(touchDownConsumedActor)
378       {
379         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
380         currentPoint.SetHitActor(touchDownConsumedActorHandle);
381         std::list<Dali::Internal::Actor*> actorLists;
382         actorLists.push_back(touchDownConsumedActor);
383         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
384       }
385       else if(lastPrimaryHitActor)
386       {
387         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
388         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
389
390         GeoAllocAndEmitTouchSignals(mCandidateActorLists, event.time, currentPoint, mLastRenderTask);
391       }
392     }
393     else
394     {
395       Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
396       if(lastPrimaryHitActor)
397       {
398         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
399         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
400
401         consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask);
402       }
403
404       // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
405       Actor* lastConsumedActor(mLastConsumedActor.GetActor());
406       if(lastConsumedActor &&
407         lastConsumedActor != lastPrimaryHitActor &&
408         lastConsumedActor != consumingActor)
409       {
410         Dali::Actor lastConsumedActorHandle(lastConsumedActor);
411         currentPoint.SetHitActor(lastConsumedActorHandle);
412         AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask);
413       }
414
415       // Tell the touch-down consuming actor as well, if required
416       Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
417       if(touchDownConsumedActor &&
418         touchDownConsumedActor != lastPrimaryHitActor &&
419         touchDownConsumedActor != lastConsumedActor &&
420         touchDownConsumedActor != consumingActor)
421       {
422         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
423
424         currentPoint.SetHitActor(touchDownConsumedActorHandle);
425         AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask);
426       }
427     }
428
429     Clear();
430     mTouchDownConsumedActor.SetActor(nullptr);
431
432     currentPoint.SetHitActor(Dali::Actor());
433
434     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
435     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
436
437     touchEventImpl->AddPoint(currentPoint);
438
439     mScene.EmitTouchedSignal(touchEventHandle);
440     return false; // No need for hit testing & already an interrupted event so just return false
441   }
442
443   // 2) Hit Testing.
444   TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
445   Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
446
447   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
448   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
449
450   RenderTaskPtr currentRenderTask;
451   bool          firstPointParsed = false;
452
453   for(auto&& currentPoint : event.points)
454   {
455     HitTestAlgorithm::Results hitTestResults;
456     hitTestResults.point     = currentPoint;
457     hitTestResults.eventTime = event.time;
458     if(!firstPointParsed)
459     {
460       firstPointParsed = true;
461       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
462
463       // Only set the currentRenderTask for the primary hit actor.
464       currentRenderTask = hitTestResults.renderTask;
465       touchEventImpl->SetRenderTask(Dali::RenderTask(currentRenderTask.Get()));
466     }
467     else
468     {
469       HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, isGeometry);
470     }
471
472     Integration::Point newPoint(currentPoint);
473     newPoint.SetHitActor(hitTestResults.actor);
474     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
475
476     touchEventImpl->AddPoint(newPoint);
477
478     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);
479   }
480
481   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
482
483   bool consumed = false;
484
485   // Emit the touch signal
486   Dali::Actor consumedActor;
487
488   Integration::Point& primaryPoint      = touchEventImpl->GetPoint(0);
489   Dali::Actor         primaryHitActor   = primaryPoint.GetHitActor();
490   PointState::Type    primaryPointState = primaryPoint.GetState();
491
492   if(currentRenderTask)
493   {
494     if(isGeometry)
495     {
496       Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
497       if(interceptedTouchActor)
498       {
499         Actor* touchConsumedActor(mLastConsumedActor.GetActor());
500         if(touchConsumedActor) // If there is a consultative actor, send events only to the consultative actor.
501         {
502           RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
503           consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
504         }
505         else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
506         {
507           Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
508           std::list<Dali::Internal::Actor*> interceptActorLists = mInterceptedActorLists;
509           consumedActor = EmitGeoTouchSignals(interceptActorLists, touchEventHandle);
510         }
511       }
512       else
513       {
514         Dali::Actor interceptedActor;
515         // Let's find out if there is an intercept actor.
516         interceptedActor = EmitGeoInterceptTouchSignals(mCandidateActorLists, mInterceptedActorLists, touchEventHandle);
517         if(interceptedActor)
518         {
519           mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
520
521           // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
522           if(mLastConsumedActor.GetActor() &&
523               mLastConsumedActor.GetActor() != interceptedActor &&
524               mLastRenderTask &&
525               mLastPrimaryPointState != PointState::FINISHED)
526           {
527             EmitTouchSignals(mLastConsumedActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
528             mTouchDownConsumedActor.SetActor(nullptr);
529             mLastConsumedActor.SetActor(nullptr);
530           }
531           else if(mLastPrimaryHitActor.GetActor() &&
532                   mLastPrimaryHitActor.GetActor() != interceptedActor &&
533                   mLastRenderTask &&
534                   mLastPrimaryPointState != PointState::FINISHED)
535           {
536             std::list<Dali::Internal::Actor*> internalActorLists = mCandidateActorLists;
537             while(!internalActorLists.empty())
538             {
539               Actor* actorImpl = internalActorLists.back();
540               // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
541               if(actorImpl->GetTouchRequired())
542               {
543                 EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
544               }
545               internalActorLists.pop_back();
546             }
547           }
548         }
549
550         Actor* touchConsumedActor(mLastConsumedActor.GetActor());
551         if(touchConsumedActor) // If there is a consultative actor, send events only to the consultative actor.
552         {
553           RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
554           consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
555         }
556         else
557         {
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);
560         }
561       }
562     }
563     else
564     {
565       Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
566       if(interceptedTouchActor)
567       {
568         Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
569         consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
570       }
571       else
572       {
573         // Emit the intercept touch signal
574         Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
575         if(interceptedActor)
576         {
577           mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
578           // If the child is being touched, INTERRUPTED is sent.
579           if(mLastPrimaryHitActor.GetActor() &&
580             mLastPrimaryHitActor.GetActor() != interceptedActor &&
581             mLastRenderTask &&
582             mLastPrimaryPointState != PointState::FINISHED)
583           {
584             EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
585             mTouchDownConsumedActor.SetActor(nullptr);
586           }
587           consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
588         }
589         else
590         {
591           consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
592         }
593       }
594     }
595
596     consumed = consumedActor ? true : false;
597
598     if(primaryPointState == PointState::MOTION)
599     {
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);
602     }
603     else
604     {
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);
607     }
608   }
609
610   if((primaryPointState == PointState::DOWN) &&
611      (touchEventImpl->GetPointCount() == 1) &&
612      (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
613   {
614     mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
615   }
616
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())
621   {
622     lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
623   }
624   else
625   {
626     lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
627   }
628   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
629   if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
630   {
631     if(mLastRenderTask)
632     {
633       Dali::Actor leaveEventConsumer;
634       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
635
636       if(isGeometry)
637       {
638         if(lastPrimaryHitActor)
639         {
640           if(!lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(lastPrimaryHitActor))
641           {
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);
646           }
647         }
648
649         consumed |= leaveEventConsumer ? true : false;
650
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)
657         {
658           if(!lastConsumedActor->IsHittable() || !IsActuallySensitive(lastConsumedActor))
659           {
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);
664           }
665         }
666       }
667       else
668       {
669         if(lastPrimaryHitActor &&
670            lastPrimaryHitActor != primaryHitActor &&
671            lastPrimaryHitActor != consumedActor)
672         {
673           if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
674           {
675             if(lastPrimaryHitActor->GetLeaveRequired())
676             {
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);
679             }
680           }
681           else
682           {
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);
687           }
688         }
689
690         consumed |= leaveEventConsumer ? true : false;
691
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)
700         {
701           if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
702           {
703             if(lastConsumedActor->GetLeaveRequired())
704             {
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);
707             }
708           }
709           else
710           {
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);
715           }
716         }
717       }
718     }
719   }
720
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)
724   {
725     Clear();
726   }
727   else
728   {
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())
731     {
732       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
733
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())
736       {
737         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
738       }
739       else
740       {
741         if(isGeometry)
742         {
743           if(lastConsumedActor && !lastConsumedActor->OnScene())
744           {
745             mLastConsumedActor.SetActor(nullptr);
746           }
747         }
748         else
749         {
750           mLastConsumedActor.SetActor(nullptr);
751         }
752       }
753
754       mLastRenderTask        = currentRenderTask;
755       mLastPrimaryPointState = primaryPointState;
756     }
757     else
758     {
759       Clear();
760     }
761   }
762
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.
765
766   if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
767   {
768     switch(primaryPointState)
769     {
770       case PointState::UP:
771       {
772         Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
773         if(touchDownConsumedActor &&
774            touchDownConsumedActor != consumedActor &&
775            touchDownConsumedActor != lastPrimaryHitActor &&
776            touchDownConsumedActor != lastConsumedActor)
777         {
778           Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
779
780           Integration::Point currentPoint = touchEventImpl->GetPoint(0);
781           currentPoint.SetHitActor(touchDownConsumedActorHandle);
782           currentPoint.SetState(PointState::INTERRUPTED);
783
784           if(isGeometry)
785           {
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 */);
789           }
790           else
791           {
792             AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
793           }
794         }
795
796         mTouchDownConsumedActor.SetActor(nullptr);
797         mInterceptedTouchActor.SetActor(nullptr);
798
799         DALI_FALLTHROUGH;
800       }
801
802       case PointState::DOWN:
803       {
804         mScene.EmitTouchedSignal(touchEventHandle);
805         break;
806       }
807
808       case PointState::MOTION:
809       case PointState::LEAVE:
810       case PointState::STATIONARY:
811       case PointState::INTERRUPTED:
812       {
813         // Ignore
814         break;
815       }
816     }
817   }
818
819   return consumed;
820 }
821
822 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
823 {
824   if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
825   {
826     Dali::Actor actorHandle(actor);
827     Integration::Point point;
828     point.SetState(PointState::INTERRUPTED);
829     point.SetHitActor(actorHandle);
830     if(actor == mLastConsumedActor.GetActor())
831     {
832       std::list<Dali::Internal::Actor*> actorLists;
833       actorLists.push_back(mLastConsumedActor.GetActor());
834       GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
835     }
836     else if(!mLastConsumedActor.GetActor())
837     {
838       GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
839     }
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;
844   }
845   else
846   {
847     if(actor == mLastPrimaryHitActor.GetActor())
848     {
849       Dali::Actor actorHandle(actor);
850       Integration::Point point;
851       point.SetState(PointState::INTERRUPTED);
852       point.SetHitActor(actorHandle);
853
854       TouchEventPtr touchEventImpl(new TouchEvent);
855       touchEventImpl->AddPoint(point);
856       Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
857
858       Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
859       if(mLastConsumedActor.GetActor() != eventConsumer)
860       {
861         EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
862       }
863
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;
868     }
869   }
870
871 }
872
873 } // namespace Internal
874
875 } // namespace Dali