< 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:169
Uncovered lines:14
Coverable lines:183
Total lines:423
Line coverage:92.3% (169 of 183)
Covered branches:0
Total branches:0
Covered methods:16
Total methods:18
Method coverage:88.8% (16 of 18)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ECSScenesUiSystem(...)0%110100%
Dispose()0%110100%
Update()0%11.0211095%
LoadedScenesOnOnRemoved(...)0%6200%
OnHideAllUiEvent(...)0%110100%
OnHideCurrentSceneUiEvent(...)0%5.395075%
ApplyParenting(...)0%11110100%
ClearCurrentSceneUI(...)0%3.013088.89%
GetCurrentScene(...)0%440100%
ApplySceneUI(...)0%5.015091.67%
GetParentContainerModel(...)0%550100%
SortSceneUiTree(...)0%880100%
SetDocumentActive(...)0%330100%
OnSceneUiVisibilityAdded(...)0%550100%
OnSceneUiVisibilitySet(...)0%12300%
SetSceneUiVisibility(...)0%5.395075%
UpdatePortableExperiencesVisibilityByFeatureToggle(...)0%5.515072.73%
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 BooleanVariable hideUiEventVariable;
 19        private readonly BaseVariable<bool> isCurrentSceneUiEnabled;
 20        private readonly BaseDictionary<int, bool> isSceneUIEnabled;
 1321        private readonly List<IParcelScene> globalScenesAddedBuffer = new ();
 22
 23        private int lastSceneNumber;
 24        private bool isPendingSceneUI;
 25        private IParcelScene currentScene;
 1326        private HashSet<IParcelScene> scenesUiToSort = new ();
 27
 1328        public ECSScenesUiSystem(UIDocument uiDocument,
 29            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 30            BaseList<IParcelScene> loadedScenes,
 31            IWorldState worldState,
 32            BooleanVariable hideUiEventVariable,
 33            BaseVariable<bool> isCurrentSceneUiEnabled,
 34            BaseDictionary<int, bool> isSceneUIEnabled)
 35        {
 1336            this.uiDocument = uiDocument;
 1337            this.internalUiContainerComponent = internalUiContainerComponent;
 1338            this.worldState = worldState;
 1339            this.loadedScenes = loadedScenes;
 1340            this.hideUiEventVariable = hideUiEventVariable;
 1341            this.isCurrentSceneUiEnabled = isCurrentSceneUiEnabled;
 1342            this.isSceneUIEnabled = isSceneUIEnabled;
 43
 1344            lastSceneNumber = -1;
 1345            isPendingSceneUI = true;
 1346            currentScene = null;
 47
 1348            loadedScenes.OnRemoved += LoadedScenesOnOnRemoved;
 1349            hideUiEventVariable.OnChange += OnHideAllUiEvent;
 1350            isCurrentSceneUiEnabled.OnChange += OnHideCurrentSceneUiEvent;
 1351            isSceneUIEnabled.OnSet += OnSceneUiVisibilitySet;
 1352            isSceneUIEnabled.OnAdded += OnSceneUiVisibilityAdded;
 53
 1354            OnHideAllUiEvent(hideUiEventVariable.Get(), false);
 1355        }
 56
 57        public void Dispose()
 58        {
 159            loadedScenes.OnRemoved -= LoadedScenesOnOnRemoved;
 160            hideUiEventVariable.OnChange -= OnHideAllUiEvent;
 161            isCurrentSceneUiEnabled.OnChange -= OnHideCurrentSceneUiEvent;
 162            isSceneUIEnabled.OnSet -= OnSceneUiVisibilitySet;
 163            isSceneUIEnabled.OnAdded -= OnSceneUiVisibilityAdded;
 164        }
 65
 66        public void Update()
 67        {
 1868            int currentSceneNumber = worldState.GetCurrentSceneNumber();
 1869            bool sceneChanged = lastSceneNumber != currentSceneNumber;
 1870            lastSceneNumber = currentSceneNumber;
 71
 1872            globalScenesAddedBuffer.Clear();
 73
 1874            ApplyParenting(ref scenesUiToSort, uiDocument, internalUiContainerComponent, currentSceneNumber, globalScene
 75
 76            // If parenting detects that the order for ui elements has changed, it should sort the ui tree
 1877            if (scenesUiToSort.Count > 0)
 078                SortSceneUiTree(internalUiContainerComponent, scenesUiToSort);
 79
 1880            UpdatePortableExperiencesVisibilityByFeatureToggle(currentScene, globalScenesAddedBuffer);
 81
 82            // clear UI if scene changed
 1883            if (sceneChanged && !isPendingSceneUI)
 84            {
 485                ClearCurrentSceneUI(uiDocument, currentScene, internalUiContainerComponent);
 486                isPendingSceneUI = currentSceneNumber > 0;
 87            }
 88
 1889            if (sceneChanged && currentScene != null && currentSceneNumber != currentScene.sceneData.sceneNumber)
 90            {
 591                currentScene = null;
 92            }
 93
 94            // UI not set for current scene yet
 1895            if (isPendingSceneUI)
 96            {
 97                // we get current scene reference
 1598                currentScene ??= GetCurrentScene(currentSceneNumber, loadedScenes);
 99
 100                // we apply current scene UI
 15101                if (currentScene != null)
 102                {
 12103                    if (ApplySceneUI(internalUiContainerComponent, uiDocument, currentScene, isCurrentSceneUiEnabled, is
 10104                        isPendingSceneUI = false;
 105
 12106                    UpdatePortableExperiencesVisibilityByFeatureToggle(currentScene, loadedScenes);
 107                }
 108            }
 18109        }
 110
 111        private void LoadedScenesOnOnRemoved(IParcelScene scene)
 112        {
 0113            if (scene.sceneData.sceneNumber == lastSceneNumber)
 114            {
 0115                lastSceneNumber = -1;
 116            }
 0117        }
 118
 119        private void OnHideAllUiEvent(bool current, bool previous)
 120        {
 15121            SetDocumentActive(uiDocument, !current);
 15122        }
 123
 124        private void OnHideCurrentSceneUiEvent(bool enabled, bool previous)
 125        {
 2126            if (currentScene == null)
 0127                return;
 128
 2129            ECSComponentData<InternalUiContainer>? currentSceneContainer =
 130                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 131
 2132            if (currentSceneContainer == null)
 0133                return;
 134
 2135            InternalUiContainer model = currentSceneContainer.Value.model;
 2136            model.rootElement.style.display = new StyleEnum<DisplayStyle>(enabled ? DisplayStyle.Flex : DisplayStyle.Non
 2137        }
 138
 139        internal static void ApplyParenting(ref HashSet<IParcelScene> scenesToSort,
 140            UIDocument uiDocument,
 141            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 142            int currentSceneNumber,
 143            List<IParcelScene> globalScenesAdded = null)
 144        {
 145            // Clear previous call so we do not accumulate
 24146            scenesToSort.Clear();
 147
 148            // check for orphan ui containers
 24149            var allContainers = internalUiContainerComponent.GetForAll();
 150
 132151            for (int i = 0; i < allContainers.Count; i++)
 152            {
 42153                var uiContainerData = allContainers[i].value;
 154
 155                // check if ui should be sort (only current and global scenes)
 42156                if (uiContainerData.model.shouldSort
 157                    && (uiContainerData.scene.isPersistent || uiContainerData.scene.sceneData.sceneNumber == currentScen
 158                {
 1159                    scenesToSort.Add(uiContainerData.scene);
 160                }
 161
 162                // add global scenes ui but
 163                // skip non-global scenes ui containers on root entity since no parenting is needed
 42164                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 165                {
 31166                    var model = uiContainerData.model;
 167
 31168                    if (uiContainerData.scene.isPersistent && model.parentElement == null)
 169                    {
 2170                        uiDocument.rootVisualElement.Add(model.rootElement);
 2171                        model.parentElement = uiDocument.rootVisualElement;
 2172                        internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, model);
 2173                        globalScenesAdded?.Add(uiContainerData.scene);
 174                    }
 175
 2176                    continue;
 177                }
 178
 179                // skip containers with parent already set
 11180                if (uiContainerData.model.parentElement != null)
 181                    continue;
 182
 11183                var parentDataModel = GetParentContainerModel(
 184                    internalUiContainerComponent,
 185                    uiContainerData.scene,
 186                    uiContainerData.model.parentId);
 187
 188                // apply parenting
 11189                if (parentDataModel != null)
 190                {
 10191                    var currentContainerModel = uiContainerData.model;
 10192                    parentDataModel.Value.rootElement.Add(uiContainerData.model.rootElement);
 10193                    currentContainerModel.parentElement = parentDataModel.Value.rootElement;
 194
 10195                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.model.parentId, parentDat
 10196                    internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity, currentContainerM
 197                }
 198            }
 24199        }
 200
 201        internal static void ClearCurrentSceneUI(UIDocument uiDocument, IParcelScene currentScene,
 202            IInternalECSComponent<InternalUiContainer> internalUiContainerComponent)
 203        {
 5204            ECSComponentData<InternalUiContainer>? currentSceneContainer =
 205                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 206
 5207            if (currentSceneContainer == null)
 0208                return;
 209
 5210            InternalUiContainer model = currentSceneContainer.Value.model;
 5211            model.parentElement = null;
 212
 5213            if (uiDocument.rootVisualElement.Contains(model.rootElement))
 214            {
 5215                uiDocument.rootVisualElement.Remove(model.rootElement);
 216            }
 217
 5218            internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 5219        }
 220
 221        internal static IParcelScene GetCurrentScene(int sceneNumber, IList<IParcelScene> loadedScenes)
 222        {
 19223            if (sceneNumber <= 0)
 2224                return null;
 225
 17226            IParcelScene currentScene = null;
 227
 44228            for (int i = 0; i < loadedScenes.Count; i++)
 229            {
 19230                if (loadedScenes[i].sceneData.sceneNumber == sceneNumber)
 231                {
 14232                    currentScene = loadedScenes[i];
 14233                    break;
 234                }
 235            }
 236
 17237            return currentScene;
 238        }
 239
 240        internal static bool ApplySceneUI(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 241            UIDocument uiDocument, IParcelScene currentScene,
 242            BaseVariable<bool> isCurrentSceneUIEnabled,
 243            BaseDictionary<int, bool> isSceneUiEnabled)
 244        {
 20245            ECSComponentData<InternalUiContainer>? sceneRootUiContainer =
 246                internalUiContainerComponent.GetFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY);
 247
 20248            if (sceneRootUiContainer != null)
 249            {
 17250                bool isVisible = isCurrentSceneUIEnabled.Get();
 251
 17252                if (isSceneUiEnabled.TryGetValue(currentScene.sceneData.sceneNumber, out bool enabled))
 0253                    isVisible &= enabled;
 254
 17255                var model = sceneRootUiContainer.Value.model;
 17256                uiDocument.rootVisualElement.Insert(0, model.rootElement);
 17257                model.parentElement = uiDocument.rootVisualElement;
 17258                model.rootElement.style.display = new StyleEnum<DisplayStyle>(isVisible ? DisplayStyle.Flex : DisplaySty
 17259                internalUiContainerComponent.PutFor(currentScene, SpecialEntityId.SCENE_ROOT_ENTITY, model);
 17260                return true;
 261            }
 262
 3263            return false;
 264        }
 265
 266        private static InternalUiContainer? GetParentContainerModel(IInternalECSComponent<InternalUiContainer> internalU
 267            IParcelScene scene, long parentId)
 268        {
 11269            InternalUiContainer? parentDataModel =
 270                internalUiContainerComponent.GetFor(scene, parentId)?.model;
 271
 272            // create root entity ui container if needed
 11273            if (parentDataModel == null && parentId == SpecialEntityId.SCENE_ROOT_ENTITY)
 274            {
 4275                parentDataModel = new InternalUiContainer(parentId);
 4276                var style = parentDataModel.Value.rootElement.style;
 277
 278                // Initialize with default values
 4279                parentDataModel.Value.rootElement.pickingMode = PickingMode.Ignore; // to avoid blocking pointer
 4280                style.width = new Length(100f, LengthUnit.Percent);
 4281                style.height = new Length(100f, LengthUnit.Percent);
 4282                style.flexDirection = new StyleEnum<FlexDirection>(FlexDirection.Row);
 4283                style.flexBasis = new StyleLength(StyleKeyword.Auto);
 4284                style.flexGrow = 0;
 4285                style.flexShrink = 1;
 4286                style.flexWrap = new StyleEnum<Wrap>(Wrap.NoWrap);
 4287                style.justifyContent = new StyleEnum<Justify>(Justify.FlexStart);
 4288                style.alignItems = new StyleEnum<Align>(Align.Stretch);
 4289                style.alignSelf = new StyleEnum<Align>(Align.Auto);
 4290                style.alignContent = new StyleEnum<Align>(Align.Stretch);
 4291                style.position = new StyleEnum<Position>(Position.Absolute);
 292            }
 293
 11294            return parentDataModel;
 295        }
 296
 297        internal static void SortSceneUiTree(IInternalECSComponent<InternalUiContainer> internalUiContainerComponent,
 298            ICollection<IParcelScene> scenesToSort)
 299        {
 3300            Dictionary<VisualElement, Dictionary<long, RightOfData>> sortSceneTree = new Dictionary<VisualElement, Dicti
 301
 302            // Setup UI sorting
 3303            var allContainers = internalUiContainerComponent.GetForAll();
 304
 38305            for (int i = 0; i < allContainers.Count; i++)
 306            {
 16307                var uiContainerData = allContainers[i].value;
 308
 16309                if (!scenesToSort.Contains(uiContainerData.scene))
 310                    continue;
 311
 16312                InternalUiContainer model = uiContainerData.model;
 313
 314                // If not parented yet, we skip it
 16315                if (model.parentElement == null)
 316                    continue;
 317
 318                // Ignore root scene UI container
 13319                if (uiContainerData.entity.entityId == SpecialEntityId.SCENE_ROOT_ENTITY)
 320                    continue;
 321
 13322                if (!sortSceneTree.TryGetValue(model.parentElement, out var sceneTreeDictionary))
 323                {
 5324                    sceneTreeDictionary = new Dictionary<long, RightOfData>();
 5325                    sortSceneTree[model.parentElement] = sceneTreeDictionary;
 326                }
 327
 13328                sceneTreeDictionary[model.rightOf] = new RightOfData(model.rootElement,
 329                    uiContainerData.entity.entityId);
 330
 13331                model.shouldSort = false;
 13332                internalUiContainerComponent.PutFor(uiContainerData.scene, uiContainerData.entity.entityId, model);
 333            }
 334
 335            // Apply UI sorting
 16336            foreach (var pairs in sortSceneTree)
 337            {
 5338                VisualElement parentElement = pairs.Key;
 5339                Dictionary<long, RightOfData> sceneSort = pairs.Value;
 340
 5341                int index = 0;
 5342                long nextElementId = 0;
 343
 18344                while (sceneSort.TryGetValue(nextElementId, out RightOfData rightOfData))
 345                {
 13346                    sceneSort.Remove(nextElementId);
 13347                    rightOfData.element.PlaceInFront(parentElement[index]);
 13348                    nextElementId = rightOfData.id;
 13349                    index++;
 13350                }
 351            }
 3352        }
 353
 354        private static void SetDocumentActive(UIDocument uiDocument, bool active)
 355        {
 15356            uiDocument.rootVisualElement.style.display = active ? DisplayStyle.Flex : DisplayStyle.None;
 15357        }
 358
 359        private void OnSceneUiVisibilityAdded(int sceneNumber, bool enabled)
 360        {
 3361            int currentSceneNumber = worldState.GetCurrentSceneNumber();
 3362            IParcelScene currentScene = worldState.GetScene(currentSceneNumber);
 363
 3364            if (enabled
 365                && currentScene != null
 366                && currentScene.sceneData.sceneNumber != sceneNumber
 367                && currentScene.sceneData.scenePortableExperienceFeatureToggles == ScenePortableExperienceFeatureToggles
 1368                return;
 369
 2370            SetSceneUiVisibility(sceneNumber, enabled);
 2371        }
 372
 373        private void OnSceneUiVisibilitySet(IEnumerable<KeyValuePair<int, bool>> list)
 374        {
 0375            foreach ((int sceneNumber, bool enabled) in list)
 0376                OnSceneUiVisibilityAdded(sceneNumber, enabled);
 0377        }
 378
 379        private void SetSceneUiVisibility(int sceneNumber, bool enabled)
 380        {
 2381            IParcelScene scene = GetCurrentScene(sceneNumber, loadedScenes);
 382
 2383            if (scene == null) return;
 384
 2385            ECSComponentData<InternalUiContainer>? sceneUiContainer =
 386                internalUiContainerComponent.GetFor(scene, SpecialEntityId.SCENE_ROOT_ENTITY);
 387
 2388            if (sceneUiContainer == null) return;
 389
 2390            sceneUiContainer.Value.model.rootElement.style.display =
 391                new StyleEnum<DisplayStyle>(enabled ? DisplayStyle.Flex : DisplayStyle.None);
 2392        }
 393
 394        private void UpdatePortableExperiencesVisibilityByFeatureToggle(IParcelScene currentScene, IReadOnlyList<IParcel
 395        {
 42396            if (currentScene == null) return;
 397
 80398            for (var i = 0; i < scenes.Count; i++)
 399            {
 22400                IParcelScene pxScene = scenes[i];
 22401                if (!pxScene.isPortableExperience) continue;
 402
 0403                ScenePortableExperienceFeatureToggles featureToggle =
 404                    currentScene.sceneData.scenePortableExperienceFeatureToggles;
 405
 0406                if (featureToggle == ScenePortableExperienceFeatureToggles.HideUi)
 0407                    SetSceneUiVisibility(pxScene.sceneData.sceneNumber, false);
 408            }
 18409        }
 410
 411        private readonly struct RightOfData
 412        {
 413            public readonly long id;
 414            public readonly VisualElement element;
 415
 416            public RightOfData(VisualElement element, long id)
 417            {
 13418                this.id = id;
 13419                this.element = element;
 13420            }
 421        }
 422    }
 423}

Methods/Properties

ECSScenesUiSystem(UnityEngine.UIElements.UIDocument, .IInternalECSComponent[InternalUiContainer], .BaseList[IParcelScene], DCL.IWorldState, BooleanVariable, .BaseVariable[Boolean], .BaseDictionary[Int32,Boolean])
Dispose()
Update()
LoadedScenesOnOnRemoved(DCL.Controllers.IParcelScene)
OnHideAllUiEvent(System.Boolean, System.Boolean)
OnHideCurrentSceneUiEvent(System.Boolean, System.Boolean)
ApplyParenting(System.Collections.Generic.HashSet`1[[DCL.Controllers.IParcelScene, DCL.Runtime.Interfaces, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]&, UnityEngine.UIElements.UIDocument, .IInternalECSComponent[InternalUiContainer], System.Int32, System.Collections.Generic.List[IParcelScene])
ClearCurrentSceneUI(UnityEngine.UIElements.UIDocument, DCL.Controllers.IParcelScene, .IInternalECSComponent[InternalUiContainer])
GetCurrentScene(System.Int32, System.Collections.Generic.IList[IParcelScene])
ApplySceneUI(.IInternalECSComponent[InternalUiContainer], UnityEngine.UIElements.UIDocument, DCL.Controllers.IParcelScene, .BaseVariable[Boolean], .BaseDictionary[Int32,Boolean])
GetParentContainerModel(.IInternalECSComponent[InternalUiContainer], DCL.Controllers.IParcelScene, System.Int64)
SortSceneUiTree(.IInternalECSComponent[InternalUiContainer], System.Collections.Generic.ICollection[IParcelScene])
SetDocumentActive(UnityEngine.UIElements.UIDocument, System.Boolean)
OnSceneUiVisibilityAdded(System.Int32, System.Boolean)
OnSceneUiVisibilitySet(System.Collections.Generic.IEnumerable[KeyValuePair`2])
SetSceneUiVisibility(System.Int32, System.Boolean)
UpdatePortableExperiencesVisibilityByFeatureToggle(DCL.Controllers.IParcelScene, System.Collections.Generic.IReadOnlyList[IParcelScene])
RightOfData(UnityEngine.UIElements.VisualElement, System.Int64)