2 * Copyright (c) 2024 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>
41 namespace Dali::Internal
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)
51 * Structure for Variables used in the ProcessHoverEvent method.
53 struct ProcessHoverEventVariables
55 ProcessHoverEventVariables(bool geometry)
56 : isGeometry(geometry)
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.
72 const char* TOUCH_POINT_STATE[PointState::INTERRUPTED + 1] =
82 bool ShouldEmitHoverEvent(const Actor& actorImpl, const Dali::HoverEvent& event)
84 PointState::Type state = event.GetState(0);
85 return actorImpl.GetHoverRequired() && (state != PointState::MOTION || actorImpl.IsDispatchHoverMotion());
89 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
91 Dali::Actor EmitHoverSignals(Dali::Actor actor, const Dali::HoverEvent& event)
93 Dali::Actor consumedActor;
97 Dali::Actor oldParent(actor.GetParent());
99 Actor& actorImpl(GetImplementation(actor));
101 bool consumed(false);
103 // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover).
104 if(ShouldEmitHoverEvent(actorImpl, event))
106 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_HOVER_EVENT_SIGNAL");
107 consumed = actorImpl.EmitHoverEventSignal(event);
112 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
113 consumedActor = Dali::Actor(&actorImpl);
117 // The actor may have been removed/reparented during the signal callbacks.
118 Dali::Actor parent = actor.GetParent();
121 (parent == oldParent))
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);
129 return consumedActor;
133 * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
135 Dali::Actor EmitGeoHoverSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::HoverEvent& hoverEvent)
137 Dali::Actor consumedActor;
139 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
140 for(; rIter != actorLists.rend(); rIter++)
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))
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))
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())))
158 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
159 consumedActor = Dali::Actor(actorImpl);
163 else if(actorImpl->EmitHoverEventSignal(hoverEvent))
165 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
166 consumedActor = Dali::Actor(actorImpl);
171 return consumedActor;
174 Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
176 HoverEventPtr hoverEvent(new HoverEvent(time));
177 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
179 hoverEvent->AddPoint(point);
181 return EmitHoverSignals(actor, hoverEventHandle);
184 Dali::Actor GeoAllocAndEmitHoverSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point)
186 HoverEventPtr hoverEvent(new HoverEvent(time));
187 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
189 hoverEvent->AddPoint(point);
191 return EmitGeoHoverSignals(actorLists, hoverEventHandle);
195 * Changes the state of the primary point to leave and emits the hover signals
197 Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state, bool isGeometry)
199 HoverEventPtr hoverEvent = HoverEvent::Clone(*originalEvent.Get());
201 DALI_ASSERT_DEBUG(NULL != actor && "NULL actor pointer");
202 Dali::Actor consumingActor;
205 Integration::Point& primaryPoint = hoverEvent->GetPoint(0);
207 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
208 Vector2 localPosition;
209 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
211 primaryPoint.SetLocalPosition(localPosition);
212 primaryPoint.SetHitActor(Dali::Actor(actor));
213 primaryPoint.SetState(state);
218 std::list<Dali::Internal::Actor*> actorLists;
219 actorLists.push_back(actor);
220 consumingActor = EmitGeoHoverSignals(actorLists, Dali::HoverEvent(hoverEvent.Get()));
224 consumingActor = EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get()));
226 return consumingActor;
230 * Used in the hit-test algorithm to check whether the actor is hoverable.
232 struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface
234 bool IsActorHittable(Actor* actor) override
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?
240 bool DescendActorHierarchy(Actor* actor) override
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.
246 bool DoesLayerConsumeHit(Layer* layer) override
248 return layer->IsHoverConsumed();
251 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
253 // Hover event is always hit.
258 uint32_t GetMilliSeconds()
260 // Get the time of a monotonic clock since its epoch.
261 auto epoch = std::chrono::steady_clock::now().time_since_epoch();
263 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
265 return static_cast<uint32_t>(duration.count());
268 } // unnamed namespace
270 struct HoverEventProcessor::Impl
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
278 static inline void EmitInterruptedEvent(HoverEventProcessor& processor, const bool isGeometry, const Integration::HoverEvent& event)
280 Dali::Actor consumingActor;
281 Integration::Point currentPoint(event.points[0]);
283 Actor* lastPrimaryHitActor(processor.mLastPrimaryHitActor.GetActor());
284 if(lastPrimaryHitActor)
286 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
287 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
290 consumingActor = GeoAllocAndEmitHoverSignals(processor.mCandidateActorLists, event.time, currentPoint);
294 consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
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)
304 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
305 currentPoint.SetHitActor(lastConsumedActorHandle);
308 std::list<Dali::Internal::Actor*> actorLists;
309 actorLists.push_back(lastConsumedActor);
310 GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint);
314 AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
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)
325 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
326 currentPoint.SetHitActor(hoverStartConsumedActorHandle);
329 std::list<Dali::Internal::Actor*> actorLists;
330 actorLists.push_back(hoverStartConsumedActor);
331 GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint);
335 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
340 processor.mHoverStartConsumedActor.SetActor(nullptr);
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
349 static inline void HitTest(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars, const Integration::HoverEvent& event)
351 bool firstPointParsed = false;
352 for(auto&& currentPoint : event.points)
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 ? Integration::Scene::TouchPropagationType::GEOMETRY : Integration::Scene::TouchPropagationType::PARENT);
359 Integration::Point newPoint(currentPoint);
360 newPoint.SetHitActor(hitTestResults.actor);
361 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
363 localVars.hoverEvent->AddPoint(newPoint);
365 DALI_LOG_INFO(gLogFilter,
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);
376 // Only set the currentRenderTask for the primary hit actor.
377 if(!firstPointParsed)
379 firstPointParsed = true;
380 localVars.currentRenderTask = hitTestResults.renderTask;
381 processor.mCandidateActorLists = hitTestResults.actorLists;
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
391 static inline void DeliverEventsToActorAndParents(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars)
393 // Emit the touch signal
394 if(localVars.currentRenderTask)
396 Dali::Actor hitActor = localVars.hoverEvent->GetHitActor(0);
398 if(localVars.isGeometry)
400 localVars.consumedActor = EmitGeoHoverSignals(processor.mCandidateActorLists, localVars.hoverEventHandle);
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())
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);
413 localVars.hoverEvent->GetPoint(0).SetState(PointState::STARTED);
414 localVars.primaryPointState = PointState::STARTED;
416 // It sends a started event and updates information.
417 localVars.consumedActor = EmitHoverSignals(hitActor, localVars.hoverEventHandle);
418 UpdateMembersWithCurrentHitInformation(processor, localVars);
422 localVars.consumedActor = EmitHoverSignals(hitActor, localVars.hoverEventHandle);
426 if(localVars.hoverEvent->GetPoint(0).GetState() != PointState::MOTION)
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()]);
441 if((localVars.primaryPointState == PointState::STARTED) &&
442 (localVars.hoverEvent->GetPointCount() == 1) &&
443 (localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene()))
445 processor.mHoverStartConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
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
454 static inline void DeliverLeaveEvent(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars)
456 if((localVars.primaryPointState == PointState::STARTED) ||
457 (localVars.primaryPointState == PointState::MOTION) ||
458 (localVars.primaryPointState == PointState::FINISHED) ||
459 (localVars.primaryPointState == PointState::STATIONARY))
461 if(processor.mLastRenderTask)
463 Dali::Actor leaveEventConsumer;
464 RenderTask& lastRenderTaskImpl = *processor.mLastRenderTask.Get();
466 if(localVars.lastPrimaryHitActor &&
467 localVars.lastPrimaryHitActor != localVars.primaryHitActor &&
468 localVars.lastPrimaryHitActor != localVars.consumedActor)
470 if(localVars.lastPrimaryHitActor->IsHittable() && IsActuallySensitive(localVars.lastPrimaryHitActor))
472 if(localVars.isGeometry)
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++)
481 std::list<Dali::Internal::Actor*>::reverse_iterator rCandidateIter = processor.mCandidateActorLists.rbegin();
482 for(; rCandidateIter != processor.mCandidateActorLists.rend(); rCandidateIter++)
484 if(*rCandidateIter == *rLastIter)
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);
495 // If the actor has been consumed, you do not need to proceed.
496 if(*rLastIter == localVars.lastConsumedActor)
502 else if(localVars.lastPrimaryHitActor->GetLeaveRequired())
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);
509 else if(localVars.primaryPointState != PointState::STARTED)
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);
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)
527 if(localVars.lastConsumedActor->IsHittable() && IsActuallySensitive(localVars.lastConsumedActor))
529 if(localVars.lastConsumedActor->GetLeaveRequired() && !localVars.isGeometry) // For geometry, we have already sent leave. There is no need to send leave repeatedly.
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);
535 else if(localVars.primaryPointState != PointState::STARTED)
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);
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
552 static inline void UpdateMembersWithCurrentHitInformation(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars)
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.
557 if(localVars.primaryPointState == PointState::FINISHED)
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())
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())
570 processor.mLastConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
574 processor.mLastConsumedActor.SetActor(nullptr);
577 processor.mLastRenderTask = localVars.currentRenderTask;
578 processor.mLastActorLists = processor.mCandidateActorLists;
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
593 static inline void DeliverInterruptedEventToHoverStartedActor(HoverEventProcessor& processor, ProcessHoverEventVariables& localVars, const Integration::HoverEvent& event)
595 if(localVars.hoverEvent->GetPointCount() == 1 && localVars.primaryPointState == PointState::FINISHED) // Only want the first hover started
597 Actor* hoverStartConsumedActor(processor.mHoverStartConsumedActor.GetActor());
598 if(hoverStartConsumedActor &&
599 hoverStartConsumedActor != localVars.consumedActor &&
600 hoverStartConsumedActor != localVars.lastPrimaryHitActor &&
601 hoverStartConsumedActor != localVars.lastConsumedActor)
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)
609 std::list<Dali::Internal::Actor*> actorLists;
610 actorLists.push_back(hoverStartConsumedActor);
611 GeoAllocAndEmitHoverSignals(actorLists, event.time, primaryPoint);
615 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
618 // Restore hover-event to original state
619 primaryPoint.SetHitActor(localVars.primaryHitActor);
620 primaryPoint.SetState(localVars.primaryPointState);
623 processor.mHoverStartConsumedActor.SetActor(nullptr);
628 HoverEventProcessor::HoverEventProcessor(Scene& scene)
630 mLastPrimaryHitActor(MakeCallback(this, &HoverEventProcessor::OnObservedActorDisconnected))
632 DALI_LOG_TRACE_METHOD(gLogFilter);
635 HoverEventProcessor::~HoverEventProcessor()
637 DALI_LOG_TRACE_METHOD(gLogFilter);
640 void HoverEventProcessor::SendInterruptedHoverEvent(Dali::Internal::Actor* actor)
643 (mLastPrimaryHitActor.GetActor() == actor || mLastConsumedActor.GetActor() == actor))
645 Integration::Point point;
646 point.SetState(PointState::INTERRUPTED);
647 point.SetHitActor(Dali::Actor(actor));
648 if(mScene.GetTouchPropagationType() == Integration::Scene::TouchPropagationType::GEOMETRY)
650 std::list<Dali::Internal::Actor*> actorLists;
651 actorLists.push_back(actor);
652 GeoAllocAndEmitHoverSignals(actorLists, 0, point);
656 AllocAndEmitHoverSignals(GetMilliSeconds(), point.GetHitActor(), point);
662 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
664 DALI_LOG_TRACE_METHOD(gLogFilter);
665 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
667 PRINT_HIERARCHY(gLogFilter);
669 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_HOVER_EVENT");
671 ProcessHoverEventVariables localVars(mScene.IsGeometryHittestEnabled());
673 // Copy so we can add the results of a hit-test.
674 localVars.hoverEvent = new HoverEvent(event.time);
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)
680 Impl::EmitInterruptedEvent(*this, localVars.isGeometry, event);
681 return; // No need for 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);
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);
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);
702 // 5) Update the processor member appropriately.
703 Impl::UpdateMembersWithCurrentHitInformation(*this, localVars);
705 // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
706 Impl::DeliverInterruptedEventToHoverStartedActor(*this, localVars, event);
709 void HoverEventProcessor::Clear()
711 mLastPrimaryHitActor.SetActor(nullptr);
712 mLastConsumedActor.SetActor(nullptr);
713 mLastRenderTask.Reset();
714 mLastActorLists.clear();
717 void HoverEventProcessor::OnObservedActorDisconnected(Dali::Internal::Actor* actor)
719 SendInterruptedHoverEvent(actor);
722 } // namespace Dali::Internal