< Summary

Class:DCL.ECS7.CanvasPainter
Assembly:DCL.ECS7.CanvasPainter
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/CanvasPainter/CanvasPainter.cs
Covered lines:153
Uncovered lines:36
Coverable lines:189
Total lines:474
Line coverage:80.9% (153 of 189)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
CanvasPainter(...)0%110100%
Dispose()0%220100%
SetScene(...)0%220100%
ClearAndHideRootLayout()0%110100%
RemoveVisualElement(...)0%3.073080%
Update()0%7.127086.67%
DrawUI(...)0%330100%
DrawUI()0%5.015093.33%
CreateContainers(...)0%440100%
CreateTextElements(...)0%3.692025%
TryToSetOrphanParents()0%5.673033.33%
SetParent(...)0%440100%
TransformToVisualElement(...)0%20200100%
GetUnit(...)0%12300%
GetOverflow(...)0%3.333066.67%
GetDisplay(...)0%5.673033.33%
GetJustify(...)0%21.527033.33%
GetWrap(...)0%5.395075%
GetFlexDirection(...)0%8.36060%
GetPosition(...)0%3.333066.67%
GetAlign(...)0%35.357016.67%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/CanvasPainter/CanvasPainter.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Linq;
 3using DCL.Controllers;
 4using DCL.ECS7.UI;
 5using DCL.ECSComponents;
 6using DCL.ECSRuntime;
 7using DCL.Models;
 8using UnityEngine;
 9using UnityEngine.UIElements;
 10using Random = UnityEngine.Random;
 11
 12namespace DCL.ECS7
 13{
 14    public struct VisualElementRepresentation
 15    {
 16        public long entityId;
 17        public long parentId;
 18        public VisualElement visualElement;
 19        public VisualElement parentVisualElement;
 20    }
 21
 22    public class CanvasPainter
 23    {
 24        private const string UI_DOCUMENT_PREFAB_PATH = "RootNode";
 25        private const int FRAMES_PER_PAINT = 10;
 26
 27        internal readonly UIDocument rootNode;
 28        internal readonly IUIDataContainer dataContainer;
 29        private readonly RendererState rendererState;
 30        private readonly BaseList<IParcelScene> loadedScenes;
 31        private readonly IUpdateEventHandler updateEventHandler;
 32        private readonly IWorldState worldState;
 33        private readonly ECSComponentsManager componentsManager;
 34
 35        internal UISceneDataContainer sceneDataContainerToUse;
 36        private IParcelScene scene;
 37
 1238        internal Dictionary<long, VisualElementRepresentation> visualElements = new Dictionary<long, VisualElementRepres
 1239        internal List<VisualElementRepresentation> orphanVisualElements = new List<VisualElementRepresentation>();
 40
 41        internal IECSReadOnlyComponentsGroup<PBUiTransform, PBUiText> textComponentGroup;
 42
 43        internal int framesCounter = 0;
 44        internal bool canvasCleared = false;
 45
 1246        public CanvasPainter(DataStore_ECS7 dataStoreEcs7, RendererState rendererState, IUpdateEventHandler updateEventH
 47        {
 1248            this.updateEventHandler = updateEventHandler;
 1249            this.loadedScenes = dataStoreEcs7.scenes;
 1250            this.rendererState = rendererState;
 1251            this.dataContainer = dataStoreEcs7.uiDataContainer;
 1252            this.worldState = worldState;
 53
 1254            var prefab = Resources.Load<UIDocument>(UI_DOCUMENT_PREFAB_PATH);
 1255            rootNode = GameObject.Instantiate(prefab);
 56
 1257            updateEventHandler.AddListener(IUpdateEventHandler.EventType.Update, Update);
 58
 59            // Group Components
 1260            textComponentGroup = componentsManager.CreateComponentGroup<PBUiTransform, PBUiText>(ComponentID.UI_TRANSFOR
 61#if UNITY_EDITOR
 1262            rootNode.name = "Scene Canvas";
 63#endif
 1264        }
 65
 66        public void Dispose()
 67        {
 1268            GameObject.Destroy(rootNode.gameObject);
 1269            updateEventHandler.RemoveListener(IUpdateEventHandler.EventType.Update, Update);
 1270            if(sceneDataContainerToUse != null)
 871                sceneDataContainerToUse.OnUITransformRemoved -= RemoveVisualElement;
 1272        }
 73
 74        internal void SetScene(IParcelScene scene)
 75        {
 976            this.scene = scene;
 77
 978            if(sceneDataContainerToUse != null)
 179                sceneDataContainerToUse.OnUITransformRemoved -= RemoveVisualElement;
 80
 81            // We get the UI information of the scene to paint their canvas
 982            sceneDataContainerToUse = dataContainer.GetDataContainer(scene);
 983            sceneDataContainerToUse.OnUITransformRemoved += RemoveVisualElement;
 984            ClearAndHideRootLayout();
 985        }
 86
 87        private void ClearAndHideRootLayout()
 88        {
 989            rootNode.rootVisualElement.visible = false;
 990            rootNode.rootVisualElement.Clear();
 991            canvasCleared = true;
 992        }
 93
 94        private void RemoveVisualElement(IDCLEntity entity)
 95        {
 96            // We try to get the parent
 197            if (visualElements.TryGetValue(entity.entityId, out VisualElementRepresentation visualElementRepresentantion
 98            {
 199                if (visualElementRepresentantion.parentVisualElement != null)
 0100                    visualElementRepresentantion.parentVisualElement.Remove(visualElementRepresentantion.visualElement);
 101
 1102                visualElements.Remove(entity.entityId);
 103            }
 1104        }
 105
 106        internal void Update()
 107        {
 5108            framesCounter++;
 5109            if (!rendererState.Get() || framesCounter < FRAMES_PER_PAINT)
 0110                return;
 111
 5112            framesCounter = 0;
 113
 114            // We look in which scene is the player, and draw the UI of that scene
 5115            bool sceneFound = false;
 12116            for (int i = 0; i < loadedScenes.Count; i++)
 117            {
 6118                if(loadedScenes[i].sceneData.id != worldState.GetCurrentSceneId())
 119                    continue;
 5120                sceneFound = true;
 5121                DrawUI(loadedScenes[i]);
 5122                break;
 123            }
 124
 125            // If we are entering in an empty parcel, or ECS6 scene, we should also clear the UI
 5126            if (!sceneFound && !canvasCleared)
 0127                ClearAndHideRootLayout();
 5128        }
 129
 130        private void DrawUI(IParcelScene scene)
 131        {
 5132            if(this.scene != scene || canvasCleared)
 5133                SetScene(scene);
 134
 5135            DrawUI();
 5136        }
 137
 138        private void DrawUI()
 139        {
 140            // If the canvas hasn't change or if there is no canvas we skip
 5141            if (!canvasCleared && (!sceneDataContainerToUse.IsDirty() || !sceneDataContainerToUse.sceneCanvasTransform.A
 0142                return;
 143
 5144            canvasCleared = false;
 5145            sceneDataContainerToUse.UIRendered();
 146
 147            // We ensure that the root canvas is visible
 5148            rootNode.rootVisualElement.visible = true;
 149
 150            // This should be the way to go when we implement the UI events
 151            // visualElement.RegisterCallback<ClickEvent>(ev => Debug.Log("Clicked"));
 152
 153            // It can happen that we received or processed the child before the parent, that make some elements orphan
 154            // If we have some orphan elements, we try to search and assign the parent here, if we don't found it, we wi
 5155            TryToSetOrphanParents();
 156
 5157            List<long> entitiesWithRendererComponent = new List<long>();
 158
 159            // This will create the text elements along their UITransform
 5160            CreateTextElements(entitiesWithRendererComponent);
 161
 162            // This will create the containers to position the elements
 5163            CreateContainers(entitiesWithRendererComponent);
 164
 165            // We set the parent of all the elements that has been created
 44166            foreach (KeyValuePair<long,VisualElementRepresentation> kvp in visualElements)
 167            {
 17168                var visualElement =  kvp.Value;
 17169                SetParent(ref visualElement);
 170            }
 5171        }
 172
 173        internal void CreateContainers(List<long> excludedEntities)
 174        {
 6175            var enumerator = sceneDataContainerToUse.sceneCanvasTransform.GetEnumerator();
 176            try
 177            {
 178                // We create all the elements that doesn't have a rendering ( Image, Text...etc) since we need them to p
 24179                while (enumerator.MoveNext())
 180                {
 18181                    var kvp = enumerator.Current;
 182                    // If the entity doesn't exist we skip
 18183                    if (!scene.entities.TryGetValue( kvp.Key, out IDCLEntity entity))
 184                        continue;
 185
 186                    // If the entity already have a rendering element, we skip since we don't need to create it again
 18187                    if (excludedEntities.Contains(entity.entityId))
 188                        continue;
 189
 190                    // We create the element to position the rendering element correctly
 18191                    var visualElement = TransformToVisualElement(kvp.Value,  new Image());
 18192                    visualElements[entity.entityId] = new VisualElementRepresentation()
 193                    {
 194                        entityId = entity.entityId,
 195                        parentId = entity.parentId,
 196                        visualElement = visualElement
 197                    };
 198                }
 6199            }
 200            finally
 201            {
 6202                enumerator.Dispose();
 6203            }
 6204        }
 205
 206        internal void CreateTextElements(List<long> entitiesWithRendererComponent)
 207        {
 208            IECSReadOnlyComponentData<PBUiTransform> componentData;
 209
 210            // We create all the text elements
 10211            for(int i = 0; i < textComponentGroup.group.Count; i++)
 212            {
 0213                componentData = textComponentGroup.group[i].componentData1;
 214
 0215                var textElement = new TextElement();
 0216                textElement.text = textComponentGroup.group[i].componentData2.model.Text;
 0217                var color = textComponentGroup.group[i].componentData2.model.TextColor;
 0218                textElement.style.color =  new UnityEngine.Color(color.R, color.G,color.B,1);
 219
 220                // We create the text element
 0221                var visualElement = TransformToVisualElement(componentData.model, textElement, false);
 222
 0223                visualElements[componentData.entity.entityId] = new VisualElementRepresentation()
 224                {
 225                    entityId = componentData.entity.entityId,
 226                    parentId = componentData.entity.parentId,
 227                    visualElement = visualElement
 228                };
 229
 230                // We add the entity to a list so it doesn't create a visual element for the PBUiTransform since the tra
 0231                entitiesWithRendererComponent.Add(componentData.entity.entityId);
 232            }
 5233        }
 234
 235        internal void TryToSetOrphanParents()
 236        {
 10237            for(int i = 0; i < orphanVisualElements.Count; i++)
 238            {
 0239                var visualElement = orphanVisualElements[i];
 0240                SetParent(ref visualElement, false);
 241
 242                // If we found the parent, we remove it and set it visible
 0243                if (orphanVisualElements[i].parentVisualElement != null)
 244                {
 0245                    orphanVisualElements[i].visualElement.visible = true;
 0246                    orphanVisualElements.Remove(orphanVisualElements[i]);
 247                }
 248            }
 5249        }
 250
 251        internal void SetParent(ref VisualElementRepresentation visualElementRepresentation, bool addToOrphanList = true
 252        {
 253            // if the parent is the root canvas, it doesn't have parent, we skip
 20254            if (visualElementRepresentation.parentId == SpecialEntityId.SCENE_ROOT_ENTITY)
 255            {
 5256                rootNode.rootVisualElement.Add(visualElementRepresentation.visualElement);
 5257                visualElementRepresentation.parentVisualElement = rootNode.rootVisualElement;
 5258            }
 259            else
 260            {
 261                // We try to get the parent
 15262                if (visualElements.TryGetValue(visualElementRepresentation.parentId, out VisualElementRepresentation par
 263                {
 14264                    parentVisualElementRepresentantion.visualElement.Add(visualElementRepresentation.visualElement);
 14265                    visualElementRepresentation.parentVisualElement = parentVisualElementRepresentantion.visualElement;
 14266                }
 1267                else if (addToOrphanList)
 268                {
 269                    // There is no father so it is orphan right now. We can try to find the father the next time, we hid
 1270                    visualElementRepresentation.visualElement.visible = false;
 1271                    orphanVisualElements.Add(visualElementRepresentation);
 272                }
 273            }
 1274        }
 275
 276        internal VisualElement TransformToVisualElement(PBUiTransform model, VisualElement element, bool randomColor = t
 277        {
 19278            element.style.display = GetDisplay(model.Display);
 19279            element.style.overflow = GetOverflow(model.Overflow);
 280
 281            // Flex
 19282            element.style.flexDirection = GetFlexDirection(model.FlexDirection);
 19283            if (!float.IsNaN(model.FlexBasis))
 6284                element.style.flexBasis = new Length(model.FlexBasis, GetUnit(model.FlexBasisUnit));
 285
 19286            element.style.flexGrow = model.FlexGrow;
 19287            element.style.flexShrink = model.FlexShrink;
 19288            element.style.flexWrap = GetWrap(model.FlexWrap);
 289
 290            // Align
 19291            if (model.AlignContent != YGAlign.FlexStart)
 3292                element.style.alignContent = GetAlign(model.AlignContent);
 19293            if (model.AlignItems != YGAlign.Auto)
 3294                element.style.alignItems = GetAlign(model.AlignItems);
 19295            if (model.AlignSelf != YGAlign.Auto)
 6296                element.style.alignSelf = GetAlign(model.AlignSelf);
 19297            element.style.justifyContent = GetJustify(model.JustifyContent);
 298
 299            // Layout size
 19300            if (!float.IsNaN(model.Height))
 19301                element.style.height = new Length(model.Height, GetUnit(model.HeightUnit));
 19302            if (!float.IsNaN(model.Width))
 19303                element.style.width = new Length(model.Width, GetUnit(model.WidthUnit));
 304
 19305            if (!float.IsNaN(model.MaxWidth))
 3306                element.style.maxWidth = new Length(model.MaxWidth, GetUnit(model.MaxWidthUnit));
 19307            if (!float.IsNaN(model.MaxHeight))
 3308                element.style.maxHeight = new Length(model.MaxHeight, GetUnit(model.MaxHeightUnit));
 309
 19310            if (!float.IsNaN(model.MinHeight))
 3311                element.style.minHeight = new Length(model.MinHeight, GetUnit(model.MinHeightUnit));
 19312            if (!float.IsNaN(model.MinWidth))
 3313                element.style.minWidth = new Length(model.MinWidth, GetUnit(model.MinWidthUnit));
 314
 315            // Paddings
 19316            if (!Mathf.Approximately(model.PaddingBottom, 0))
 3317                element.style.paddingBottom = new Length(model.PaddingBottom, GetUnit(model.PaddingBottomUnit));
 19318            if (!Mathf.Approximately(model.PaddingLeft, 0))
 3319                element.style.paddingLeft = new Length(model.PaddingLeft, GetUnit(model.PaddingLeftUnit));
 19320            if (!Mathf.Approximately(model.PaddingRight, 0))
 3321                element.style.paddingRight = new Length(model.PaddingRight, GetUnit(model.PaddingRightUnit));
 19322            if (!Mathf.Approximately(model.PaddingTop, 0))
 3323                element.style.paddingTop = new Length(model.PaddingTop, GetUnit(model.PaddingTopUnit));
 324
 325            // Margins
 19326            if (!Mathf.Approximately(model.MarginLeft, 0))
 3327                element.style.marginLeft = new Length(model.MarginLeft, GetUnit(model.MarginLeftUnit));
 19328            if (!Mathf.Approximately(model.MarginRight, 0))
 3329                element.style.marginRight = new Length(model.MarginRight, GetUnit(model.MarginRightUnit));
 19330            if (!Mathf.Approximately(model.MarginBottom, 0))
 3331                element.style.marginBottom = new Length(model.MarginBottom, GetUnit(model.MarginBottomUnit));
 19332            if (!Mathf.Approximately(model.MarginTop, 0))
 3333                element.style.marginTop = new Length(model.MarginTop, GetUnit(model.MarginTopUnit));
 334
 335            // Borders
 19336            element.style.borderBottomWidth = model.BorderBottom;
 19337            element.style.borderLeftWidth = model.BorderLeft;
 19338            element.style.borderRightWidth = model.BorderRight;
 19339            element.style.borderTopWidth = model.BorderTop;
 340
 341            // Position
 19342            element.style.position = GetPosition(model.PositionType);
 343
 344            // This is for debugging purposes, we will change this to a proper approach so devs can debug the UI easily.
 19345            if (randomColor)
 19346                element.style.backgroundColor = Random.ColorHSV();
 347
 19348            return element;
 349        }
 350
 351        internal LengthUnit GetUnit(YGUnit unit)
 352        {
 353            switch (unit)
 354            {
 355                case YGUnit.Point:
 0356                    return LengthUnit.Pixel;
 357                case YGUnit.Percent:
 0358                    return LengthUnit.Percent;
 359                default:
 0360                    return LengthUnit.Pixel;
 361            }
 362        }
 363
 364        internal StyleEnum<Overflow> GetOverflow (YGOverflow overflow)
 365        {
 366            switch (overflow)
 367            {
 368                case YGOverflow.Visible:
 17369                    return Overflow.Visible;
 370                case YGOverflow.Hidden:
 3371                    return Overflow.Hidden;
 372                default:
 0373                    return Overflow.Visible;
 374            }
 375        }
 376
 377        internal StyleEnum<DisplayStyle> GetDisplay (YGDisplay display)
 378        {
 379            switch (display)
 380            {
 381                case YGDisplay.Flex:
 20382                    return DisplayStyle.Flex;
 383                    break;
 384                case YGDisplay.None:
 0385                    return DisplayStyle.None;
 386                default:
 0387                    return DisplayStyle.Flex;
 388            }
 389        }
 390
 391        internal StyleEnum<Justify> GetJustify (YGJustify justify)
 392        {
 393            switch (justify)
 394            {
 395                case YGJustify.FlexStart:
 16396                    return Justify.FlexStart;
 397                case YGJustify.Center:
 5398                    return Justify.Center;
 399                case YGJustify.FlexEnd:
 0400                    return Justify.FlexEnd;
 401                case YGJustify.SpaceBetween:
 0402                    return Justify.SpaceBetween;
 403                case YGJustify.SpaceAround:
 0404                    return Justify.SpaceAround;
 405                default:
 0406                    return Justify.FlexStart;
 407            }
 408        }
 409
 410        internal StyleEnum<Wrap> GetWrap (YGWrap wrap)
 411        {
 412            switch (wrap)
 413            {
 414                case YGWrap.NoWrap:
 16415                    return Wrap.NoWrap;
 416                case YGWrap.Wrap:
 3417                    return Wrap.Wrap;
 418                case YGWrap.WrapReverse:
 2419                    return Wrap.WrapReverse;
 420                default:
 0421                    return Wrap.Wrap;
 422            }
 423        }
 424
 425        internal StyleEnum<FlexDirection> GetFlexDirection (YGFlexDirection direction)
 426        {
 427            switch (direction)
 428            {
 429                case YGFlexDirection.Column:
 5430                    return FlexDirection.Column;
 431                case YGFlexDirection.ColumnReverse:
 0432                    return FlexDirection.ColumnReverse;
 433                case YGFlexDirection.Row:
 15434                    return FlexDirection.Row;
 435                case YGFlexDirection.RowReverse:
 2436                    return FlexDirection.RowReverse;
 437                default:
 0438                    return FlexDirection.Row;
 439            }
 440        }
 441
 442        internal StyleEnum<Position> GetPosition(YGPositionType  positionType)
 443        {
 444            switch (positionType)
 445            {
 446                case YGPositionType.Relative:
 17447                    return Position.Relative;
 448                case YGPositionType.Absolute:
 4449                    return Position.Absolute;
 450                default:
 0451                    return Position.Relative;
 452            }
 453        }
 454
 455        internal StyleEnum<Align> GetAlign(YGAlign align)
 456        {
 457            switch (align)
 458            {
 459                case YGAlign.Auto:
 0460                    return Align.Auto;
 461                case YGAlign.FlexStart:
 0462                    return Align.FlexStart;
 463                case YGAlign.Center:
 21464                    return Align.Center;
 465                case YGAlign.FlexEnd:
 0466                    return Align.FlexEnd;
 467                case YGAlign.Stretch:
 0468                    return Align.Stretch;
 469                default:
 0470                    return Align.Auto;
 471            }
 472        }
 473    }
 474}