< 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:131
Uncovered lines:2
Coverable lines:133
Total lines:329
Line coverage:98.4% (131 of 133)
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%220100%
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;
 9using UnityEngine.UIElements;
 10
 11namespace ECSSystems.ScenesUiSystem
 12{
 13    public class ECSScenesUiSystem : IDisposable
 14    {
 15        private readonly UIDocument uiDocument;
 16        private readonly IInternalECSComponent<InternalUiContainer> internalUiContainerComponent;
 17        private readonly IWorldState worldState;
 18        private readonly BaseList<IParcelScene> loadedScenes;
 19        private readonly BaseVariable<bool> loadingHudVisibleVariable;
 20
 21        private int lastSceneNumber;
 22        private bool isPendingSceneUI;
 23        private IParcelScene currentScene;
 24
 925        public ECSScenesUiSystem(UIDocument uiDocument,
 26            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 27            BaseList<IParcelScene> loadedScenes,
 28            IWorldState worldState,
 29            BaseVariable<bool> loadingHudVisibleVariable)
 30        {
 931            this.uiDocument = uiDocument;
 932            this.internalUiContainerComponent = internalUiContainerComponent;
 933            this.worldState = worldState;
 934            this.loadedScenes = loadedScenes;
 935            this.loadingHudVisibleVariable = loadingHudVisibleVariable;
 36
 937            lastSceneNumber = -1;
 938            isPendingSceneUI = true;
 939            currentScene = null;
 40
 941            loadedScenes.OnRemoved += LoadedScenesOnOnRemoved;
 942            loadingHudVisibleVariable.OnChange += LoadingHudVisibleOnOnChange;
 43
 944            LoadingHudVisibleOnOnChange(loadingHudVisibleVariable.Get(), false);
 945        }
 46
 47        public void Dispose()
 48        {
 249            loadedScenes.OnRemoved -= LoadedScenesOnOnRemoved;
 250            loadingHudVisibleVariable.OnChange -= LoadingHudVisibleOnOnChange;
 251        }
 52
 53        public void Update()
 54        {
 2855            int currentSceneNumber = worldState.GetCurrentSceneNumber();
 2856            bool sceneChanged = lastSceneNumber != currentSceneNumber;
 2857            lastSceneNumber = currentSceneNumber;
 58
 2859            HashSet<IParcelScene> scenesUiToSort = ApplyParenting(uiDocument, internalUiContainerComponent, currentScene
 60
 61            // If parenting detects that the order for ui elements has changed, it should sort the ui tree
 2862            if (scenesUiToSort.Count > 0)
 63            {
 064                SortSceneUiTree(internalUiContainerComponent, scenesUiToSort);
 65            }
 66
 67            // clear UI if scene changed
 2868            if (sceneChanged && !isPendingSceneUI)
 69            {
 470                ClearCurrentSceneUI(uiDocument, currentScene, internalUiContainerComponent);
 471                isPendingSceneUI = currentSceneNumber > 0;
 72            }
 73
 2874            if (sceneChanged && currentScene != null && currentSceneNumber != currentScene.sceneData.sceneNumber)
 75            {
 576                currentScene = null;
 77            }
 78
 79            // UI not set for current scene yet
 2880            if (isPendingSceneUI)
 81            {
 82                // we get current scene reference
 2583                currentScene ??= GetCurrentScene(currentSceneNumber, loadedScenes);
 84
 85                // we apply current scene UI
 2586                if (currentScene != null)
 87                {
 1988                    if (ApplySceneUI(internalUiContainerComponent, uiDocument, currentScene))
 89                    {
 790                        isPendingSceneUI = false;
 91                    }
 92                }
 93            }
 2894        }
 95
 96        private void LoadedScenesOnOnRemoved(IParcelScene scene)
 97        {
 198            if (scene.sceneData.sceneNumber == lastSceneNumber)
 99            {
 1100                lastSceneNumber = -1;
 101            }
 1102        }
 103
 104        private void LoadingHudVisibleOnOnChange(bool current, bool previous)
 105        {
 11106            SetDocumentActive(uiDocument, !current);
 11107        }
 108
 109        internal static HashSet<IParcelScene> ApplyParenting(UIDocument uiDocument,
 110            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent, int currentSceneNumber)
 111        {
 34112            HashSet<IParcelScene> scenesToSort = new HashSet<IParcelScene>();
 113
 114            // check for orphan ui containers
 34115            var allContainers = internalUiContainerComponent.GetForAll();
 116
 142117            for (int i = 0; i < allContainers.Count; i++)
 118            {
 37119                var uiContainerData = allContainers[i].value;
 120
 121                // check if ui should be sort (only current and global scenes)
 37122                if (uiContainerData.model.shouldSort
 123                    && (uiContainerData.scene.isPersistent || uiContainerData.scene.sceneData.sceneNumber == currentScen
 124                {
 1125                    scenesToSort.Add(uiContainerData.scene);
 126                }
 127
 128                // add global scenes ui but
 129                // skip non-global scenes ui containers on root entity since no parenting is needed
 37130                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 131                {
 26132                    var model = uiContainerData.model;
 133
 26134                    if (uiContainerData.scene.isPersistent && model.parentElement == null)
 135                    {
 2136                        uiDocument.rootVisualElement.Add(model.rootElement);
 2137                        model.parentElement = uiDocument.rootVisualElement;
 2138                        internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, model);
 139                    }
 140
 2141                    continue;
 142                }
 143
 144                // skip containers with parent already set
 11145                if (uiContainerData.model.parentElement != null)
 146                    continue;
 147
 10148                InternalUiContainer parentDataModel = GetParentContainerModel(
 149                    internalUiContainerComponent,
 150                    uiContainerData.scene,
 151                    uiContainerData.model.parentId);
 152
 153                // apply parenting
 10154                if (parentDataModel != null)
 155                {
 9156                    var currentContainerModel = uiContainerData.model;
 9157                    parentDataModel.rootElement.Add(uiContainerData.model.rootElement);
 9158                    currentContainerModel.parentElement = parentDataModel.rootElement;
 159
 9160                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.model.parentId, parentDat
 9161                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, currentContainerM
 162                }
 163            }
 164
 34165            return scenesToSort;
 166        }
 167
 168        internal static void ClearCurrentSceneUI(UIDocument uiDocument, IParcelScene currentScene,
 169            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent)
 170        {
 5171            IECSReadOnlyComponentData<InternalUiContainer> currentSceneContainer =
 172                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 173
 5174            if (currentSceneContainer == null)
 0175                return;
 176
 5177            InternalUiContainer model = currentSceneContainer.model;
 5178            model.parentElement = null;
 179
 5180            if (uiDocument.rootVisualElement.Contains(model.rootElement))
 181            {
 5182                uiDocument.rootVisualElement.Remove(model.rootElement);
 183            }
 184
 5185            internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 5186        }
 187
 188        internal static IParcelScene GetCurrentScene(int sceneNumber, IList<IParcelScene> loadedScenes)
 189        {
 18190            if (sceneNumber <= 0)
 5191                return null;
 192
 13193            IParcelScene currentScene = null;
 194
 32195            for (int i = 0; i < loadedScenes.Count; i++)
 196            {
 13197                if (loadedScenes[i].sceneData.sceneNumber == sceneNumber)
 198                {
 10199                    currentScene = loadedScenes[i];
 10200                    break;
 201                }
 202            }
 203
 13204            return currentScene;
 205        }
 206
 207        internal static bool ApplySceneUI(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 208            UIDocument uiDocument, IParcelScene currentScene)
 209        {
 21210            IECSReadOnlyComponentData<InternalUiContainer> sceneRootUiContainer =
 211                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 212
 21213            if (sceneRootUiContainer != null)
 214            {
 8215                var model = sceneRootUiContainer.model;
 8216                uiDocument.rootVisualElement.Insert(0, model.rootElement);
 8217                model.parentElement = uiDocument.rootVisualElement;
 8218                internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 8219                return true;
 220            }
 221
 13222            return false;
 223        }
 224
 225        private static InternalUiContainer GetParentContainerModel(IInternalECSComponent<InternalUiContainer> internalUi
 226            IParcelScene scene, long parentId)
 227        {
 10228            InternalUiContainer parentDataModel =
 229                internalUiContainerComponent.GetFor(scene, parentId)?.model;
 230
 231            // create root entity ui container if needed
 10232            if (parentDataModel == null && parentId == SpecialEntityId.SCENE_ROOT_ENTITY)
 233            {
 4234                parentDataModel = new InternalUiContainer(parentId);
 4235                var style = parentDataModel.rootElement.style;
 236
 237                // Initialize with default values
 4238                parentDataModel.rootElement.pickingMode = PickingMode.Ignore; // to avoid blocking pointer
 4239                style.width = new Length(100f, LengthUnit.Percent);
 4240                style.height = new Length(100f, LengthUnit.Percent);
 4241                style.flexDirection = new StyleEnum<FlexDirection>(FlexDirection.Row);
 4242                style.flexBasis = new StyleLength(StyleKeyword.Auto);
 4243                style.flexGrow = 0;
 4244                style.flexShrink = 1;
 4245                style.flexWrap = new StyleEnum<Wrap>(Wrap.NoWrap);
 4246                style.justifyContent = new StyleEnum<Justify>(Justify.FlexStart);
 4247                style.alignItems = new StyleEnum<Align>(Align.Stretch);
 4248                style.alignSelf = new StyleEnum<Align>(Align.Auto);
 4249                style.alignContent = new StyleEnum<Align>(Align.Stretch);
 250            }
 251
 10252            return parentDataModel;
 253        }
 254
 255        internal static void SortSceneUiTree(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 256            ICollection<IParcelScene> scenesToSort)
 257        {
 3258            Dictionary<VisualElement, Dictionary<long, RightOfData>> sortSceneTree = new Dictionary<VisualElement, Dicti
 259
 260            // Setup UI sorting
 3261            var allContainers = internalUiContainerComponent.GetForAll();
 262
 38263            for (int i = 0; i < allContainers.Count; i++)
 264            {
 16265                var uiContainerData = allContainers[i].value;
 266
 16267                if (!scenesToSort.Contains(uiContainerData.scene))
 268                    continue;
 269
 16270                InternalUiContainer model = uiContainerData.model;
 271
 272                // If not parented yet, we skip it
 16273                if (model.parentElement == null)
 274                    continue;
 275
 276                // Ignore root scene UI container
 13277                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 278                    continue;
 279
 13280                if (!sortSceneTree.TryGetValue(model.parentElement, out var sceneTreeDictionary))
 281                {
 5282                    sceneTreeDictionary = new Dictionary<long, RightOfData>();
 5283                    sortSceneTree[model.parentElement] = sceneTreeDictionary;
 284                }
 285
 13286                sceneTreeDictionary[model.rigthOf] = new RightOfData(model.rootElement,
 287                    uiContainerData.entity.entityId);
 288
 13289                model.shouldSort = false;
 13290                internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity.entityId, model);
 291            }
 292
 293            // Apply UI sorting
 16294            foreach (var pairs in sortSceneTree)
 295            {
 5296                VisualElement parentElement = pairs.Key;
 5297                Dictionary<long, RightOfData> sceneSort = pairs.Value;
 298
 5299                int index = 0;
 5300                long nextElementId = 0;
 301
 18302                while (sceneSort.TryGetValue(nextElementId, out RightOfData rightOfData))
 303                {
 13304                    sceneSort.Remove(nextElementId);
 13305                    rightOfData.element.PlaceInFront(parentElement[index]);
 13306                    nextElementId = rightOfData.id;
 13307                    index++;
 13308                }
 309            }
 3310        }
 311
 312        private static void SetDocumentActive(UIDocument uiDocument, bool active)
 313        {
 11314            uiDocument.rootVisualElement.style.display = active ? DisplayStyle.Flex : DisplayStyle.None;
 11315        }
 316
 317        private readonly struct RightOfData
 318        {
 319            public readonly long id;
 320            public readonly VisualElement element;
 321
 322            public RightOfData(VisualElement element, long id)
 323            {
 13324                this.id = id;
 13325                this.element = element;
 13326            }
 327        }
 328    }
 329}