Touch and Hover event propagrated by geometry way.(4)
[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, ActorObserver& lastConsumedActor)
108 {
109   interceptActorList.clear();
110   Dali::Actor interceptedActor;
111   for(auto&& actor : actorLists)
112   {
113     // If there is a consumed actor, the intercept is sent only up to the moment before the consumed actor.
114     if(lastConsumedActor.GetActor() == actor)
115     {
116         break;
117     }
118     interceptActorList.push_back(actor);
119     if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
120     {
121       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
122       if(actor->EmitInterceptTouchEventSignal(touchEvent))
123       {
124         interceptedActor = Dali::Actor(actor);
125         break;
126       }
127     }
128   }
129   return interceptedActor;
130 }
131
132 /**
133  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
134  */
135 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
136 {
137   Dali::Actor consumedActor;
138
139   if(actor)
140   {
141     Dali::Actor oldParent(actor.GetParent());
142
143     Actor& actorImpl(GetImplementation(actor));
144
145     bool consumed(false);
146
147     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
148     if(ShouldEmitTouchEvent(actorImpl, touchEvent))
149     {
150       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
151       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
152     }
153
154     if(consumed)
155     {
156       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
157       consumedActor = Dali::Actor(&actorImpl);
158     }
159     else
160     {
161       // The actor may have been removed/reparented during the signal callbacks.
162       Dali::Actor parent = actor.GetParent();
163
164       if(parent &&
165          (parent == oldParent))
166       {
167         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
168         consumedActor = EmitTouchSignals(parent, touchEvent);
169       }
170     }
171   }
172
173   return consumedActor;
174 }
175
176 /**
177  *  Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
178  */
179 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
180 {
181   Dali::Actor consumedActor;
182
183   std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
184   for (; rIter != actorLists.rend(); rIter++)
185   {
186     Actor* actorImpl(*rIter);
187     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
188     if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
189     {
190       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
191       if(actorImpl->EmitTouchEventSignal(touchEvent))
192       {
193         // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
194         consumedActor = Dali::Actor(actorImpl);
195         break;
196       }
197     }
198   }
199   return consumedActor;
200 }
201
202 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
203 {
204   TouchEventPtr    touchEvent(new TouchEvent(time));
205   Dali::TouchEvent touchEventHandle(touchEvent.Get());
206
207   touchEvent->AddPoint(point);
208   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
209
210   return EmitTouchSignals(actor, touchEventHandle);
211 }
212
213 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
214 {
215   TouchEventPtr    touchEvent(new TouchEvent(time));
216   Dali::TouchEvent touchEventHandle(touchEvent.Get());
217
218   touchEvent->AddPoint(point);
219   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
220
221   return EmitGeoTouchSignals(actorLists, touchEventHandle);
222 }
223
224 /**
225  * Changes the state of the primary point to leave and emits the touch signals
226  */
227 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
228 {
229   Dali::Actor consumingActor;
230
231   if(actor)
232   {
233     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
234     touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
235
236     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
237
238     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
239     Vector2        localPosition;
240     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
241
242     primaryPoint.SetLocalPosition(localPosition);
243     primaryPoint.SetHitActor(Dali::Actor(actor));
244     primaryPoint.SetState(state);
245
246     if(isGeometry)
247     {
248       std::list<Dali::Internal::Actor*> actorLists;
249       actorLists.push_back(actor);
250       consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
251     }
252     else
253     {
254       consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
255     }
256   }
257
258   return consumingActor;
259 }
260
261 /**
262  * @brief Parses the primary touch point by performing a hit-test if necessary
263  *
264  * @param[out] hitTestResults The hit test results are put into this variable
265  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
266  * @param[in] lastRenderTask The last render task member
267  * @param[in] currentPoint The current point information
268  * @param[in] scene The scene that this touch is related to
269  * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
270  */
271 void ParsePrimaryTouchPoint(
272   HitTestAlgorithm::Results& hitTestResults,
273   ActorObserver&             capturingTouchActorObserver,
274   ActorObserver&             ownTouchActorObserver,
275   const RenderTaskPtr&       lastRenderTask,
276   const Integration::Point&  currentPoint,
277   const Internal::Scene&     scene,
278   std::list<Dali::Internal::Actor*>& actorLists)
279 {
280   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
281
282   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
283   if(capturingTouchActor && lastRenderTask)
284   {
285     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
286     hitTestResults.renderTask     = lastRenderTask;
287     const Vector2& screenPosition = currentPoint.GetScreenPosition();
288     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
289   }
290   else
291   {
292     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
293     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
294
295     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
296     {
297       bool isGeometry = scene.IsGeometryHittestEnabled();
298       // If we've just started touch, then check whether the actor has requested to capture all touch events
299       Actor* hitActor = &GetImplementation(hitTestResults.actor);
300       if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
301       {
302         capturingTouchActorObserver.SetActor(hitActor);
303       }
304       if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
305       {
306         ownTouchActorObserver.SetActor(hitActor);
307       }
308       if(isGeometry)
309       {
310         actorLists = hitTestResults.actorLists;
311       }
312     }
313   }
314 }
315
316 } // unnamed namespace
317
318 TouchEventProcessor::TouchEventProcessor(Scene& scene)
319 : mScene(scene),
320   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
321   mLastConsumedActor(),
322   mCapturingTouchActor(),
323   mOwnTouchActor(),
324   mTouchDownConsumedActor(),
325   mInterceptedTouchActor(),
326   mLastRenderTask(),
327   mLastPrimaryPointState(PointState::FINISHED)
328 {
329   DALI_LOG_TRACE_METHOD(gLogFilter);
330 }
331
332 TouchEventProcessor::~TouchEventProcessor()
333 {
334   DALI_LOG_TRACE_METHOD(gLogFilter);
335 }
336
337 void TouchEventProcessor::Clear()
338 {
339   mLastPrimaryHitActor.SetActor(nullptr);
340   mLastConsumedActor.SetActor(nullptr);
341   mCapturingTouchActor.SetActor(nullptr);
342   mOwnTouchActor.SetActor(nullptr);
343   mInterceptedTouchActor.SetActor(nullptr);
344   mLastRenderTask.Reset();
345   mLastPrimaryPointState = PointState::FINISHED;
346 }
347
348 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
349 {
350   DALI_LOG_TRACE_METHOD(gLogFilter);
351   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
352
353   PRINT_HIERARCHY(gLogFilter);
354
355   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
356
357   bool isGeometry = mScene.IsGeometryHittestEnabled();
358
359   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
360   //    and emit the stage signal as well.
361
362   if(event.points[0].GetState() == PointState::INTERRUPTED)
363   {
364     Dali::Actor        consumingActor;
365     Integration::Point currentPoint(event.points[0]);
366
367     if(isGeometry)
368     {
369       // Since the geometry way only receives touch events from the consumed actor,
370       // it first searches for whether there is a consumed actor and then sends the event
371       Actor* touchConsumedActor(mLastConsumedActor.GetActor());
372       Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
373       Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
374       if(touchConsumedActor)
375       {
376         Dali::Actor touchConsumedActorHandle(touchConsumedActor);
377         currentPoint.SetHitActor(touchConsumedActorHandle);
378         std::list<Dali::Internal::Actor*> actorLists;
379         actorLists.push_back(touchConsumedActor);
380         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
381       }
382       else if(touchDownConsumedActor)
383       {
384         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
385         currentPoint.SetHitActor(touchDownConsumedActorHandle);
386         std::list<Dali::Internal::Actor*> actorLists;
387         actorLists.push_back(touchDownConsumedActor);
388         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
389       }
390       else if(lastPrimaryHitActor)
391       {
392         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
393         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
394
395         GeoAllocAndEmitTouchSignals(mCandidateActorLists, event.time, currentPoint, mLastRenderTask);
396       }
397     }
398     else
399     {
400       Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
401       if(lastPrimaryHitActor)
402       {
403         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
404         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
405
406         consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask);
407       }
408
409       // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
410       Actor* lastConsumedActor(mLastConsumedActor.GetActor());
411       if(lastConsumedActor &&
412         lastConsumedActor != lastPrimaryHitActor &&
413         lastConsumedActor != consumingActor)
414       {
415         Dali::Actor lastConsumedActorHandle(lastConsumedActor);
416         currentPoint.SetHitActor(lastConsumedActorHandle);
417         AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask);
418       }
419
420       // Tell the touch-down consuming actor as well, if required
421       Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
422       if(touchDownConsumedActor &&
423         touchDownConsumedActor != lastPrimaryHitActor &&
424         touchDownConsumedActor != lastConsumedActor &&
425         touchDownConsumedActor != consumingActor)
426       {
427         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
428
429         currentPoint.SetHitActor(touchDownConsumedActorHandle);
430         AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask);
431       }
432     }
433
434     Clear();
435     mTouchDownConsumedActor.SetActor(nullptr);
436
437     currentPoint.SetHitActor(Dali::Actor());
438
439     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
440     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
441
442     touchEventImpl->AddPoint(currentPoint);
443
444     mScene.EmitTouchedSignal(touchEventHandle);
445     return false; // No need for hit testing & already an interrupted event so just return false
446   }
447
448   // 2) Hit Testing.
449   TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
450   Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
451
452   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
453   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
454
455   RenderTaskPtr currentRenderTask;
456   bool          firstPointParsed = false;
457
458   for(auto&& currentPoint : event.points)
459   {
460     HitTestAlgorithm::Results hitTestResults;
461     hitTestResults.point     = currentPoint;
462     hitTestResults.eventTime = event.time;
463     if(!firstPointParsed)
464     {
465       firstPointParsed = true;
466       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
467
468       // Only set the currentRenderTask for the primary hit actor.
469       currentRenderTask = hitTestResults.renderTask;
470       touchEventImpl->SetRenderTask(Dali::RenderTask(currentRenderTask.Get()));
471     }
472     else
473     {
474       HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, isGeometry);
475     }
476
477     Integration::Point newPoint(currentPoint);
478     newPoint.SetHitActor(hitTestResults.actor);
479     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
480
481     touchEventImpl->AddPoint(newPoint);
482
483     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);
484   }
485
486   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
487
488   bool consumed = false;
489
490   // Emit the touch signal
491   Dali::Actor consumedActor;
492
493   Integration::Point& primaryPoint      = touchEventImpl->GetPoint(0);
494   Dali::Actor         primaryHitActor   = primaryPoint.GetHitActor();
495   PointState::Type    primaryPointState = primaryPoint.GetState();
496
497   if(currentRenderTask)
498   {
499     if(isGeometry)
500     {
501       Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
502       if(interceptedTouchActor)
503       {
504         Actor* touchConsumedActor(mLastConsumedActor.GetActor());
505         if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
506         {
507           RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
508           consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
509         }
510         else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
511         {
512           Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
513           std::list<Dali::Internal::Actor*> interceptActorLists = mInterceptedActorLists;
514           consumedActor = EmitGeoTouchSignals(interceptActorLists, touchEventHandle);
515         }
516       }
517       else
518       {
519         Dali::Actor interceptedActor;
520         // Let's find out if there is an intercept actor.
521         interceptedActor = EmitGeoInterceptTouchSignals(mCandidateActorLists, mInterceptedActorLists, touchEventHandle, mLastConsumedActor);
522         if(interceptedActor)
523         {
524           mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
525
526           // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
527           if(mLastConsumedActor.GetActor() &&
528               mLastConsumedActor.GetActor() != interceptedActor &&
529               mLastRenderTask &&
530               mLastPrimaryPointState != PointState::FINISHED)
531           {
532             EmitTouchSignals(mLastConsumedActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
533             mTouchDownConsumedActor.SetActor(nullptr);
534             mLastConsumedActor.SetActor(nullptr);
535           }
536           else if(mLastPrimaryHitActor.GetActor() &&
537                   mLastPrimaryHitActor.GetActor() != interceptedActor &&
538                   mLastRenderTask &&
539                   mLastPrimaryPointState != PointState::FINISHED)
540           {
541             std::list<Dali::Internal::Actor*>::reverse_iterator rIter = mCandidateActorLists.rbegin();
542             for (; rIter != mCandidateActorLists.rend(); rIter++)
543             {
544               Actor* actorImpl(*rIter);
545               EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
546             }
547           }
548         }
549
550         Actor* touchConsumedActor(mLastConsumedActor.GetActor());
551         if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed 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