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