2 * Copyright (c) 2021 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();
158 } // unnamed namespace
160 HoverEventProcessor::HoverEventProcessor(Scene& scene)
163 DALI_LOG_TRACE_METHOD(gLogFilter);
166 HoverEventProcessor::~HoverEventProcessor()
168 DALI_LOG_TRACE_METHOD(gLogFilter);
171 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
173 DALI_LOG_TRACE_METHOD(gLogFilter);
174 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
176 PointState::Type state = static_cast<PointState::Type>(event.points[0].GetState());
178 PRINT_HIERARCHY(gLogFilter);
180 // Copy so we can add the results of a hit-test.
181 HoverEventPtr hoverEvent(new HoverEvent(event.time));
183 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
184 // and emit the stage signal as well.
186 if(state == PointState::INTERRUPTED)
188 Dali::Actor consumingActor;
189 Integration::Point currentPoint(event.points[0]);
191 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
192 if(lastPrimaryHitActor)
194 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
195 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
196 consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
199 // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
200 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
201 if(lastConsumedActor &&
202 lastConsumedActor != lastPrimaryHitActor &&
203 lastConsumedActor != consumingActor)
205 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
206 currentPoint.SetHitActor(lastConsumedActorHandle);
207 AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
210 // Tell the hover-start consuming actor as well, if required
211 Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
212 if(hoverStartConsumedActor &&
213 hoverStartConsumedActor != lastPrimaryHitActor &&
214 hoverStartConsumedActor != lastConsumedActor &&
215 hoverStartConsumedActor != consumingActor)
217 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
218 currentPoint.SetHitActor(hoverStartConsumedActorHandle);
219 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
222 mLastPrimaryHitActor.SetActor(nullptr);
223 mLastConsumedActor.SetActor(nullptr);
224 mHoverStartConsumedActor.SetActor(nullptr);
225 mLastRenderTask.Reset();
227 return; // No need for hit testing
232 Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
234 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
235 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
237 RenderTaskPtr currentRenderTask;
238 bool firstPointParsed = false;
240 for(auto&& currentPoint : event.points)
242 HitTestAlgorithm::Results hitTestResults;
243 ActorHoverableCheck actorHoverableCheck;
244 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck);
246 Integration::Point newPoint(currentPoint);
247 newPoint.SetHitActor(hitTestResults.actor);
248 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
250 hoverEvent->AddPoint(newPoint);
252 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);
254 // Only set the currentRenderTask for the primary hit actor.
255 if(!firstPointParsed)
257 firstPointParsed = true;
258 currentRenderTask = hitTestResults.renderTask;
262 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
264 // Emit the touch signal
265 Dali::Actor consumedActor;
266 if(currentRenderTask)
268 consumedActor = EmitHoverSignals(hoverEvent->GetHitActor(0), hoverEventHandle);
271 Integration::Point primaryPoint = hoverEvent->GetPoint(0);
272 Dali::Actor primaryHitActor = primaryPoint.GetHitActor();
273 PointState::Type primaryPointState = primaryPoint.GetState();
275 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() : "");
276 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() : "");
278 if((primaryPointState == PointState::STARTED) &&
279 (hoverEvent->GetPointCount() == 1) &&
280 (consumedActor && GetImplementation(consumedActor).OnScene()))
282 mHoverStartConsumedActor.SetActor(&GetImplementation(consumedActor));
285 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
286 // hit actor. Also process the last consumed actor in the same manner.
288 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
289 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
290 if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::FINISHED) || (primaryPointState == PointState::STATIONARY))
294 Dali::Actor leaveEventConsumer;
295 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
297 if(lastPrimaryHitActor &&
298 lastPrimaryHitActor != primaryHitActor &&
299 lastPrimaryHitActor != consumedActor)
301 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
303 if(lastPrimaryHitActor->GetLeaveRequired())
305 DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
306 leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
311 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
312 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
313 DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
314 leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
318 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
319 // consumed actor's listeners may need to be informed (through a leave event).
320 // Further checks here to ensure we do not signal the same actor twice for the same event.
321 if(lastConsumedActor &&
322 lastConsumedActor != consumedActor &&
323 lastConsumedActor != lastPrimaryHitActor &&
324 lastConsumedActor != primaryHitActor &&
325 lastConsumedActor != leaveEventConsumer)
327 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
329 if(lastConsumedActor->GetLeaveRequired())
331 DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
332 EmitHoverSignals(lastConsumedActor, lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
337 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
338 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
339 DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
340 EmitHoverSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
346 // 5) If our primary point is a FINISHED event, then the primary point (in multi-touch) will change next
347 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
349 if(primaryPointState == PointState::FINISHED)
351 mLastPrimaryHitActor.SetActor(nullptr);
352 mLastConsumedActor.SetActor(nullptr);
353 mLastRenderTask.Reset();
357 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
358 if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
360 mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
362 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
363 if(consumedActor && GetImplementation(consumedActor).OnScene())
365 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
369 mLastConsumedActor.SetActor(nullptr);
372 mLastRenderTask = currentRenderTask;
376 mLastPrimaryHitActor.SetActor(nullptr);
377 mLastConsumedActor.SetActor(nullptr);
378 mLastRenderTask.Reset();
382 // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
384 if(hoverEvent->GetPointCount() == 1) // Only want the first hover started
386 switch(primaryPointState)
388 case PointState::FINISHED:
390 Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
391 if(hoverStartConsumedActor &&
392 hoverStartConsumedActor != consumedActor &&
393 hoverStartConsumedActor != lastPrimaryHitActor &&
394 hoverStartConsumedActor != lastConsumedActor)
396 Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
397 Integration::Point primaryPoint = hoverEvent->GetPoint(0);
398 primaryPoint.SetHitActor(hoverStartConsumedActorHandle);
399 primaryPoint.SetState(PointState::INTERRUPTED);
400 AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
402 // Restore hover-event to original state
403 primaryPoint.SetHitActor(primaryHitActor);
404 primaryPoint.SetState(primaryPointState);
407 mHoverStartConsumedActor.SetActor(nullptr);
409 // No break, Fallthrough
411 case PointState::STARTED:
412 case PointState::MOTION:
413 case PointState::LEAVE:
414 case PointState::STATIONARY:
415 case PointState::INTERRUPTED:
424 } // namespace Internal