Revert "[Tizen] Not execute the remove callback"
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hover-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/hover-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/hover-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/hover-event-impl.h>
34 #include <dali/internal/event/events/multi-point-event-util.h>
35 #include <dali/internal/event/render-tasks/render-task-impl.h>
36 #include <dali/public-api/math/vector2.h>
37
38 namespace Dali
39 {
40 namespace Internal
41 {
42 namespace
43 {
44 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_HOVER_PROCESSOR");
47
48 const char* TOUCH_POINT_STATE[PointState::INTERRUPTED + 1] =
49   {
50     "STARTED",
51     "FINISHED",
52     "MOTION",
53     "LEAVE",
54     "STATIONARY",
55     "INTERRUPTED",
56 };
57
58 #endif // defined(DEBUG_ENABLED)
59
60 /**
61  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
62  */
63 Dali::Actor EmitHoverSignals(Dali::Actor actor, const Dali::HoverEvent& event)
64 {
65   Dali::Actor consumedActor;
66
67   if(actor)
68   {
69     Dali::Actor oldParent(actor.GetParent());
70
71     Actor& actorImpl(GetImplementation(actor));
72
73     bool consumed(false);
74
75     // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover).
76     if(actorImpl.GetHoverRequired())
77     {
78       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_HOVER_EVENT_SIGNAL");
79       consumed = actorImpl.EmitHoverEventSignal(event);
80     }
81
82     if(consumed)
83     {
84       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
85       consumedActor = Dali::Actor(&actorImpl);
86     }
87     else
88     {
89       // The actor may have been removed/reparented during the signal callbacks.
90       Dali::Actor parent = actor.GetParent();
91
92       if(parent &&
93          (parent == oldParent))
94       {
95         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
96         consumedActor = EmitHoverSignals(parent, event);
97       }
98     }
99   }
100
101   return consumedActor;
102 }
103
104 Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point)
105 {
106   HoverEventPtr    hoverEvent(new HoverEvent(time));
107   Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
108
109   hoverEvent->AddPoint(point);
110
111   return EmitHoverSignals(actor, hoverEventHandle);
112 }
113
114 /**
115  * Changes the state of the primary point to leave and emits the hover signals
116  */
117 Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state)
118 {
119   HoverEventPtr hoverEvent = HoverEvent::Clone(*originalEvent.Get());
120
121   DALI_ASSERT_DEBUG(NULL != actor && "NULL actor pointer");
122   if(actor)
123   {
124     Integration::Point& primaryPoint = hoverEvent->GetPoint(0);
125
126     const Vector2& screenPosition = primaryPoint.GetScreenPosition();
127     Vector2        localPosition;
128     actor->ScreenToLocal(renderTask, localPosition.x, localPosition.y, screenPosition.x, screenPosition.y);
129
130     primaryPoint.SetLocalPosition(localPosition);
131     primaryPoint.SetHitActor(Dali::Actor(actor));
132     primaryPoint.SetState(state);
133   }
134
135   return EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get()));
136 }
137
138 /**
139  * Used in the hit-test algorithm to check whether the actor is hoverable.
140  */
141 struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface
142 {
143   bool IsActorHittable(Actor* actor) override
144   {
145     return actor->GetHoverRequired() && // Does the Application or derived actor type require a hover event?
146            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
147   }
148
149   bool DescendActorHierarchy(Actor* actor) override
150   {
151     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
152            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
153   }
154
155   bool DoesLayerConsumeHit(Layer* layer) override
156   {
157     return layer->IsHoverConsumed();
158   }
159
160   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
161   {
162     // Hover event is always hit.
163     return true;
164   }
165 };
166
167 } // unnamed namespace
168
169 HoverEventProcessor::HoverEventProcessor(Scene& scene)
170 : mScene(scene)
171 {
172   DALI_LOG_TRACE_METHOD(gLogFilter);
173 }
174
175 HoverEventProcessor::~HoverEventProcessor()
176 {
177   DALI_LOG_TRACE_METHOD(gLogFilter);
178 }
179
180 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
181 {
182   DALI_LOG_TRACE_METHOD(gLogFilter);
183   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
184
185   PointState::Type state = static_cast<PointState::Type>(event.points[0].GetState());
186
187   PRINT_HIERARCHY(gLogFilter);
188
189   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_HOVER_EVENT");
190
191   // Copy so we can add the results of a hit-test.
192   HoverEventPtr hoverEvent(new HoverEvent(event.time));
193
194   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
195   //    and emit the stage signal as well.
196
197   if(state == PointState::INTERRUPTED)
198   {
199     Dali::Actor        consumingActor;
200     Integration::Point currentPoint(event.points[0]);
201
202     Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
203     if(lastPrimaryHitActor)
204     {
205       Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
206       currentPoint.SetHitActor(lastPrimaryHitActorHandle);
207       consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
208     }
209
210     // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
211     Actor* lastConsumedActor(mLastConsumedActor.GetActor());
212     if(lastConsumedActor &&
213        lastConsumedActor != lastPrimaryHitActor &&
214        lastConsumedActor != consumingActor)
215     {
216       Dali::Actor lastConsumedActorHandle(lastConsumedActor);
217       currentPoint.SetHitActor(lastConsumedActorHandle);
218       AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
219     }
220
221     // Tell the hover-start consuming actor as well, if required
222     Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
223     if(hoverStartConsumedActor &&
224        hoverStartConsumedActor != lastPrimaryHitActor &&
225        hoverStartConsumedActor != lastConsumedActor &&
226        hoverStartConsumedActor != consumingActor)
227     {
228       Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
229       currentPoint.SetHitActor(hoverStartConsumedActorHandle);
230       AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
231     }
232
233     mLastPrimaryHitActor.SetActor(nullptr);
234     mLastConsumedActor.SetActor(nullptr);
235     mHoverStartConsumedActor.SetActor(nullptr);
236     mLastRenderTask.Reset();
237
238     return; // No need for hit testing
239   }
240
241   // 2) Hit Testing.
242
243   Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
244
245   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
246   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
247
248   RenderTaskPtr currentRenderTask;
249   bool          firstPointParsed = false;
250
251   for(auto&& currentPoint : event.points)
252   {
253     HitTestAlgorithm::Results hitTestResults;
254     ActorHoverableCheck       actorHoverableCheck;
255     HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck);
256
257     Integration::Point newPoint(currentPoint);
258     newPoint.SetHitActor(hitTestResults.actor);
259     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
260
261     hoverEvent->AddPoint(newPoint);
262
263     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);
264
265     // Only set the currentRenderTask for the primary hit actor.
266     if(!firstPointParsed)
267     {
268       firstPointParsed  = true;
269       currentRenderTask = hitTestResults.renderTask;
270     }
271   }
272
273   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
274
275   // Emit the touch signal
276   Dali::Actor consumedActor;
277   if(currentRenderTask)
278   {
279     Dali::Actor hitActor = hoverEvent->GetHitActor(0);
280     // If the actor is hit first, the hover is started.
281     if(hitActor &&
282        mLastPrimaryHitActor.GetActor() != hitActor &&
283        state == PointState::MOTION)
284     {
285       Actor* hitActorImpl = &GetImplementation(hitActor);
286       if(hitActorImpl->GetLeaveRequired())
287       {
288         hoverEvent->GetPoint(0).SetState(PointState::STARTED);
289       }
290     }
291     consumedActor = EmitHoverSignals(hitActor, hoverEventHandle);
292   }
293
294   Integration::Point primaryPoint      = hoverEvent->GetPoint(0);
295   Dali::Actor        primaryHitActor   = primaryPoint.GetHitActor();
296   PointState::Type   primaryPointState = primaryPoint.GetState();
297
298   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() : "");
299   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() : "");
300
301   if((primaryPointState == PointState::STARTED) &&
302      (hoverEvent->GetPointCount() == 1) &&
303      (consumedActor && GetImplementation(consumedActor).OnScene()))
304   {
305     mHoverStartConsumedActor.SetActor(&GetImplementation(consumedActor));
306   }
307
308   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
309   //    hit actor.  Also process the last consumed actor in the same manner.
310
311   Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
312   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
313   if((primaryPointState == PointState::STARTED) || (primaryPointState == PointState::MOTION) || (primaryPointState == PointState::FINISHED) || (primaryPointState == PointState::STATIONARY))
314   {
315     if(mLastRenderTask)
316     {
317       Dali::Actor leaveEventConsumer;
318       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
319
320       if(lastPrimaryHitActor &&
321          lastPrimaryHitActor != primaryHitActor &&
322          lastPrimaryHitActor != consumedActor)
323       {
324         if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
325         {
326           if(lastPrimaryHitActor->GetLeaveRequired())
327           {
328             DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
329             leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
330           }
331         }
332         else if(primaryPointState != PointState::STARTED)
333         {
334           // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
335           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
336           DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
337           leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
338         }
339       }
340
341       // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
342       // consumed actor's listeners may need to be informed (through a leave event).
343       // Further checks here to ensure we do not signal the same actor twice for the same event.
344       if(lastConsumedActor &&
345          lastConsumedActor != consumedActor &&
346          lastConsumedActor != lastPrimaryHitActor &&
347          lastConsumedActor != primaryHitActor &&
348          lastConsumedActor != leaveEventConsumer)
349       {
350         if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
351         {
352           if(lastConsumedActor->GetLeaveRequired())
353           {
354             DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
355             EmitHoverSignals(lastConsumedActor, lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
356           }
357         }
358         else if(primaryPointState != PointState::STARTED)
359         {
360           // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
361           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
362           DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Consume):     (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
363           EmitHoverSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
364         }
365       }
366     }
367   }
368
369   // 5) If our primary point is a FINISHED event, then the primary point (in multi-touch) will change next
370   //    time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
371
372   if(primaryPointState == PointState::FINISHED)
373   {
374     mLastPrimaryHitActor.SetActor(nullptr);
375     mLastConsumedActor.SetActor(nullptr);
376     mLastRenderTask.Reset();
377   }
378   else
379   {
380     // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
381     if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
382     {
383       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
384
385       // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
386       if(consumedActor && GetImplementation(consumedActor).OnScene())
387       {
388         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
389       }
390       else
391       {
392         mLastConsumedActor.SetActor(nullptr);
393       }
394
395       mLastRenderTask = currentRenderTask;
396     }
397     else
398     {
399       mLastPrimaryHitActor.SetActor(nullptr);
400       mLastConsumedActor.SetActor(nullptr);
401       mLastRenderTask.Reset();
402     }
403   }
404
405   // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
406
407   if(hoverEvent->GetPointCount() == 1) // Only want the first hover started
408   {
409     switch(primaryPointState)
410     {
411       case PointState::FINISHED:
412       {
413         Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
414         if(hoverStartConsumedActor &&
415            hoverStartConsumedActor != consumedActor &&
416            hoverStartConsumedActor != lastPrimaryHitActor &&
417            hoverStartConsumedActor != lastConsumedActor)
418         {
419           Dali::Actor        hoverStartConsumedActorHandle(hoverStartConsumedActor);
420           Integration::Point primaryPoint = hoverEvent->GetPoint(0);
421           primaryPoint.SetHitActor(hoverStartConsumedActorHandle);
422           primaryPoint.SetState(PointState::INTERRUPTED);
423           AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
424
425           // Restore hover-event to original state
426           primaryPoint.SetHitActor(primaryHitActor);
427           primaryPoint.SetState(primaryPointState);
428         }
429
430         mHoverStartConsumedActor.SetActor(nullptr);
431       }
432         // No break, Fallthrough
433
434       case PointState::STARTED:
435       case PointState::MOTION:
436       case PointState::LEAVE:
437       case PointState::STATIONARY:
438       case PointState::INTERRUPTED:
439       {
440         // Ignore
441         break;
442       }
443     }
444   }
445 }
446
447 } // namespace Internal
448
449 } // namespace Dali