2 * Copyright (c) 2023 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>
46 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TOUCH_PROCESSOR");
49 #endif // defined(DEBUG_ENABLED)
51 const char* TOUCH_POINT_STATE[6] =
61 bool ShouldEmitInterceptTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
63 PointState::Type state = event.GetState(0);
64 return actorImpl.GetInterceptTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
67 bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event)
69 PointState::Type state = event.GetState(0);
70 return actorImpl.GetTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion());
74 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
76 Dali::Actor interceptedActor;
80 Dali::Actor parent = actor.GetParent();
83 // Recursively deliver events to the actor and its parents for intercept touch event.
84 interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
89 bool intercepted = false;
90 Actor& actorImpl(GetImplementation(actor));
91 if(ShouldEmitInterceptTouchEvent(actorImpl, touchEvent))
93 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
94 intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
97 interceptedActor = Dali::Actor(&actorImpl);
102 return interceptedActor;
107 Dali::Actor EmitGeoInterceptTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, std::list<Dali::Internal::Actor*>& interceptActorList, const Dali::TouchEvent& touchEvent)
109 interceptActorList.clear();
110 Dali::Actor interceptedActor;
111 for(auto&& actor : actorLists)
113 interceptActorList.push_back(actor);
114 if(ShouldEmitInterceptTouchEvent(*actor, touchEvent))
116 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
117 if(actor->EmitInterceptTouchEventSignal(touchEvent))
119 interceptedActor = Dali::Actor(actor);
124 return interceptedActor;
128 * Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
130 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
132 Dali::Actor consumedActor;
136 Dali::Actor oldParent(actor.GetParent());
138 Actor& actorImpl(GetImplementation(actor));
140 bool consumed(false);
142 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
143 if(ShouldEmitTouchEvent(actorImpl, touchEvent))
145 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
146 consumed = actorImpl.EmitTouchEventSignal(touchEvent);
151 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
152 consumedActor = Dali::Actor(&actorImpl);
156 // The actor may have been removed/reparented during the signal callbacks.
157 Dali::Actor parent = actor.GetParent();
160 (parent == oldParent))
162 // One of the actor's parents may consumed the event and they should be set as the consumed actor.
163 consumedActor = EmitTouchSignals(parent, touchEvent);
168 return consumedActor;
172 * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached.
174 Dali::Actor EmitGeoTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, const Dali::TouchEvent& touchEvent)
176 Dali::Actor consumedActor;
178 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = actorLists.rbegin();
179 for (; rIter != actorLists.rend(); rIter++)
181 Actor* actorImpl(*rIter);
182 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
183 if(ShouldEmitTouchEvent(*actorImpl, touchEvent))
185 DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
186 if(actorImpl->EmitTouchEventSignal(touchEvent))
188 // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
189 consumedActor = Dali::Actor(actorImpl);
194 return consumedActor;
197 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask)
199 TouchEventPtr touchEvent(new TouchEvent(time));
200 Dali::TouchEvent touchEventHandle(touchEvent.Get());
202 touchEvent->AddPoint(point);
203 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
205 return EmitTouchSignals(actor, touchEventHandle);
208 Dali::Actor GeoAllocAndEmitTouchSignals(std::list<Dali::Internal::Actor*>& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask)
210 TouchEventPtr touchEvent(new TouchEvent(time));
211 Dali::TouchEvent touchEventHandle(touchEvent.Get());
213 touchEvent->AddPoint(point);
214 touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get()));
216 return EmitGeoTouchSignals(actorLists, touchEventHandle);
220 * Changes the state of the primary point to leave and emits the touch signals
222 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry)
224 Dali::Actor consumingActor;
228 TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
229 touchEventImpl->SetRenderTask(Dali::RenderTask(&renderTask));
231 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
233 const Vector2& screenPosition = primaryPoint.GetScreenPosition();
234 Vector2 localPosition;
235 actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
237 primaryPoint.SetLocalPosition(localPosition);
238 primaryPoint.SetHitActor(Dali::Actor(actor));
239 primaryPoint.SetState(state);
243 std::list<Dali::Internal::Actor*> actorLists;
244 actorLists.push_back(actor);
245 consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get()));
249 consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
253 return consumingActor;
257 * @brief Parses the primary touch point by performing a hit-test if necessary
259 * @param[out] hitTestResults The hit test results are put into this variable
260 * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
261 * @param[in] lastRenderTask The last render task member
262 * @param[in] currentPoint The current point information
263 * @param[in] scene The scene that this touch is related to
264 * @param[in] actorLists The list of actors that can be touched, from leaf actor to root.
266 void ParsePrimaryTouchPoint(
267 HitTestAlgorithm::Results& hitTestResults,
268 ActorObserver& capturingTouchActorObserver,
269 ActorObserver& ownTouchActorObserver,
270 const RenderTaskPtr& lastRenderTask,
271 const Integration::Point& currentPoint,
272 const Internal::Scene& scene,
273 std::list<Dali::Internal::Actor*>& actorLists)
275 Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
277 // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
278 if(capturingTouchActor && lastRenderTask)
280 hitTestResults.actor = Dali::Actor(capturingTouchActor);
281 hitTestResults.renderTask = lastRenderTask;
282 const Vector2& screenPosition = currentPoint.GetScreenPosition();
283 capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
287 Actor* ownTouchActor = ownTouchActorObserver.GetActor();
288 HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled());
290 if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
292 bool isGeometry = scene.IsGeometryHittestEnabled();
293 // If we've just started touch, then check whether the actor has requested to capture all touch events
294 Actor* hitActor = &GetImplementation(hitTestResults.actor);
295 if(hitActor->CapturesAllTouchAfterStart() || isGeometry)
297 capturingTouchActorObserver.SetActor(hitActor);
299 if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry)
301 ownTouchActorObserver.SetActor(hitActor);
305 actorLists = hitTestResults.actorLists;
311 } // unnamed namespace
313 TouchEventProcessor::TouchEventProcessor(Scene& scene)
315 mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
316 mLastConsumedActor(),
317 mCapturingTouchActor(),
319 mTouchDownConsumedActor(),
320 mInterceptedTouchActor(),
322 mLastPrimaryPointState(PointState::FINISHED)
324 DALI_LOG_TRACE_METHOD(gLogFilter);
327 TouchEventProcessor::~TouchEventProcessor()
329 DALI_LOG_TRACE_METHOD(gLogFilter);
332 void TouchEventProcessor::Clear()
334 mLastPrimaryHitActor.SetActor(nullptr);
335 mLastConsumedActor.SetActor(nullptr);
336 mCapturingTouchActor.SetActor(nullptr);
337 mOwnTouchActor.SetActor(nullptr);
338 mInterceptedTouchActor.SetActor(nullptr);
339 mLastRenderTask.Reset();
340 mLastPrimaryPointState = PointState::FINISHED;
343 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
345 DALI_LOG_TRACE_METHOD(gLogFilter);
346 DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
348 PRINT_HIERARCHY(gLogFilter);
350 DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
352 bool isGeometry = mScene.IsGeometryHittestEnabled();
354 // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
355 // and emit the stage signal as well.
357 if(event.points[0].GetState() == PointState::INTERRUPTED)
359 Dali::Actor consumingActor;
360 Integration::Point currentPoint(event.points[0]);
364 // Since the geometry way only receives touch events from the consumed actor,
365 // it first searches for whether there is a consumed actor and then sends the event
366 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
367 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
368 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
369 if(touchConsumedActor)
371 Dali::Actor touchConsumedActorHandle(touchConsumedActor);
372 currentPoint.SetHitActor(touchConsumedActorHandle);
373 std::list<Dali::Internal::Actor*> actorLists;
374 actorLists.push_back(touchConsumedActor);
375 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
377 else if(touchDownConsumedActor)
379 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
380 currentPoint.SetHitActor(touchDownConsumedActorHandle);
381 std::list<Dali::Internal::Actor*> actorLists;
382 actorLists.push_back(touchDownConsumedActor);
383 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask);
385 else if(lastPrimaryHitActor)
387 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
388 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
390 GeoAllocAndEmitTouchSignals(mCandidateActorLists, event.time, currentPoint, mLastRenderTask);
395 Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
396 if(lastPrimaryHitActor)
398 Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
399 currentPoint.SetHitActor(lastPrimaryHitActorHandle);
401 consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask);
404 // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
405 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
406 if(lastConsumedActor &&
407 lastConsumedActor != lastPrimaryHitActor &&
408 lastConsumedActor != consumingActor)
410 Dali::Actor lastConsumedActorHandle(lastConsumedActor);
411 currentPoint.SetHitActor(lastConsumedActorHandle);
412 AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask);
415 // Tell the touch-down consuming actor as well, if required
416 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
417 if(touchDownConsumedActor &&
418 touchDownConsumedActor != lastPrimaryHitActor &&
419 touchDownConsumedActor != lastConsumedActor &&
420 touchDownConsumedActor != consumingActor)
422 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
424 currentPoint.SetHitActor(touchDownConsumedActorHandle);
425 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask);
430 mTouchDownConsumedActor.SetActor(nullptr);
432 currentPoint.SetHitActor(Dali::Actor());
434 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
435 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
437 touchEventImpl->AddPoint(currentPoint);
439 mScene.EmitTouchedSignal(touchEventHandle);
440 return false; // No need for hit testing & already an interrupted event so just return false
444 TouchEventPtr touchEventImpl(new TouchEvent(event.time));
445 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
447 DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
448 DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
450 RenderTaskPtr currentRenderTask;
451 bool firstPointParsed = false;
453 for(auto&& currentPoint : event.points)
455 HitTestAlgorithm::Results hitTestResults;
456 hitTestResults.point = currentPoint;
457 hitTestResults.eventTime = event.time;
458 if(!firstPointParsed)
460 firstPointParsed = true;
461 ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists);
463 // Only set the currentRenderTask for the primary hit actor.
464 currentRenderTask = hitTestResults.renderTask;
465 touchEventImpl->SetRenderTask(Dali::RenderTask(currentRenderTask.Get()));
469 HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, isGeometry);
472 Integration::Point newPoint(currentPoint);
473 newPoint.SetHitActor(hitTestResults.actor);
474 newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
476 touchEventImpl->AddPoint(newPoint);
478 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);
481 // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
483 bool consumed = false;
485 // Emit the touch signal
486 Dali::Actor consumedActor;
488 Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
489 Dali::Actor primaryHitActor = primaryPoint.GetHitActor();
490 PointState::Type primaryPointState = primaryPoint.GetState();
492 if(currentRenderTask)
496 Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
497 if(interceptedTouchActor)
499 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
500 if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
502 RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
503 consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
505 else // If there is an intercepted actor, send a touch event starting from the intercepted actor.
507 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
508 std::list<Dali::Internal::Actor*> interceptActorLists = mInterceptedActorLists;
509 consumedActor = EmitGeoTouchSignals(interceptActorLists, touchEventHandle);
514 Dali::Actor interceptedActor;
515 // Let's find out if there is an intercept actor.
516 interceptedActor = EmitGeoInterceptTouchSignals(mCandidateActorLists, mInterceptedActorLists, touchEventHandle);
519 mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
521 // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously.
522 if(mLastConsumedActor.GetActor() &&
523 mLastConsumedActor.GetActor() != interceptedActor &&
525 mLastPrimaryPointState != PointState::FINISHED)
527 EmitTouchSignals(mLastConsumedActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
528 mTouchDownConsumedActor.SetActor(nullptr);
529 mLastConsumedActor.SetActor(nullptr);
531 else if(mLastPrimaryHitActor.GetActor() &&
532 mLastPrimaryHitActor.GetActor() != interceptedActor &&
534 mLastPrimaryPointState != PointState::FINISHED)
536 std::list<Dali::Internal::Actor*>::reverse_iterator rIter = mCandidateActorLists.rbegin();
537 for (; rIter != mCandidateActorLists.rend(); rIter++)
539 Actor* actorImpl(*rIter);
540 EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
545 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
546 if(touchConsumedActor) // If there is a consumed actor, send events only to the consumed actor.
548 RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
549 consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
553 // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
554 consumedActor = EmitGeoTouchSignals(interceptedActor ? mInterceptedActorLists : mCandidateActorLists, touchEventHandle);
560 Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
561 if(interceptedTouchActor)
563 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
564 consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
568 // Emit the intercept touch signal
569 Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
572 mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
573 // If the child is being touched, INTERRUPTED is sent.
574 if(mLastPrimaryHitActor.GetActor() &&
575 mLastPrimaryHitActor.GetActor() != interceptedActor &&
577 mLastPrimaryPointState != PointState::FINISHED)
579 EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
580 mTouchDownConsumedActor.SetActor(nullptr);
582 consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
586 consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
591 consumed = consumedActor ? true : false;
593 if(primaryPointState == PointState::MOTION)
595 DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n", primaryHitActor ? reinterpret_cast<void*>(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y, isGeometry);
596 DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) id(%d), name(%s), state(%s), isGeo : %d \n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], isGeometry);
600 DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n", primaryHitActor ? reinterpret_cast<void*>(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y, isGeometry);
601 DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s), isGeo : %d \n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<int32_t>(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], isGeometry);
605 if((primaryPointState == PointState::DOWN) &&
606 (touchEventImpl->GetPointCount() == 1) &&
607 (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
609 mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
612 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
613 // hit actor. Also process the last consumed actor in the same manner.
614 Actor* lastPrimaryHitActor(nullptr);
615 if(mInterceptedTouchActor.GetActor())
617 lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
621 lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
623 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
624 if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
628 Dali::Actor leaveEventConsumer;
629 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
633 if(lastPrimaryHitActor)
635 if(!lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(lastPrimaryHitActor))
637 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
638 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
639 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
640 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
644 consumed |= leaveEventConsumer ? true : false;
646 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
647 // consumed actor's listeners may need to be informed (through a leave event).
648 // Further checks here to ensure we do not signal the same actor twice for the same event.
649 if(lastConsumedActor &&
650 lastConsumedActor != lastPrimaryHitActor &&
651 lastConsumedActor != leaveEventConsumer)
653 if(!lastConsumedActor->IsHittable() || !IsActuallySensitive(lastConsumedActor))
655 // At this point mLastConsumedActor 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(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
658 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
664 if(lastPrimaryHitActor &&
665 lastPrimaryHitActor != primaryHitActor &&
666 lastPrimaryHitActor != consumedActor)
668 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
670 if(lastPrimaryHitActor->GetLeaveRequired())
672 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
673 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
678 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
679 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
680 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
681 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
685 consumed |= leaveEventConsumer ? true : false;
687 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
688 // consumed actor's listeners may need to be informed (through a leave event).
689 // Further checks here to ensure we do not signal the same actor twice for the same event.
690 if(lastConsumedActor &&
691 lastConsumedActor != consumedActor &&
692 lastConsumedActor != lastPrimaryHitActor &&
693 lastConsumedActor != primaryHitActor &&
694 lastConsumedActor != leaveEventConsumer)
696 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
698 if(lastConsumedActor->GetLeaveRequired())
700 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
701 EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
706 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
707 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
708 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
709 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
716 // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
717 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
718 if(primaryPointState == PointState::UP)
724 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
725 if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
727 mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
729 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
730 if(consumedActor && GetImplementation(consumedActor).OnScene())
732 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
738 if(lastConsumedActor && !lastConsumedActor->OnScene())
740 mLastConsumedActor.SetActor(nullptr);
745 mLastConsumedActor.SetActor(nullptr);
749 mLastRenderTask = currentRenderTask;
750 mLastPrimaryPointState = primaryPointState;
758 // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
759 // emit the stage touched event if required.
761 if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
763 switch(primaryPointState)
767 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
768 if(touchDownConsumedActor &&
769 touchDownConsumedActor != consumedActor &&
770 touchDownConsumedActor != lastPrimaryHitActor &&
771 touchDownConsumedActor != lastConsumedActor)
773 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
775 Integration::Point currentPoint = touchEventImpl->GetPoint(0);
776 currentPoint.SetHitActor(touchDownConsumedActorHandle);
777 currentPoint.SetState(PointState::INTERRUPTED);
781 std::list<Dali::Internal::Actor*> actorLists;
782 actorLists.push_back(touchDownConsumedActor);
783 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
787 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
791 mTouchDownConsumedActor.SetActor(nullptr);
792 mInterceptedTouchActor.SetActor(nullptr);
797 case PointState::DOWN:
799 mScene.EmitTouchedSignal(touchEventHandle);
803 case PointState::MOTION:
804 case PointState::LEAVE:
805 case PointState::STATIONARY:
806 case PointState::INTERRUPTED:
817 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
819 if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
821 Dali::Actor actorHandle(actor);
822 Integration::Point point;
823 point.SetState(PointState::INTERRUPTED);
824 point.SetHitActor(actorHandle);
825 if(actor == mLastConsumedActor.GetActor())
827 std::list<Dali::Internal::Actor*> actorLists;
828 actorLists.push_back(mLastConsumedActor.GetActor());
829 GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
831 else if(!mLastConsumedActor.GetActor())
833 GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
835 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
836 mLastConsumedActor.SetActor(nullptr);
837 mLastRenderTask.Reset();
838 mLastPrimaryPointState = PointState::FINISHED;
842 if(actor == mLastPrimaryHitActor.GetActor())
844 Dali::Actor actorHandle(actor);
845 Integration::Point point;
846 point.SetState(PointState::INTERRUPTED);
847 point.SetHitActor(actorHandle);
849 TouchEventPtr touchEventImpl(new TouchEvent);
850 touchEventImpl->AddPoint(point);
851 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
853 Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
854 if(mLastConsumedActor.GetActor() != eventConsumer)
856 EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
859 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
860 mLastConsumedActor.SetActor(nullptr);
861 mLastRenderTask.Reset();
862 mLastPrimaryPointState = PointState::FINISHED;
868 } // namespace Internal