< Summary

Class:DCL.PointerHoverController
Assembly:DCL.Components.Events
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/UUIDEventComponentsPlugin/UUIDComponent/PointerEventsController/PointerHoverController.cs
Covered lines:64
Uncovered lines:1
Coverable lines:65
Total lines:188
Line coverage:98.4% (64 of 65)
Covered branches:0
Total branches:0
Covered methods:7
Total methods:8
Method coverage:87.5% (7 of 8)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
PointerHoverController(...)0%110100%
OnRaycastHit(...)0%550100%
ResolveActiveListeners(...)0%660100%
SetHoverActive(...)0%11110100%
EventObjectCanBeHovered(...)0%440100%
AreSameEntity(...)0%5.024060%
ResetHoveredObject()0%660100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/UUIDEventComponentsPlugin/UUIDComponent/PointerEventsController/PointerHoverController.cs

#LineLine coverage
 1using DCL.Components;
 2using DCL.Helpers;
 3using DCLPlugins.UUIDEventComponentsPlugin.UUIDComponent.Interfaces;
 4using System;
 5using System.Collections.Generic;
 6using UnityEngine;
 7using UnityEngine.Pool;
 8
 9namespace DCL
 10{
 11    public class PointerHoverController
 12    {
 13        /// <summary>
 14        /// There is only one hovered object at a time
 15        /// but it contain listeners with different activation range
 16        /// </summary>
 17        private struct PointerEventInfo
 18        {
 19            /// <summary>
 20            /// The root object hit by raycast
 21            /// </summary>
 22            public GameObject GameObject;
 23
 24            /// <summary>
 25            /// Listeners that were activated when they became within the range.
 26            /// </summary>
 27            public List<IPointerEvent> ActiveListeners;
 28
 29            /// <summary>
 30            /// Listeners that were not active
 31            /// </summary>
 32            public List<IPointerEvent> InactiveListeners;
 33        }
 34
 35        public event Action OnPointerHoverStarts;
 36        public event Action OnPointerHoverEnds;
 37
 8538        private PointerEventInfo currentHoveredObjectInfo = new ()
 39        {
 40            ActiveListeners = new List<IPointerEvent>(),
 41            InactiveListeners = new List<IPointerEvent>()
 42        };
 43
 44        private readonly InputController_Legacy inputControllerLegacy;
 45        private readonly InteractionHoverCanvasController hoverCanvas;
 46
 047        public bool IsObjectHovered => currentHoveredObjectInfo.GameObject;
 48
 8549        public PointerHoverController(InputController_Legacy inputControllerLegacy, InteractionHoverCanvasController hov
 50        {
 8551            this.inputControllerLegacy = inputControllerLegacy;
 8552            this.hoverCanvas = hoverCanvas;
 8553        }
 54
 55        public void OnRaycastHit(RaycastHit hit, ColliderInfo colliderInfo, GameObject targetObject,
 56            Type typeToUse)
 57        {
 652158            if (targetObject == currentHoveredObjectInfo.GameObject)
 59            {
 648960                ResolveActiveListeners(hit.distance, colliderInfo, typeToUse);
 61
 648962                if (currentHoveredObjectInfo.ActiveListeners.Count == 0)
 163                    ResetHoveredObject();
 64            }
 65            else
 66            {
 3267                ResetHoveredObject();
 3268                targetObject.GetComponentsInChildren(currentHoveredObjectInfo.InactiveListeners);
 3269                ResolveActiveListeners(hit.distance, colliderInfo, typeToUse);
 70
 71                // if the object was hovered but no listeners could accept it,
 72                // count it as it was not hovered at all
 3273                if (currentHoveredObjectInfo.ActiveListeners.Count > 0)
 74                {
 1875                    currentHoveredObjectInfo.GameObject = targetObject;
 1876                    OnPointerHoverStarts?.Invoke();
 77                }
 78            }
 652079        }
 80
 81        private void ResolveActiveListeners(float distance, ColliderInfo colliderInfo, Type typeToUse)
 82        {
 652183            var inactiveListenersCount = currentHoveredObjectInfo.InactiveListeners.Count;
 84
 652185            var activeListeners = ListPool<IPointerEvent>.Get();
 652186            activeListeners.AddRange(currentHoveredObjectInfo.ActiveListeners);
 87
 2602088            foreach (var activeListener in activeListeners)
 89            {
 648990                if (!EventObjectCanBeHovered(typeToUse, activeListener, colliderInfo, distance))
 91                {
 92                    // Deactivate Listener
 193                    activeListener.SetHoverState(false);
 194                    currentHoveredObjectInfo.InactiveListeners.Add(activeListener);
 195                    currentHoveredObjectInfo.ActiveListeners.Remove(activeListener);
 96                }
 97            }
 98
 652199            ListPool<IPointerEvent>.Release(activeListeners);
 100
 6521101            var index = 0;
 102
 6541103            while (inactiveListenersCount > 0)
 104            {
 20105                var inactiveListener = currentHoveredObjectInfo.InactiveListeners[index];
 106
 20107                if (EventObjectCanBeHovered(typeToUse, inactiveListener, colliderInfo, distance))
 108                {
 19109                    currentHoveredObjectInfo.ActiveListeners.Add(inactiveListener);
 19110                    currentHoveredObjectInfo.InactiveListeners.RemoveAt(index);
 111                }
 1112                else index++;
 113
 20114                inactiveListenersCount--;
 115            }
 116
 6521117            bool isEntityShowingHoverFeedback = false;
 118
 119            // Maintain the previous logic to notify about hover state every frame
 26056120            foreach (IPointerEvent activeListener in currentHoveredObjectInfo.ActiveListeners)
 6507121                isEntityShowingHoverFeedback = SetHoverActive(activeListener, isEntityShowingHoverFeedback);
 6521122        }
 123
 124        private bool SetHoverActive(IPointerEvent pointerEvent, bool isEntityShowingHoverFeedback)
 125        {
 126            // It's a copy of old logic, it's not entirely clear why it functions this way exactly
 127
 128            // OnPointerDown/OnClick and OnPointerUp should display their hover feedback at different moments
 129            // If cursor is unlocked we ignore the button being pressed, avatars use case.
 6507130            if (pointerEvent is IPointerInputEvent e && Utils.IsCursorLocked)
 131            {
 6499132                bool eventButtonIsPressed = inputControllerLegacy.IsPressed(e.GetActionButton());
 133
 6499134                switch (e.GetEventType())
 135                {
 2136                    case PointerInputEventType.UP when eventButtonIsPressed:
 2137                    case PointerInputEventType.CLICK when !eventButtonIsPressed:
 6495138                    case PointerInputEventType.DOWN when !eventButtonIsPressed:
 6497139                        e.SetHoverState(true);
 6497140                        return isEntityShowingHoverFeedback || e.ShouldShowHoverFeedback();
 141                }
 142
 2143                if (!isEntityShowingHoverFeedback)
 2144                    e.SetHoverState(false);
 145            }
 8146            else pointerEvent.SetHoverState(true);
 147
 10148            return isEntityShowingHoverFeedback;
 149        }
 150
 151        private static bool EventObjectCanBeHovered(Type typeToUse, IPointerEvent pointerEvent, ColliderInfo colliderInf
 6509152            typeToUse.IsInstanceOfType(pointerEvent) &&
 153            pointerEvent.IsAtHoverDistance(distance) &&
 154            pointerEvent.IsVisible() &&
 155            AreSameEntity(pointerEvent, colliderInfo);
 156
 157        private static bool AreSameEntity(IPointerEvent pointerInputEvent, ColliderInfo colliderInfo)
 158        {
 6508159            if (pointerInputEvent == null) return false;
 6508160            if (pointerInputEvent.entity == null && colliderInfo.entity == null) return true;
 6508161            return pointerInputEvent.entity == colliderInfo.entity;
 162        }
 163
 164        public void ResetHoveredObject()
 165        {
 1361166            if (currentHoveredObjectInfo.GameObject != null)
 167            {
 20168                foreach (var listener in currentHoveredObjectInfo.ActiveListeners)
 169                {
 170                    // Respect Unity GO lifecycle
 5171                    if (Equals(listener, null))
 172                        continue;
 173
 5174                    listener.SetHoverState(false);
 175                }
 176
 5177                currentHoveredObjectInfo.GameObject = null;
 5178                currentHoveredObjectInfo.ActiveListeners.Clear();
 5179                currentHoveredObjectInfo.InactiveListeners.Clear();
 180
 5181                OnPointerHoverEnds?.Invoke();
 182            }
 183
 1361184            if (hoverCanvas != null)
 1361185                hoverCanvas.SetHoverState(false);
 1361186        }
 187    }
 188}