< Summary

Class:ECSSystems.ScenesUiSystem.ECSScenesUiSystem
Assembly:ECS7Plugin.Systems.ScenesUiSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/Systems/ScenesUiSystem/ECSScenesUiSystem.cs
Covered lines:116
Uncovered lines:5
Coverable lines:121
Total lines:314
Line coverage:95.8% (116 of 121)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ECSScenesUiSystem(...)0%110100%
Dispose()0%110100%
Update()0%11.0211094.12%
LoadedScenesOnOnRemoved(...)0%6200%
LoadingHudVisibleOnOnChange(...)0%110100%
ApplyParenting(...)0%10100100%
ClearCurrentSceneUI(...)0%3.013088.89%
GetCurrentScene(...)0%440100%
ApplySceneUI(...)0%220100%
GetParentContainerModel(...)0%550100%
SortSceneUiTree(...)0%880100%
SetDocumentActive(...)0%330100%
RightOfData(...)0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/Systems/ScenesUiSystem/ECSScenesUiSystem.cs

#LineLine coverage
 1using DCL;
 2using DCL.Controllers;
 3using DCL.ECS7.InternalComponents;
 4using DCL.ECSRuntime;
 5using DCL.Models;
 6using System;
 7using System.Collections.Generic;
 8using UnityEngine.UIElements;
 9
 10namespace ECSSystems.ScenesUiSystem
 11{
 12    public class ECSScenesUiSystem : IDisposable
 13    {
 14        private readonly UIDocument uiDocument;
 15        private readonly IInternalECSComponent<InternalUiContainer> internalUiContainerComponent;
 16        private readonly IWorldState worldState;
 17        private readonly BaseList<IParcelScene> loadedScenes;
 18        private readonly BaseVariable<bool> loadingHudVisibleVariable;
 19
 20        private int lastSceneNumber;
 21        private bool isPendingSceneUI;
 22        private IParcelScene currentScene;
 23
 824        public ECSScenesUiSystem(UIDocument uiDocument,
 25            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 26            BaseList<IParcelScene> loadedScenes,
 27            IWorldState worldState,
 28            BaseVariable<bool> loadingHudVisibleVariable)
 29        {
 830            this.uiDocument = uiDocument;
 831            this.internalUiContainerComponent = internalUiContainerComponent;
 832            this.worldState = worldState;
 833            this.loadedScenes = loadedScenes;
 834            this.loadingHudVisibleVariable = loadingHudVisibleVariable;
 35
 836            lastSceneNumber = -1;
 837            isPendingSceneUI = true;
 838            currentScene = null;
 39
 840            loadedScenes.OnRemoved += LoadedScenesOnOnRemoved;
 841            loadingHudVisibleVariable.OnChange += LoadingHudVisibleOnOnChange;
 42
 843            LoadingHudVisibleOnOnChange(loadingHudVisibleVariable.Get(), false);
 844        }
 45
 46        public void Dispose()
 47        {
 148            loadedScenes.OnRemoved -= LoadedScenesOnOnRemoved;
 149            loadingHudVisibleVariable.OnChange -= LoadingHudVisibleOnOnChange;
 150        }
 51
 52        public void Update()
 53        {
 1554            int currentSceneNumber = worldState.GetCurrentSceneNumber();
 1555            bool sceneChanged = lastSceneNumber != currentSceneNumber;
 1556            lastSceneNumber = currentSceneNumber;
 57
 1558            HashSet<IParcelScene> scenesUiToSort = ApplyParenting(uiDocument, internalUiContainerComponent, currentScene
 59
 60            // If parenting detects that the order for ui elements has changed, it should sort the ui tree
 1561            if (scenesUiToSort.Count > 0)
 62            {
 063                SortSceneUiTree(internalUiContainerComponent, scenesUiToSort);
 64            }
 65
 66            // clear UI if scene changed
 1567            if (sceneChanged && !isPendingSceneUI)
 68            {
 469                ClearCurrentSceneUI(uiDocument, currentScene, internalUiContainerComponent);
 470                isPendingSceneUI = currentSceneNumber > 0;
 71            }
 72
 1573            if (sceneChanged && currentScene != null && currentSceneNumber != currentScene.sceneData.sceneNumber)
 74            {
 575                currentScene = null;
 76            }
 77
 78            // UI not set for current scene yet
 1579            if (isPendingSceneUI)
 80            {
 81                // we get current scene reference
 1282                currentScene ??= GetCurrentScene(currentSceneNumber, loadedScenes);
 83
 84                // we apply current scene UI
 1285                if (currentScene != null)
 86                {
 987                    if (ApplySceneUI(internalUiContainerComponent, uiDocument, currentScene))
 88                    {
 789                        isPendingSceneUI = false;
 90                    }
 91                }
 92            }
 1593        }
 94
 95        private void LoadedScenesOnOnRemoved(IParcelScene scene)
 96        {
 097            if (scene.sceneData.sceneNumber == lastSceneNumber)
 98            {
 099                lastSceneNumber = -1;
 100            }
 0101        }
 102
 103        private void LoadingHudVisibleOnOnChange(bool current, bool previous)
 104        {
 10105            SetDocumentActive(uiDocument, !current);
 10106        }
 107
 108        internal static HashSet<IParcelScene> ApplyParenting(UIDocument uiDocument,
 109            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent, int currentSceneNumber)
 110        {
 20111            HashSet<IParcelScene> scenesToSort = new HashSet<IParcelScene>();
 112
 113            // check for orphan ui containers
 20114            var allContainers = internalUiContainerComponent.GetForAll();
 115
 102116            for (int i = 0; i < allContainers.Count; i++)
 117            {
 31118                var uiContainerData = allContainers[i].value;
 119
 120                // check if ui should be sort (only current and global scenes)
 31121                if (uiContainerData.model.shouldSort
 122                    && (uiContainerData.scene.isPersistent || uiContainerData.scene.sceneData.sceneNumber == currentScen
 123                {
 1124                    scenesToSort.Add(uiContainerData.scene);
 125                }
 126
 127                // add global scenes ui but
 128                // skip non-global scenes ui containers on root entity since no parenting is needed
 31129                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 130                {
 25131                    var model = uiContainerData.model;
 132
 25133                    if (uiContainerData.scene.isPersistent && model.parentElement == null)
 134                    {
 2135                        uiDocument.rootVisualElement.Add(model.rootElement);
 2136                        model.parentElement = uiDocument.rootVisualElement;
 2137                        internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, model);
 138                    }
 139
 2140                    continue;
 141                }
 142
 143                // skip containers with parent already set
 6144                if (uiContainerData.model.parentElement != null)
 145                    continue;
 146
 5147                InternalUiContainer parentDataModel = GetParentContainerModel(
 148                    internalUiContainerComponent,
 149                    uiContainerData.scene,
 150                    uiContainerData.model.parentId);
 151
 152                // apply parenting
 5153                if (parentDataModel != null)
 154                {
 4155                    var currentContainerModel = uiContainerData.model;
 4156                    parentDataModel.rootElement.Add(uiContainerData.model.rootElement);
 4157                    currentContainerModel.parentElement = parentDataModel.rootElement;
 158
 4159                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.model.parentId, parentDat
 4160                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, currentContainerM
 161                }
 162            }
 163
 20164            return scenesToSort;
 165        }
 166
 167        internal static void ClearCurrentSceneUI(UIDocument uiDocument, IParcelScene currentScene,
 168            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent)
 169        {
 5170            IECSReadOnlyComponentData<InternalUiContainer> currentSceneContainer =
 171                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 172
 5173            if (currentSceneContainer == null)
 0174                return;
 175
 5176            InternalUiContainer model = currentSceneContainer.model;
 5177            model.parentElement = null;
 178
 5179            if (uiDocument.rootVisualElement.Contains(model.rootElement))
 180            {
 5181                uiDocument.rootVisualElement.Remove(model.rootElement);
 182            }
 183
 5184            internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 5185        }
 186
 187        internal static IParcelScene GetCurrentScene(int sceneNumber, IList<IParcelScene> loadedScenes)
 188        {
 14189            if (sceneNumber <= 0)
 2190                return null;
 191
 12192            IParcelScene currentScene = null;
 193
 30194            for (int i = 0; i < loadedScenes.Count; i++)
 195            {
 12196                if (loadedScenes[i].sceneData.sceneNumber == sceneNumber)
 197                {
 9198                    currentScene = loadedScenes[i];
 9199                    break;
 200                }
 201            }
 202
 12203            return currentScene;
 204        }
 205
 206        internal static bool ApplySceneUI(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 207            UIDocument uiDocument, IParcelScene currentScene)
 208        {
 11209            IECSReadOnlyComponentData<InternalUiContainer> sceneRootUiContainer =
 210                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 211
 11212            if (sceneRootUiContainer != null)
 213            {
 8214                var model = sceneRootUiContainer.model;
 8215                uiDocument.rootVisualElement.Insert(0, model.rootElement);
 8216                model.parentElement = uiDocument.rootVisualElement;
 8217                internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 8218                return true;
 219            }
 220
 3221            return false;
 222        }
 223
 224        private static InternalUiContainer GetParentContainerModel(IInternalECSComponent<InternalUiContainer> internalUi
 225            IParcelScene scene, long parentId)
 226        {
 5227            InternalUiContainer parentDataModel =
 228                internalUiContainerComponent.GetFor(scene, parentId)?.model;
 229
 230            // create root entity ui container if needed
 5231            if (parentDataModel == null && parentId == SpecialEntityId.SCENE_ROOT_ENTITY)
 232            {
 3233                parentDataModel = new InternalUiContainer();
 234            }
 235
 5236            return parentDataModel;
 237        }
 238
 239        internal static void SortSceneUiTree(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 240            ICollection<IParcelScene> scenesToSort)
 241        {
 2242            Dictionary<VisualElement, Dictionary<long, RightOfData>> sortSceneTree = new Dictionary<VisualElement, Dicti
 243
 244            // Setup UI sorting
 2245            var allContainers = internalUiContainerComponent.GetForAll();
 246
 24247            for (int i = 0; i < allContainers.Count; i++)
 248            {
 10249                var uiContainerData = allContainers[i].value;
 250
 10251                if (!scenesToSort.Contains(uiContainerData.scene))
 252                    continue;
 253
 10254                InternalUiContainer model = uiContainerData.model;
 255
 256                // If not parented yet, we skip it
 10257                if (model.parentElement == null)
 258                    continue;
 259
 260                // Ignore root scene UI container
 8261                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 262                    continue;
 263
 8264                if (!sortSceneTree.TryGetValue(model.parentElement, out var sceneTreeDictionary))
 265                {
 2266                    sceneTreeDictionary = new Dictionary<long, RightOfData>();
 2267                    sortSceneTree[model.parentElement] = sceneTreeDictionary;
 268                }
 269
 8270                sceneTreeDictionary[model.rigthOf] = new RightOfData(model.rootElement,
 271                    uiContainerData.entity.entityId);
 272
 8273                model.shouldSort = false;
 8274                internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity.entityId, model);
 275            }
 276
 277            // Apply UI sorting
 8278            foreach (var pairs in sortSceneTree)
 279            {
 2280                VisualElement parentElement = pairs.Key;
 2281                Dictionary<long, RightOfData> sceneSort = pairs.Value;
 282
 2283                int index = 0;
 2284                long nextElementId = 0;
 285
 10286                while (sceneSort.TryGetValue(nextElementId, out RightOfData rightOfData))
 287                {
 8288                    sceneSort.Remove(nextElementId);
 8289                    parentElement.Remove(rightOfData.element);
 8290                    parentElement.Insert(index, rightOfData.element);
 8291                    nextElementId = rightOfData.id;
 8292                    index++;
 8293                }
 294            }
 2295        }
 296
 297        private static void SetDocumentActive(UIDocument uiDocument, bool active)
 298        {
 10299            uiDocument.rootVisualElement.style.display = active ? DisplayStyle.Flex : DisplayStyle.None;
 10300        }
 301
 302        private readonly struct RightOfData
 303        {
 304            public readonly long id;
 305            public readonly VisualElement element;
 306
 307            public RightOfData(VisualElement element, long id)
 308            {
 8309                this.id = id;
 8310                this.element = element;
 8311            }
 312        }
 313    }
 314}