< Summary

Class:ECSSystems.PointerInputSystem.ECSPointerInputSystem
Assembly:ECS7Plugin.Systems.PointerInput
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/Systems/PointerInputSystem/ECSPointerInputSystem.cs
Covered lines:113
Uncovered lines:4
Coverable lines:117
Total lines:370
Line coverage:96.5% (113 of 117)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
CreateSystem(...)0%110100%
Update(...)0%56.0556097.47%
GetEntityWithCollider(...)0%3.023087.5%
GetPointerEventsForEntity(...)0%220100%
ShowPointerDownHover(...)0%110100%
ShowPointerUpHover(...)0%110100%
ShowHoverTooltips(...)0%77096%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/Systems/PointerInputSystem/ECSPointerInputSystem.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using DCL;
 4using DCL.Controllers;
 5using DCL.ECS7.InternalComponents;
 6using DCL.ECSComponents;
 7using DCL.ECSRuntime;
 8using DCL.Models;
 9using UnityEngine;
 10using Ray = UnityEngine.Ray;
 11
 12namespace ECSSystems.PointerInputSystem
 13{
 14    public static class ECSPointerInputSystem
 15    {
 16        private class State
 17        {
 18            public IInternalECSComponent<InternalColliders> pointerColliderComponent;
 19            public IInternalECSComponent<InternalInputEventResults> inputResultComponent;
 20            public ECSComponent<PBPointerEvents> pointerEvents;
 21            public DataStore_ECS7 dataStoreEcs7;
 22            public bool isLastInputPointerDown;
 23            public IWorldState worldState;
 24            public EntityInput lastInputDown;
 25            public EntityInput lastInputHover;
 26            public EntityInput lastHoverFeedback;
 27            public IECSInteractionHoverCanvas interactionHoverCanvas;
 28        }
 29
 30        private class EntityInput
 31        {
 32            public IDCLEntity entity;
 33            public IParcelScene scene;
 34            public string sceneId;
 35            public bool hasValue;
 36        }
 37
 38        private enum InputHitType
 39        {
 40            None,
 41            PointerDown,
 42            PointerUp,
 43            PointerHover
 44        }
 45
 46        public static Action CreateSystem(
 47            IInternalECSComponent<InternalColliders> pointerColliderComponent,
 48            IInternalECSComponent<InternalInputEventResults> inputResultComponent,
 49            ECSComponent<PBPointerEvents> pointerEvents,
 50            IECSInteractionHoverCanvas interactionHoverCanvas,
 51            IWorldState worldState,
 52            DataStore_ECS7 dataStoreEcs)
 53        {
 1454            var state = new State()
 55            {
 56                pointerColliderComponent = pointerColliderComponent,
 57                inputResultComponent = inputResultComponent,
 58                pointerEvents = pointerEvents,
 59                interactionHoverCanvas = interactionHoverCanvas,
 60                isLastInputPointerDown = false,
 61                dataStoreEcs7 = dataStoreEcs,
 62                worldState = worldState,
 63                lastInputDown = new EntityInput() { hasValue = false },
 64                lastInputHover = new EntityInput() { hasValue = false },
 65                lastHoverFeedback = new EntityInput() { hasValue = false },
 66            };
 3567            return () => Update(state);
 68        }
 69
 70        private static void Update(State state)
 71        {
 2172            DataStore_ECS7.PointerEvent currentPointerInput = state.dataStoreEcs7.lastPointerInputEvent;
 73
 2174            bool isHit = state.dataStoreEcs7.lastPointerRayHit.hasValue && state.dataStoreEcs7.lastPointerRayHit.didHit;
 2175            bool isPointerDown = currentPointerInput.hasValue && currentPointerInput.isButtonDown;
 2176            bool isPointerUp = state.isLastInputPointerDown && currentPointerInput.hasValue && !currentPointerInput.isBu
 77
 2178            DataStore_ECS7.RaycastEvent.Hit raycastHit = state.dataStoreEcs7.lastPointerRayHit.hit;
 2179            Ray raycastRay = state.dataStoreEcs7.lastPointerRayHit.ray;
 80
 2181            IECSReadOnlyComponentData<InternalColliders> colliderData = isHit
 82                ? state.pointerColliderComponent
 83                       .GetEntityWithCollider(raycastHit.collider)
 84                : null;
 85
 2186            bool isRaycastHitValidEntity = colliderData != null;
 2187            bool isHoveringInput = !isPointerDown && !isPointerUp && !state.isLastInputPointerDown;
 2188            bool isHoveringExit = !isRaycastHitValidEntity && state.lastInputHover.hasValue;
 89
 2190            IList<PBPointerEvents.Types.Entry> hoverEvents = isRaycastHitValidEntity
 91                ? state.pointerEvents.GetPointerEventsForEntity(colliderData.scene, colliderData.entity)
 92                : null;
 93
 94            // show hover tooltip for pointer down
 2195            if (hoverEvents != null && isHoveringInput)
 96            {
 397                if (!state.lastHoverFeedback.hasValue || state.lastHoverFeedback.entity != colliderData.entity)
 98                {
 399                    state.interactionHoverCanvas.ShowPointerDownHover(hoverEvents, raycastHit.distance);
 3100                    state.lastHoverFeedback.hasValue = true;
 3101                    state.lastHoverFeedback.entity = colliderData.entity;
 3102                    state.lastHoverFeedback.scene = colliderData.scene;
 3103                    state.lastHoverFeedback.sceneId = colliderData.scene.sceneData.id;
 104                }
 3105            }
 106            // show hover tooltip for pointer up
 18107            else if (hoverEvents != null && state.isLastInputPointerDown && !isPointerUp)
 108            {
 3109                if (!state.lastHoverFeedback.hasValue && state.lastInputDown.entity == colliderData.entity)
 110                {
 3111                    state.interactionHoverCanvas.ShowPointerUpHover(hoverEvents, raycastHit.distance, (ActionButton)curr
 3112                    state.lastHoverFeedback.hasValue = true;
 3113                    state.lastHoverFeedback.entity = colliderData.entity;
 3114                    state.lastHoverFeedback.scene = colliderData.scene;
 3115                    state.lastHoverFeedback.sceneId = colliderData.scene.sceneData.id;
 116                }
 3117            }
 118            // hide hover tooltip
 15119            else if (state.lastHoverFeedback.hasValue)
 120            {
 0121                state.interactionHoverCanvas.Hide();
 0122                state.lastHoverFeedback.hasValue = false;
 123            }
 124
 21125            InputHitType inputHitType = InputHitType.None;
 126
 21127            if (isRaycastHitValidEntity)
 128            {
 19129                inputHitType = isPointerDown ? InputHitType.PointerDown
 130                    : isPointerUp ? InputHitType.PointerUp
 131                    : isHoveringInput ? InputHitType.PointerHover
 132                    : InputHitType.None;
 133            }
 134
 135            // process entity hit with input
 136            switch (inputHitType)
 137            {
 138                case InputHitType.PointerDown:
 7139                    state.lastInputDown.entity = colliderData.entity;
 7140                    state.lastInputDown.scene = colliderData.scene;
 7141                    state.lastInputDown.sceneId = colliderData.scene.sceneData.id;
 7142                    state.lastInputDown.hasValue = true;
 143
 7144                    state.inputResultComponent.AddEvent(colliderData.scene, new InternalInputEventResults.EventData()
 145                    {
 146                        analog = 1,
 147                        button = (ActionButton)currentPointerInput.buttonId,
 148                        hit = ProtoConvertUtils.ToPBRaycasHit(colliderData.entity.entityId, null,
 149                            raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal),
 150                        type = PointerEventType.Down
 151                    });
 7152                    break;
 153
 154                case InputHitType.PointerUp:
 2155                    bool validInputDownExist = state.lastInputDown.hasValue;
 2156                    EntityInput lastInputDownData = state.lastInputDown;
 157
 158                    // did it hit same entity as pointer down hit?
 2159                    if (validInputDownExist && colliderData.entity.entityId == lastInputDownData.entity.entityId
 160                                            && colliderData.scene.sceneData.id == lastInputDownData.sceneId)
 161                    {
 1162                        state.inputResultComponent.AddEvent(colliderData.scene, new InternalInputEventResults.EventData(
 163                        {
 164                            analog = 1,
 165                            button = (ActionButton)currentPointerInput.buttonId,
 166                            hit = ProtoConvertUtils.ToPBRaycasHit(colliderData.entity.entityId, null,
 167                                raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal),
 168                            type = PointerEventType.Up
 169                        });
 1170                    }
 171                    // did it hit different entity as pointer down hit?
 1172                    else if (validInputDownExist)
 173                    {
 1174                        bool isEntityFromSameScene = colliderData.scene.sceneData.id == lastInputDownData.sceneId;
 1175                        bool isValidScene = isEntityFromSameScene || state.worldState.ContainsScene(lastInputDownData.sc
 176
 1177                        if (isValidScene)
 178                        {
 1179                            state.inputResultComponent.AddEvent(lastInputDownData.scene, new InternalInputEventResults.E
 180                            {
 181                                analog = 1,
 182                                button = (ActionButton)currentPointerInput.buttonId,
 183                                hit = ProtoConvertUtils.ToPBRaycasHit(-1, null,
 184                                    raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal, false),
 185                                type = PointerEventType.Up
 186                            });
 187                        }
 188                    }
 2189                    state.lastInputDown.hasValue = false;
 2190                    break;
 191
 192                case InputHitType.PointerHover:
 7193                    bool isPreviouslyHoveredEntity = state.lastInputHover.hasValue;
 7194                    bool isHoveringNewEntity = !isPreviouslyHoveredEntity
 195                                               || state.lastInputHover.entity.entityId != colliderData.entity.entityId
 196                                               || state.lastInputHover.sceneId != colliderData.scene.sceneData.id;
 197
 198                    // was other entity previously hovered?
 7199                    if (isPreviouslyHoveredEntity && isHoveringNewEntity)
 200                    {
 1201                        bool isValidScene = colliderData.scene.sceneData.id == state.lastInputHover.sceneId
 202                                            || state.worldState.ContainsScene(state.lastInputHover.sceneId);
 203
 1204                        if (isValidScene)
 205                        {
 1206                            state.inputResultComponent.AddEvent(state.lastInputHover.scene, new InternalInputEventResult
 207                            {
 208                                analog = 1,
 209                                button = (ActionButton)currentPointerInput.buttonId,
 210                                hit = ProtoConvertUtils.ToPBRaycasHit(state.lastInputHover.entity.entityId, null,
 211                                    raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal),
 212                                type = PointerEventType.HoverLeave
 213                            });
 214                        }
 215                    }
 216
 217                    // hover enter
 7218                    if (isHoveringNewEntity)
 219                    {
 7220                        state.lastInputHover.entity = colliderData.entity;
 7221                        state.lastInputHover.scene = colliderData.scene;
 7222                        state.lastInputHover.sceneId = colliderData.scene.sceneData.id;
 7223                        state.lastInputHover.hasValue = true;
 224
 7225                        state.inputResultComponent.AddEvent(colliderData.scene, new InternalInputEventResults.EventData(
 226                        {
 227                            analog = 1,
 228                            button = (ActionButton)currentPointerInput.buttonId,
 229                            hit = ProtoConvertUtils.ToPBRaycasHit(colliderData.entity.entityId, null,
 230                                raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal),
 231                            type = PointerEventType.HoverEnter
 232                        });
 233                    }
 234                    break;
 235            }
 236
 237            // no entity hit
 21238            if (!isRaycastHitValidEntity)
 239            {
 2240                if (isPointerUp)
 241                {
 1242                    if (state.lastInputDown.hasValue)
 243                    {
 244                        // input up without hit but with valid input down
 1245                        bool isValidScene = state.worldState.ContainsScene(state.lastInputDown.sceneId);
 1246                        if (isValidScene)
 247                        {
 1248                            state.inputResultComponent.AddEvent(state.lastInputDown.scene, new InternalInputEventResults
 249                            {
 250                                analog = 1,
 251                                button = (ActionButton)currentPointerInput.buttonId,
 252                                hit = ProtoConvertUtils.ToPBRaycasHit(-1, null,
 253                                    raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal, false),
 254                                type = PointerEventType.Up
 255                            });
 256                        }
 257                    }
 258
 1259                    state.lastInputDown.hasValue = false;
 260                }
 2261                if (isHoveringExit)
 262                {
 1263                    bool isValidScene = state.worldState.ContainsScene(state.lastInputHover.sceneId);
 264
 1265                    if (isValidScene)
 266                    {
 1267                        state.inputResultComponent.AddEvent(state.lastInputHover.scene, new InternalInputEventResults.Ev
 268                        {
 269                            analog = 1,
 270                            button = (ActionButton)currentPointerInput.buttonId,
 271                            hit = ProtoConvertUtils.ToPBRaycasHit(state.lastInputHover.entity.entityId, null,
 272                                raycastRay, raycastHit.distance, raycastHit.point, raycastHit.normal),
 273                            type = PointerEventType.HoverLeave
 274                        });
 275                    }
 1276                    state.lastInputHover.hasValue = false;
 277                }
 278            }
 279
 21280            state.dataStoreEcs7.lastPointerInputEvent.hasValue = false;
 21281            state.dataStoreEcs7.lastPointerRayHit.hasValue = false;
 21282            state.isLastInputPointerDown = isPointerDown || (!isPointerUp && state.isLastInputPointerDown);
 21283        }
 284
 285        private static IECSReadOnlyComponentData<InternalColliders> GetEntityWithCollider(
 286            this IInternalECSComponent<InternalColliders> pointerColliderComponent,
 287            Collider collider)
 288        {
 19289            var collidersData = pointerColliderComponent.GetForAll();
 42290            for (int i = 0; i < collidersData.Count; i++)
 291            {
 21292                var colliderData = collidersData[i].value;
 21293                if (colliderData.model.colliders.Contains(collider))
 294                {
 19295                    return colliderData;
 296                }
 297            }
 0298            return null;
 299        }
 300
 301        private static IList<PBPointerEvents.Types.Entry> GetPointerEventsForEntity(this ECSComponent<PBPointerEvents> c
 302            IParcelScene scene, IDCLEntity entity)
 303        {
 19304            var componentData = component.Get(scene, entity);
 19305            return componentData?.model.PointerEvents;
 306        }
 307
 308        private static void ShowPointerDownHover(this IECSInteractionHoverCanvas canvas,
 309            IList<PBPointerEvents.Types.Entry> entityEvents, float distance)
 310        {
 3311            canvas.ShowHoverTooltips(entityEvents, (pointerEvent) =>
 4312                pointerEvent.EventType == PointerEventType.Down
 313                && pointerEvent.EventInfo.GetShowFeedback()
 314                && distance <= pointerEvent.EventInfo.GetMaxDistance()
 315            );
 3316        }
 317
 318        private static void ShowPointerUpHover(this IECSInteractionHoverCanvas canvas,
 319            IList<PBPointerEvents.Types.Entry> entityEvents, float distance, ActionButton expectedButton)
 320        {
 3321            canvas.ShowHoverTooltips(entityEvents, (pointerEvent) =>
 3322                pointerEvent.EventType == PointerEventType.Up
 323                && pointerEvent.EventInfo.GetShowFeedback()
 324                && distance <= pointerEvent.EventInfo.GetMaxDistance()
 325                && (pointerEvent.EventInfo.GetButton() == expectedButton || pointerEvent.EventInfo.GetButton() == Action
 326            );
 3327        }
 328
 329        private static void ShowHoverTooltips(this IECSInteractionHoverCanvas canvas,
 330            IList<PBPointerEvents.Types.Entry> entityEvents, Func<PBPointerEvents.Types.Entry, bool> filter)
 331        {
 6332            if (entityEvents is null)
 0333                return;
 334
 6335            bool anyTooltipAdded = false;
 6336            int eventIndex = 0;
 36337            for (int i = 0; i < canvas.tooltipsCount; i++)
 338            {
 12339                PBPointerEvents.Types.Entry pointerEvent = null;
 18340                for (; eventIndex < entityEvents.Count; eventIndex++)
 341                {
 7342                    pointerEvent = entityEvents[eventIndex];
 7343                    if (filter(pointerEvent))
 344                    {
 4345                        eventIndex++;
 4346                        break;
 347                    }
 3348                    pointerEvent = null;
 349                }
 350
 12351                if (!(pointerEvent is null))
 352                {
 4353                    anyTooltipAdded = true;
 4354                    canvas.SetTooltipText(i, pointerEvent.EventInfo.GetHoverText());
 4355                    canvas.SetTooltipInput(i, pointerEvent.EventInfo.GetButton());
 4356                    canvas.SetTooltipActive(i, true);
 4357                }
 358                else
 359                {
 8360                    canvas.SetTooltipActive(i, false);
 361                }
 362            }
 363
 6364            if (anyTooltipAdded)
 365            {
 3366                canvas.Show();
 367            }
 6368        }
 369    }
 370}