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 // If there is a consumed actor, the intercept is sent only up to the moment before the consumed actor.
136 if(lastConsumedActor.GetActor() == actor)
140 interceptActorList.push_back(actor);
141 if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
143 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
144 if(actor->EmitInterceptTouchEventSignal(touchEvent))
146 interceptedActor = Dali::Actor(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);
509 Actor* interceptedTouchActor(processor.mInterceptedTouchActor.GetActor());
510 if(interceptedTouchActor)
512 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
513 localVars.consumedActor = EmitTouchSignals(interceptedTouchActorHandle, localVars.touchEventHandle);
517 // Emit the intercept touch signal
518 Dali::Actor interceptedActor = EmitInterceptTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
521 processor.mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
522 // If the child is being touched, INTERRUPTED is sent.
523 if(processor.mLastPrimaryHitActor.GetActor() &&
524 processor.mLastPrimaryHitActor.GetActor() != interceptedActor &&
525 processor.mLastRenderTask &&
526 processor.mLastPrimaryPointState != PointState::FINISHED)
528 EmitTouchSignals(processor.mLastPrimaryHitActor.GetActor(), *processor.mLastRenderTask.Get(), localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
529 processor.mTouchDownConsumedActor.SetActor(nullptr);
531 localVars.consumedActor = EmitTouchSignals(interceptedActor, localVars.touchEventHandle);
535 localVars.consumedActor = EmitTouchSignals(localVars.primaryHitActor, localVars.touchEventHandle);
540 consumed = localVars.consumedActor ? true : false;
542 if(localVars.primaryPointState == PointState::MOTION)
544 DALI_LOG_INFO(gLogFilter,
546 "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
547 localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
548 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
549 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
550 TOUCH_POINT_STATE[localVars.primaryPointState],
551 localVars.primaryPoint->GetScreenPosition().x,
552 localVars.primaryPoint->GetScreenPosition().y,
553 localVars.isGeometry);
554 DALI_LOG_INFO(gLogFilter,
556 "ConsumedActor: (%p) id(%d), name(%s), state(%s), isGeo : %d \n",
557 localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
558 localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
559 localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
560 TOUCH_POINT_STATE[localVars.primaryPointState],
561 localVars.isGeometry);
565 DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n",
566 localVars.primaryHitActor ? reinterpret_cast<void*>(&localVars.primaryHitActor.GetBaseObject()) : NULL,
567 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
568 localVars.primaryHitActor ? localVars.primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
569 TOUCH_POINT_STATE[localVars.primaryPointState],
570 localVars.primaryPoint->GetScreenPosition().x,
571 localVars.primaryPoint->GetScreenPosition().y,
572 localVars.isGeometry);
573 DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s), isGeo : %d \n",
574 localVars.consumedActor ? reinterpret_cast<void*>(&localVars.consumedActor.GetBaseObject()) : NULL,
575 localVars.consumedActor ? localVars.consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1,
576 localVars.consumedActor ? localVars.consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "",
577 TOUCH_POINT_STATE[localVars.primaryPointState],
578 localVars.isGeometry);
582 if((localVars.primaryPointState == PointState::DOWN) &&
583 (localVars.touchEventImpl->GetPointCount() == 1) &&
584 (localVars.consumedActor && localVars.consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
586 processor.mTouchDownConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
593 * Deliver Leave event to last hit or consuming actor if required.
594 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
595 * @return True if consumed, false otherwise.
597 static inline bool DeliverLeaveEvent(ProcessTouchEventVariables& localVars)
599 bool consumed = false;
600 TouchEventProcessor& processor(localVars.processor);
601 if((localVars.primaryPointState == PointState::MOTION) || (localVars.primaryPointState == PointState::UP) || (localVars.primaryPointState == PointState::STATIONARY))
603 if(processor.mLastRenderTask)
605 Dali::Actor leaveEventConsumer;
606 RenderTask& lastRenderTaskImpl = *processor.mLastRenderTask.Get();
608 if(localVars.isGeometry)
610 if(localVars.lastPrimaryHitActor)
612 if(!localVars.lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(localVars.lastPrimaryHitActor))
614 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
615 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
616 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetName().data());
617 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
621 consumed = leaveEventConsumer ? true : false;
623 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
624 // consumed actor's listeners may need to be informed (through a leave event).
625 // Further checks here to ensure we do not signal the same actor twice for the same event.
626 if(localVars.lastConsumedActor &&
627 localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
628 localVars.lastConsumedActor != leaveEventConsumer)
630 if(!localVars.lastConsumedActor->IsHittable() || !IsActuallySensitive(localVars.lastConsumedActor))
632 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
633 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
634 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetName().data());
635 EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
641 if(localVars.lastPrimaryHitActor &&
642 localVars.lastPrimaryHitActor != localVars.primaryHitActor &&
643 localVars.lastPrimaryHitActor != localVars.consumedActor)
645 if(localVars.lastPrimaryHitActor->IsHittable() && IsActuallySensitive(localVars.lastPrimaryHitActor))
647 if(localVars.lastPrimaryHitActor->GetLeaveRequired())
649 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
650 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
655 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
656 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
657 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastPrimaryHitActor), localVars.lastPrimaryHitActor->GetId(), localVars.lastPrimaryHitActor->GetName().data());
658 leaveEventConsumer = EmitTouchSignals(localVars.lastPrimaryHitActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
662 consumed |= leaveEventConsumer ? true : false;
664 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
665 // consumed actor's listeners may need to be informed (through a leave event).
666 // Further checks here to ensure we do not signal the same actor twice for the same event.
667 if(localVars.lastConsumedActor &&
668 localVars.lastConsumedActor != localVars.consumedActor &&
669 localVars.lastConsumedActor != localVars.lastPrimaryHitActor &&
670 localVars.lastConsumedActor != localVars.primaryHitActor &&
671 localVars.lastConsumedActor != leaveEventConsumer)
673 if(localVars.lastConsumedActor->IsHittable() && IsActuallySensitive(localVars.lastConsumedActor))
675 if(localVars.lastConsumedActor->GetLeaveRequired())
677 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
678 EmitTouchSignals(localVars.lastConsumedActor, lastRenderTaskImpl, localVars.touchEventImpl, PointState::LEAVE, localVars.isGeometry);
683 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
684 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
685 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(localVars.lastConsumedActor), localVars.lastConsumedActor->GetId(), localVars.lastConsumedActor->GetName().data());
686 EmitTouchSignals(processor.mLastConsumedActor.GetActor(), lastRenderTaskImpl, localVars.touchEventImpl, PointState::INTERRUPTED, localVars.isGeometry);
696 * Update the processor member appropriately by handling the final up event, and setting the last hit/consumed events etc.
697 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
699 static inline void UpdateMembersWithCurrentHitInformation(ProcessTouchEventVariables& localVars)
701 // If our primary point is an Up event, then the primary point (in multi-touch) will change next
702 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
704 TouchEventProcessor& processor(localVars.processor);
705 if(localVars.primaryPointState == PointState::UP)
711 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
712 if(localVars.primaryHitActor && GetImplementation(localVars.primaryHitActor).OnScene())
714 processor.mLastPrimaryHitActor.SetActor(&GetImplementation(localVars.primaryHitActor));
716 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
717 if(localVars.consumedActor && GetImplementation(localVars.consumedActor).OnScene())
719 processor.mLastConsumedActor.SetActor(&GetImplementation(localVars.consumedActor));
723 if(localVars.isGeometry)
725 if(localVars.lastConsumedActor && !localVars.lastConsumedActor->OnScene())
727 processor.mLastConsumedActor.SetActor(nullptr);
732 processor.mLastConsumedActor.SetActor(nullptr);
736 processor.mLastRenderTask = localVars.currentRenderTask;
737 processor.mLastPrimaryPointState = localVars.primaryPointState;
747 * Deliver an event to the touch-down actor and emit from the scene if required.
748 * @param[in/out] localVars The struct of stack variables used by ProcessTouchEvent
749 * @param[in] event The touch event that has occurred
751 static inline void DeliverEventToTouchDownActorAndScene(ProcessTouchEventVariables& localVars, const Integration::TouchEvent& event)
753 TouchEventProcessor& processor(localVars.processor);
754 if(localVars.touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
756 switch(localVars.primaryPointState)
760 Actor* touchDownConsumedActor(processor.mTouchDownConsumedActor.GetActor());
761 if(touchDownConsumedActor &&
762 touchDownConsumedActor != localVars.consumedActor &&
763 touchDownConsumedActor != localVars.lastPrimaryHitActor &&
764 touchDownConsumedActor != localVars.lastConsumedActor)
766 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
768 Integration::Point currentPoint = localVars.touchEventImpl->GetPoint(0);
769 currentPoint.SetHitActor(touchDownConsumedActorHandle);
770 currentPoint.SetState(PointState::INTERRUPTED);
772 if(localVars.isGeometry)
774 std::list<Dali::Internal::Actor*> actorLists;
775 actorLists.push_back(touchDownConsumedActor);
776 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
780 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
784 processor.mTouchDownConsumedActor.SetActor(nullptr);
785 processor.mInterceptedTouchActor.SetActor(nullptr);
790 case PointState::DOWN:
792 processor.mScene.EmitTouchedSignal(localVars.touchEventHandle);
796 case PointState::MOTION:
797 case PointState::LEAVE:
798 case PointState::STATIONARY:
799 case PointState::INTERRUPTED:
809 TouchEventProcessor::TouchEventProcessor(Scene& scene)
811 mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
812 mLastConsumedActor(),
813 mCapturingTouchActor(),
815 mTouchDownConsumedActor(),
816 mInterceptedTouchActor(),
818 mLastPrimaryPointState(PointState::FINISHED)
820 DALI_LOG_TRACE_METHOD(gLogFilter);
823 TouchEventProcessor::~TouchEventProcessor()
825 DALI_LOG_TRACE_METHOD(gLogFilter);
828 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
830 DALI_LOG_TRACE_METHOD(gLogFilter);
831 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
833 PRINT_HIERARCHY(gLogFilter);
835 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
837 ProcessTouchEventVariables localVars(*this, mScene.IsGeometryHittestEnabled());
839 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
840 // and emit the stage signal as well.
842 if(event.points[0].GetState() == PointState::INTERRUPTED)
844 Impl::EmitInterruptedEvent(localVars, event);
845 return false; // No need for hit testing & already an interrupted event so just return false
849 localVars.touchEventImpl = new TouchEvent(event.time);
850 localVars.touchEventHandle = Dali::TouchEvent(localVars.touchEventImpl.Get());
852 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
853 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
855 bool firstPointParsed = false;
856 for(auto&& currentPoint : event.points)
858 HitTestAlgorithm::Results hitTestResults;
859 hitTestResults.point = currentPoint;
860 hitTestResults.eventTime = event.time;
861 if(!firstPointParsed)
863 firstPointParsed = true;
864 ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
866 // Only set the currentRenderTask for the primary hit actor.
867 localVars.currentRenderTask = hitTestResults.renderTask;
868 localVars.touchEventImpl->SetRenderTask(Dali::RenderTask(localVars.currentRenderTask.Get()));
872 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, localVars.isGeometry);
875 Integration::Point newPoint(currentPoint);
876 newPoint.SetHitActor(hitTestResults.actor);
877 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
879 localVars.touchEventImpl->AddPoint(newPoint);
881 DALI_LOG_INFO(gLogFilter,
883 " State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n",
884 TOUCH_POINT_STATE[currentPoint.GetState()],
885 currentPoint.GetScreenPosition().x,
886 currentPoint.GetScreenPosition().y,
887 (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL),
888 (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""),
889 hitTestResults.actorCoordinates.x,
890 hitTestResults.actorCoordinates.y);
893 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
894 localVars.primaryPoint = &localVars.touchEventImpl->GetPoint(0);
895 localVars.primaryHitActor = localVars.primaryPoint->GetHitActor();
896 localVars.primaryPointState = localVars.primaryPoint->GetState();
898 bool consumed = Impl::DeliverEventsToActorAndParents(localVars);
900 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
901 // hit actor. Also process the last consumed actor in the same manner.
902 localVars.lastPrimaryHitActor = mInterceptedTouchActor.GetActor() ? mInterceptedTouchActor.GetActor() : mLastPrimaryHitActor.GetActor();
903 localVars.lastConsumedActor = mLastConsumedActor.GetActor();
904 consumed |= Impl::DeliverLeaveEvent(localVars);
906 // 5) Update the processor member appropriately.
907 Impl::UpdateMembersWithCurrentHitInformation(localVars);
909 // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
910 // emit the stage touched event if required.
911 Impl::DeliverEventToTouchDownActorAndScene(localVars, event);
916 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
918 if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
920 Dali::Actor actorHandle(actor);
921 Integration::Point point;
922 point.SetState(PointState::INTERRUPTED);
923 point.SetHitActor(actorHandle);
924 if(actor == mLastConsumedActor.GetActor())
926 std::list<Dali::Internal::Actor*> actorLists;
927 actorLists.push_back(mLastConsumedActor.GetActor());
928 GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
930 else if(!mLastConsumedActor.GetActor())
932 GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
934 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
935 mLastConsumedActor.SetActor(nullptr);
936 mLastRenderTask.Reset();
937 mLastPrimaryPointState = PointState::FINISHED;
941 if(actor == mLastPrimaryHitActor.GetActor())
943 Dali::Actor actorHandle(actor);
944 Integration::Point point;
945 point.SetState(PointState::INTERRUPTED);
946 point.SetHitActor(actorHandle);
948 TouchEventPtr touchEventImpl(new TouchEvent);
949 touchEventImpl->AddPoint(point);
950 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
952 Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
953 if(mLastConsumedActor.GetActor() != eventConsumer)
955 EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
958 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
959 mLastConsumedActor.SetActor(nullptr);
960 mLastRenderTask.Reset();
961 mLastPrimaryPointState = PointState::FINISHED;
966 void TouchEventProcessor::Clear()
968 mLastPrimaryHitActor.SetActor(nullptr);
969 mLastConsumedActor.SetActor(nullptr);
970 mCapturingTouchActor.SetActor(nullptr);
971 mOwnTouchActor.SetActor(nullptr);
972 mInterceptedTouchActor.SetActor(nullptr);
973 mLastRenderTask.Reset();
974 mLastPrimaryPointState = PointState::FINISHED;
977 } // namespace Dali::Internal