Fixed typos in log and add change log level
[platform/core/uifw/dali-core.git] / dali / internal / event / events / touch-event-processor.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/touch-event-processor.h>
20
21 #if defined(DEBUG_ENABLED)
22 #include <sstream>
23 #endif
24
25 // INTERNAL INCLUDES
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>
39
40 namespace Dali
41 {
42 namespace Internal
43 {
44 namespace
45 {
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)
50
51 const char* TOUCH_POINT_STATE[6] =
52   {
53     "DOWN",
54     "UP",
55     "MOTION",
56     "LEAVE",
57     "STATIONARY",
58     "INTERRUPTED",
59 };
60
61
62 Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
63 {
64   Dali::Actor interceptedActor;
65
66   if(actor)
67   {
68     Dali::Actor parent = actor.GetParent();
69     if(parent)
70     {
71       // Recursively deliver events to the actor and its parents for intercept touch event.
72       interceptedActor = EmitInterceptTouchSignals(parent, touchEvent);
73     }
74
75     if(!interceptedActor)
76     {
77       bool   intercepted = false;
78       Actor& actorImpl(GetImplementation(actor));
79       if(actorImpl.GetInterceptTouchRequired())
80       {
81         DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL");
82         intercepted = actorImpl.EmitInterceptTouchEventSignal(touchEvent);
83         if(intercepted)
84         {
85           interceptedActor = Dali::Actor(&actorImpl);
86         }
87       }
88     }
89   }
90
91   return interceptedActor;
92 }
93
94 /**
95  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
96  */
97 Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent)
98 {
99   Dali::Actor consumedActor;
100
101   if(actor)
102   {
103     Dali::Actor oldParent(actor.GetParent());
104
105     Actor& actorImpl(GetImplementation(actor));
106
107     bool consumed(false);
108
109     // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch).
110     if(actorImpl.GetTouchRequired())
111     {
112       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL");
113       consumed = actorImpl.EmitTouchEventSignal(touchEvent);
114     }
115
116     if(consumed)
117     {
118       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
119       consumedActor = Dali::Actor(&actorImpl);
120     }
121     else
122     {
123       // The actor may have been removed/reparented during the signal callbacks.
124       Dali::Actor parent = actor.GetParent();
125
126       if(parent &&
127          (parent == oldParent))
128       {
129         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
130         consumedActor = EmitTouchSignals(parent, touchEvent);
131       }
132     }
133   }
134
135   return consumedActor;
136 }
137
138 Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
139 {
140   TouchEventPtr    touchEvent(new TouchEvent(time));
141   Dali::TouchEvent touchEventHandle(touchEvent.Get());
142
143   touchEvent->AddPoint(point);
144
145   return EmitTouchSignals(actor, touchEventHandle);
146 }
147
148 /**
149  * Changes the state of the primary point to leave and emits the touch signals
150  */
151 Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state)
152 {
153   Dali::Actor consumingActor;
154
155   if(actor)
156   {
157     TouchEventPtr touchEventImpl = TouchEvent::Clone(*originalTouchEvent.Get());
158
159     Integration::Point& primaryPoint = touchEventImpl->GetPoint(0);
160
161     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
162     Vector2        localPosition;
163     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
164
165     primaryPoint.SetLocalPosition(localPosition);
166     primaryPoint.SetHitActor(Dali::Actor(actor));
167     primaryPoint.SetState(state);
168
169     consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get()));
170   }
171
172   return consumingActor;
173 }
174
175 /**
176  * @brief Parses the primary touch point by performing a hit-test if necessary
177  *
178  * @param[out] hitTestResults The hit test results are put into this variable
179  * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
180  * @param[in] lastRenderTask The last render task member
181  * @param[in] currentPoint The current point information
182  * @param[in] scene The scene that this touch is related to
183  */
184 void ParsePrimaryTouchPoint(
185   HitTestAlgorithm::Results& hitTestResults,
186   ActorObserver&             capturingTouchActorObserver,
187   ActorObserver&             ownTouchActorObserver,
188   const RenderTaskPtr&       lastRenderTask,
189   const Integration::Point&  currentPoint,
190   const Internal::Scene&     scene)
191 {
192   Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
193
194   // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
195   if(capturingTouchActor && lastRenderTask)
196   {
197     hitTestResults.actor          = Dali::Actor(capturingTouchActor);
198     hitTestResults.renderTask     = lastRenderTask;
199     const Vector2& screenPosition = currentPoint.GetScreenPosition();
200     capturingTouchActor->ScreenToLocal(*lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y);
201   }
202   else
203   {
204     Actor* ownTouchActor = ownTouchActorObserver.GetActor();
205     HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor);
206
207     if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor)
208     {
209       // If we've just started touch, then check whether the actor has requested to capture all touch events
210       Actor* hitActor = &GetImplementation(hitTestResults.actor);
211       if(hitActor->CapturesAllTouchAfterStart())
212       {
213         capturingTouchActorObserver.SetActor(hitActor);
214       }
215       if(hitActor->IsAllowedOnlyOwnTouch())
216       {
217         ownTouchActorObserver.SetActor(hitActor);
218       }
219     }
220   }
221 }
222
223 } // unnamed namespace
224
225 TouchEventProcessor::TouchEventProcessor(Scene& scene)
226 : mScene(scene),
227   mLastPrimaryHitActor(MakeCallback(this, &TouchEventProcessor::OnObservedActorDisconnected)),
228   mLastConsumedActor(),
229   mCapturingTouchActor(),
230   mOwnTouchActor(),
231   mTouchDownConsumedActor(),
232   mInterceptedTouchActor(),
233   mLastRenderTask(),
234   mLastPrimaryPointState(PointState::FINISHED)
235 {
236   DALI_LOG_TRACE_METHOD(gLogFilter);
237 }
238
239 TouchEventProcessor::~TouchEventProcessor()
240 {
241   DALI_LOG_TRACE_METHOD(gLogFilter);
242 }
243
244 void TouchEventProcessor::Clear()
245 {
246   mLastPrimaryHitActor.SetActor(nullptr);
247   mLastConsumedActor.SetActor(nullptr);
248   mCapturingTouchActor.SetActor(nullptr);
249   mOwnTouchActor.SetActor(nullptr);
250   mInterceptedTouchActor.SetActor(nullptr);
251   mLastRenderTask.Reset();
252   mLastPrimaryPointState = PointState::FINISHED;
253 }
254
255 bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event)
256 {
257   DALI_LOG_TRACE_METHOD(gLogFilter);
258   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty TouchEvent sent from Integration\n");
259
260   PRINT_HIERARCHY(gLogFilter);
261
262   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT");
263
264   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
265   //    and emit the stage signal as well.
266
267   if(event.points[0].GetState() == PointState::INTERRUPTED)
268   {
269     Dali::Actor        consumingActor;
270     Integration::Point currentPoint(event.points[0]);
271
272     Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
273     if(lastPrimaryHitActor)
274     {
275       Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
276       currentPoint.SetHitActor(lastPrimaryHitActorHandle);
277
278       consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
279     }
280
281     // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
282     Actor* lastConsumedActor(mLastConsumedActor.GetActor());
283     if(lastConsumedActor &&
284        lastConsumedActor != lastPrimaryHitActor &&
285        lastConsumedActor != consumingActor)
286     {
287       Dali::Actor lastConsumedActorHandle(lastConsumedActor);
288       currentPoint.SetHitActor(lastConsumedActorHandle);
289       AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint);
290     }
291
292     // Tell the touch-down consuming actor as well, if required
293     Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
294     if(touchDownConsumedActor &&
295        touchDownConsumedActor != lastPrimaryHitActor &&
296        touchDownConsumedActor != lastConsumedActor &&
297        touchDownConsumedActor != consumingActor)
298     {
299       Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
300
301       currentPoint.SetHitActor(touchDownConsumedActorHandle);
302       AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint);
303     }
304
305     Clear();
306     mTouchDownConsumedActor.SetActor(nullptr);
307
308     currentPoint.SetHitActor(Dali::Actor());
309
310     TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
311     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
312
313     touchEventImpl->AddPoint(currentPoint);
314
315     mScene.EmitTouchedSignal(touchEventHandle);
316     return false; // No need for hit testing & already an interrupted event so just return false
317   }
318
319   // 2) Hit Testing.
320   TouchEventPtr    touchEventImpl(new TouchEvent(event.time));
321   Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
322
323   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
324   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
325
326   RenderTaskPtr currentRenderTask;
327   bool          firstPointParsed = false;
328
329   for(auto&& currentPoint : event.points)
330   {
331     HitTestAlgorithm::Results hitTestResults;
332     hitTestResults.point     = currentPoint;
333     hitTestResults.eventTime = event.time;
334     if(!firstPointParsed)
335     {
336       firstPointParsed = true;
337       ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene);
338
339       // Only set the currentRenderTask for the primary hit actor.
340       currentRenderTask = hitTestResults.renderTask;
341     }
342     else
343     {
344       HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults);
345     }
346
347     Integration::Point newPoint(currentPoint);
348     newPoint.SetHitActor(hitTestResults.actor);
349     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
350
351     touchEventImpl->AddPoint(newPoint);
352
353     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);
354   }
355
356   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
357
358   bool consumed = false;
359
360   // Emit the touch signal
361   Dali::Actor consumedActor;
362
363   Integration::Point& primaryPoint      = touchEventImpl->GetPoint(0);
364   Dali::Actor         primaryHitActor   = primaryPoint.GetHitActor();
365   PointState::Type    primaryPointState = primaryPoint.GetState();
366
367   if(currentRenderTask)
368   {
369     Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor());
370     if(interceptedTouchActor)
371     {
372       Dali::Actor interceptedTouchActorHandle(interceptedTouchActor);
373       consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle);
374     }
375     else
376     {
377       // Emit the intercept touch signal
378       Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle);
379       if(interceptedActor)
380       {
381         mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor));
382         // If the child is being touched, INTERRUPTED is sent.
383         if(mLastPrimaryHitActor.GetActor() &&
384           mLastPrimaryHitActor.GetActor() != interceptedActor &&
385           mLastRenderTask &&
386           mLastPrimaryPointState != PointState::FINISHED)
387         {
388           EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED);
389           mTouchDownConsumedActor.SetActor(nullptr);
390         }
391         consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle);
392       }
393       else
394       {
395         consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle);
396       }
397     }
398     consumed = consumedActor ? true : false;
399
400     if(primaryPointState == PointState::MOTION)
401     {
402       DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f)\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);
403       DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor:   (%p) id(%d), name(%s), state(%s)\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]);
404     }
405     else
406     {
407       DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s)\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]);
408       DALI_LOG_RELEASE_INFO("ConsumedActor:  (%p), id(%d), name(%s), state(%s)\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]);
409     }
410   }
411
412   if((primaryPointState == PointState::DOWN) &&
413      (touchEventImpl->GetPointCount() == 1) &&
414      (consumedActor && consumedActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE)))
415   {
416     mTouchDownConsumedActor.SetActor(&GetImplementation(consumedActor));
417   }
418
419   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
420   //    hit actor.  Also process the last consumed actor in the same manner.
421   Actor* lastPrimaryHitActor(nullptr);
422   if(mInterceptedTouchActor.GetActor())
423   {
424     lastPrimaryHitActor = mInterceptedTouchActor.GetActor();
425   }
426   else
427   {
428     lastPrimaryHitActor = mLastPrimaryHitActor.GetActor();
429   }
430   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
431   if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::UP) || (primaryPointState == PointState::STATIONARY))
432   {
433     if(mLastRenderTask)
434     {
435       Dali::Actor leaveEventConsumer;
436       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
437
438       if(lastPrimaryHitActor &&
439          lastPrimaryHitActor != primaryHitActor &&
440          lastPrimaryHitActor != consumedActor)
441       {
442         if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
443         {
444           if(lastPrimaryHitActor->GetLeaveRequired())
445           {
446             DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
447             leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
448           }
449         }
450         else
451         {
452           // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
453           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
454           DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data());
455           leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
456         }
457       }
458
459       consumed |= leaveEventConsumer ? true : false;
460
461       // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
462       // consumed actor's listeners may need to be informed (through a leave event).
463       // Further checks here to ensure we do not signal the same actor twice for the same event.
464       if(lastConsumedActor &&
465          lastConsumedActor != consumedActor &&
466          lastConsumedActor != lastPrimaryHitActor &&
467          lastConsumedActor != primaryHitActor &&
468          lastConsumedActor != leaveEventConsumer)
469       {
470         if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
471         {
472           if(lastConsumedActor->GetLeaveRequired())
473           {
474             DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
475             EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE);
476           }
477         }
478         else
479         {
480           // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
481           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
482           DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data());
483           EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED);
484         }
485       }
486     }
487   }
488
489   // 5) If our primary point is an Up event, then the primary point (in multi-touch) will change next
490   //    time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
491   if(primaryPointState == PointState::UP)
492   {
493     Clear();
494   }
495   else
496   {
497     // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
498     if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
499     {
500       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
501
502       // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
503       if(consumedActor && GetImplementation(consumedActor).OnScene())
504       {
505         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
506       }
507       else
508       {
509         mLastConsumedActor.SetActor(nullptr);
510       }
511
512       mLastRenderTask        = currentRenderTask;
513       mLastPrimaryPointState = primaryPointState;
514     }
515     else
516     {
517       Clear();
518     }
519   }
520
521   // 6) Emit an interrupted event to the touch-down actor if it hasn't consumed the up and
522   //    emit the stage touched event if required.
523
524   if(touchEventImpl->GetPointCount() == 1) // Only want the first touch and the last release
525   {
526     switch(primaryPointState)
527     {
528       case PointState::UP:
529       {
530         Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor());
531         if(touchDownConsumedActor &&
532            touchDownConsumedActor != consumedActor &&
533            touchDownConsumedActor != lastPrimaryHitActor &&
534            touchDownConsumedActor != lastConsumedActor)
535         {
536           Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor);
537
538           Integration::Point currentPoint = touchEventImpl->GetPoint(0);
539           currentPoint.SetHitActor(touchDownConsumedActorHandle);
540           currentPoint.SetState(PointState::INTERRUPTED);
541
542           AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint);
543         }
544
545         mTouchDownConsumedActor.SetActor(nullptr);
546         mInterceptedTouchActor.SetActor(nullptr);
547
548         DALI_FALLTHROUGH;
549       }
550
551       case PointState::DOWN:
552       {
553         mScene.EmitTouchedSignal(touchEventHandle);
554         break;
555       }
556
557       case PointState::MOTION:
558       case PointState::LEAVE:
559       case PointState::STATIONARY:
560       case PointState::INTERRUPTED:
561       {
562         // Ignore
563         break;
564       }
565     }
566   }
567
568   return consumed;
569 }
570
571 void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor)
572 {
573   if(actor == mLastPrimaryHitActor.GetActor())
574   {
575     Dali::Actor actorHandle(actor);
576
577     Integration::Point point;
578     point.SetState(PointState::INTERRUPTED);
579     point.SetHitActor(actorHandle);
580
581     TouchEventPtr touchEventImpl(new TouchEvent);
582     touchEventImpl->AddPoint(point);
583     Dali::TouchEvent touchEventHandle(touchEventImpl.Get());
584
585     Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle);
586
587     if(mLastConsumedActor.GetActor() != eventConsumer)
588     {
589       EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle);
590     }
591
592     // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
593
594     mLastConsumedActor.SetActor(nullptr);
595     mLastRenderTask.Reset();
596     mLastPrimaryPointState = PointState::FINISHED;
597   }
598 }
599
600 } // namespace Internal
601
602 } // namespace Dali