2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/events/hover-event-processor.h>
21 #if defined(DEBUG_ENABLED)
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>
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)
52 const char* TOUCH_POINT_STATE[PointState::INTERRUPTED + 1] =
62 bool ShouldEmitHoverEvent(const Actor& actorImpl, const Dali::HoverEvent& event)
64 PointState::Type state = event.GetState(0);
65 return actorImpl.GetHoverRequired() && (state!= PointState::MOTION || actorImpl.IsDispatchHoverMotion());
69 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
71 Dali::Actor EmitHoverSignals(Dali::Actor actor, const Dali::HoverEvent& event)
73 Dali::Actor consumedActor;
77 Dali::Actor oldParent(actor.GetParent());
79 Actor& actorImpl(GetImplementation(actor));
83 // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover).
84 if(ShouldEmitHoverEvent(actorImpl, event))
86 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_HOVER_EVENT_SIGNAL");
87 consumed = actorImpl.EmitHoverEventSignal(event);
92 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
93 consumedActor = Dali::Actor(&actorImpl);
97 // The actor may have been removed/reparented during the signal callbacks.
98 Dali::Actor parent = actor.GetParent();
101 (parent == oldParent))
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);
109 return consumedActor;
113 * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
115 Dali::Actor EmitGeoHoverSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::HoverEvent& hoverEvent)
117 Dali::Actor consumedActor;
119 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
120 for (; rIter != actorLists.rend(); rIter++)
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))
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))
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())))
138 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
139 consumedActor = Dali::Actor(actorImpl);
143 else if(actorImpl->EmitHoverEventSignal(hoverEvent))
145 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
146 consumedActor = Dali::Actor(actorImpl);
151 return consumedActor;
155 Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
157 HoverEventPtr hoverEvent(new HoverEvent(time));
158 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
160 hoverEvent->AddPoint(point);
162 return EmitHoverSignals(actor, hoverEventHandle);
165 Dali::Actor GeoAllocAndEmitHoverSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point)
167 HoverEventPtr hoverEvent(new HoverEvent(time));
168 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
170 hoverEvent->AddPoint(point);
172 return EmitGeoHoverSignals(actorLists, hoverEventHandle);
177 * Changes the state of the primary point to leave and emits the hover signals
179 Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state, bool isGeometry)
181 HoverEventPtr hoverEvent = HoverEvent::Clone(*originalEvent.Get());
183 DALI_ASSERT_DEBUG(NULL != actor && "NULL actor pointer");
184 Dali::Actor consumingActor;
187 Integration::Point& primaryPoint = hoverEvent->GetPoint(0);
189 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
190 Vector2 localPosition;
191 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
193 primaryPoint.SetLocalPosition(localPosition);
194 primaryPoint.SetHitActor(Dali::Actor(actor));
195 primaryPoint.SetState(state);
200 std::list<Dali::Internal::Actor*> actorLists;
201 actorLists.push_back(actor);
202 consumingActor = EmitGeoHoverSignals(actorLists, Dali::HoverEvent(hoverEvent.Get()));
206 consumingActor = EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get()));
208 return consumingActor;
212 * Used in the hit-test algorithm to check whether the actor is hoverable.
214 struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface
216 bool IsActorHittable(Actor* actor) override
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?
222 bool DescendActorHierarchy(Actor* actor) override
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.
228 bool DoesLayerConsumeHit(Layer* layer) override
230 return layer->IsHoverConsumed();
233 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override
235 // Hover event is always hit.
240 uint32_t GetMilliSeconds()
242 // Get the time of a monotonic clock since its epoch.
243 auto epoch = std::chrono::steady_clock::now().time_since_epoch();
245 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
247 return static_cast<uint32_t>(duration.count());
250 } // unnamed namespace
252 HoverEventProcessor::HoverEventProcessor(Scene& scene)
254 mLastPrimaryHitActor(MakeCallback(this, &HoverEventProcessor::OnObservedActorDisconnected))
256 DALI_LOG_TRACE_METHOD(gLogFilter);
259 HoverEventProcessor::~HoverEventProcessor()
261 DALI_LOG_TRACE_METHOD(gLogFilter);
264 void HoverEventProcessor::SendInterruptedHoverEvent(Dali::Internal::Actor* actor)
267 (mLastPrimaryHitActor.GetActor() == actor || mLastConsumedActor.GetActor() == actor))
269 Integration::Point point;
270 point.SetState(PointState::INTERRUPTED);
271 point.SetHitActor(Dali::Actor(actor));
272 if(mScene.IsGeometryHittestEnabled())
274 std::list<Dali::Internal::Actor*> actorLists;
275 actorLists.push_back(actor);
276 GeoAllocAndEmitHoverSignals(actorLists, 0, point);
280 AllocAndEmitHoverSignals(GetMilliSeconds(), point.GetHitActor(), point);
286 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
288 DALI_LOG_TRACE_METHOD(gLogFilter);
289 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
291 PointState::Type state = static_cast<PointState::Type>(event.points[0].GetState());
293 PRINT_HIERARCHY(gLogFilter);
295 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_HOVER_EVENT");
297 bool isGeometry = mScene.IsGeometryHittestEnabled();
299 // Copy so we can add the results of a hit-test.
300 HoverEventPtr hoverEvent(new HoverEvent(event.time));
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.
305 if(state == PointState::INTERRUPTED)
307 Dali::Actor consumingActor;
308 Integration::Point currentPoint(event.points[0]);
310 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
311 if(lastPrimaryHitActor)
313 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
314 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
317 consumingActor = GeoAllocAndEmitHoverSignals(mCandidateActorLists, event.time, currentPoint);
321 consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
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)
331 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
332 currentPoint.SetHitActor(lastConsumedActorHandle);
335 std::list<Dali::Internal::Actor*> actorLists;
336 actorLists.push_back(lastConsumedActor);
337 GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint);
341 AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
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)
352 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
353 currentPoint.SetHitActor(hoverStartConsumedActorHandle);
356 std::list<Dali::Internal::Actor*> actorLists;
357 actorLists.push_back(hoverStartConsumedActor);
358 GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint);
362 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
367 mHoverStartConsumedActor.SetActor(nullptr);
368 return; // No need for hit testing
373 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
375 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
376 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
378 RenderTaskPtr currentRenderTask;
379 bool firstPointParsed = false;
381 for(auto&& currentPoint : event.points)
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);
388 Integration::Point newPoint(currentPoint);
389 newPoint.SetHitActor(hitTestResults.actor);
390 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
392 hoverEvent->AddPoint(newPoint);
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);
396 // Only set the currentRenderTask for the primary hit actor.
397 if(!firstPointParsed)
399 firstPointParsed = true;
400 currentRenderTask = hitTestResults.renderTask;
401 mCandidateActorLists = hitTestResults.actorLists;
405 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
407 Integration::Point primaryPoint = hoverEvent->GetPoint(0);
408 Dali::Actor primaryHitActor = primaryPoint.GetHitActor();
409 PointState::Type primaryPointState = primaryPoint.GetState();
411 // Emit the touch signal
412 Dali::Actor consumedActor;
413 if(currentRenderTask)
415 Dali::Actor hitActor = hoverEvent->GetHitActor(0);
419 consumedActor = EmitGeoHoverSignals(mCandidateActorLists, hoverEventHandle);
423 // If the actor is hit first, the hover is started.
425 mLastPrimaryHitActor.GetActor() != hitActor &&
426 state == PointState::MOTION)
428 Actor* hitActorImpl = &GetImplementation(hitActor);
429 if(hitActorImpl->GetLeaveRequired())
431 hoverEvent->GetPoint(0).SetState(PointState::STARTED);
434 consumedActor = EmitHoverSignals(hitActor, hoverEventHandle);
437 if(hoverEvent->GetPoint(0).GetState() != PointState::MOTION)
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()]);
444 if((primaryPointState == PointState::STARTED) &&
445 (hoverEvent->GetPointCount() == 1) &&
446 (consumedActor && GetImplementation(consumedActor).OnScene()))
448 mHoverStartConsumedActor.SetActor(&GetImplementation(consumedActor));
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.
454 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
455 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
456 if((primaryPointState == PointState::STARTED) || (primaryPointState == PointState::MOTION) || (primaryPointState == PointState::FINISHED) || (primaryPointState == PointState::STATIONARY))
460 Dali::Actor leaveEventConsumer;
461 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
463 if(lastPrimaryHitActor &&
464 lastPrimaryHitActor != primaryHitActor &&
465 lastPrimaryHitActor != consumedActor)
467 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
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++)
478 std::list<Dali::Internal::Actor*>::reverse_iterator rCandidateIter = mCandidateActorLists.rbegin();
479 for(; rCandidateIter != mCandidateActorLists.rend(); rCandidateIter++)
481 if(*rCandidateIter == *rLastIter)
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);
492 // If the actor has been consumed, you do not need to proceed.
493 if(*rLastIter == lastConsumedActor)
499 else if(lastPrimaryHitActor->GetLeaveRequired())
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);
506 else if(primaryPointState != PointState::STARTED)
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);
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)
524 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
526 if(lastConsumedActor->GetLeaveRequired() && !isGeometry) // For geometry, we have already sent leave. There is no need to send leave repeatedly.
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);
532 else if(primaryPointState != PointState::STARTED)
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);
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.
546 if(primaryPointState == PointState::FINISHED)
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())
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())
559 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
563 mLastConsumedActor.SetActor(nullptr);
566 mLastRenderTask = currentRenderTask;
567 mLastActorLists = mCandidateActorLists;
575 // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
577 if(hoverEvent->GetPointCount() == 1) // Only want the first hover started
579 switch(primaryPointState)
581 case PointState::FINISHED:
583 Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
584 if(hoverStartConsumedActor &&
585 hoverStartConsumedActor != consumedActor &&
586 hoverStartConsumedActor != lastPrimaryHitActor &&
587 hoverStartConsumedActor != lastConsumedActor)
589 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
590 Integration::Point primaryPoint = hoverEvent->GetPoint(0);
591 primaryPoint.SetHitActor(hoverStartConsumedActorHandle);
592 primaryPoint.SetState(PointState::INTERRUPTED);
595 std::list<Dali::Internal::Actor*> actorLists;
596 actorLists.push_back(hoverStartConsumedActor);
597 GeoAllocAndEmitHoverSignals(actorLists, event.time, primaryPoint);
601 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
604 // Restore hover-event to original state
605 primaryPoint.SetHitActor(primaryHitActor);
606 primaryPoint.SetState(primaryPointState);
609 mHoverStartConsumedActor.SetActor(nullptr);
611 // No break, Fallthrough
613 case PointState::STARTED:
614 case PointState::MOTION:
615 case PointState::LEAVE:
616 case PointState::STATIONARY:
617 case PointState::INTERRUPTED:
626 void HoverEventProcessor::Clear()
628 mLastPrimaryHitActor.SetActor(nullptr);
629 mLastConsumedActor.SetActor(nullptr);
630 mLastRenderTask.Reset();
631 mLastActorLists.clear();
634 void HoverEventProcessor::OnObservedActorDisconnected(Dali::Internal::Actor* actor)
636 SendInterruptedHoverEvent(actor);
639 } // namespace Internal