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