[dali_2.3.27] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hover-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/hover-event-processor.h>
20
21 #if defined(DEBUG_ENABLED)
22 #include <sstream>
23 #endif
24
25 // EXTERNAL INCLUDES
26 #include <chrono>
27
28 // INTERNAL INCLUDES
29 #include <dali/integration-api/debug.h>
30 #include <dali/integration-api/events/hover-event-integ.h>
31 #include <dali/integration-api/trace.h>
32 #include <dali/internal/event/actors/actor-impl.h>
33 #include <dali/internal/event/actors/layer-impl.h>
34 #include <dali/internal/event/common/scene-impl.h>
35 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
36 #include <dali/internal/event/events/hover-event-impl.h>
37 #include <dali/internal/event/events/multi-point-event-util.h>
38 #include <dali/internal/event/render-tasks/render-task-impl.h>
39 #include <dali/public-api/math/vector2.h>
40
41 namespace Dali::Internal
42 {
43 namespace
44 {
45 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
46 #if defined(DEBUG_ENABLED)
47 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_HOVER_PROCESSOR");
48 #endif // defined(DEBUG_ENABLED)
49
50 /**
51  * Structure for Variables used in the ProcessHoverEvent method.
52  */
53 struct ProcessHoverEventVariables
54 {
55   ProcessHoverEventVariables(bool geometry)
56   : isGeometry(geometry)
57   {
58   }
59
60   const bool          isGeometry;                             ///< Whether it's a geometry or not.
61   Actor*              lastPrimaryHitActor{nullptr};           ///< The last primary hit-actor.
62   Actor*              lastConsumedActor{nullptr};             ///< The last consuming actor.
63   HoverEventPtr       hoverEvent;                             ///< The current hover-event-impl.
64   Dali::HoverEvent    hoverEventHandle;                       ///< The handle to the hover-event-impl.
65   RenderTaskPtr       currentRenderTask;                      ///< The current render-task.
66   Dali::Actor         consumedActor;                          ///< The actor that consumed the event.
67   Dali::Actor         primaryHitActor;                        ///< The actor that has been hit by the primary point.
68   Integration::Point* primaryPoint{nullptr};                  ///< The primary point of the hit.
69   PointState::Type    primaryPointState{PointState::STARTED}; ///< The state of the primary point.
70 };
71
72 const char* TOUCH_POINT_STATE[PointState::INTERRUPTED + 1] =
73   {
74     "STARTED",
75     "FINISHED",
76     "MOTION",
77     "LEAVE",
78     "STATIONARY",
79     "INTERRUPTED",
80 };
81
82 bool ShouldEmitHoverEvent(const Actor& actorImpl, const Dali::HoverEvent& event)
83 {
84   PointState::Type state = event.GetState(0);
85   return actorImpl.GetHoverRequired() && (state != PointState::MOTION || actorImpl.IsDispatchHoverMotion());
86 }
87
88 /**
89  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
90  */
91 Dali::Actor EmitHoverSignals(Dali::Actor actor, const Dali::HoverEvent& event)
92 {
93   Dali::Actor consumedActor;
94
95   if(actor)
96   {
97     Dali::Actor oldParent(actor.GetParent());
98
99     Actor& actorImpl(GetImplementation(actor));
100
101     bool consumed(false);
102
103     // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover).
104     if(ShouldEmitHoverEvent(actorImpl, event))
105     {
106       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_HOVER_EVENT_SIGNAL");
107       consumed = actorImpl.EmitHoverEventSignal(event);
108     }
109
110     if(consumed)
111     {
112       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
113       consumedActor = Dali::Actor(&actorImpl);
114     }
115     else
116     {
117       // The actor may have been removed/reparented during the signal callbacks.
118       Dali::Actor parent = actor.GetParent();
119
120       if(parent &&
121          (parent == oldParent))
122       {
123         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
124         consumedActor = EmitHoverSignals(parent, event);
125       }
126     }
127   }
128
129   return consumedActor;
130 }
131
132 /**
133  *  Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
134  */
135 Dali::Actor EmitGeoHoverSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::HoverEvent& hoverEvent)
136 {
137   Dali::Actor consumedActor;
138
139   std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
140   for(; rIter != actorLists.rend(); rIter++)
141   {
142     Actor* actorImpl(*rIter);
143     // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover).
144     if(ShouldEmitHoverEvent(*actorImpl, hoverEvent))
145     {
146       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_HOVER_EVENT_SIGNAL");
147       PointState::Type currentState = actorImpl->GetHoverState();
148       actorImpl->SetHoverState(hoverEvent.GetState(0));
149       // If hover event is newly entering the actor, update it to the started state.
150       if(hoverEvent.GetState(0) == PointState::MOTION &&
151          (currentState == PointState::FINISHED || currentState == PointState::INTERRUPTED || currentState == PointState::LEAVE))
152       {
153         HoverEventPtr newHoverEvent = HoverEvent::Clone(GetImplementation(hoverEvent));
154         newHoverEvent->GetPoint(0).SetState(PointState::STARTED);
155         actorImpl->SetHoverState(PointState::STARTED); //update state
156         if(actorImpl->EmitHoverEventSignal(Dali::HoverEvent(newHoverEvent.Get())))
157         {
158           // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
159           consumedActor = Dali::Actor(actorImpl);
160           break;
161         }
162       }
163       else if(actorImpl->EmitHoverEventSignal(hoverEvent))
164       {
165         // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
166         consumedActor = Dali::Actor(actorImpl);
167         break;
168       }
169     }
170   }
171   return consumedActor;
172 }
173
174 Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
175 {
176   HoverEventPtr    hoverEvent(new HoverEvent(time));
177   Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
178
179   hoverEvent->AddPoint(point);
180
181   return EmitHoverSignals(actor, hoverEventHandle);
182 }
183
184 Dali::Actor GeoAllocAndEmitHoverSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point)
185 {
186   HoverEventPtr    hoverEvent(new HoverEvent(time));
187   Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
188
189   hoverEvent->AddPoint(point);
190
191   return EmitGeoHoverSignals(actorLists, hoverEventHandle);
192 }
193
194 /**
195  * Changes the state of the primary point to leave and emits the hover signals
196  */
197 Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state, bool isGeometry)
198 {
199   HoverEventPtr hoverEvent = HoverEvent::Clone(*originalEvent.Get());
200
201   DALI_ASSERT_DEBUG(NULL != actor && "NULL actor pointer");
202   Dali::Actor consumingActor;
203   if(actor)
204   {
205     Integration::Point& primaryPoint = hoverEvent->GetPoint(0);
206
207     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
208     Vector2        localPosition;
209     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
210
211     primaryPoint.SetLocalPosition(localPosition);
212     primaryPoint.SetHitActor(Dali::Actor(actor));
213     primaryPoint.SetState(state);
214   }
215
216   if(isGeometry)
217   {
218     std::list<Dali::Internal::Actor*> actorLists;
219     actorLists.push_back(actor);
220     consumingActor = EmitGeoHoverSignals(actorLists, Dali::HoverEvent(hoverEvent.Get()));
221   }
222   else
223   {
224     consumingActor = EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get()));
225   }
226   return consumingActor;
227 }
228
229 /**
230  * Used in the hit-test algorithm to check whether the actor is hoverable.
231  */
232 struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface
233 {
234   bool IsActorHittable(Actor* actor) override
235   {
236     return actor->GetHoverRequired() && // Does the Application or derived actor type require a hover event?
237            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
238   }
239
240   bool DescendActorHierarchy(Actor* actor) override
241   {
242     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
243            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
244   }
245
246   bool DoesLayerConsumeHit(Layer* layer) override
247   {
248     return layer->IsHoverConsumed();
249   }
250
251   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override
252   {
253     // Hover event is always hit.
254     return true;
255   }
256 };
257
258 uint32_t GetMilliSeconds()
259 {
260   // Get the time of a monotonic clock since its epoch.
261   auto epoch = std::chrono::steady_clock::now().time_since_epoch();
262
263   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
264
265   return static_cast<uint32_t>(duration.count());
266 }
267
268 } // unnamed namespace
269
270 struct HoverEventProcessor::Impl
271 {
272   /**
273    * Emits an interrupted event while processing the latest hover event.
274    * @param[in/out]  processor  The hover-event-processor
275    * @param[in]  isGeometry  Whether it's a geometry or not
276    * @param[in]  event  The hover event that has occurred
277    */
278   static inline void EmitInterruptedEvent(HoverEventProcessor& processor, const bool isGeometry, const Integration::HoverEvent& event)
279   {
280     Dali::Actor        consumingActor;
281     Integration::Point currentPoint(event.points[0]);
282
283     Actor* lastPrimaryHitActor(processor.mLastPrimaryHitActor.GetActor());
284     if(lastPrimaryHitActor)
285     {
286       Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
287       currentPoint.SetHitActor(lastPrimaryHitActorHandle);
288       if(isGeometry)
289       {
290         consumingActor = GeoAllocAndEmitHoverSignals(processor.mCandidateActorLists, event.time, currentPoint);
291       }
292       else
293       {
294         consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
295       }
296     }
297
298     // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
299     Actor* lastConsumedActor(processor.mLastConsumedActor.GetActor());
300     if(lastConsumedActor &&
301        lastConsumedActor != lastPrimaryHitActor &&
302        lastConsumedActor != consumingActor)
303     {
304       Dali::Actor lastConsumedActorHandle(lastConsumedActor);
305       currentPoint.SetHitActor(lastConsumedActorHandle);
306       if(isGeometry)
307       {
308         std::list<Dali::Internal::Actor*> actorLists;
309         actorLists.push_back(lastConsumedActor);
310         GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint);
311       }
312       else
313       {
314         AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
315       }
316     }
317
318     // Tell the hover-start consuming actor as well, if required
319     Actor* hoverStartConsumedActor(processor.mHoverStartConsumedActor.GetActor());
320     if(hoverStartConsumedActor &&
321        hoverStartConsumedActor != lastPrimaryHitActor &&
322        hoverStartConsumedActor != lastConsumedActor &&
323        hoverStartConsumedActor != consumingActor)
324     {
325       Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
326       currentPoint.SetHitActor(hoverStartConsumedActorHandle);
327       if(isGeometry)
328       {
329         std::list<Dali::Internal::Actor*> actorLists;
330         actorLists.push_back(hoverStartConsumedActor);
331         GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint);
332       }
333       else
334       {
335         AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
336       }
337     }
338
339     processor.Clear();
340     processor.mHoverStartConsumedActor.SetActor(nullptr);
341   }
342
343   /**
344    * Performs a hit-test and sets the variables in processor and localVars appropriately.
345    * @param[in/out]  processor  The hover-event-processor
346    * @param[in/out]  localVars  The struct of stack variables used by ProcessHoverEvent
347    * @param[in]  event  The hover event that has occurred
348    */
349   static inline void HitTest(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars, const Integration::HoverEvent& event)
350   {
351     bool firstPointParsed = false;
352     for(auto&& currentPoint : event.points)
353     {
354       HitTestAlgorithm::Results hitTestResults;
355       hitTestResults.eventTime = event.time;
356       ActorHoverableCheck actorHoverableCheck;
357       HitTestAlgorithm::HitTest(processor.mScene.GetSize(), processor.mScene.GetRenderTaskList(), processor.mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck, localVars.isGeometry);
358
359       Integration::Point newPoint(currentPoint);
360       newPoint.SetHitActor(hitTestResults.actor);
361       newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
362
363       localVars.hoverEvent->AddPoint(newPoint);
364
365       DALI_LOG_INFO(gLogFilter,
366                     Debug::General,
367                     "  State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n",
368                     TOUCH_POINT_STATE[currentPoint.GetState()],
369                     currentPoint.GetScreenPosition().x,
370                     currentPoint.GetScreenPosition().y,
371                     (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL),
372                     (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""),
373                     hitTestResults.actorCoordinates.x,
374                     hitTestResults.actorCoordinates.y);
375
376       // Only set the currentRenderTask for the primary hit actor.
377       if(!firstPointParsed)
378       {
379         firstPointParsed               = true;
380         localVars.currentRenderTask    = hitTestResults.renderTask;
381         processor.mCandidateActorLists = hitTestResults.actorLists;
382       }
383     }
384   }
385
386   /**
387    * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
388    * @param[in/out]  processor  The hover-event-processor
389    * @param[in/out]  localVars  The struct of stack variables used by ProcessHoverEvent
390    */
391   static inline void DeliverEventsToActorAndParents(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars)
392   {
393     // Emit the touch signal
394     if(localVars.currentRenderTask)
395     {
396       Dali::Actor hitActor = localVars.hoverEvent->GetHitActor(0);
397
398       if(localVars.isGeometry)
399       {
400         localVars.consumedActor = EmitGeoHoverSignals(processor.mCandidateActorLists, localVars.hoverEventHandle);
401       }
402       else
403       {
404         // If the actor is hit first, the hover is started.
405         if(hitActor && processor.mLastPrimaryHitActor.GetActor() != hitActor &&
406            localVars.primaryPointState == PointState::MOTION && GetImplementation(hitActor).GetLeaveRequired())
407         {
408             // A leave event is sent to the previous actor first.
409             localVars.lastPrimaryHitActor = processor.mLastPrimaryHitActor.GetActor();
410             localVars.lastConsumedActor   = processor.mLastConsumedActor.GetActor();
411             Impl::DeliverLeaveEvent(processor, localVars);
412
413             localVars.hoverEvent->GetPoint(0).SetState(PointState::STARTED);
414             localVars.primaryPointState = PointState::STARTED;
415
416             // It sends a started event and updates information.
417             localVars.consumedActor = EmitHoverSignals(hitActor, localVars.hoverEventHandle);
418             UpdateMembersWithCurrentHitInformation(processor, localVars);
419         }
420         else
421         {
422           localVars.consumedActor = EmitHoverSignals(hitActor, localVars.hoverEventHandle);
423         }
424       }
425
426       if(localVars.hoverEvent->GetPoint(0).GetState() != PointState::MOTION)
427       {
428         DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s)\n",
429                               localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
430                               localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
431                               localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
432                               TOUCH_POINT_STATE[localVars.hoverEvent->GetPoint(0).GetState()]);
433         DALI_LOG_RELEASE_INFO("ConsumedActor:  (%p), id(%d), name(%s), state(%s)\n",
434                               localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
435                               localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
436                               localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
437                               TOUCH_POINT_STATE[localVars.hoverEvent->GetPoint(0).GetState()]);
438       }
439     }
440
441     if((localVars.primaryPointState == PointState::STARTED) &&
442        (localVars.hoverEvent->GetPointCount() == 1) &&
443        (localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene()))
444     {
445       processor.mHoverStartConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
446     }
447   }
448
449   /**
450    * Deliver Leave event to last hit or consuming actor if required.
451    * @param[in/out]  processor  The hover-event-processor
452    * @param[in/out]  localVars  The struct of stack variables used by ProcessHoverEvent
453    */
454   static inline void DeliverLeaveEvent(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars)
455   {
456     if((localVars.primaryPointState == PointState::STARTED) ||
457        (localVars.primaryPointState == PointState::MOTION) ||
458        (localVars.primaryPointState == PointState::FINISHED) ||
459        (localVars.primaryPointState == PointState::STATIONARY))
460     {
461       if(processor.mLastRenderTask)
462       {
463         Dali::Actor leaveEventConsumer;
464         RenderTask& lastRenderTaskImpl = *processor.mLastRenderTask.Get();
465
466         if(localVars.lastPrimaryHitActor &&
467            localVars.lastPrimaryHitActor != localVars.primaryHitActor &&
468            localVars.lastPrimaryHitActor != localVars.consumedActor)
469         {
470           if(localVars.lastPrimaryHitActor->IsHittable() && IsActuallySensitive(localVars.lastPrimaryHitActor))
471           {
472             if(localVars.isGeometry)
473             {
474               // This is a situation where actors who received a hover event must leave.
475               // Compare the lastActorList that received the hover event and the CandidateActorList that can receive the new hover event
476               // If the hover event can no longer be received, Leave is sent.
477               std::list<Dali::Internal::Actor*>::reverse_iterator rLastIter = processor.mLastActorLists.rbegin();
478               for(; rLastIter != processor.mLastActorLists.rend(); rLastIter++)
479               {
480                 bool                                                find           = false;
481                 std::list<Dali::Internal::Actor*>::reverse_iterator rCandidateIter = processor.mCandidateActorLists.rbegin();
482                 for(; rCandidateIter != processor.mCandidateActorLists.rend(); rCandidateIter++)
483                 {
484                   if(*rCandidateIter == *rLastIter)
485                   {
486                     find = true;
487                     break;
488                   }
489                 }
490                 if(!find)
491                 {
492                   DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(*rLastIter), (*rLastIter)->GetId(), (*rLastIter)->GetName().data());
493                   leaveEventConsumer = EmitHoverSignals(*rLastIter, lastRenderTaskImpl, localVars.hoverEvent, PointState::LEAVE, localVars.isGeometry);
494                 }
495                 // If the actor has been consumed, you do not need to proceed.
496                 if(*rLastIter == localVars.lastConsumedActor)
497                 {
498                   break;
499                 }
500               }
501             }
502             else if(localVars.lastPrimaryHitActor->GetLeaveRequired())
503             {
504               // In the case of isGeometry, it is not propagated but only sent to actors who are not hittable.
505               DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
506               leaveEventConsumer = EmitHoverSignals(processor.mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, localVars.hoverEvent, PointState::LEAVE, localVars.isGeometry);
507             }
508           }
509           else if(localVars.primaryPointState != PointState::STARTED)
510           {
511             // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
512             // An interrupted event is sent to allow some actors to go back to their original state (i.e. Button controls)
513             DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
514             leaveEventConsumer = EmitHoverSignals(processor.mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, localVars.hoverEvent, PointState::INTERRUPTED, localVars.isGeometry);
515           }
516         }
517
518         // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
519         // consumed actor's listeners may need to be informed (through a leave event).
520         // Further checks here to ensure we do not signal the same actor twice for the same event.
521         if(localVars.lastConsumedActor &&
522            localVars.lastConsumedActor != localVars.consumedActor &&
523            localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
524            localVars.lastConsumedActor != localVars.primaryHitActor &&
525            localVars.lastConsumedActor != leaveEventConsumer)
526         {
527           if(localVars.lastConsumedActor->IsHittable() && IsActuallySensitive(localVars.lastConsumedActor))
528           {
529             if(localVars.lastConsumedActor->GetLeaveRequired() && !localVars.isGeometry) // For geometry, we have already sent leave. There is no need to send leave repeatedly.
530             {
531               DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
532               EmitHoverSignals(localVars.lastConsumedActor, lastRenderTaskImpl, localVars.hoverEvent, PointState::LEAVE, localVars.isGeometry);
533             }
534           }
535           else if(localVars.primaryPointState != PointState::STARTED)
536           {
537             // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
538             // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
539             DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
540             EmitHoverSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.hoverEvent, PointState::INTERRUPTED, localVars.isGeometry);
541           }
542         }
543       }
544     }
545   }
546
547   /**
548    * Update the processor member appropriately by handling the final up event, and setting the last hit/consumed events etc.
549    * @param[in/out]  processor  The hover-event-processor
550    * @param[in/out] localVars The struct of stack variables used by ProcessHoverEvent
551    */
552   static inline void UpdateMembersWithCurrentHitInformation(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars)
553   {
554     // If our primary point is a FINISHED event, then the primary point (in multi-touch) will change next
555     // time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
556
557     if(localVars.primaryPointState == PointState::FINISHED)
558     {
559       processor.Clear();
560     }
561     else
562     {
563       // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
564       if(localVars.primaryHitActor && GetImplementation(localVars.primaryHitActor).OnScene())
565       {
566         processor.mLastPrimaryHitActor.SetActor(&GetImplementation(localVars.primaryHitActor));
567         // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
568         if(localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene())
569         {
570           processor.mLastConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
571         }
572         else
573         {
574           processor.mLastConsumedActor.SetActor(nullptr);
575         }
576
577         processor.mLastRenderTask = localVars.currentRenderTask;
578         processor.mLastActorLists = processor.mCandidateActorLists;
579       }
580       else
581       {
582         processor.Clear();
583       }
584     }
585   }
586
587   /**
588    * Deliver an interrupted event to the hover started actor as required.
589    * @param[in/out]  processor  The hover-event-processor
590    * @param[in/out]  localVars  The struct of stack variables used by ProcessHoverEvent
591    * @param[in]  event  The hover event that has occurred
592    */
593   static inline void DeliverInterruptedEventToHoverStartedActor(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars, const Integration::HoverEvent& event)
594   {
595     if(localVars.hoverEvent->GetPointCount() == 1 && localVars.primaryPointState == PointState::FINISHED) // Only want the first hover started
596     {
597       Actor* hoverStartConsumedActor(processor.mHoverStartConsumedActor.GetActor());
598       if(hoverStartConsumedActor &&
599          hoverStartConsumedActor != localVars.consumedActor &&
600          hoverStartConsumedActor != localVars.lastPrimaryHitActor &&
601          hoverStartConsumedActor != localVars.lastConsumedActor)
602       {
603         Dali::Actor        hoverStartConsumedActorHandle(hoverStartConsumedActor);
604         Integration::Point primaryPoint = localVars.hoverEvent->GetPoint(0);
605         primaryPoint.SetHitActor(hoverStartConsumedActorHandle);
606         primaryPoint.SetState(PointState::INTERRUPTED);
607         if(localVars.isGeometry)
608         {
609           std::list<Dali::Internal::Actor*> actorLists;
610           actorLists.push_back(hoverStartConsumedActor);
611           GeoAllocAndEmitHoverSignals(actorLists, event.time, primaryPoint);
612         }
613         else
614         {
615           AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
616         }
617
618         // Restore hover-event to original state
619         primaryPoint.SetHitActor(localVars.primaryHitActor);
620         primaryPoint.SetState(localVars.primaryPointState);
621       }
622
623       processor.mHoverStartConsumedActor.SetActor(nullptr);
624     }
625   }
626 };
627
628 HoverEventProcessor::HoverEventProcessor(Scene& scene)
629 : mScene(scene),
630   mLastPrimaryHitActor(MakeCallback(this, &HoverEventProcessor::OnObservedActorDisconnected))
631 {
632   DALI_LOG_TRACE_METHOD(gLogFilter);
633 }
634
635 HoverEventProcessor::~HoverEventProcessor()
636 {
637   DALI_LOG_TRACE_METHOD(gLogFilter);
638 }
639
640 void HoverEventProcessor::SendInterruptedHoverEvent(Dali::Internal::Actor* actor)
641 {
642   if(actor &&
643      (mLastPrimaryHitActor.GetActor() == actor || mLastConsumedActor.GetActor() == actor))
644   {
645     Integration::Point point;
646     point.SetState(PointState::INTERRUPTED);
647     point.SetHitActor(Dali::Actor(actor));
648     if(mScene.IsGeometryHittestEnabled())
649     {
650       std::list<Dali::Internal::Actor*> actorLists;
651       actorLists.push_back(actor);
652       GeoAllocAndEmitHoverSignals(actorLists, 0, point);
653     }
654     else
655     {
656       AllocAndEmitHoverSignals(GetMilliSeconds(), point.GetHitActor(), point);
657     }
658     Clear();
659   }
660 }
661
662 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
663 {
664   DALI_LOG_TRACE_METHOD(gLogFilter);
665   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
666
667   PRINT_HIERARCHY(gLogFilter);
668
669   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_HOVER_EVENT");
670
671   ProcessHoverEventVariables localVars(mScene.IsGeometryHittestEnabled());
672
673   // Copy so we can add the results of a hit-test.
674   localVars.hoverEvent = new HoverEvent(event.time);
675
676   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
677   //    and emit the stage signal as well.
678   if(event.points[0].GetState() == PointState::INTERRUPTED)
679   {
680     Impl::EmitInterruptedEvent(*this, localVars.isGeometry, event);
681     return; // No need for hit testing
682   }
683
684   // 2) Hit Testing.
685   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
686   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
687   localVars.hoverEventHandle = Dali::HoverEvent(localVars.hoverEvent.Get());
688   Impl::HitTest(*this, localVars, event);
689
690   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
691   localVars.primaryPoint      = &localVars.hoverEvent->GetPoint(0);
692   localVars.primaryHitActor   = localVars.primaryPoint->GetHitActor();
693   localVars.primaryPointState = localVars.primaryPoint->GetState();
694   Impl::DeliverEventsToActorAndParents(*this, localVars);
695
696   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
697   //    hit actor.  Also process the last consumed actor in the same manner.
698   localVars.lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
699   localVars.lastConsumedActor   = mLastConsumedActor.GetActor();
700   Impl::DeliverLeaveEvent(*this, localVars);
701
702   // 5) Update the processor member appropriately.
703   Impl::UpdateMembersWithCurrentHitInformation(*this, localVars);
704
705   // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
706   Impl::DeliverInterruptedEventToHoverStartedActor(*this, localVars, event);
707 }
708
709 void HoverEventProcessor::Clear()
710 {
711   mLastPrimaryHitActor.SetActor(nullptr);
712   mLastConsumedActor.SetActor(nullptr);
713   mLastRenderTask.Reset();
714   mLastActorLists.clear();
715 }
716
717 void HoverEventProcessor::OnObservedActorDisconnected(Dali::Internal::Actor* actor)
718 {
719   SendInterruptedHoverEvent(actor);
720 }
721
722 } // namespace Dali::Internal