< 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:62
Uncovered lines:1
Coverable lines:63
Total lines:185
Line coverage:98.4% (62 of 63)
Covered branches:0
Total branches:0

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%330100%
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        {
 1623658            if (targetObject == currentHoveredObjectInfo.GameObject)
 59            {
 1620460                ResolveActiveListeners(hit.distance, colliderInfo, typeToUse);
 61
 1620462                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            }
 1623579        }
 80
 81        private void ResolveActiveListeners(float distance, ColliderInfo colliderInfo, Type typeToUse)
 82        {
 1623683            var inactiveListenersCount = currentHoveredObjectInfo.InactiveListeners.Count;
 84
 1623685            var activeListeners = ListPool<IPointerEvent>.Get();
 1623686            activeListeners.AddRange(currentHoveredObjectInfo.ActiveListeners);
 87
 6488088            foreach (var activeListener in activeListeners)
 89            {
 1620490                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
 1623699            ListPool<IPointerEvent>.Release(activeListeners);
 100
 16236101            var index = 0;
 102
 16256103            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
 16236117            bool isEntityShowingHoverFeedback = false;
 118
 119            // Maintain the previous logic to notify about hover state every frame
 64916120            foreach (IPointerEvent activeListener in currentHoveredObjectInfo.ActiveListeners)
 16222121                isEntityShowingHoverFeedback = SetHoverActive(activeListener, isEntityShowingHoverFeedback);
 16236122        }
 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.
 16222130            if (pointerEvent is IPointerInputEvent e && Utils.IsCursorLocked)
 131            {
 16214132                bool eventButtonIsPressed = inputControllerLegacy.IsPressed(e.GetActionButton());
 133
 16214134                switch (e.GetEventType())
 135                {
 2136                    case PointerInputEventType.UP when eventButtonIsPressed:
 2137                    case PointerInputEventType.CLICK when !eventButtonIsPressed:
 16210138                    case PointerInputEventType.DOWN when !eventButtonIsPressed:
 16212139                        e.SetHoverState(true);
 16212140                        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
 16224152            typeToUse.IsInstanceOfType(pointerEvent) &&
 153            pointerEvent.IsAtHoverDistance(distance) &&
 154            pointerEvent.IsVisible() &&
 155            AreSameEntity(pointerEvent, colliderInfo);
 156
 157        private static bool AreSameEntity(IPointerEvent pointerInputEvent, ColliderInfo colliderInfo) =>
 16223158            pointerInputEvent != null && colliderInfo.entity != null &&
 159            pointerInputEvent.entity == colliderInfo.entity;
 160
 161        public void ResetHoveredObject()
 162        {
 1646163            if (currentHoveredObjectInfo.GameObject != null)
 164            {
 20165                foreach (var listener in currentHoveredObjectInfo.ActiveListeners)
 166                {
 167                    // Respect Unity GO lifecycle
 5168                    if (Equals(listener, null))
 169                        continue;
 170
 5171                    listener.SetHoverState(false);
 172                }
 173
 5174                currentHoveredObjectInfo.GameObject = null;
 5175                currentHoveredObjectInfo.ActiveListeners.Clear();
 5176                currentHoveredObjectInfo.InactiveListeners.Clear();
 177
 5178                OnPointerHoverEnds?.Invoke();
 179            }
 180
 1646181            if (hoverCanvas != null)
 1646182                hoverCanvas.SetHoverState(false);
 1646183        }
 184    }
 185}