Merge "Use DepthIndex for 3D rendering order" 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 // 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* touchConsumedActor(mLastConsumedActor.GetActor());
497       Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
498       if(touchConsumedActor) // If there is a consultative actor, send events only to the consultative actor.
499       {
500         RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
501         consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
502       }
503       else if(interceptedTouchActor) // If there is an intercepted actor, send a touch event starting from the intercepted actor.
504       {
505         Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
506         std::list<Dali::Internal::Actor*> interceptActorLists = mInterceptedActorLists;
507         consumedActor = EmitGeoTouchSignals(interceptActorLists, touchEventHandle);
508       }
509       else
510       {
511         Dali::Actor interceptedActor;
512         // Let's find out if there is an intercept actor.
513         interceptedActor = EmitGeoInterceptTouchSignals(mCandidateActorLists, mInterceptedActorLists, touchEventHandle);
514         if(interceptedActor)
515         {
516           mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
517
518           // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
519           if(mLastConsumedActor.GetActor() &&
520               mLastConsumedActor.GetActor() != interceptedActor &&
521               mLastRenderTask &&
522               mLastPrimaryPointState != PointState::FINISHED)
523           {
524             EmitTouchSignals(mLastConsumedActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
525             mTouchDownConsumedActor.SetActor(nullptr);
526             mLastConsumedActor.SetActor(nullptr);
527           }
528           else if(mLastPrimaryHitActor.GetActor() &&
529                   mLastPrimaryHitActor.GetActor() != interceptedActor &&
530                   mLastRenderTask &&
531                   mLastPrimaryPointState != PointState::FINISHED)
532           {
533             std::list<Dali::Internal::Actor*> internalActorLists = mCandidateActorLists;
534             while(!internalActorLists.empty())
535             {
536               Actor* actorImpl = internalActorLists.back();
537               // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
538               if(actorImpl->GetTouchRequired())
539               {
540                 EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
541               }
542               internalActorLists.pop_back();
543             }
544           }
545         }
546
547         // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
548         consumedActor = EmitGeoTouchSignals(interceptedActor ? mInterceptedActorLists : mCandidateActorLists, touchEventHandle);
549       }
550     }
551     else
552     {
553       Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
554       if(interceptedTouchActor)
555       {
556         Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
557         consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
558       }
559       else
560       {
561         // Emit the intercept touch signal
562         Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
563         if(interceptedActor)
564         {
565           mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
566           // If the child is being touched, INTERRUPTED is sent.
567           if(mLastPrimaryHitActor.GetActor() &&
568             mLastPrimaryHitActor.GetActor() != interceptedActor &&
569             mLastRenderTask &&
570             mLastPrimaryPointState != PointState::FINISHED)
571           {
572             EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
573             mTouchDownConsumedActor.SetActor(nullptr);
574           }
575           consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
576         }
577         else
578         {
579           consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
580         }
581       }
582     }
583
584     consumed = consumedActor ? true : false;
585
586     if(primaryPointState == PointState::MOTION)
587     {
588       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);
589       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);
590     }
591     else
592     {
593       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);
594       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);
595     }
596   }
597
598   if((primaryPointState == PointState::DOWN) &&
599      (touchEventImpl->GetPointCount() == 1) &&
600      (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
601   {
602     mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
603   }
604
605   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
606   //    hit actor.  Also process the last consumed actor in the same manner.
607   Actor* lastPrimaryHitActor(nullptr);
608   if(mInterceptedTouchActor.GetActor())
609   {
610     lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
611   }
612   else
613   {
614     lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
615   }
616   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
617   if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
618   {
619     if(mLastRenderTask)
620     {
621       Dali::Actor leaveEventConsumer;
622       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
623
624       if(isGeometry)
625       {
626         if(lastPrimaryHitActor)
627         {
628           if(!lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(lastPrimaryHitActor))
629           {
630             // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
631             // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
632             DALI_LOG_RELEASE_INFO("InterruptedActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
633             leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
634           }
635         }
636
637         consumed |= leaveEventConsumer ? true : false;
638
639         // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
640         // consumed actor's listeners may need to be informed (through a leave event).
641         // Further checks here to ensure we do not signal the same actor twice for the same event.
642         if(lastConsumedActor &&
643            lastConsumedActor != lastPrimaryHitActor &&
644            lastConsumedActor != leaveEventConsumer)
645         {
646           if(!lastConsumedActor->IsHittable() || !IsActuallySensitive(lastConsumedActor))
647           {
648             // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
649             // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
650             DALI_LOG_RELEASE_INFO("InterruptedActor(Consume):     (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
651             EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
652           }
653         }
654       }
655       else
656       {
657         if(lastPrimaryHitActor &&
658            lastPrimaryHitActor != primaryHitActor &&
659            lastPrimaryHitActor != consumedActor)
660         {
661           if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
662           {
663             if(lastPrimaryHitActor->GetLeaveRequired())
664             {
665               DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
666               leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
667             }
668           }
669           else
670           {
671             // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
672             // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
673             DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
674             leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
675           }
676         }
677
678         consumed |= leaveEventConsumer ? true : false;
679
680         // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
681         // consumed actor's listeners may need to be informed (through a leave event).
682         // Further checks here to ensure we do not signal the same actor twice for the same event.
683         if(lastConsumedActor &&
684           lastConsumedActor != consumedActor &&
685           lastConsumedActor != lastPrimaryHitActor &&
686           lastConsumedActor != primaryHitActor &&
687           lastConsumedActor != leaveEventConsumer)
688         {
689           if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
690           {
691             if(lastConsumedActor->GetLeaveRequired())
692             {
693               DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
694               EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
695             }
696           }
697           else
698           {
699             // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
700             // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
701             DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
702             EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
703           }
704         }
705       }
706     }
707   }
708
709   // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
710   //    time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
711   if(primaryPointState == PointState::UP)
712   {
713     Clear();
714   }
715   else
716   {
717     // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
718     if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
719     {
720       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
721
722       // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
723       if(consumedActor && GetImplementation(consumedActor).OnScene())
724       {
725         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
726       }
727       else
728       {
729         if(isGeometry)
730         {
731           if(lastConsumedActor && !lastConsumedActor->OnScene())
732           {
733             mLastConsumedActor.SetActor(nullptr);
734           }
735         }
736         else
737         {
738           mLastConsumedActor.SetActor(nullptr);
739         }
740       }
741
742       mLastRenderTask        = currentRenderTask;
743       mLastPrimaryPointState = primaryPointState;
744     }
745     else
746     {
747       Clear();
748     }
749   }
750
751   // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
752   //    emit the stage touched event if required.
753
754   if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
755   {
756     switch(primaryPointState)
757     {
758       case PointState::UP:
759       {
760         Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
761         if(touchDownConsumedActor &&
762            touchDownConsumedActor != consumedActor &&
763            touchDownConsumedActor != lastPrimaryHitActor &&
764            touchDownConsumedActor != lastConsumedActor)
765         {
766           Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
767
768           Integration::Point currentPoint = touchEventImpl->GetPoint(0);
769           currentPoint.SetHitActor(touchDownConsumedActorHandle);
770           currentPoint.SetState(PointState::INTERRUPTED);
771
772           if(isGeometry)
773           {
774             std::list<Dali::Internal::Actor*> actorLists;
775             actorLists.push_back(touchDownConsumedActor);
776             GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
777           }
778           else
779           {
780             AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
781           }
782         }
783
784         mTouchDownConsumedActor.SetActor(nullptr);
785         mInterceptedTouchActor.SetActor(nullptr);
786
787         DALI_FALLTHROUGH;
788       }
789
790       case PointState::DOWN:
791       {
792         mScene.EmitTouchedSignal(touchEventHandle);
793         break;
794       }
795
796       case PointState::MOTION:
797       case PointState::LEAVE:
798       case PointState::STATIONARY:
799       case PointState::INTERRUPTED:
800       {
801         // Ignore
802         break;
803       }
804     }
805   }
806
807   return consumed;
808 }
809
810 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
811 {
812   if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
813   {
814     Dali::Actor actorHandle(actor);
815     Integration::Point point;
816     point.SetState(PointState::INTERRUPTED);
817     point.SetHitActor(actorHandle);
818     if(actor == mLastConsumedActor.GetActor())
819     {
820       std::list<Dali::Internal::Actor*> actorLists;
821       actorLists.push_back(mLastConsumedActor.GetActor());
822       GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
823     }
824     else if(!mLastConsumedActor.GetActor())
825     {
826       GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
827     }
828     // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
829     mLastConsumedActor.SetActor(nullptr);
830     mLastRenderTask.Reset();
831     mLastPrimaryPointState = PointState::FINISHED;
832   }
833   else
834   {
835     if(actor == mLastPrimaryHitActor.GetActor())
836     {
837       Dali::Actor actorHandle(actor);
838       Integration::Point point;
839       point.SetState(PointState::INTERRUPTED);
840       point.SetHitActor(actorHandle);
841
842       TouchEventPtr touchEventImpl(new TouchEvent);
843       touchEventImpl->AddPoint(point);
844       Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
845
846       Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
847       if(mLastConsumedActor.GetActor() != eventConsumer)
848       {
849         EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
850       }
851
852       // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
853       mLastConsumedActor.SetActor(nullptr);
854       mLastRenderTask.Reset();
855       mLastPrimaryPointState = PointState::FINISHED;
856     }
857   }
858
859 }
860
861 } // namespace Internal
862
863 } // namespace Dali