2 * Copyright (c) 2022 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)
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/events/hover-event-integ.h>
28 #include <dali/internal/event/actors/actor-impl.h>
29 #include <dali/internal/event/actors/layer-impl.h>
30 #include <dali/internal/event/common/scene-impl.h>
31 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
32 #include <dali/internal/event/events/hover-event-impl.h>
33 #include <dali/internal/event/events/multi-point-event-util.h>
34 #include <dali/internal/event/render-tasks/render-task-impl.h>
35 #include <dali/public-api/math/vector2.h>
43 #if defined(DEBUG_ENABLED)
44 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_HOVER_PROCESSOR");
46 const char* TOUCH_POINT_STATE[PointState::INTERRUPTED + 1] =
56 #endif // defined(DEBUG_ENABLED)
59 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
61 Dali::Actor EmitHoverSignals(Dali::Actor actor, const Dali::HoverEvent& event)
63 Dali::Actor consumedActor;
67 Dali::Actor oldParent(actor.GetParent());
69 Actor& actorImpl(GetImplementation(actor));
73 // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover).
74 if(actorImpl.GetHoverRequired())
76 consumed = actorImpl.EmitHoverEventSignal(event);
81 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
82 consumedActor = Dali::Actor(&actorImpl);
86 // The actor may have been removed/reparented during the signal callbacks.
87 Dali::Actor parent = actor.GetParent();
90 (parent == oldParent))
92 // One of the actor's parents may consumed the event and they should be set as the consumed actor.
93 consumedActor = EmitHoverSignals(parent, event);
101 Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
103 HoverEventPtr hoverEvent(new HoverEvent(time));
104 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
106 hoverEvent->AddPoint(point);
108 return EmitHoverSignals(actor, hoverEventHandle);
112 * Changes the state of the primary point to leave and emits the hover signals
114 Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state)
116 HoverEventPtr hoverEvent = HoverEvent::Clone(*originalEvent.Get());
118 DALI_ASSERT_DEBUG(NULL != actor && "NULL actor pointer");
121 Integration::Point& primaryPoint = hoverEvent->GetPoint(0);
123 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
124 Vector2 localPosition;
125 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
127 primaryPoint.SetLocalPosition(localPosition);
128 primaryPoint.SetHitActor(Dali::Actor(actor));
129 primaryPoint.SetState(state);
132 return EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get()));
136 * Used in the hit-test algorithm to check whether the actor is hoverable.
138 struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface
140 bool IsActorHittable(Actor* actor) override
142 return actor->GetHoverRequired() && // Does the Application or derived actor type require a hover event?
143 actor->IsHittable(); // Is actor sensitive, visible and on the scene?
146 bool DescendActorHierarchy(Actor* actor) override
148 return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
149 actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
152 bool DoesLayerConsumeHit(Layer* layer) override
154 return layer->IsHoverConsumed();
157 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
159 // Hover event is always hit.
164 } // unnamed namespace
166 HoverEventProcessor::HoverEventProcessor(Scene& scene)
169 DALI_LOG_TRACE_METHOD(gLogFilter);
172 HoverEventProcessor::~HoverEventProcessor()
174 DALI_LOG_TRACE_METHOD(gLogFilter);
177 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
179 DALI_LOG_TRACE_METHOD(gLogFilter);
180 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
182 PointState::Type state = static_cast<PointState::Type>(event.points[0].GetState());
184 PRINT_HIERARCHY(gLogFilter);
186 // Copy so we can add the results of a hit-test.
187 HoverEventPtr hoverEvent(new HoverEvent(event.time));
189 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
190 // and emit the stage signal as well.
192 if(state == PointState::INTERRUPTED)
194 Dali::Actor consumingActor;
195 Integration::Point currentPoint(event.points[0]);
197 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
198 if(lastPrimaryHitActor)
200 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
201 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
202 consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
205 // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
206 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
207 if(lastConsumedActor &&
208 lastConsumedActor != lastPrimaryHitActor &&
209 lastConsumedActor != consumingActor)
211 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
212 currentPoint.SetHitActor(lastConsumedActorHandle);
213 AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
216 // Tell the hover-start consuming actor as well, if required
217 Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
218 if(hoverStartConsumedActor &&
219 hoverStartConsumedActor != lastPrimaryHitActor &&
220 hoverStartConsumedActor != lastConsumedActor &&
221 hoverStartConsumedActor != consumingActor)
223 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
224 currentPoint.SetHitActor(hoverStartConsumedActorHandle);
225 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
228 mLastPrimaryHitActor.SetActor(nullptr);
229 mLastConsumedActor.SetActor(nullptr);
230 mHoverStartConsumedActor.SetActor(nullptr);
231 mLastRenderTask.Reset();
233 return; // No need for hit testing
238 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
240 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
241 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
243 RenderTaskPtr currentRenderTask;
244 bool firstPointParsed = false;
246 for(auto&& currentPoint : event.points)
248 HitTestAlgorithm::Results hitTestResults;
249 ActorHoverableCheck actorHoverableCheck;
250 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck);
252 Integration::Point newPoint(currentPoint);
253 newPoint.SetHitActor(hitTestResults.actor);
254 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
256 hoverEvent->AddPoint(newPoint);
258 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);
260 // Only set the currentRenderTask for the primary hit actor.
261 if(!firstPointParsed)
263 firstPointParsed = true;
264 currentRenderTask = hitTestResults.renderTask;
268 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
270 // Emit the touch signal
271 Dali::Actor consumedActor;
272 if(currentRenderTask)
274 consumedActor = EmitHoverSignals(hoverEvent->GetHitActor(0), hoverEventHandle);
277 Integration::Point primaryPoint = hoverEvent->GetPoint(0);
278 Dali::Actor primaryHitActor = primaryPoint.GetHitActor();
279 PointState::Type primaryPointState = primaryPoint.GetState();
281 DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) %s\n", primaryHitActor ? reinterpret_cast<void*>(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
282 DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) %s\n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
284 if((primaryPointState == PointState::STARTED) &&
285 (hoverEvent->GetPointCount() == 1) &&
286 (consumedActor && GetImplementation(consumedActor).OnScene()))
288 mHoverStartConsumedActor.SetActor(&GetImplementation(consumedActor));
291 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
292 // hit actor. Also process the last consumed actor in the same manner.
294 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
295 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
296 if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::FINISHED) || (primaryPointState == PointState::STATIONARY))
300 Dali::Actor leaveEventConsumer;
301 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
303 if(lastPrimaryHitActor &&
304 lastPrimaryHitActor != primaryHitActor &&
305 lastPrimaryHitActor != consumedActor)
307 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
309 if(lastPrimaryHitActor->GetLeaveRequired())
311 DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
312 leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
317 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
318 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
319 DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
320 leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
324 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
325 // consumed actor's listeners may need to be informed (through a leave event).
326 // Further checks here to ensure we do not signal the same actor twice for the same event.
327 if(lastConsumedActor &&
328 lastConsumedActor != consumedActor &&
329 lastConsumedActor != lastPrimaryHitActor &&
330 lastConsumedActor != primaryHitActor &&
331 lastConsumedActor != leaveEventConsumer)
333 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
335 if(lastConsumedActor->GetLeaveRequired())
337 DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
338 EmitHoverSignals(lastConsumedActor, lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
343 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
344 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
345 DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
346 EmitHoverSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
352 // 5) If our primary point is a FINISHED event, then the primary point (in multi-touch) will change next
353 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
355 if(primaryPointState == PointState::FINISHED)
357 mLastPrimaryHitActor.SetActor(nullptr);
358 mLastConsumedActor.SetActor(nullptr);
359 mLastRenderTask.Reset();
363 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
364 if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
366 mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
368 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
369 if(consumedActor && GetImplementation(consumedActor).OnScene())
371 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
375 mLastConsumedActor.SetActor(nullptr);
378 mLastRenderTask = currentRenderTask;
382 mLastPrimaryHitActor.SetActor(nullptr);
383 mLastConsumedActor.SetActor(nullptr);
384 mLastRenderTask.Reset();
388 // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
390 if(hoverEvent->GetPointCount() == 1) // Only want the first hover started
392 switch(primaryPointState)
394 case PointState::FINISHED:
396 Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
397 if(hoverStartConsumedActor &&
398 hoverStartConsumedActor != consumedActor &&
399 hoverStartConsumedActor != lastPrimaryHitActor &&
400 hoverStartConsumedActor != lastConsumedActor)
402 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
403 Integration::Point primaryPoint = hoverEvent->GetPoint(0);
404 primaryPoint.SetHitActor(hoverStartConsumedActorHandle);
405 primaryPoint.SetState(PointState::INTERRUPTED);
406 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
408 // Restore hover-event to original state
409 primaryPoint.SetHitActor(primaryHitActor);
410 primaryPoint.SetState(primaryPointState);
413 mHoverStartConsumedActor.SetActor(nullptr);
415 // No break, Fallthrough
417 case PointState::STARTED:
418 case PointState::MOTION:
419 case PointState::LEAVE:
420 case PointState::STATIONARY:
421 case PointState::INTERRUPTED:
430 } // namespace Internal