If Hittable is false, actor should not receive events except INTERRUPTED
[platform/core/uifw/dali-core.git] / dali / internal / event / events / touch-event-processor.cpp
1 /*
2  * Copyright (c) 2024 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::Internal
41 {
42 namespace
43 {
44 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TOUCH_PROCESSOR");
47 #endif // defined(DEBUG_ENABLED)
48
49 /**
50  * Structure for Variables used in the ProcessTouchEvent method.
51  */
52 struct ProcessTouchEventVariables
53 {
54   ProcessTouchEventVariables(TouchEventProcessor& eventProcessor, bool geometry)
55   : processor(eventProcessor),
56     isGeometry(geometry)
57   {
58   }
59
60   TouchEventProcessor& processor;                              ///< A handle to the touch-event-processor.
61   const bool           isGeometry;                             ///< Whether it's a geometry or not.
62   Actor*               lastPrimaryHitActor{nullptr};           ///< The last primary hit-actor.
63   Actor*               lastConsumedActor{nullptr};             ///< The last consuming actor.
64   TouchEventPtr        touchEventImpl;                         ///< The current touch-event-impl.
65   Dali::TouchEvent     touchEventHandle;                       ///< The handle to the touch-event-impl.
66   RenderTaskPtr        currentRenderTask;                      ///< The current render-task.
67   Dali::Actor          consumedActor;                          ///< The actor that consumed the event.
68   Dali::Actor          primaryHitActor;                        ///< The actor that has been hit by the primary point.
69   Integration::Point*  primaryPoint{nullptr};                  ///< The primary point of the hit.
70   PointState::Type     primaryPointState{PointState::STARTED}; ///< The state of the primary point.
71 };
72
73 const char* TOUCH_POINT_STATE[6] =
74   {
75     "DOWN",
76     "UP",
77     "MOTION",
78     "LEAVE",
79     "STATIONARY",
80     "INTERRUPTED",
81 };
82
83 bool ShouldEmitInterceptTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
84 {
85   PointState::Type state = event.GetState(0);
86   return actorImpl.GetInterceptTouchRequired() && (actorImpl.IsHittable() || state == PointState::INTERRUPTED ) && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
87 }
88
89 bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
90 {
91   PointState::Type state = event.GetState(0);
92   return actorImpl.GetTouchRequired() && (actorImpl.IsHittable() || state == PointState::INTERRUPTED ) && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
93 }
94
95 // child -> parent
96 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
97 {
98   Dali::Actor interceptedActor;
99
100   if(actor)
101   {
102     Dali::Actor parent = actor.GetParent();
103     if(parent)
104     {
105       // Recursively deliver events to the actor and its parents for intercept touch event.
106       interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
107     }
108
109     if(!interceptedActor)
110     {
111       bool   intercepted = false;
112       Actor& actorImpl(GetImplementation(actor));
113       if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
114       {
115         DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
116         intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
117         if(intercepted)
118         {
119           interceptedActor = Dali::Actor(&actorImpl);
120         }
121       }
122     }
123   }
124   return interceptedActor;
125 }
126
127 // geometry
128 // child -> below
129 Dali::Actor EmitGeoInterceptTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, std::list<Dali::Internal::Actor*>& interceptActorList, const Dali::TouchEvent& touchEvent, ActorObserver& lastConsumedActor)
130 {
131   interceptActorList.clear();
132   Dali::Actor interceptedActor;
133   for(auto&& actor : actorLists)
134   {
135     interceptActorList.push_back(actor);
136     if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
137     {
138       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
139       if(actor->EmitInterceptTouchEventSignal(touchEvent))
140       {
141         interceptedActor = Dali::Actor(actor);
142         break;
143       }
144     }
145     // If there is a consumed actor, the intercept is sent only up to the moment before the consumed actor.
146     if(lastConsumedActor.GetActor() == actor)
147     {
148       break;
149     }
150   }
151   return interceptedActor;
152 }
153
154 /**
155  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
156  */
157 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
158 {
159   Dali::Actor consumedActor;
160
161   if(actor)
162   {
163     Dali::Actor oldParent(actor.GetParent());
164
165     Actor& actorImpl(GetImplementation(actor));
166
167     bool consumed(false);
168
169     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
170     if(ShouldEmitTouchEvent(actorImpl, touchEvent))
171     {
172       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
173       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
174     }
175
176     if(consumed)
177     {
178       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
179       consumedActor = Dali::Actor(&actorImpl);
180     }
181     else
182     {
183       // The actor may have been removed/reparented during the signal callbacks.
184       Dali::Actor parent = actor.GetParent();
185
186       if(parent &&
187          (parent == oldParent))
188       {
189         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
190         consumedActor = EmitTouchSignals(parent, touchEvent);
191       }
192     }
193   }
194
195   return consumedActor;
196 }
197
198 /**
199  *  Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
200  */
201 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
202 {
203   Dali::Actor consumedActor;
204
205   std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
206   for(; rIter != actorLists.rend(); rIter++)
207   {
208     Actor* actorImpl(*rIter);
209     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
210     if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
211     {
212       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
213       if(actorImpl->EmitTouchEventSignal(touchEvent))
214       {
215         // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
216         consumedActor = Dali::Actor(actorImpl);
217         break;
218       }
219     }
220   }
221   return consumedActor;
222 }
223
224 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
225 {
226   TouchEventPtr    touchEvent(new TouchEvent(time));
227   Dali::TouchEvent touchEventHandle(touchEvent.Get());
228
229   touchEvent->AddPoint(point);
230   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
231
232   return EmitTouchSignals(actor, touchEventHandle);
233 }
234
235 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
236 {
237   TouchEventPtr    touchEvent(new TouchEvent(time));
238   Dali::TouchEvent touchEventHandle(touchEvent.Get());
239
240   touchEvent->AddPoint(point);
241   touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
242
243   return EmitGeoTouchSignals(actorLists, touchEventHandle);
244 }
245
246 /**
247  * Changes the state of the primary point to leave and emits the touch signals
248  */
249 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
250 {
251   Dali::Actor consumingActor;
252
253   if(actor)
254   {
255     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
256     touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
257
258     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
259
260     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
261     Vector2        localPosition;
262     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
263
264     primaryPoint.SetLocalPosition(localPosition);
265     primaryPoint.SetHitActor(Dali::Actor(actor));
266     primaryPoint.SetState(state);
267
268     if(isGeometry)
269     {
270       std::list<Dali::Internal::Actor*> actorLists;
271       actorLists.push_back(actor);
272       consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
273     }
274     else
275     {
276       consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
277     }
278   }
279
280   return consumingActor;
281 }
282
283 /**
284  * @brief Parses the primary touch point by performing a hit-test if necessary
285  *
286  * @param[out] hitTestResults The hit test results are put into this variable
287  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
288  * @param[in] lastRenderTask The last render task member
289  * @param[in] currentPoint The current point information
290  * @param[in] scene The scene that this touch is related to
291  * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
292  */
293 void ParsePrimaryTouchPoint(
294   HitTestAlgorithm::Results&         hitTestResults,
295   ActorObserver&                     capturingTouchActorObserver,
296   ActorObserver&                     ownTouchActorObserver,
297   const RenderTaskPtr&               lastRenderTask,
298   const Integration::Point&          currentPoint,
299   const Internal::Scene&             scene,
300   std::list<Dali::Internal::Actor*>& actorLists)
301 {
302   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
303
304   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
305   if(capturingTouchActor && lastRenderTask)
306   {
307     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
308     hitTestResults.renderTask     = lastRenderTask;
309     const Vector2& screenPosition = currentPoint.GetScreenPosition();
310     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
311   }
312   else
313   {
314     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
315     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
316
317     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
318     {
319       bool isGeometry = scene.IsGeometryHittestEnabled();
320       // If we've just started touch, then check whether the actor has requested to capture all touch events
321       Actor* hitActor = &GetImplementation(hitTestResults.actor);
322       if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
323       {
324         capturingTouchActorObserver.SetActor(hitActor);
325       }
326       if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
327       {
328         ownTouchActorObserver.SetActor(hitActor);
329       }
330       if(isGeometry)
331       {
332         actorLists = hitTestResults.actorLists;
333       }
334     }
335   }
336 }
337
338 } // unnamed namespace
339
340 struct TouchEventProcessor::Impl
341 {
342   /**
343    * Emits an interrupted event.
344    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
345    * @param[in] event The touch event that has occurred
346    */
347   static inline void EmitInterruptedEvent(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
348   {
349     Dali::Actor        consumingActor;
350     Integration::Point currentPoint(event.points[0]);
351
352     if(localVars.isGeometry)
353     {
354       // Since the geometry way only receives touch events from the consumed actor,
355       // it first searches for whether there is a consumed actor and then sends the event
356       Actor* touchConsumedActor(localVars.processor.mLastConsumedActor.GetActor());
357       Actor* touchDownConsumedActor(localVars.processor.mTouchDownConsumedActor.GetActor());
358       Actor* lastPrimaryHitActor(localVars.processor.mLastPrimaryHitActor.GetActor());
359       if(touchConsumedActor)
360       {
361         Dali::Actor touchConsumedActorHandle(touchConsumedActor);
362         currentPoint.SetHitActor(touchConsumedActorHandle);
363         std::list<Dali::Internal::Actor*> actorLists;
364         actorLists.push_back(touchConsumedActor);
365         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
366       }
367       else if(touchDownConsumedActor)
368       {
369         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
370         currentPoint.SetHitActor(touchDownConsumedActorHandle);
371         std::list<Dali::Internal::Actor*> actorLists;
372         actorLists.push_back(touchDownConsumedActor);
373         GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
374       }
375       else if(lastPrimaryHitActor)
376       {
377         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
378         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
379
380         GeoAllocAndEmitTouchSignals(localVars.processor.mCandidateActorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
381       }
382     }
383     else
384     {
385       Actor* lastPrimaryHitActor(localVars.processor.mLastPrimaryHitActor.GetActor());
386       if(lastPrimaryHitActor)
387       {
388         Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
389         currentPoint.SetHitActor(lastPrimaryHitActorHandle);
390
391         consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, localVars.processor.mLastRenderTask);
392       }
393
394       // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
395       Actor* lastConsumedActor(localVars.processor.mLastConsumedActor.GetActor());
396       if(lastConsumedActor &&
397          lastConsumedActor != lastPrimaryHitActor &&
398          lastConsumedActor != consumingActor)
399       {
400         Dali::Actor lastConsumedActorHandle(lastConsumedActor);
401         currentPoint.SetHitActor(lastConsumedActorHandle);
402         AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, localVars.processor.mLastRenderTask);
403       }
404
405       // Tell the touch-down consuming actor as well, if required
406       Actor* touchDownConsumedActor(localVars.processor.mTouchDownConsumedActor.GetActor());
407       if(touchDownConsumedActor &&
408          touchDownConsumedActor != lastPrimaryHitActor &&
409          touchDownConsumedActor != lastConsumedActor &&
410          touchDownConsumedActor != consumingActor)
411       {
412         Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
413
414         currentPoint.SetHitActor(touchDownConsumedActorHandle);
415         AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, localVars.processor.mLastRenderTask);
416       }
417     }
418
419     localVars.processor.Clear();
420     localVars.processor.mTouchDownConsumedActor.SetActor(nullptr);
421
422     currentPoint.SetHitActor(Dali::Actor());
423
424     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
425     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
426
427     touchEventImpl->AddPoint(currentPoint);
428
429     localVars.processor.mScene.EmitTouchedSignal(touchEventHandle);
430   }
431
432   /**
433    * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
434    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
435    * @return True if consumed, false otherwise.
436    */
437   static inline bool DeliverEventsToActorAndParents(ProcessTouchEventVariables& localVars)
438   {
439     bool                 consumed  = false;
440     TouchEventProcessor& processor = localVars.processor;
441     if(localVars.currentRenderTask)
442     {
443       if(localVars.isGeometry)
444       {
445         Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
446         if(interceptedTouchActor)
447         {
448           Actor* touchConsumedActor(processor.mLastConsumedActor.GetActor());
449           if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
450           {
451             RenderTask& currentRenderTaskImpl = *localVars.currentRenderTask.Get();
452             localVars.consumedActor           = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, localVars.touchEventImpl, localVars.primaryPointState, localVars.isGeometry);
453           }
454           else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
455           {
456             Dali::Actor                       interceptedTouchActorHandle(interceptedTouchActor);
457             std::list<Dali::Internal::Actor*> interceptActorLists = localVars.processor.mInterceptedActorLists;
458             localVars.consumedActor                               = EmitGeoTouchSignals(interceptActorLists, localVars.touchEventHandle);
459           }
460         }
461         else
462         {
463           Dali::Actor interceptedActor;
464           // Let's find out if there is an intercept actor.
465           interceptedActor = EmitGeoInterceptTouchSignals(processor.mCandidateActorLists, processor.mInterceptedActorLists, localVars.touchEventHandle, processor.mLastConsumedActor);
466           if(interceptedActor)
467           {
468             processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
469
470             // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
471             if(processor.mLastConsumedActor.GetActor() &&
472                processor.mLastConsumedActor.GetActor() != interceptedActor &&
473                processor.mLastRenderTask &&
474                processor.mLastPrimaryPointState != PointState::FINISHED)
475             {
476               EmitTouchSignals(processor.mLastConsumedActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
477               processor.mTouchDownConsumedActor.SetActor(nullptr);
478               processor.mLastConsumedActor.SetActor(nullptr);
479             }
480             else if(processor.mLastPrimaryHitActor.GetActor() &&
481                     processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
482                     processor.mLastRenderTask &&
483                     processor.mLastPrimaryPointState != PointState::FINISHED)
484             {
485               std::list<Dali::Internal::Actor*>::reverse_iterator rIter = processor.mCandidateActorLists.rbegin();
486               for(; rIter != processor.mCandidateActorLists.rend(); rIter++)
487               {
488                 Actor* actorImpl(*rIter);
489                 if(actorImpl == interceptedActor)
490                 {
491                   break;
492                 }
493                 EmitTouchSignals(actorImpl, *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
494               }
495             }
496           }
497
498           Actor* touchConsumedActor(processor.mLastConsumedActor.GetActor());
499           if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
500           {
501             RenderTask& currentRenderTaskImpl = *localVars.currentRenderTask.Get();
502             localVars.consumedActor           = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, localVars.touchEventImpl, localVars.primaryPointState, localVars.isGeometry);
503           }
504           else
505           {
506             // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
507             localVars.consumedActor = EmitGeoTouchSignals(interceptedActor ? processor.mInterceptedActorLists : processor.mCandidateActorLists, localVars.touchEventHandle);
508
509             if(localVars.consumedActor)
510             {
511               // If consumed, the actors who previously received the touch are interrupted, indicating that the touch has been consumed by another actor.
512               // backworkd
513               if(localVars.primaryPointState != PointState::DOWN)
514               {
515                 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = std::find(processor.mCandidateActorLists.rbegin(), processor.mCandidateActorLists.rend(), localVars.consumedActor);
516                 if(rIter != processor.mCandidateActorLists.rend())
517                 {
518                   for(++rIter; rIter != processor.mCandidateActorLists.rend(); ++rIter)
519                   {
520                     Actor* actorImpl(*rIter);
521                     EmitTouchSignals(actorImpl, *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
522                   }
523                 }
524               }
525
526               //forward
527               std::list<Dali::Internal::Actor*>::iterator iter = std::find(processor.mCandidateActorLists.begin(), processor.mCandidateActorLists.end(), localVars.consumedActor);
528               if(iter != processor.mCandidateActorLists.end())
529               {
530                 for(++iter; iter != processor.mCandidateActorLists.end(); ++iter)
531                 {
532                   Actor* actorImpl(*iter);
533                   EmitTouchSignals(actorImpl, *localVars.currentRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
534                 }
535               }
536             }
537           }
538         }
539       }
540       else
541       {
542         Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
543         if(interceptedTouchActor)
544         {
545           Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
546           localVars.consumedActor = EmitTouchSignals(interceptedTouchActorHandle, localVars.touchEventHandle);
547         }
548         else
549         {
550           // Emit the intercept touch signal
551           Dali::Actor interceptedActor = EmitInterceptTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
552           if(interceptedActor)
553           {
554             processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
555             // If the child is being touched, INTERRUPTED is sent.
556             if(processor.mLastPrimaryHitActor.GetActor() &&
557                processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
558                processor.mLastRenderTask &&
559                processor.mLastPrimaryPointState != PointState::FINISHED)
560             {
561               EmitTouchSignals(processor.mLastPrimaryHitActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
562               processor.mTouchDownConsumedActor.SetActor(nullptr);
563             }
564             localVars.consumedActor = EmitTouchSignals(interceptedActor, localVars.touchEventHandle);
565           }
566           else
567           {
568             localVars.consumedActor = EmitTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
569           }
570         }
571       }
572
573       consumed = localVars.consumedActor ? true : false;
574
575       if(localVars.primaryPointState == PointState::MOTION)
576       {
577         DALI_LOG_INFO(gLogFilter,
578                       Debug::Concise,
579                       "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
580                       localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
581                       localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
582                       localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
583                       TOUCH_POINT_STATE[localVars.primaryPointState],
584                       localVars.primaryPoint->GetScreenPosition().x,
585                       localVars.primaryPoint->GetScreenPosition().y,
586                       localVars.isGeometry);
587         DALI_LOG_INFO(gLogFilter,
588                       Debug::Concise,
589                       "ConsumedActor:   (%p) id(%d), name(%s), state(%s), isGeo : %d \n",
590                       localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
591                       localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
592                       localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
593                       TOUCH_POINT_STATE[localVars.primaryPointState],
594                       localVars.isGeometry);
595       }
596       else
597       {
598         DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
599                               localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
600                               localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
601                               localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
602                               TOUCH_POINT_STATE[localVars.primaryPointState],
603                               localVars.primaryPoint->GetScreenPosition().x,
604                               localVars.primaryPoint->GetScreenPosition().y,
605                               localVars.isGeometry);
606         DALI_LOG_RELEASE_INFO("ConsumedActor:  (%p), id(%d), name(%s), state(%s), isGeo : %d \n",
607                               localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
608                               localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
609                               localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
610                               TOUCH_POINT_STATE[localVars.primaryPointState],
611                               localVars.isGeometry);
612       }
613     }
614
615     if((localVars.primaryPointState == PointState::DOWN) &&
616        (localVars.touchEventImpl->GetPointCount() == 1) &&
617        (localVars.consumedActor && localVars.consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
618     {
619       processor.mTouchDownConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
620     }
621
622     return consumed;
623   }
624
625   /**
626    * Deliver Leave event to last hit or consuming actor if required.
627    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
628    * @return True if consumed, false otherwise.
629    */
630   static inline bool DeliverLeaveEvent(ProcessTouchEventVariables& localVars)
631   {
632     bool                 consumed = false;
633     TouchEventProcessor& processor(localVars.processor);
634     if((localVars.primaryPointState == PointState::MOTION) || (localVars.primaryPointState == PointState::UP) || (localVars.primaryPointState == PointState::STATIONARY))
635     {
636       if(processor.mLastRenderTask)
637       {
638         Dali::Actor leaveEventConsumer;
639         RenderTask& lastRenderTaskImpl = *processor.mLastRenderTask.Get();
640
641         if(localVars.isGeometry)
642         {
643           if(localVars.lastPrimaryHitActor)
644           {
645             if(!localVars.lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(localVars.lastPrimaryHitActor))
646             {
647               // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
648               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
649               DALI_LOG_RELEASE_INFO("InterruptedActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetName().data());
650               leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
651             }
652           }
653
654           consumed = leaveEventConsumer ? true : false;
655
656           // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
657           // consumed actor's listeners may need to be informed (through a leave event).
658           // Further checks here to ensure we do not signal the same actor twice for the same event.
659           if(localVars.lastConsumedActor &&
660              localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
661              localVars.lastConsumedActor != leaveEventConsumer)
662           {
663             if(!localVars.lastConsumedActor->IsHittable() || !IsActuallySensitive(localVars.lastConsumedActor))
664             {
665               // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
666               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
667               DALI_LOG_RELEASE_INFO("InterruptedActor(Consume):     (%p) %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetName().data());
668               EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
669             }
670           }
671         }
672         else
673         {
674           if(localVars.lastPrimaryHitActor &&
675              localVars.lastPrimaryHitActor != localVars.primaryHitActor &&
676              localVars.lastPrimaryHitActor != localVars.consumedActor)
677           {
678             if(localVars.lastPrimaryHitActor->IsHittable() && IsActuallySensitive(localVars.lastPrimaryHitActor))
679             {
680               if(localVars.lastPrimaryHitActor->GetLeaveRequired())
681               {
682                 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
683                 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
684               }
685             }
686             else
687             {
688               // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
689               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
690               DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
691               leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
692             }
693           }
694
695           consumed |= leaveEventConsumer ? true : false;
696
697           // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
698           // consumed actor's listeners may need to be informed (through a leave event).
699           // Further checks here to ensure we do not signal the same actor twice for the same event.
700           if(localVars.lastConsumedActor &&
701              localVars.lastConsumedActor != localVars.consumedActor &&
702              localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
703              localVars.lastConsumedActor != localVars.primaryHitActor &&
704              localVars.lastConsumedActor != leaveEventConsumer)
705           {
706             if(localVars.lastConsumedActor->IsHittable() && IsActuallySensitive(localVars.lastConsumedActor))
707             {
708               if(localVars.lastConsumedActor->GetLeaveRequired())
709               {
710                 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
711                 EmitTouchSignals(localVars.lastConsumedActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
712               }
713             }
714             else
715             {
716               // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
717               // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
718               DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
719               EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
720             }
721           }
722         }
723       }
724     }
725     return consumed;
726   }
727
728   /**
729    * Update the processor member appropriately by handling the final up event, and setting the last hit/consumed events etc.
730    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
731    */
732   static inline void UpdateMembersWithCurrentHitInformation(ProcessTouchEventVariables& localVars)
733   {
734     // If our primary point is an Up event, then the primary point (in multi-touch) will change next
735     // time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
736
737     TouchEventProcessor& processor(localVars.processor);
738     if(localVars.primaryPointState == PointState::UP)
739     {
740       processor.Clear();
741     }
742     else
743     {
744       // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
745       if(localVars.primaryHitActor && GetImplementation(localVars.primaryHitActor).OnScene())
746       {
747         processor.mLastPrimaryHitActor.SetActor(&GetImplementation(localVars.primaryHitActor));
748
749         // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
750         if(localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene())
751         {
752           processor.mLastConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
753         }
754         else
755         {
756           if(localVars.isGeometry)
757           {
758             if(localVars.lastConsumedActor && !localVars.lastConsumedActor->OnScene())
759             {
760               processor.mLastConsumedActor.SetActor(nullptr);
761             }
762           }
763           else
764           {
765             processor.mLastConsumedActor.SetActor(nullptr);
766           }
767         }
768
769         processor.mLastRenderTask        = localVars.currentRenderTask;
770         processor.mLastPrimaryPointState = localVars.primaryPointState;
771       }
772       else
773       {
774         processor.Clear();
775       }
776     }
777   }
778
779   /**
780    * Deliver an event to the touch-down actor and emit from the scene if required.
781    * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
782    * @param[in] event The touch event that has occurred
783    */
784   static inline void DeliverEventToTouchDownActorAndScene(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
785   {
786     TouchEventProcessor& processor(localVars.processor);
787     if(localVars.touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
788     {
789       switch(localVars.primaryPointState)
790       {
791         case PointState::UP:
792         {
793           Actor* touchDownConsumedActor(processor.mTouchDownConsumedActor.GetActor());
794           if(touchDownConsumedActor &&
795              touchDownConsumedActor != localVars.consumedActor &&
796              touchDownConsumedActor != localVars.lastPrimaryHitActor &&
797              touchDownConsumedActor != localVars.lastConsumedActor)
798           {
799             Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
800
801             Integration::Point currentPoint = localVars.touchEventImpl->GetPoint(0);
802             currentPoint.SetHitActor(touchDownConsumedActorHandle);
803             currentPoint.SetState(PointState::INTERRUPTED);
804
805             if(localVars.isGeometry)
806             {
807               std::list<Dali::Internal::Actor*> actorLists;
808               actorLists.push_back(touchDownConsumedActor);
809               GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
810             }
811             else
812             {
813               AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
814             }
815           }
816
817           processor.mTouchDownConsumedActor.SetActor(nullptr);
818           processor.mInterceptedTouchActor.SetActor(nullptr);
819
820           DALI_FALLTHROUGH;
821         }
822
823         case PointState::DOWN:
824         {
825           processor.mScene.EmitTouchedSignal(localVars.touchEventHandle);
826           break;
827         }
828
829         case PointState::MOTION:
830         case PointState::LEAVE:
831         case PointState::STATIONARY:
832         case PointState::INTERRUPTED:
833         {
834           // Ignore
835           break;
836         }
837       }
838     }
839   }
840 };
841
842 TouchEventProcessor::TouchEventProcessor(Scene& scene)
843 : mScene(scene),
844   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
845   mLastConsumedActor(),
846   mCapturingTouchActor(),
847   mOwnTouchActor(),
848   mTouchDownConsumedActor(),
849   mInterceptedTouchActor(),
850   mLastRenderTask(),
851   mLastPrimaryPointState(PointState::FINISHED)
852 {
853   DALI_LOG_TRACE_METHOD(gLogFilter);
854 }
855
856 TouchEventProcessor::~TouchEventProcessor()
857 {
858   DALI_LOG_TRACE_METHOD(gLogFilter);
859 }
860
861 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
862 {
863   DALI_LOG_TRACE_METHOD(gLogFilter);
864   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
865
866   PRINT_HIERARCHY(gLogFilter);
867
868   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
869
870   ProcessTouchEventVariables localVars(*this, mScene.IsGeometryHittestEnabled());
871
872   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
873   //    and emit the stage signal as well.
874
875   if(event.points[0].GetState() == PointState::INTERRUPTED)
876   {
877     Impl::EmitInterruptedEvent(localVars, event);
878     return false; // No need for hit testing & already an interrupted event so just return false
879   }
880
881   // 2) Hit Testing.
882   localVars.touchEventImpl   = new TouchEvent(event.time);
883   localVars.touchEventHandle = Dali::TouchEvent(localVars.touchEventImpl.Get());
884
885   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
886   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
887
888   bool firstPointParsed = false;
889   for(auto&& currentPoint : event.points)
890   {
891     HitTestAlgorithm::Results hitTestResults;
892     hitTestResults.point     = currentPoint;
893     hitTestResults.eventTime = event.time;
894     if(!firstPointParsed)
895     {
896       firstPointParsed = true;
897       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
898
899       // Only set the currentRenderTask for the primary hit actor.
900       localVars.currentRenderTask = hitTestResults.renderTask;
901       localVars.touchEventImpl->SetRenderTask(Dali::RenderTask(localVars.currentRenderTask.Get()));
902     }
903     else
904     {
905       Actor* capturingTouchActor = mCapturingTouchActor.GetActor();
906       if(capturingTouchActor && mLastRenderTask)
907       {
908         hitTestResults.actor          = Dali::Actor(capturingTouchActor);
909         hitTestResults.renderTask     = mLastRenderTask;
910         const Vector2& screenPosition = currentPoint.GetScreenPosition();
911         capturingTouchActor->ScreenToLocal(*mLastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
912       }
913       else
914       {
915         HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, localVars.isGeometry);
916       }
917     }
918
919     Integration::Point newPoint(currentPoint);
920     newPoint.SetHitActor(hitTestResults.actor);
921     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
922
923     localVars.touchEventImpl->AddPoint(newPoint);
924
925     DALI_LOG_INFO(gLogFilter,
926                   Debug::General,
927                   "  State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n",
928                   TOUCH_POINT_STATE[currentPoint.GetState()],
929                   currentPoint.GetScreenPosition().x,
930                   currentPoint.GetScreenPosition().y,
931                   (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL),
932                   (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""),
933                   hitTestResults.actorCoordinates.x,
934                   hitTestResults.actorCoordinates.y);
935   }
936
937   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
938   localVars.primaryPoint      = &localVars.touchEventImpl->GetPoint(0);
939   localVars.primaryHitActor   = localVars.primaryPoint->GetHitActor();
940   localVars.primaryPointState = localVars.primaryPoint->GetState();
941
942   bool consumed = Impl::DeliverEventsToActorAndParents(localVars);
943
944   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
945   //    hit actor.  Also process the last consumed actor in the same manner.
946   localVars.lastPrimaryHitActor = mInterceptedTouchActor.GetActor() ? mInterceptedTouchActor.GetActor() : mLastPrimaryHitActor.GetActor();
947   localVars.lastConsumedActor   = mLastConsumedActor.GetActor();
948   consumed |= Impl::DeliverLeaveEvent(localVars);
949
950   // 5) Update the processor member appropriately.
951   Impl::UpdateMembersWithCurrentHitInformation(localVars);
952
953   // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
954   //    emit the stage touched event if required.
955   Impl::DeliverEventToTouchDownActorAndScene(localVars, event);
956
957   return consumed;
958 }
959
960 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
961 {
962   if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
963   {
964     Dali::Actor        actorHandle(actor);
965     Integration::Point point;
966     point.SetState(PointState::INTERRUPTED);
967     point.SetHitActor(actorHandle);
968     if(actor == mLastConsumedActor.GetActor())
969     {
970       std::list<Dali::Internal::Actor*> actorLists;
971       actorLists.push_back(mLastConsumedActor.GetActor());
972       GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
973     }
974     else if(!mLastConsumedActor.GetActor())
975     {
976       GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
977     }
978     // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
979     mLastConsumedActor.SetActor(nullptr);
980     mLastRenderTask.Reset();
981     mLastPrimaryPointState = PointState::FINISHED;
982   }
983   else
984   {
985     if(actor == mLastPrimaryHitActor.GetActor())
986     {
987       Dali::Actor        actorHandle(actor);
988       Integration::Point point;
989       point.SetState(PointState::INTERRUPTED);
990       point.SetHitActor(actorHandle);
991
992       TouchEventPtr touchEventImpl(new TouchEvent);
993       touchEventImpl->AddPoint(point);
994       Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
995
996       Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
997       if(mLastConsumedActor.GetActor() != eventConsumer)
998       {
999         EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
1000       }
1001
1002       // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
1003       mLastConsumedActor.SetActor(nullptr);
1004       mLastRenderTask.Reset();
1005       mLastPrimaryPointState = PointState::FINISHED;
1006     }
1007   }
1008 }
1009
1010 void TouchEventProcessor::Clear()
1011 {
1012   mLastPrimaryHitActor.SetActor(nullptr);
1013   mLastConsumedActor.SetActor(nullptr);
1014   mCapturingTouchActor.SetActor(nullptr);
1015   mOwnTouchActor.SetActor(nullptr);
1016   mInterceptedTouchActor.SetActor(nullptr);
1017   mLastRenderTask.Reset();
1018   mLastPrimaryPointState = PointState::FINISHED;
1019 }
1020
1021 } // namespace Dali::Internal