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