< 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:111
Uncovered lines:4
Coverable lines:115
Total lines:293
Line coverage:96.5% (111 of 115)
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%220100%
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 System;
 2using System.Collections.Generic;
 3using DCL;
 4using DCL.Controllers;
 5using DCL.ECS7.InternalComponents;
 6using DCL.ECSRuntime;
 7using DCL.Models;
 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
 724        public ECSScenesUiSystem(UIDocument uiDocument,
 25            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 26            BaseList<IParcelScene> loadedScenes,
 27            IWorldState worldState,
 28            BaseVariable<bool> loadingHudVisibleVariable)
 29        {
 730            this.uiDocument = uiDocument;
 731            this.internalUiContainerComponent = internalUiContainerComponent;
 732            this.worldState = worldState;
 733            this.loadedScenes = loadedScenes;
 734            this.loadingHudVisibleVariable = loadingHudVisibleVariable;
 35
 736            lastSceneNumber = -1;
 737            isPendingSceneUI = true;
 738            currentScene = null;
 39
 740            loadedScenes.OnRemoved += LoadedScenesOnOnRemoved;
 741            loadingHudVisibleVariable.OnChange += LoadingHudVisibleOnOnChange;
 42
 743            LoadingHudVisibleOnOnChange(loadingHudVisibleVariable.Get(), false);
 744        }
 45
 46        public void Dispose()
 47        {
 148            loadedScenes.OnRemoved -= LoadedScenesOnOnRemoved;
 149            loadingHudVisibleVariable.OnChange -= LoadingHudVisibleOnOnChange;
 150        }
 51
 52        public void Update()
 53        {
 1254            int currentSceneNumber = worldState.GetCurrentSceneNumber();
 1255            bool sceneChanged = lastSceneNumber != currentSceneNumber;
 1256            lastSceneNumber = currentSceneNumber;
 57
 1258            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
 1261            if (scenesUiToSort.Count > 0)
 62            {
 063                SortSceneUiTree(internalUiContainerComponent, scenesUiToSort);
 64            }
 65
 66            // clear UI if scene changed
 1267            if (sceneChanged && !isPendingSceneUI)
 68            {
 369                ClearCurrentSceneUI(uiDocument);
 370                isPendingSceneUI = currentSceneNumber > 0;
 71            }
 1272            if (sceneChanged && currentScene != null && currentSceneNumber != currentScene.sceneData.sceneNumber)
 73            {
 474                currentScene = null;
 75            }
 76
 77            // UI not set for current scene yet
 1278            if (isPendingSceneUI)
 79            {
 80                // we get current scene reference
 1181                currentScene ??= GetCurrentScene(currentSceneNumber, loadedScenes);
 82
 83                // we apply current scene UI
 1184                if (currentScene != null)
 85                {
 886                    if (ApplySceneUI(internalUiContainerComponent, uiDocument, currentScene))
 87                    {
 688                        isPendingSceneUI = false;
 89                    }
 90                }
 91            }
 1292        }
 93
 94        private void LoadedScenesOnOnRemoved(IParcelScene scene)
 95        {
 096            if (scene.sceneData.sceneNumber == lastSceneNumber)
 97            {
 098                lastSceneNumber = -1;
 99            }
 0100        }
 101
 102        private void LoadingHudVisibleOnOnChange(bool current, bool previous)
 103        {
 9104            SetDocumentActive(uiDocument, !current);
 9105        }
 106
 107        internal static HashSet<IParcelScene> ApplyParenting(UIDocument uiDocument,
 108            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent, int currentSceneNumber)
 109        {
 17110            HashSet<IParcelScene> scenesToSort = new HashSet<IParcelScene>();
 111
 112            // check for orphan ui containers
 17113            var allContainers = internalUiContainerComponent.GetForAll();
 92114            for (int i = 0; i < allContainers.Count; i++)
 115            {
 29116                var uiContainerData = allContainers[i].value;
 117
 118                // check if ui should be sort (only current and global scenes)
 29119                if (uiContainerData.model.shouldSort
 120                    && (uiContainerData.scene.isPersistent || uiContainerData.scene.sceneData.sceneNumber == currentScen
 121                {
 1122                    scenesToSort.Add(uiContainerData.scene);
 123                }
 124
 125                // add global scenes ui but
 126                // skip non-global scenes ui containers on root entity since no parenting is needed
 29127                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 128                {
 23129                    var model = uiContainerData.model;
 23130                    if (uiContainerData.scene.isPersistent && model.parentElement == null)
 131                    {
 2132                        uiDocument.rootVisualElement.Add(model.rootElement);
 2133                        model.parentElement = uiDocument.rootVisualElement;
 2134                        internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, model);
 135                    }
 2136                    continue;
 137                }
 138
 139                // skip containers with parent already set
 6140                if (uiContainerData.model.parentElement != null)
 141                    continue;
 142
 5143                InternalUiContainer parentDataModel = GetParentContainerModel(
 144                    internalUiContainerComponent,
 145                    uiContainerData.scene,
 146                    uiContainerData.model.parentId);
 147
 148                // apply parenting
 5149                if (parentDataModel != null)
 150                {
 4151                    var currentContainerModel = uiContainerData.model;
 4152                    parentDataModel.rootElement.Add(uiContainerData.model.rootElement);
 4153                    currentContainerModel.parentElement = parentDataModel.rootElement;
 154
 4155                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.model.parentId, parentDat
 4156                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, currentContainerM
 157                }
 158            }
 17159            return scenesToSort;
 160        }
 161
 162        internal static void ClearCurrentSceneUI(UIDocument uiDocument)
 163        {
 4164            if (uiDocument.rootVisualElement.childCount > 0)
 165            {
 4166                uiDocument.rootVisualElement.RemoveAt(0);
 167            }
 4168        }
 169
 170        internal static IParcelScene GetCurrentScene(int sceneNumber, IList<IParcelScene> loadedScenes)
 171        {
 13172            if (sceneNumber <= 0)
 2173                return null;
 174
 11175            IParcelScene currentScene = null;
 28176            for (int i = 0; i < loadedScenes.Count; i++)
 177            {
 11178                if (loadedScenes[i].sceneData.sceneNumber == sceneNumber)
 179                {
 8180                    currentScene = loadedScenes[i];
 8181                    break;
 182                }
 183            }
 184
 11185            return currentScene;
 186        }
 187
 188        internal static bool ApplySceneUI(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 189            UIDocument uiDocument, IParcelScene currentScene)
 190        {
 10191            IECSReadOnlyComponentData<InternalUiContainer> sceneRootUiContainer =
 192                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 193
 10194            if (sceneRootUiContainer != null)
 195            {
 7196                var model = sceneRootUiContainer.model;
 7197                uiDocument.rootVisualElement.Insert(0, model.rootElement);
 7198                model.parentElement = uiDocument.rootVisualElement;
 7199                internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 7200                return true;
 201            }
 3202            return false;
 203        }
 204
 205        private static InternalUiContainer GetParentContainerModel(IInternalECSComponent<InternalUiContainer> internalUi
 206            IParcelScene scene, long parentId)
 207        {
 5208            InternalUiContainer parentDataModel =
 209                internalUiContainerComponent.GetFor(scene, parentId)?.model;
 210
 211            // create root entity ui container if needed
 5212            if (parentDataModel == null && parentId == SpecialEntityId.SCENE_ROOT_ENTITY)
 213            {
 3214                parentDataModel = new InternalUiContainer();
 215            }
 5216            return parentDataModel;
 217        }
 218
 219        internal static void SortSceneUiTree(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 220            ICollection<IParcelScene> scenesToSort)
 221        {
 2222            Dictionary<VisualElement, Dictionary<long, RightOfData>> sortSceneTree = new Dictionary<VisualElement, Dicti
 223
 224            // Setup UI sorting
 2225            var allContainers = internalUiContainerComponent.GetForAll();
 24226            for (int i = 0; i < allContainers.Count; i++)
 227            {
 10228                var uiContainerData = allContainers[i].value;
 229
 10230                if (!scenesToSort.Contains(uiContainerData.scene))
 231                    continue;
 232
 10233                InternalUiContainer model = uiContainerData.model;
 234
 235                // If not parented yet, we skip it
 10236                if (model.parentElement == null)
 237                    continue;
 238
 239                // Ignore root scene UI container
 8240                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 241                    continue;
 242
 8243                if (!sortSceneTree.TryGetValue(model.parentElement, out var sceneTreeDictionary))
 244                {
 2245                    sceneTreeDictionary = new Dictionary<long, RightOfData>();
 2246                    sortSceneTree[model.parentElement] = sceneTreeDictionary;
 247                }
 248
 8249                sceneTreeDictionary[model.rigthOf] = new RightOfData(model.rootElement,
 250                    uiContainerData.entity.entityId);
 251
 8252                model.shouldSort = false;
 8253                internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity.entityId, model);
 254            }
 255
 256            // Apply UI sorting
 8257            foreach (var pairs in sortSceneTree)
 258            {
 2259                VisualElement parentElement = pairs.Key;
 2260                Dictionary<long, RightOfData> sceneSort = pairs.Value;
 261
 2262                int index = 0;
 2263                long nextElementId = 0;
 264
 10265                while (sceneSort.TryGetValue(nextElementId, out RightOfData rightOfData))
 266                {
 8267                    sceneSort.Remove(nextElementId);
 8268                    parentElement.Remove(rightOfData.element);
 8269                    parentElement.Insert(index, rightOfData.element);
 8270                    nextElementId = rightOfData.id;
 8271                    index++;
 8272                }
 273            }
 2274        }
 275
 276        private static void SetDocumentActive(UIDocument uiDocument, bool active)
 277        {
 9278            uiDocument.rootVisualElement.style.display = active ? DisplayStyle.Flex : DisplayStyle.None;
 9279        }
 280
 281        private readonly struct RightOfData
 282        {
 283            public readonly long id;
 284            public readonly VisualElement element;
 285
 286            public RightOfData(VisualElement element, long id)
 287            {
 8288                this.id = id;
 8289                this.element = element;
 8290            }
 291        }
 292    }
 293}