[Tizen] Not execute the remove callback
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hover-event-processor.cpp
1 /*
2  * Copyright (c) 2021 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
158 } // unnamed namespace
159
160 HoverEventProcessor::HoverEventProcessor(Scene& scene)
161 : mScene(scene)
162 {
163   DALI_LOG_TRACE_METHOD(gLogFilter);
164 }
165
166 HoverEventProcessor::~HoverEventProcessor()
167 {
168   DALI_LOG_TRACE_METHOD(gLogFilter);
169 }
170
171 void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event)
172 {
173   DALI_LOG_TRACE_METHOD(gLogFilter);
174   DALI_ASSERT_ALWAYS(!event.points.empty() && "Empty HoverEvent sent from Integration\n");
175
176   PointState::Type state = static_cast<PointState::Type>(event.points[0].GetState());
177
178   PRINT_HIERARCHY(gLogFilter);
179
180   // Copy so we can add the results of a hit-test.
181   HoverEventPtr hoverEvent(new HoverEvent(event.time));
182
183   // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this
184   //    and emit the stage signal as well.
185
186   if(state == PointState::INTERRUPTED)
187   {
188     Dali::Actor        consumingActor;
189     Integration::Point currentPoint(event.points[0]);
190
191     Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
192     if(lastPrimaryHitActor)
193     {
194       Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor);
195       currentPoint.SetHitActor(lastPrimaryHitActorHandle);
196       consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint);
197     }
198
199     // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed).
200     Actor* lastConsumedActor(mLastConsumedActor.GetActor());
201     if(lastConsumedActor &&
202        lastConsumedActor != lastPrimaryHitActor &&
203        lastConsumedActor != consumingActor)
204     {
205       Dali::Actor lastConsumedActorHandle(lastConsumedActor);
206       currentPoint.SetHitActor(lastConsumedActorHandle);
207       AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint);
208     }
209
210     // Tell the hover-start consuming actor as well, if required
211     Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
212     if(hoverStartConsumedActor &&
213        hoverStartConsumedActor != lastPrimaryHitActor &&
214        hoverStartConsumedActor != lastConsumedActor &&
215        hoverStartConsumedActor != consumingActor)
216     {
217       Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor);
218       currentPoint.SetHitActor(hoverStartConsumedActorHandle);
219       AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint);
220     }
221
222     mLastPrimaryHitActor.SetActor(nullptr);
223     mLastConsumedActor.SetActor(nullptr);
224     mHoverStartConsumedActor.SetActor(nullptr);
225     mLastRenderTask.Reset();
226
227     return; // No need for hit testing
228   }
229
230   // 2) Hit Testing.
231
232   Dali::HoverEvent hoverEventHandle(hoverEvent.Get());
233
234   DALI_LOG_INFO(gLogFilter, Debug::Concise, "\n");
235   DALI_LOG_INFO(gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount());
236
237   RenderTaskPtr currentRenderTask;
238   bool          firstPointParsed = false;
239
240   for(auto&& currentPoint : event.points)
241   {
242     HitTestAlgorithm::Results hitTestResults;
243     ActorHoverableCheck       actorHoverableCheck;
244     HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck);
245
246     Integration::Point newPoint(currentPoint);
247     newPoint.SetHitActor(hitTestResults.actor);
248     newPoint.SetLocalPosition(hitTestResults.actorCoordinates);
249
250     hoverEvent->AddPoint(newPoint);
251
252     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);
253
254     // Only set the currentRenderTask for the primary hit actor.
255     if(!firstPointParsed)
256     {
257       firstPointParsed  = true;
258       currentRenderTask = hitTestResults.renderTask;
259     }
260   }
261
262   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
263
264   // Emit the touch signal
265   Dali::Actor consumedActor;
266   if(currentRenderTask)
267   {
268     consumedActor = EmitHoverSignals(hoverEvent->GetHitActor(0), hoverEventHandle);
269   }
270
271   Integration::Point primaryPoint      = hoverEvent->GetPoint(0);
272   Dali::Actor        primaryHitActor   = primaryPoint.GetHitActor();
273   PointState::Type   primaryPointState = primaryPoint.GetState();
274
275   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() : "");
276   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() : "");
277
278   if((primaryPointState == PointState::STARTED) &&
279      (hoverEvent->GetPointCount() == 1) &&
280      (consumedActor && GetImplementation(consumedActor).OnScene()))
281   {
282     mHoverStartConsumedActor.SetActor(&GetImplementation(consumedActor));
283   }
284
285   // 4) Check if the last primary hit actor requires a leave event and if it was different to the current primary
286   //    hit actor.  Also process the last consumed actor in the same manner.
287
288   Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor());
289   Actor* lastConsumedActor(mLastConsumedActor.GetActor());
290   if((primaryPointState == PointState::MOTION) || (primaryPointState == PointState::FINISHED) || (primaryPointState == PointState::STATIONARY))
291   {
292     if(mLastRenderTask)
293     {
294       Dali::Actor leaveEventConsumer;
295       RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get();
296
297       if(lastPrimaryHitActor &&
298          lastPrimaryHitActor != primaryHitActor &&
299          lastPrimaryHitActor != consumedActor)
300       {
301         if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor))
302         {
303           if(lastPrimaryHitActor->GetLeaveRequired())
304           {
305             DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
306             leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
307           }
308         }
309         else
310         {
311           // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one.
312           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
313           DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Hit):     (%p) %s\n", reinterpret_cast<void*>(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data());
314           leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
315         }
316       }
317
318       // Check if the motion event has been consumed by another actor's listener.  In this case, the previously
319       // consumed actor's listeners may need to be informed (through a leave event).
320       // Further checks here to ensure we do not signal the same actor twice for the same event.
321       if(lastConsumedActor &&
322          lastConsumedActor != consumedActor &&
323          lastConsumedActor != lastPrimaryHitActor &&
324          lastConsumedActor != primaryHitActor &&
325          lastConsumedActor != leaveEventConsumer)
326       {
327         if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor))
328         {
329           if(lastConsumedActor->GetLeaveRequired())
330           {
331             DALI_LOG_INFO(gLogFilter, Debug::Concise, "LeaveActor(Consume): (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
332             EmitHoverSignals(lastConsumedActor, lastRenderTaskImpl, hoverEvent, PointState::LEAVE);
333           }
334         }
335         else
336         {
337           // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one.
338           // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls)
339           DALI_LOG_INFO(gLogFilter, Debug::Concise, "InterruptedActor(Consume):     (%p) %s\n", reinterpret_cast<void*>(lastConsumedActor), lastConsumedActor->GetName().data());
340           EmitHoverSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED);
341         }
342       }
343     }
344   }
345
346   // 5) If our primary point is a FINISHED event, then the primary point (in multi-touch) will change next
347   //    time so set our last primary actor to NULL.  Do the same to the last consumed actor as well.
348
349   if(primaryPointState == PointState::FINISHED)
350   {
351     mLastPrimaryHitActor.SetActor(nullptr);
352     mLastConsumedActor.SetActor(nullptr);
353     mLastRenderTask.Reset();
354   }
355   else
356   {
357     // The primaryHitActor may have been removed from the scene so ensure it is still on the scene before setting members.
358     if(primaryHitActor && GetImplementation(primaryHitActor).OnScene())
359     {
360       mLastPrimaryHitActor.SetActor(&GetImplementation(primaryHitActor));
361
362       // Only observe the consumed actor if we have a primaryHitActor (check if it is still on the scene).
363       if(consumedActor && GetImplementation(consumedActor).OnScene())
364       {
365         mLastConsumedActor.SetActor(&GetImplementation(consumedActor));
366       }
367       else
368       {
369         mLastConsumedActor.SetActor(nullptr);
370       }
371
372       mLastRenderTask = currentRenderTask;
373     }
374     else
375     {
376       mLastPrimaryHitActor.SetActor(nullptr);
377       mLastConsumedActor.SetActor(nullptr);
378       mLastRenderTask.Reset();
379     }
380   }
381
382   // 6) Emit an interrupted event to the hover-started actor if it hasn't consumed the FINISHED.
383
384   if(hoverEvent->GetPointCount() == 1) // Only want the first hover started
385   {
386     switch(primaryPointState)
387     {
388       case PointState::FINISHED:
389       {
390         Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor());
391         if(hoverStartConsumedActor &&
392            hoverStartConsumedActor != consumedActor &&
393            hoverStartConsumedActor != lastPrimaryHitActor &&
394            hoverStartConsumedActor != lastConsumedActor)
395         {
396           Dali::Actor        hoverStartConsumedActorHandle(hoverStartConsumedActor);
397           Integration::Point primaryPoint = hoverEvent->GetPoint(0);
398           primaryPoint.SetHitActor(hoverStartConsumedActorHandle);
399           primaryPoint.SetState(PointState::INTERRUPTED);
400           AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint);
401
402           // Restore hover-event to original state
403           primaryPoint.SetHitActor(primaryHitActor);
404           primaryPoint.SetState(primaryPointState);
405         }
406
407         mHoverStartConsumedActor.SetActor(nullptr);
408       }
409         // No break, Fallthrough
410
411       case PointState::STARTED:
412       case PointState::MOTION:
413       case PointState::LEAVE:
414       case PointState::STATIONARY:
415       case PointState::INTERRUPTED:
416       {
417         // Ignore
418         break;
419       }
420     }
421   }
422 }
423
424 } // namespace Internal
425
426 } // namespace Dali