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/touch-event-processor.h>
21 #if defined(DEBUG_ENABLED)
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/integration-api/trace.h>
29 #include <dali/internal/event/actors/actor-impl.h>
30 #include <dali/internal/event/actors/layer-impl.h>
31 #include <dali/internal/event/common/scene-impl.h>
32 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
33 #include <dali/internal/event/events/multi-point-event-util.h>
34 #include <dali/internal/event/events/touch-event-impl.h>
35 #include <dali/internal/event/render-tasks/render-task-impl.h>
36 #include <dali/public-api/events/touch-event.h>
37 #include <dali/public-api/math/vector2.h>
38 #include <dali/public-api/signals/callback.h>
40 namespace Dali::Internal
44 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TOUCH_PROCESSOR");
47 #endif // defined(DEBUG_ENABLED)
50 * Structure for Variables used in the ProcessTouchEvent method.
52 struct ProcessTouchEventVariables
54 ProcessTouchEventVariables(TouchEventProcessor& eventProcessor, bool geometry)
55 : processor(eventProcessor),
60 TouchEventProcessor& processor; ///< A handle to the touch-event-processor.
61 const bool isGeometry; ///< Whether it's a geometry or not.
62 Actor* lastPrimaryHitActor{nullptr}; ///< The last primary hit-actor.
63 Actor* lastConsumedActor{nullptr}; ///< The last consuming actor.
64 TouchEventPtr touchEventImpl; ///< The current touch-event-impl.
65 Dali::TouchEvent touchEventHandle; ///< The handle to the touch-event-impl.
66 RenderTaskPtr currentRenderTask; ///< The current render-task.
67 Dali::Actor consumedActor; ///< The actor that consumed the event.
68 Dali::Actor primaryHitActor; ///< The actor that has been hit by the primary point.
69 Integration::Point* primaryPoint{nullptr}; ///< The primary point of the hit.
70 PointState::Type primaryPointState{PointState::STARTED}; ///< The state of the primary point.
73 const char* TOUCH_POINT_STATE[6] =
83 bool ShouldEmitInterceptTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
85 PointState::Type state = event.GetState(0);
86 return actorImpl.GetInterceptTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
89 bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
91 PointState::Type state = event.GetState(0);
92 return actorImpl.GetTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
96 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
98 Dali::Actor interceptedActor;
102 Dali::Actor parent = actor.GetParent();
105 // Recursively deliver events to the actor and its parents for intercept touch event.
106 interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
109 if(!interceptedActor)
111 bool intercepted = false;
112 Actor& actorImpl(GetImplementation(actor));
113 if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
115 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
116 intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
119 interceptedActor = Dali::Actor(&actorImpl);
124 return interceptedActor;
129 Dali::Actor EmitGeoInterceptTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, std::list<Dali::Internal::Actor*>& interceptActorList, const Dali::TouchEvent& touchEvent, ActorObserver& lastConsumedActor)
131 interceptActorList.clear();
132 Dali::Actor interceptedActor;
133 for(auto&& actor : actorLists)
135 interceptActorList.push_back(actor);
136 if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
138 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
139 if(actor->EmitInterceptTouchEventSignal(touchEvent))
141 interceptedActor = Dali::Actor(actor);
145 // If there is a consumed actor, the intercept is sent only up to the moment before the consumed actor.
146 if(lastConsumedActor.GetActor() == actor)
151 return interceptedActor;
155 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
157 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
159 Dali::Actor consumedActor;
163 Dali::Actor oldParent(actor.GetParent());
165 Actor& actorImpl(GetImplementation(actor));
167 bool consumed(false);
169 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
170 if(ShouldEmitTouchEvent(actorImpl, touchEvent))
172 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
173 consumed = actorImpl.EmitTouchEventSignal(touchEvent);
178 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
179 consumedActor = Dali::Actor(&actorImpl);
183 // The actor may have been removed/reparented during the signal callbacks.
184 Dali::Actor parent = actor.GetParent();
187 (parent == oldParent))
189 // One of the actor's parents may consumed the event and they should be set as the consumed actor.
190 consumedActor = EmitTouchSignals(parent, touchEvent);
195 return consumedActor;
199 * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
201 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
203 Dali::Actor consumedActor;
205 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
206 for(; rIter != actorLists.rend(); rIter++)
208 Actor* actorImpl(*rIter);
209 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
210 if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
212 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
213 if(actorImpl->EmitTouchEventSignal(touchEvent))
215 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
216 consumedActor = Dali::Actor(actorImpl);
221 return consumedActor;
224 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
226 TouchEventPtr touchEvent(new TouchEvent(time));
227 Dali::TouchEvent touchEventHandle(touchEvent.Get());
229 touchEvent->AddPoint(point);
230 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
232 return EmitTouchSignals(actor, touchEventHandle);
235 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
237 TouchEventPtr touchEvent(new TouchEvent(time));
238 Dali::TouchEvent touchEventHandle(touchEvent.Get());
240 touchEvent->AddPoint(point);
241 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
243 return EmitGeoTouchSignals(actorLists, touchEventHandle);
247 * Changes the state of the primary point to leave and emits the touch signals
249 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
251 Dali::Actor consumingActor;
255 TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
256 touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
258 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
260 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
261 Vector2 localPosition;
262 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
264 primaryPoint.SetLocalPosition(localPosition);
265 primaryPoint.SetHitActor(Dali::Actor(actor));
266 primaryPoint.SetState(state);
270 std::list<Dali::Internal::Actor*> actorLists;
271 actorLists.push_back(actor);
272 consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
276 consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
280 return consumingActor;
284 * @brief Parses the primary touch point by performing a hit-test if necessary
286 * @param[out] hitTestResults The hit test results are put into this variable
287 * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
288 * @param[in] lastRenderTask The last render task member
289 * @param[in] currentPoint The current point information
290 * @param[in] scene The scene that this touch is related to
291 * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
293 void ParsePrimaryTouchPoint(
294 HitTestAlgorithm::Results& hitTestResults,
295 ActorObserver& capturingTouchActorObserver,
296 ActorObserver& ownTouchActorObserver,
297 const RenderTaskPtr& lastRenderTask,
298 const Integration::Point& currentPoint,
299 const Internal::Scene& scene,
300 std::list<Dali::Internal::Actor*>& actorLists)
302 Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
304 // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
305 if(capturingTouchActor && lastRenderTask)
307 hitTestResults.actor = Dali::Actor(capturingTouchActor);
308 hitTestResults.renderTask = lastRenderTask;
309 const Vector2& screenPosition = currentPoint.GetScreenPosition();
310 capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
314 Actor* ownTouchActor = ownTouchActorObserver.GetActor();
315 HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
317 if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
319 bool isGeometry = scene.IsGeometryHittestEnabled();
320 // If we've just started touch, then check whether the actor has requested to capture all touch events
321 Actor* hitActor = &GetImplementation(hitTestResults.actor);
322 if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
324 capturingTouchActorObserver.SetActor(hitActor);
326 if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
328 ownTouchActorObserver.SetActor(hitActor);
332 actorLists = hitTestResults.actorLists;
338 } // unnamed namespace
340 struct TouchEventProcessor::Impl
343 * Emits an interrupted event.
344 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
345 * @param[in] event The touch event that has occurred
347 static inline void EmitInterruptedEvent(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
349 Dali::Actor consumingActor;
350 Integration::Point currentPoint(event.points[0]);
352 if(localVars.isGeometry)
354 // Since the geometry way only receives touch events from the consumed actor,
355 // it first searches for whether there is a consumed actor and then sends the event
356 Actor* touchConsumedActor(localVars.processor.mLastConsumedActor.GetActor());
357 Actor* touchDownConsumedActor(localVars.processor.mTouchDownConsumedActor.GetActor());
358 Actor* lastPrimaryHitActor(localVars.processor.mLastPrimaryHitActor.GetActor());
359 if(touchConsumedActor)
361 Dali::Actor touchConsumedActorHandle(touchConsumedActor);
362 currentPoint.SetHitActor(touchConsumedActorHandle);
363 std::list<Dali::Internal::Actor*> actorLists;
364 actorLists.push_back(touchConsumedActor);
365 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
367 else if(touchDownConsumedActor)
369 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
370 currentPoint.SetHitActor(touchDownConsumedActorHandle);
371 std::list<Dali::Internal::Actor*> actorLists;
372 actorLists.push_back(touchDownConsumedActor);
373 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
375 else if(lastPrimaryHitActor)
377 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
378 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
380 GeoAllocAndEmitTouchSignals(localVars.processor.mCandidateActorLists, event.time, currentPoint, localVars.processor.mLastRenderTask);
385 Actor* lastPrimaryHitActor(localVars.processor.mLastPrimaryHitActor.GetActor());
386 if(lastPrimaryHitActor)
388 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
389 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
391 consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, localVars.processor.mLastRenderTask);
394 // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
395 Actor* lastConsumedActor(localVars.processor.mLastConsumedActor.GetActor());
396 if(lastConsumedActor &&
397 lastConsumedActor != lastPrimaryHitActor &&
398 lastConsumedActor != consumingActor)
400 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
401 currentPoint.SetHitActor(lastConsumedActorHandle);
402 AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, localVars.processor.mLastRenderTask);
405 // Tell the touch-down consuming actor as well, if required
406 Actor* touchDownConsumedActor(localVars.processor.mTouchDownConsumedActor.GetActor());
407 if(touchDownConsumedActor &&
408 touchDownConsumedActor != lastPrimaryHitActor &&
409 touchDownConsumedActor != lastConsumedActor &&
410 touchDownConsumedActor != consumingActor)
412 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
414 currentPoint.SetHitActor(touchDownConsumedActorHandle);
415 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, localVars.processor.mLastRenderTask);
419 localVars.processor.Clear();
420 localVars.processor.mTouchDownConsumedActor.SetActor(nullptr);
422 currentPoint.SetHitActor(Dali::Actor());
424 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
425 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
427 touchEventImpl->AddPoint(currentPoint);
429 localVars.processor.mScene.EmitTouchedSignal(touchEventHandle);
433 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
434 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
435 * @return True if consumed, false otherwise.
437 static inline bool DeliverEventsToActorAndParents(ProcessTouchEventVariables& localVars)
439 bool consumed = false;
440 TouchEventProcessor& processor = localVars.processor;
441 if(localVars.currentRenderTask)
443 if(localVars.isGeometry)
445 Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
446 if(interceptedTouchActor)
448 Actor* touchConsumedActor(processor.mLastConsumedActor.GetActor());
449 if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
451 RenderTask& currentRenderTaskImpl = *localVars.currentRenderTask.Get();
452 localVars.consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, localVars.touchEventImpl, localVars.primaryPointState, localVars.isGeometry);
454 else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
456 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
457 std::list<Dali::Internal::Actor*> interceptActorLists = localVars.processor.mInterceptedActorLists;
458 localVars.consumedActor = EmitGeoTouchSignals(interceptActorLists, localVars.touchEventHandle);
463 Dali::Actor interceptedActor;
464 // Let's find out if there is an intercept actor.
465 interceptedActor = EmitGeoInterceptTouchSignals(processor.mCandidateActorLists, processor.mInterceptedActorLists, localVars.touchEventHandle, processor.mLastConsumedActor);
468 processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
470 // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
471 if(processor.mLastConsumedActor.GetActor() &&
472 processor.mLastConsumedActor.GetActor() != interceptedActor &&
473 processor.mLastRenderTask &&
474 processor.mLastPrimaryPointState != PointState::FINISHED)
476 EmitTouchSignals(processor.mLastConsumedActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
477 processor.mTouchDownConsumedActor.SetActor(nullptr);
478 processor.mLastConsumedActor.SetActor(nullptr);
480 else if(processor.mLastPrimaryHitActor.GetActor() &&
481 processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
482 processor.mLastRenderTask &&
483 processor.mLastPrimaryPointState != PointState::FINISHED)
485 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = processor.mCandidateActorLists.rbegin();
486 for(; rIter != processor.mCandidateActorLists.rend(); rIter++)
488 Actor* actorImpl(*rIter);
489 EmitTouchSignals(actorImpl, *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
494 Actor* touchConsumedActor(processor.mLastConsumedActor.GetActor());
495 if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
497 RenderTask& currentRenderTaskImpl = *localVars.currentRenderTask.Get();
498 localVars.consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, localVars.touchEventImpl, localVars.primaryPointState, localVars.isGeometry);
502 // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
503 localVars.consumedActor = EmitGeoTouchSignals(interceptedActor ? processor.mInterceptedActorLists : processor.mCandidateActorLists, localVars.touchEventHandle);
505 // If consumed, the actors who previously received the touch are interrupted, indicating that the touch has been consumed by another actor.
506 if(localVars.consumedActor && localVars.primaryPointState != PointState::DOWN)
508 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = std::find(processor.mCandidateActorLists.rbegin(), processor.mCandidateActorLists.rend(), localVars.consumedActor);
509 for(++rIter; rIter != processor.mCandidateActorLists.rend(); ++rIter)
511 Actor* actorImpl(*rIter);
512 EmitTouchSignals(actorImpl, *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
520 Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
521 if(interceptedTouchActor)
523 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
524 localVars.consumedActor = EmitTouchSignals(interceptedTouchActorHandle, localVars.touchEventHandle);
528 // Emit the intercept touch signal
529 Dali::Actor interceptedActor = EmitInterceptTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
532 processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
533 // If the child is being touched, INTERRUPTED is sent.
534 if(processor.mLastPrimaryHitActor.GetActor() &&
535 processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
536 processor.mLastRenderTask &&
537 processor.mLastPrimaryPointState != PointState::FINISHED)
539 EmitTouchSignals(processor.mLastPrimaryHitActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
540 processor.mTouchDownConsumedActor.SetActor(nullptr);
542 localVars.consumedActor = EmitTouchSignals(interceptedActor, localVars.touchEventHandle);
546 localVars.consumedActor = EmitTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
551 consumed = localVars.consumedActor ? true : false;
553 if(localVars.primaryPointState == PointState::MOTION)
555 DALI_LOG_INFO(gLogFilter,
557 "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
558 localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
559 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
560 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
561 TOUCH_POINT_STATE[localVars.primaryPointState],
562 localVars.primaryPoint->GetScreenPosition().x,
563 localVars.primaryPoint->GetScreenPosition().y,
564 localVars.isGeometry);
565 DALI_LOG_INFO(gLogFilter,
567 "ConsumedActor: (%p) id(%d), name(%s), state(%s), isGeo : %d \n",
568 localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
569 localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
570 localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
571 TOUCH_POINT_STATE[localVars.primaryPointState],
572 localVars.isGeometry);
576 DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
577 localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
578 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
579 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
580 TOUCH_POINT_STATE[localVars.primaryPointState],
581 localVars.primaryPoint->GetScreenPosition().x,
582 localVars.primaryPoint->GetScreenPosition().y,
583 localVars.isGeometry);
584 DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s), isGeo : %d \n",
585 localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
586 localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
587 localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
588 TOUCH_POINT_STATE[localVars.primaryPointState],
589 localVars.isGeometry);
593 if((localVars.primaryPointState == PointState::DOWN) &&
594 (localVars.touchEventImpl->GetPointCount() == 1) &&
595 (localVars.consumedActor && localVars.consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
597 processor.mTouchDownConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
604 * Deliver Leave event to last hit or consuming actor if required.
605 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
606 * @return True if consumed, false otherwise.
608 static inline bool DeliverLeaveEvent(ProcessTouchEventVariables& localVars)
610 bool consumed = false;
611 TouchEventProcessor& processor(localVars.processor);
612 if((localVars.primaryPointState == PointState::MOTION) || (localVars.primaryPointState == PointState::UP) || (localVars.primaryPointState == PointState::STATIONARY))
614 if(processor.mLastRenderTask)
616 Dali::Actor leaveEventConsumer;
617 RenderTask& lastRenderTaskImpl = *processor.mLastRenderTask.Get();
619 if(localVars.isGeometry)
621 if(localVars.lastPrimaryHitActor)
623 if(!localVars.lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(localVars.lastPrimaryHitActor))
625 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
626 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
627 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetName().data());
628 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
632 consumed = leaveEventConsumer ? true : false;
634 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
635 // consumed actor's listeners may need to be informed (through a leave event).
636 // Further checks here to ensure we do not signal the same actor twice for the same event.
637 if(localVars.lastConsumedActor &&
638 localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
639 localVars.lastConsumedActor != leaveEventConsumer)
641 if(!localVars.lastConsumedActor->IsHittable() || !IsActuallySensitive(localVars.lastConsumedActor))
643 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
644 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
645 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetName().data());
646 EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
652 if(localVars.lastPrimaryHitActor &&
653 localVars.lastPrimaryHitActor != localVars.primaryHitActor &&
654 localVars.lastPrimaryHitActor != localVars.consumedActor)
656 if(localVars.lastPrimaryHitActor->IsHittable() && IsActuallySensitive(localVars.lastPrimaryHitActor))
658 if(localVars.lastPrimaryHitActor->GetLeaveRequired())
660 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
661 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
666 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
667 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
668 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
669 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
673 consumed |= leaveEventConsumer ? true : false;
675 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
676 // consumed actor's listeners may need to be informed (through a leave event).
677 // Further checks here to ensure we do not signal the same actor twice for the same event.
678 if(localVars.lastConsumedActor &&
679 localVars.lastConsumedActor != localVars.consumedActor &&
680 localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
681 localVars.lastConsumedActor != localVars.primaryHitActor &&
682 localVars.lastConsumedActor != leaveEventConsumer)
684 if(localVars.lastConsumedActor->IsHittable() && IsActuallySensitive(localVars.lastConsumedActor))
686 if(localVars.lastConsumedActor->GetLeaveRequired())
688 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
689 EmitTouchSignals(localVars.lastConsumedActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
694 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
695 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
696 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
697 EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
707 * Update the processor member appropriately by handling the final up event, and setting the last hit/consumed events etc.
708 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
710 static inline void UpdateMembersWithCurrentHitInformation(ProcessTouchEventVariables& localVars)
712 // If our primary point is an Up event, then the primary point (in multi-touch) will change next
713 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
715 TouchEventProcessor& processor(localVars.processor);
716 if(localVars.primaryPointState == PointState::UP)
722 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
723 if(localVars.primaryHitActor && GetImplementation(localVars.primaryHitActor).OnScene())
725 processor.mLastPrimaryHitActor.SetActor(&GetImplementation(localVars.primaryHitActor));
727 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
728 if(localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene())
730 processor.mLastConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
734 if(localVars.isGeometry)
736 if(localVars.lastConsumedActor && !localVars.lastConsumedActor->OnScene())
738 processor.mLastConsumedActor.SetActor(nullptr);
743 processor.mLastConsumedActor.SetActor(nullptr);
747 processor.mLastRenderTask = localVars.currentRenderTask;
748 processor.mLastPrimaryPointState = localVars.primaryPointState;
758 * Deliver an event to the touch-down actor and emit from the scene if required.
759 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
760 * @param[in] event The touch event that has occurred
762 static inline void DeliverEventToTouchDownActorAndScene(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
764 TouchEventProcessor& processor(localVars.processor);
765 if(localVars.touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
767 switch(localVars.primaryPointState)
771 Actor* touchDownConsumedActor(processor.mTouchDownConsumedActor.GetActor());
772 if(touchDownConsumedActor &&
773 touchDownConsumedActor != localVars.consumedActor &&
774 touchDownConsumedActor != localVars.lastPrimaryHitActor &&
775 touchDownConsumedActor != localVars.lastConsumedActor)
777 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
779 Integration::Point currentPoint = localVars.touchEventImpl->GetPoint(0);
780 currentPoint.SetHitActor(touchDownConsumedActorHandle);
781 currentPoint.SetState(PointState::INTERRUPTED);
783 if(localVars.isGeometry)
785 std::list<Dali::Internal::Actor*> actorLists;
786 actorLists.push_back(touchDownConsumedActor);
787 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
791 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
795 processor.mTouchDownConsumedActor.SetActor(nullptr);
796 processor.mInterceptedTouchActor.SetActor(nullptr);
801 case PointState::DOWN:
803 processor.mScene.EmitTouchedSignal(localVars.touchEventHandle);
807 case PointState::MOTION:
808 case PointState::LEAVE:
809 case PointState::STATIONARY:
810 case PointState::INTERRUPTED:
820 TouchEventProcessor::TouchEventProcessor(Scene& scene)
822 mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
823 mLastConsumedActor(),
824 mCapturingTouchActor(),
826 mTouchDownConsumedActor(),
827 mInterceptedTouchActor(),
829 mLastPrimaryPointState(PointState::FINISHED)
831 DALI_LOG_TRACE_METHOD(gLogFilter);
834 TouchEventProcessor::~TouchEventProcessor()
836 DALI_LOG_TRACE_METHOD(gLogFilter);
839 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
841 DALI_LOG_TRACE_METHOD(gLogFilter);
842 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
844 PRINT_HIERARCHY(gLogFilter);
846 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
848 ProcessTouchEventVariables localVars(*this, mScene.IsGeometryHittestEnabled());
850 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
851 // and emit the stage signal as well.
853 if(event.points[0].GetState() == PointState::INTERRUPTED)
855 Impl::EmitInterruptedEvent(localVars, event);
856 return false; // No need for hit testing & already an interrupted event so just return false
860 localVars.touchEventImpl = new TouchEvent(event.time);
861 localVars.touchEventHandle = Dali::TouchEvent(localVars.touchEventImpl.Get());
863 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
864 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
866 bool firstPointParsed = false;
867 for(auto&& currentPoint : event.points)
869 HitTestAlgorithm::Results hitTestResults;
870 hitTestResults.point = currentPoint;
871 hitTestResults.eventTime = event.time;
872 if(!firstPointParsed)
874 firstPointParsed = true;
875 ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
877 // Only set the currentRenderTask for the primary hit actor.
878 localVars.currentRenderTask = hitTestResults.renderTask;
879 localVars.touchEventImpl->SetRenderTask(Dali::RenderTask(localVars.currentRenderTask.Get()));
883 Actor* capturingTouchActor = mCapturingTouchActor.GetActor();
884 if(capturingTouchActor && mLastRenderTask)
886 hitTestResults.actor = Dali::Actor(capturingTouchActor);
887 hitTestResults.renderTask = mLastRenderTask;
888 const Vector2& screenPosition = currentPoint.GetScreenPosition();
889 capturingTouchActor->ScreenToLocal(*mLastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
893 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, localVars.isGeometry);
897 Integration::Point newPoint(currentPoint);
898 newPoint.SetHitActor(hitTestResults.actor);
899 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
901 localVars.touchEventImpl->AddPoint(newPoint);
903 DALI_LOG_INFO(gLogFilter,
905 " State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n",
906 TOUCH_POINT_STATE[currentPoint.GetState()],
907 currentPoint.GetScreenPosition().x,
908 currentPoint.GetScreenPosition().y,
909 (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL),
910 (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""),
911 hitTestResults.actorCoordinates.x,
912 hitTestResults.actorCoordinates.y);
915 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
916 localVars.primaryPoint = &localVars.touchEventImpl->GetPoint(0);
917 localVars.primaryHitActor = localVars.primaryPoint->GetHitActor();
918 localVars.primaryPointState = localVars.primaryPoint->GetState();
920 bool consumed = Impl::DeliverEventsToActorAndParents(localVars);
922 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
923 // hit actor. Also process the last consumed actor in the same manner.
924 localVars.lastPrimaryHitActor = mInterceptedTouchActor.GetActor() ? mInterceptedTouchActor.GetActor() : mLastPrimaryHitActor.GetActor();
925 localVars.lastConsumedActor = mLastConsumedActor.GetActor();
926 consumed |= Impl::DeliverLeaveEvent(localVars);
928 // 5) Update the processor member appropriately.
929 Impl::UpdateMembersWithCurrentHitInformation(localVars);
931 // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
932 // emit the stage touched event if required.
933 Impl::DeliverEventToTouchDownActorAndScene(localVars, event);
938 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
940 if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
942 Dali::Actor actorHandle(actor);
943 Integration::Point point;
944 point.SetState(PointState::INTERRUPTED);
945 point.SetHitActor(actorHandle);
946 if(actor == mLastConsumedActor.GetActor())
948 std::list<Dali::Internal::Actor*> actorLists;
949 actorLists.push_back(mLastConsumedActor.GetActor());
950 GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
952 else if(!mLastConsumedActor.GetActor())
954 GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
956 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
957 mLastConsumedActor.SetActor(nullptr);
958 mLastRenderTask.Reset();
959 mLastPrimaryPointState = PointState::FINISHED;
963 if(actor == mLastPrimaryHitActor.GetActor())
965 Dali::Actor actorHandle(actor);
966 Integration::Point point;
967 point.SetState(PointState::INTERRUPTED);
968 point.SetHitActor(actorHandle);
970 TouchEventPtr touchEventImpl(new TouchEvent);
971 touchEventImpl->AddPoint(point);
972 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
974 Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
975 if(mLastConsumedActor.GetActor() != eventConsumer)
977 EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
980 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
981 mLastConsumedActor.SetActor(nullptr);
982 mLastRenderTask.Reset();
983 mLastPrimaryPointState = PointState::FINISHED;
988 void TouchEventProcessor::Clear()
990 mLastPrimaryHitActor.SetActor(nullptr);
991 mLastConsumedActor.SetActor(nullptr);
992 mCapturingTouchActor.SetActor(nullptr);
993 mOwnTouchActor.SetActor(nullptr);
994 mInterceptedTouchActor.SetActor(nullptr);
995 mLastRenderTask.Reset();
996 mLastPrimaryPointState = PointState::FINISHED;
999 } // namespace Dali::Internal