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 consultative actor, send events only to the consultative 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*> internalActorLists = mCandidateActorLists;
537 while(!internalActorLists.empty())
539 Actor* actorImpl = internalActorLists.back();
540 // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
541 if(actorImpl->GetTouchRequired())
543 EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
545 internalActorLists.pop_back();
550 Actor* touchConsumedActor(mLastConsumedActor.GetActor());
551 if(touchConsumedActor) // If there is a consultative actor, send events only to the consultative actor.
553 RenderTask& currentRenderTaskImpl = *currentRenderTask.Get();
554 consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry);
558 // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor.
559 consumedActor = EmitGeoTouchSignals(interceptedActor ? mInterceptedActorLists : mCandidateActorLists, touchEventHandle);
565 Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
566 if(interceptedTouchActor)
568 Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
569 consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
573 // Emit the intercept touch signal
574 Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
577 mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
578 // If the child is being touched, INTERRUPTED is sent.
579 if(mLastPrimaryHitActor.GetActor() &&
580 mLastPrimaryHitActor.GetActor() != interceptedActor &&
582 mLastPrimaryPointState != PointState::FINISHED)
584 EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry);
585 mTouchDownConsumedActor.SetActor(nullptr);
587 consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
591 consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
596 consumed = consumedActor ? true : false;
598 if(primaryPointState == PointState::MOTION)
600 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);
601 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);
605 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);
606 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);
610 if((primaryPointState == PointState::DOWN) &&
611 (touchEventImpl->GetPointCount() == 1) &&
612 (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
614 mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
617 // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
618 // hit actor. Also process the last consumed actor in the same manner.
619 Actor* lastPrimaryHitActor(nullptr);
620 if(mInterceptedTouchActor.GetActor())
622 lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
626 lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
628 Actor* lastConsumedActor(mLastConsumedActor.GetActor());
629 if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
633 Dali::Actor leaveEventConsumer;
634 RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
638 if(lastPrimaryHitActor)
640 if(!lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(lastPrimaryHitActor))
642 // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
643 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
644 DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
645 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
649 consumed |= leaveEventConsumer ? true : false;
651 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
652 // consumed actor's listeners may need to be informed (through a leave event).
653 // Further checks here to ensure we do not signal the same actor twice for the same event.
654 if(lastConsumedActor &&
655 lastConsumedActor != lastPrimaryHitActor &&
656 lastConsumedActor != leaveEventConsumer)
658 if(!lastConsumedActor->IsHittable() || !IsActuallySensitive(lastConsumedActor))
660 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
661 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
662 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
663 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
669 if(lastPrimaryHitActor &&
670 lastPrimaryHitActor != primaryHitActor &&
671 lastPrimaryHitActor != consumedActor)
673 if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
675 if(lastPrimaryHitActor->GetLeaveRequired())
677 DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
678 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
683 // At this point mLastPrimaryHitActor 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(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
686 leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
690 consumed |= leaveEventConsumer ? true : false;
692 // Check if the motion event has been consumed by another actor's listener. In this case, the previously
693 // consumed actor's listeners may need to be informed (through a leave event).
694 // Further checks here to ensure we do not signal the same actor twice for the same event.
695 if(lastConsumedActor &&
696 lastConsumedActor != consumedActor &&
697 lastConsumedActor != lastPrimaryHitActor &&
698 lastConsumedActor != primaryHitActor &&
699 lastConsumedActor != leaveEventConsumer)
701 if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
703 if(lastConsumedActor->GetLeaveRequired())
705 DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
706 EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry);
711 // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
712 // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
713 DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
714 EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry);
721 // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
722 // time so set our last primary actor to NULL. Do the same to the last consumed actor as well.
723 if(primaryPointState == PointState::UP)
729 // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
730 if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
732 mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
734 // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
735 if(consumedActor && GetImplementation(consumedActor).OnScene())
737 mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
743 if(lastConsumedActor && !lastConsumedActor->OnScene())
745 mLastConsumedActor.SetActor(nullptr);
750 mLastConsumedActor.SetActor(nullptr);
754 mLastRenderTask = currentRenderTask;
755 mLastPrimaryPointState = primaryPointState;
763 // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
764 // emit the stage touched event if required.
766 if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
768 switch(primaryPointState)
772 Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
773 if(touchDownConsumedActor &&
774 touchDownConsumedActor != consumedActor &&
775 touchDownConsumedActor != lastPrimaryHitActor &&
776 touchDownConsumedActor != lastConsumedActor)
778 Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
780 Integration::Point currentPoint = touchEventImpl->GetPoint(0);
781 currentPoint.SetHitActor(touchDownConsumedActorHandle);
782 currentPoint.SetState(PointState::INTERRUPTED);
786 std::list<Dali::Internal::Actor*> actorLists;
787 actorLists.push_back(touchDownConsumedActor);
788 GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */);
792 AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */);
796 mTouchDownConsumedActor.SetActor(nullptr);
797 mInterceptedTouchActor.SetActor(nullptr);
802 case PointState::DOWN:
804 mScene.EmitTouchedSignal(touchEventHandle);
808 case PointState::MOTION:
809 case PointState::LEAVE:
810 case PointState::STATIONARY:
811 case PointState::INTERRUPTED:
822 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
824 if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor()))
826 Dali::Actor actorHandle(actor);
827 Integration::Point point;
828 point.SetState(PointState::INTERRUPTED);
829 point.SetHitActor(actorHandle);
830 if(actor == mLastConsumedActor.GetActor())
832 std::list<Dali::Internal::Actor*> actorLists;
833 actorLists.push_back(mLastConsumedActor.GetActor());
834 GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask);
836 else if(!mLastConsumedActor.GetActor())
838 GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask);
840 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
841 mLastConsumedActor.SetActor(nullptr);
842 mLastRenderTask.Reset();
843 mLastPrimaryPointState = PointState::FINISHED;
847 if(actor == mLastPrimaryHitActor.GetActor())
849 Dali::Actor actorHandle(actor);
850 Integration::Point point;
851 point.SetState(PointState::INTERRUPTED);
852 point.SetHitActor(actorHandle);
854 TouchEventPtr touchEventImpl(new TouchEvent);
855 touchEventImpl->AddPoint(point);
856 Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
858 Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
859 if(mLastConsumedActor.GetActor() != eventConsumer)
861 EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
864 // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
865 mLastConsumedActor.SetActor(nullptr);
866 mLastRenderTask.Reset();
867 mLastPrimaryPointState = PointState::FINISHED;
873 } // namespace Internal