[Tizen] Add TOUCH_FOCUSABLE property
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.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/hit-test-algorithm-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/internal/event/actors/actor-impl.h>
24 #include <dali/internal/event/actors/camera-actor-impl.h>
25 #include <dali/internal/event/actors/layer-impl.h>
26 #include <dali/internal/event/actors/layer-list.h>
27 #include <dali/internal/event/common/projection.h>
28 #include <dali/internal/event/events/ray-test.h>
29 #include <dali/internal/event/render-tasks/render-task-impl.h>
30 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
31 #include <dali/public-api/actors/layer.h>
32 #include <dali/public-api/math/vector2.h>
33 #include <dali/public-api/math/vector4.h>
34
35 namespace Dali
36 {
37 namespace Internal
38 {
39 namespace HitTestAlgorithm
40 {
41 namespace
42 {
43 struct HitActor
44 {
45   HitActor()
46   : actor(nullptr),
47     distance(std::numeric_limits<float>::max()),
48     depth(std::numeric_limits<int>::min())
49   {
50   }
51
52   Actor*  actor;       ///< The actor hit (if actor is hit, then this is initialised).
53   Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
54   float   distance;    ///< Distance from ray origin to hit actor.
55   int32_t depth;       ///< Depth index of this actor.
56 };
57
58 /**
59  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
60  */
61 struct HitTestFunctionWrapper : public HitTestInterface
62 {
63   /**
64    * Constructor
65    *
66    * @param[in] func HitTestFunction to call with an Actor handle.
67    */
68   HitTestFunctionWrapper(Dali::HitTestAlgorithm::HitTestFunction func)
69   : mFunc(func)
70   {
71   }
72
73   bool IsActorHittable(Actor* actor) override
74   {
75     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::CHECK_ACTOR);
76   }
77
78   bool DescendActorHierarchy(Actor* actor) override
79   {
80     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE);
81   }
82
83   bool DoesLayerConsumeHit(Layer* layer) override
84   {
85     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
86     // where the caller may want to check for something completely different.
87     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
88     return false;
89   }
90
91   Dali::HitTestAlgorithm::HitTestFunction mFunc;
92 };
93
94 /**
95  * Used in the hit-test algorithm to check whether the actor is touchable.
96  * It is used by the touch event processor.
97  */
98 struct ActorTouchableCheck : public HitTestInterface
99 {
100   bool IsActorHittable(Actor* actor) override
101   {
102     return (actor->GetTouchRequired() || actor->IsTouchFocusable()) && // Does the Application or derived actor type require a touch event? or focusable by touch?
103            actor->IsHittable();                                        // Is actor sensitive, visible and on the scene?
104   }
105
106   bool DescendActorHierarchy(Actor* actor) override
107   {
108     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
109            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
110   }
111
112   bool DoesLayerConsumeHit(Layer* layer) override
113   {
114     return layer->IsTouchConsumed();
115   }
116 };
117
118 /**
119  * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
120  */
121 bool IsActorExclusiveToAnotherRenderTask(const Actor&                               actor,
122                                          const RenderTask&                          renderTask,
123                                          const RenderTaskList::ExclusivesContainer& exclusives)
124
125 {
126   if(exclusives.size())
127   {
128     for(const auto& exclusive : exclusives)
129     {
130       if((exclusive.renderTaskPtr != &renderTask) && (exclusive.actor.GetActor() == &actor))
131       {
132         return true;
133       }
134     }
135   }
136   return false;
137 }
138
139 /**
140  * Recursively hit test all the actors, without crossing into other layers.
141  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
142  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
143  * of touch vector). The closest Hit-Tested Actor is that which is returned.
144  * Exceptions to this rule are:
145  * - When comparing against renderable parents, if Actor is the same distance
146  * or closer than it's renderable parent, then it takes priority.
147  */
148 HitActor HitTestWithinLayer(Actor&                                     actor,
149                             const RenderTask&                          renderTask,
150                             const RenderTaskList::ExclusivesContainer& exclusives,
151                             const Vector4&                             rayOrigin,
152                             const Vector4&                             rayDir,
153                             float&                                     nearClippingPlane,
154                             float&                                     farClippingPlane,
155                             HitTestInterface&                          hitCheck,
156                             bool&                                      overlayHit,
157                             bool                                       layerIs3d,
158                             uint32_t                                   clippingDepth,
159                             uint32_t                                   clippingBitPlaneMask,
160                             const RayTest&                             rayTest)
161 {
162   HitActor hit;
163
164   if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
165   {
166     return hit;
167   }
168
169   // For clipping, regardless of whether we have hit this actor or not,
170   // we increase the clipping depth if we have hit a clipping actor.
171   // This is used later to ensure all nested clipped children have hit
172   // all clipping actors also for them to be counted as hit.
173   uint32_t newClippingDepth = clippingDepth;
174   bool     clippingActor    = actor.GetClippingMode() != ClippingMode::DISABLED;
175   if(clippingActor)
176   {
177     ++newClippingDepth;
178   }
179
180   // If we are a clipping actor or hittable...
181   if(clippingActor || hitCheck.IsActorHittable(&actor))
182   {
183     Vector3 size(actor.GetCurrentSize());
184
185     // Ensure the actor has a valid size.
186     // If so, perform a quick ray sphere test to see if our ray is close to the actor.
187     if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
188     {
189       Vector2 hitPointLocal;
190       float   distance;
191
192       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
193       if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
194       {
195         if(distance >= nearClippingPlane && distance <= farClippingPlane)
196         {
197           // If the hit has happened on a clipping actor, then add this clipping depth to the mask of hit clipping depths.
198           // This mask shows all the actors that have been hit at different clipping depths.
199           if(clippingActor)
200           {
201             clippingBitPlaneMask |= 1u << clippingDepth;
202           }
203
204           if(overlayHit && !actor.IsOverlay())
205           {
206             // If we have already hit an overlay and current actor is not an overlay ignore current actor.
207           }
208           else
209           {
210             if(actor.IsOverlay())
211             {
212               overlayHit = true;
213             }
214
215             // At this point we have hit an actor.
216             // Now perform checks for clipping.
217             // Assume we have hit the actor first as if it is not clipped this would be the case.
218             bool haveHitActor = true;
219
220             // Check if we are performing clipping. IE. if any actors so far have clipping enabled - not necessarily this one.
221             // We can do this by checking the clipping depth has a value 1 or above.
222             if(newClippingDepth >= 1u)
223             {
224               // Now for us to count this actor as hit, we must have also hit
225               // all CLIPPING actors up to this point in the hierarchy as well.
226               // This information is stored in the clippingBitPlaneMask we updated above.
227               // Here we calculate a comparison mask by setting all the bits up to the current depth value.
228               // EG. a depth of 4 (10000 binary) = a mask of 1111 binary.
229               // This allows us a fast way of comparing all bits are set up to this depth.
230               // Note: If the current Actor has clipping, that is included in the depth mask too.
231               uint32_t clippingDepthMask = (1u << newClippingDepth) - 1u;
232
233               // The two masks must be equal to be a hit, as we are already assuming a hit
234               // (for non-clipping mode) then they must be not-equal to disqualify the hit.
235               if(clippingBitPlaneMask != clippingDepthMask)
236               {
237                 haveHitActor = false;
238               }
239             }
240
241             if(haveHitActor)
242             {
243               hit.actor       = &actor;
244               hit.hitPosition = hitPointLocal;
245               hit.distance    = distance;
246               hit.depth       = actor.GetSortingDepth();
247
248               if(actor.GetRendererCount() > 0)
249               {
250                 //Get renderer with maximum depth
251                 int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
252                 for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
253                 {
254                   int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
255                   if(depth > rendererMaxDepth)
256                   {
257                     rendererMaxDepth = depth;
258                   }
259                 }
260                 hit.depth += rendererMaxDepth;
261               }
262             }
263           }
264         }
265       }
266     }
267   }
268
269   // Find a child hit, until we run out of actors in the current layer.
270   HitActor childHit;
271   if(actor.GetChildCount() > 0)
272   {
273     childHit.distance        = std::numeric_limits<float>::max();
274     childHit.depth           = std::numeric_limits<int32_t>::min();
275     ActorContainer& children = actor.GetChildrenInternal();
276
277     // Hit test ALL children and calculate their distance.
278     bool parentIsRenderable = actor.IsRenderable();
279
280     for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
281     {
282       // Descend tree only if...
283       if(!(*iter)->IsLayer() &&                           // Child is NOT a layer, hit testing current layer only
284          (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
285       {
286         HitActor currentHit(HitTestWithinLayer((*iter->Get()),
287                                                renderTask,
288                                                exclusives,
289                                                rayOrigin,
290                                                rayDir,
291                                                nearClippingPlane,
292                                                farClippingPlane,
293                                                hitCheck,
294                                                overlayHit,
295                                                layerIs3d,
296                                                newClippingDepth,
297                                                clippingBitPlaneMask,
298                                                rayTest));
299
300         // Make sure the set hit actor is actually hittable. This is usually required when we have some
301         // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
302         // are hittable or not.
303         if(currentHit.actor && !hitCheck.IsActorHittable(currentHit.actor))
304         {
305           continue;
306         }
307
308         bool updateChildHit = false;
309         if(currentHit.distance >= 0.0f)
310         {
311           if(layerIs3d)
312           {
313             updateChildHit = ((currentHit.depth > childHit.depth) ||
314                               ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
315           }
316           else
317           {
318             updateChildHit = currentHit.depth >= childHit.depth;
319           }
320         }
321
322         if(updateChildHit)
323         {
324           if(!parentIsRenderable || currentHit.depth > hit.depth ||
325              (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
326           {
327             childHit = currentHit;
328           }
329         }
330       }
331     }
332   }
333
334   return (childHit.actor) ? childHit : hit;
335 }
336
337 /**
338  * Return true if actor is sourceActor or a descendent of sourceActor
339  */
340 bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
341 {
342   if(&sourceActor == &actor)
343   {
344     return true;
345   }
346
347   Actor* parent = actor.GetParent();
348   if(parent)
349   {
350     return IsWithinSourceActors(sourceActor, *parent);
351   }
352
353   // Not within source actors
354   return false;
355 }
356
357 /**
358  * Returns true if the layer and all of the layer's parents are visible and sensitive.
359  */
360 inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
361 {
362   bool hittable(true);
363
364   if(layer.IsClipping())
365   {
366     ClippingBox box = layer.GetClippingBox();
367
368     if(screenCoordinates.x < static_cast<float>(box.x) ||
369        screenCoordinates.x > static_cast<float>(box.x + box.width) ||
370        screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
371        screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
372     {
373       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
374       hittable = false;
375     }
376   }
377
378   if(hittable)
379   {
380     Actor* actor(&layer);
381
382     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
383     while(actor && hittable)
384     {
385       if(!hitCheck.DescendActorHierarchy(actor))
386       {
387         hittable = false;
388         break;
389       }
390       actor = actor->GetParent();
391     }
392   }
393
394   return hittable;
395 }
396
397 /**
398  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
399  */
400 void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
401 {
402   CameraActor* cameraActor = renderTask.GetCameraActor();
403   nearClippingPlane        = cameraActor->GetNearClippingPlane();
404   farClippingPlane         = cameraActor->GetFarClippingPlane();
405 }
406
407 /**
408  * Hit test a RenderTask
409  */
410 bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
411                        const Vector2&                             sceneSize,
412                        LayerList&                                 layers,
413                        RenderTask&                                renderTask,
414                        Vector2                                    screenCoordinates,
415                        Results&                                   results,
416                        HitTestInterface&                          hitCheck,
417                        const RayTest&                             rayTest)
418 {
419   if(renderTask.IsHittable(screenCoordinates))
420   {
421     Viewport viewport;
422     renderTask.GetViewport(viewport);
423     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
424        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
425        screenCoordinates.y < static_cast<float>(viewport.y) ||
426        screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
427     {
428       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
429       return false;
430     }
431
432     float nearClippingPlane, farClippingPlane;
433     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
434
435     // Determine the layer depth of the source actor
436     Actor* sourceActor(renderTask.GetSourceActor());
437     if(sourceActor)
438     {
439       Dali::Layer layer(sourceActor->GetLayer());
440       if(layer)
441       {
442         const uint32_t sourceActorDepth(layer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
443
444         CameraActor* cameraActor     = renderTask.GetCameraActor();
445         bool         pickingPossible = cameraActor->BuildPickingRay(
446           screenCoordinates,
447           viewport,
448           results.rayOrigin,
449           results.rayDirection);
450         if(!pickingPossible)
451         {
452           return false;
453         }
454
455         // Hit test starting with the top layer, working towards the bottom layer.
456         HitActor hit;
457         bool     overlayHit       = false;
458         bool     layerConsumesHit = false;
459
460         for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
461         {
462           Layer* layer(layers.GetLayer(i));
463           overlayHit = false;
464
465           // Ensure layer is touchable (also checks whether ancestors are also touchable)
466           if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
467           {
468             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
469             if(sourceActorDepth == static_cast<uint32_t>(i))
470             {
471               // Recursively hit test the source actor & children, without crossing into other layers.
472               hit = HitTestWithinLayer(*sourceActor,
473                                        renderTask,
474                                        exclusives,
475                                        results.rayOrigin,
476                                        results.rayDirection,
477                                        nearClippingPlane,
478                                        farClippingPlane,
479                                        hitCheck,
480                                        overlayHit,
481                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
482                                        0u,
483                                        0u,
484                                        rayTest);
485             }
486             else if(IsWithinSourceActors(*sourceActor, *layer))
487             {
488               // Recursively hit test all the actors, without crossing into other layers.
489               hit = HitTestWithinLayer(*layer,
490                                        renderTask,
491                                        exclusives,
492                                        results.rayOrigin,
493                                        results.rayDirection,
494                                        nearClippingPlane,
495                                        farClippingPlane,
496                                        hitCheck,
497                                        overlayHit,
498                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
499                                        0u,
500                                        0u,
501                                        rayTest);
502             }
503
504             // If this layer is set to consume the hit, then do not check any layers behind it
505             if(hitCheck.DoesLayerConsumeHit(layer))
506             {
507               layerConsumesHit = true;
508               break;
509             }
510           }
511         }
512
513         if(hit.actor)
514         {
515           results.renderTask       = RenderTaskPtr(&renderTask);
516           results.actor            = Dali::Actor(hit.actor);
517           results.actorCoordinates = hit.hitPosition;
518
519           return true; // Success
520         }
521
522         if(layerConsumesHit)
523         {
524           return true; // Also success if layer is consuming the hit
525         }
526       }
527     }
528   }
529   return false;
530 }
531
532 /**
533  * Iterate through the RenderTaskList and perform hit testing.
534  *
535  * @param[in] sceneSize The scene size the tests will be performed in
536  * @param[in] layers The list of layers to test
537  * @param[in] taskList The list of render tasks
538  * @param[out] results Ray information calculated by the camera
539  * @param[in] hitCheck The hit testing interface object to use
540  * @param[in] onScreen True to test on-screen, false to test off-screen
541  * @return True if we have a hit, false otherwise
542  */
543 bool HitTestRenderTaskList(const Vector2&    sceneSize,
544                            LayerList&        layers,
545                            RenderTaskList&   taskList,
546                            const Vector2&    screenCoordinates,
547                            Results&          results,
548                            HitTestInterface& hitCheck,
549                            bool              onScreen)
550 {
551   RenderTaskList::RenderTaskContainer&                  tasks      = taskList.GetTasks();
552   RenderTaskList::RenderTaskContainer::reverse_iterator endIter    = tasks.rend();
553   const auto&                                           exclusives = taskList.GetExclusivesList();
554   RayTest                                               rayTest;
555
556   for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
557   {
558     RenderTask& renderTask            = *iter->Get();
559     const bool  isOffscreenRenderTask = renderTask.GetFrameBuffer();
560     if((onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask))
561     {
562       // Skip to next task
563       continue;
564     }
565
566     if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
567     {
568       // Return true when an actor is hit (or layer in our render-task consumes the hit)
569       return true; // don't bother checking off screen tasks
570     }
571   }
572
573   return false;
574 }
575
576 /**
577  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
578  *
579  * @param[in] sceneSize The scene size the tests will be performed in
580  * @param[in] layers The list of layers to test
581  * @param[in] taskList The list of render tasks
582  * @param[out] results Ray information calculated by the camera
583  * @param[in] hitCheck The hit testing interface object to use
584  * @param[in] onScreen True to test on-screen, false to test off-screen
585  * @return True if we have a hit, false otherwise
586  */
587 bool HitTestForEachRenderTask(const Vector2&    sceneSize,
588                               LayerList&        layers,
589                               RenderTaskList&   taskList,
590                               const Vector2&    screenCoordinates,
591                               Results&          results,
592                               HitTestInterface& hitCheck)
593 {
594   bool result = false;
595
596   // Check on-screen tasks before off-screen ones.
597   // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
598   if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, true) ||
599      HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, false))
600   {
601     // Found hit.
602     result = true;
603   }
604
605   return result;
606 }
607
608 } // unnamed namespace
609
610 HitTestInterface::~HitTestInterface() = default;
611
612 bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func)
613 {
614   bool wasHit(false);
615   // Hit-test the regular on-scene actors
616   Results                hitTestResults;
617   HitTestFunctionWrapper hitTestFunctionWrapper(func);
618   if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper))
619   {
620     results.actor            = hitTestResults.actor;
621     results.actorCoordinates = hitTestResults.actorCoordinates;
622     wasHit                   = true;
623   }
624   return wasHit;
625 }
626
627 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface)
628 {
629   bool wasHit(false);
630
631   // Hit-test the regular on-scene actors
632   if(!wasHit)
633   {
634     wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface);
635   }
636   return wasHit;
637 }
638
639 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results)
640 {
641   ActorTouchableCheck actorTouchableCheck;
642   return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck);
643 }
644
645 } // namespace HitTestAlgorithm
646
647 } // namespace Internal
648
649 } // namespace Dali