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