< 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:144
Uncovered lines:45
Coverable lines:189
Total lines:479
Line coverage:76.1% (144 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.475073.33%
CreateContainers(...)0%440100%
CreateTextElements(...)0%3.692025%
TryToSetOrphanParents()0%5.673033.33%
SetParent(...)0%440100%
TransformToVisualElement(...)0%20200100%
GetUnit(...)0%12300%
GetOverflow(...)0%5.673033.33%
GetDisplay(...)0%5.673033.33%
GetJustify(...)0%35.357016.67%
GetWrap(...)0%15.555025%
GetFlexDirection(...)0%24.436020%
GetPosition(...)0%5.673033.33%
GetAlign(...)0%35.357016.67%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL;
 5using DCL.Controllers;
 6using DCL.ECS7;
 7using DCL.ECS7.UI;
 8using DCL.ECSComponents;
 9using DCL.ECSRuntime;
 10using DCL.Models;
 11using UnityEngine;
 12using UnityEngine.EventSystems;
 13using UnityEngine.UIElements;
 14using Environment = DCL.Environment;
 15using Random = UnityEngine.Random;
 16
 17namespace DCL.ECS7
 18{
 19    public struct VisualElementRepresentation
 20    {
 21        public long entityId;
 22        public long parentId;
 23        public VisualElement visualElement;
 24        public VisualElement parentVisualElement;
 25    }
 26
 27    public class CanvasPainter
 28    {
 29        private const string UI_DOCUMENT_PREFAB_PATH = "RootNode";
 30        private const int FRAMES_PER_PAINT = 10;
 31
 32        internal readonly UIDocument rootNode;
 33        internal readonly IUIDataContainer dataContainer;
 34        private readonly RendererState rendererState;
 35        private readonly BaseList<IParcelScene> loadedScenes;
 36        private readonly IUpdateEventHandler updateEventHandler;
 37        private readonly IWorldState worldState;
 38        private readonly ECSComponentsManager componentsManager;
 39
 40        internal UISceneDataContainer sceneDataContainerToUse;
 41        private IParcelScene scene;
 42
 843        internal Dictionary<long, VisualElementRepresentation> visualElements = new Dictionary<long, VisualElementRepres
 844        internal List<VisualElementRepresentation> orphanVisualElements = new List<VisualElementRepresentation>();
 45
 46        internal IECSReadOnlyComponentsGroup<PBUiTransform, PBUiText> textComponentGroup;
 47
 48        internal int framesCounter = 0;
 49        internal bool canvasCleared = false;
 50
 851        public CanvasPainter(DataStore_ECS7 dataStoreEcs7, RendererState rendererState, IUpdateEventHandler updateEventH
 52        {
 853            this.updateEventHandler = updateEventHandler;
 854            this.loadedScenes = dataStoreEcs7.scenes;
 855            this.rendererState = rendererState;
 856            this.dataContainer = dataStoreEcs7.uiDataContainer;
 857            this.worldState = worldState;
 58
 859            var prefab = Resources.Load<UIDocument>(UI_DOCUMENT_PREFAB_PATH);
 860            rootNode = GameObject.Instantiate(prefab);
 61
 862            updateEventHandler.AddListener(IUpdateEventHandler.EventType.Update, Update);
 63
 64            // Group Components
 865            textComponentGroup = componentsManager.CreateComponentGroup<PBUiTransform, PBUiText>(ComponentID.UI_TRANSFOR
 66#if UNITY_EDITOR
 867            rootNode.name = "Scene Canvas";
 68#endif
 869        }
 70
 71        public void Dispose()
 72        {
 873            GameObject.Destroy(rootNode.gameObject);
 874            updateEventHandler.RemoveListener(IUpdateEventHandler.EventType.Update, Update);
 875            if(sceneDataContainerToUse != null)
 476                sceneDataContainerToUse.OnUITransformRemoved -= RemoveVisualElement;
 877        }
 78
 79        internal void SetScene(IParcelScene scene)
 80        {
 581            this.scene = scene;
 82
 583            if(sceneDataContainerToUse != null)
 184                sceneDataContainerToUse.OnUITransformRemoved -= RemoveVisualElement;
 85
 86            // We get the UI information of the scene to paint their canvas
 587            sceneDataContainerToUse = dataContainer.GetDataContainer(scene);
 588            sceneDataContainerToUse.OnUITransformRemoved += RemoveVisualElement;
 589            ClearAndHideRootLayout();
 590        }
 91
 92        private void ClearAndHideRootLayout()
 93        {
 594            rootNode.rootVisualElement.visible = false;
 595            rootNode.rootVisualElement.Clear();
 596            canvasCleared = true;
 597        }
 98
 99        private void RemoveVisualElement(IDCLEntity entity)
 100        {
 101            // We try to get the parent
 1102            if (visualElements.TryGetValue(entity.entityId, out VisualElementRepresentation visualElementRepresentantion
 103            {
 1104                if (visualElementRepresentantion.parentVisualElement != null)
 0105                    visualElementRepresentantion.parentVisualElement.Remove(visualElementRepresentantion.visualElement);
 106
 1107                visualElements.Remove(entity.entityId);
 108            }
 1109        }
 110
 111        internal void Update()
 112        {
 1113            framesCounter++;
 1114            if (!rendererState.Get() || framesCounter < FRAMES_PER_PAINT)
 0115                return;
 116
 1117            framesCounter = 0;
 118
 119            // We look in which scene is the player, and draw the UI of that scene
 1120            bool sceneFound = false;
 4121            for (int i = 0; i < loadedScenes.Count; i++)
 122            {
 2123                if(loadedScenes[i].sceneData.id != worldState.currentSceneId)
 124                    continue;
 1125                sceneFound = true;
 1126                DrawUI(loadedScenes[i]);
 1127                break;
 128            }
 129
 130            // If we are entering in an empty parcel, or ECS6 scene, we should also clear the UI
 1131            if (!sceneFound && !canvasCleared)
 0132                ClearAndHideRootLayout();
 1133        }
 134
 135        private void DrawUI(IParcelScene scene)
 136        {
 1137            if(this.scene != scene || canvasCleared)
 1138                SetScene(scene);
 139
 1140            DrawUI();
 1141        }
 142
 143        private void DrawUI()
 144        {
 145            // If the canvas hasn't change or if there is no canvas we skip
 1146            if (!canvasCleared && (!sceneDataContainerToUse.IsDirty() || !sceneDataContainerToUse.sceneCanvasTransform.A
 0147                return;
 148
 1149            canvasCleared = false;
 1150            sceneDataContainerToUse.UIRendered();
 151
 152            // We ensure that the root canvas is visible
 1153            rootNode.rootVisualElement.visible = true;
 154
 155            // This should be the way to go when we implement the UI events
 156            // visualElement.RegisterCallback<ClickEvent>(ev => Debug.Log("Clicked"));
 157
 158            // It can happen that we received or processed the child before the parent, that make some elements orphan
 159            // If we have some orphan elements, we try to search and assign the parent here, if we don't found it, we wi
 1160            TryToSetOrphanParents();
 161
 1162            List<long> entitiesWithRendererComponent = new List<long>();
 163
 164            // This will create the text elements along their UITransform
 1165            CreateTextElements(entitiesWithRendererComponent);
 166
 167            // This will create the containers to position the elements
 1168            CreateContainers(entitiesWithRendererComponent);
 169
 170            // We set the parent of all the elements that has been created
 2171            foreach (KeyValuePair<long,VisualElementRepresentation> kvp in visualElements)
 172            {
 0173                var visualElement =  kvp.Value;
 0174                SetParent(ref visualElement);
 175            }
 1176        }
 177
 178        internal void CreateContainers(List<long> excludedEntities)
 179        {
 2180            var enumerator = sceneDataContainerToUse.sceneCanvasTransform.GetEnumerator();
 181            try
 182            {
 183                // We create all the elements that doesn't have a rendering ( Image, Text...etc) since we need them to p
 3184                while (enumerator.MoveNext())
 185                {
 1186                    var kvp = enumerator.Current;
 187                    // If the entity doesn't exist we skip
 1188                    if (!scene.entities.TryGetValue( kvp.Key, out IDCLEntity entity))
 189                        continue;
 190
 191                    // If the entity already have a rendering element, we skip since we don't need to create it again
 1192                    if (excludedEntities.Contains(entity.entityId))
 193                        continue;
 194
 195                    // We create the element to position the rendering element correctly
 1196                    var visualElement = TransformToVisualElement(kvp.Value,  new Image());
 1197                    visualElements[entity.entityId] = new VisualElementRepresentation()
 198                    {
 199                        entityId = entity.entityId,
 200                        parentId = entity.parentId,
 201                        visualElement = visualElement
 202                    };
 203                }
 2204            }
 205            finally
 206            {
 2207                enumerator.Dispose();
 2208            }
 2209        }
 210
 211        internal void CreateTextElements(List<long> entitiesWithRendererComponent)
 212        {
 213            IECSReadOnlyComponentData<PBUiTransform> componentData;
 214
 215            // We create all the text elements
 2216            for(int i = 0; i < textComponentGroup.group.Count; i++)
 217            {
 0218                componentData = textComponentGroup.group[i].componentData1;
 219
 0220                var textElement = new TextElement();
 0221                textElement.text = textComponentGroup.group[i].componentData2.model.Text;
 0222                var color = textComponentGroup.group[i].componentData2.model.TextColor;
 0223                textElement.style.color =  new UnityEngine.Color(color.R, color.G,color.B,1);
 224
 225                // We create the text element
 0226                var visualElement = TransformToVisualElement(componentData.model, textElement, false);
 227
 0228                visualElements[componentData.entity.entityId] = new VisualElementRepresentation()
 229                {
 230                    entityId = componentData.entity.entityId,
 231                    parentId = componentData.entity.parentId,
 232                    visualElement = visualElement
 233                };
 234
 235                // We add the entity to a list so it doesn't create a visual element for the PBUiTransform since the tra
 0236                entitiesWithRendererComponent.Add(componentData.entity.entityId);
 237            }
 1238        }
 239
 240        internal void TryToSetOrphanParents()
 241        {
 2242            for(int i = 0; i < orphanVisualElements.Count; i++)
 243            {
 0244                var visualElement = orphanVisualElements[i];
 0245                SetParent(ref visualElement, false);
 246
 247                // If we found the parent, we remove it and set it visible
 0248                if (orphanVisualElements[i].parentVisualElement != null)
 249                {
 0250                    orphanVisualElements[i].visualElement.visible = true;
 0251                    orphanVisualElements.Remove(orphanVisualElements[i]);
 252                }
 253            }
 1254        }
 255
 256        internal void SetParent(ref VisualElementRepresentation visualElementRepresentation, bool addToOrphanList = true
 257        {
 258            // if the parent is the root canvas, it doesn't have parent, we skip
 3259            if (visualElementRepresentation.parentId == SpecialEntityId.SCENE_ROOT_ENTITY)
 260            {
 1261                rootNode.rootVisualElement.Add(visualElementRepresentation.visualElement);
 1262                visualElementRepresentation.parentVisualElement = rootNode.rootVisualElement;
 1263            }
 264            else
 265            {
 266                // We try to get the parent
 2267                if (visualElements.TryGetValue(visualElementRepresentation.parentId, out VisualElementRepresentation par
 268                {
 1269                    parentVisualElementRepresentantion.visualElement.Add(visualElementRepresentation.visualElement);
 1270                    visualElementRepresentation.parentVisualElement = parentVisualElementRepresentantion.visualElement;
 1271                }
 1272                else if (addToOrphanList)
 273                {
 274                    // There is no father so it is orphan right now. We can try to find the father the next time, we hid
 1275                    visualElementRepresentation.visualElement.visible = false;
 1276                    orphanVisualElements.Add(visualElementRepresentation);
 277                }
 278            }
 1279        }
 280
 281        internal VisualElement TransformToVisualElement(PBUiTransform model, VisualElement element, bool randomColor = t
 282        {
 2283            element.style.display = GetDisplay(model.Display);
 2284            element.style.overflow = GetOverflow(model.Overflow);
 285
 286            // Flex
 2287            element.style.flexDirection = GetFlexDirection(model.FlexDirection);
 2288            if (!float.IsNaN(model.FlexBasis))
 2289                element.style.flexBasis = new Length(model.FlexBasis, GetUnit(model.FlexBasisUnit));
 290
 2291            element.style.flexGrow = model.FlexGrow;
 2292            element.style.flexShrink = model.FlexShrink;
 2293            element.style.flexWrap = GetWrap(model.FlexWrap);
 294
 295            // Align
 2296            if (model.AlignContent != YGAlign.FlexStart)
 2297                element.style.alignContent = GetAlign(model.AlignContent);
 2298            if (model.AlignItems != YGAlign.Auto)
 2299                element.style.alignItems = GetAlign(model.AlignItems);
 2300            if (model.AlignSelf != YGAlign.Auto)
 2301                element.style.alignSelf = GetAlign(model.AlignSelf);
 2302            element.style.justifyContent = GetJustify(model.JustifyContent);
 303
 304            // Layout size
 2305            if (!float.IsNaN(model.Height))
 2306                element.style.height = new Length(model.Height, GetUnit(model.HeightUnit));
 2307            if (!float.IsNaN(model.Width))
 2308                element.style.width = new Length(model.Width, GetUnit(model.WidthUnit));
 309
 2310            if (!float.IsNaN(model.MaxWidth))
 2311                element.style.maxWidth = new Length(model.MaxWidth, GetUnit(model.MaxWidthUnit));
 2312            if (!float.IsNaN(model.MaxHeight))
 2313                element.style.maxHeight = new Length(model.MaxHeight, GetUnit(model.MaxHeightUnit));
 314
 2315            if (!float.IsNaN(model.MinHeight))
 2316                element.style.minHeight = new Length(model.MinHeight, GetUnit(model.MinHeightUnit));
 2317            if (!float.IsNaN(model.MinWidth))
 2318                element.style.minWidth = new Length(model.MinWidth, GetUnit(model.MinWidthUnit));
 319
 320            // Paddings
 2321            if (!Mathf.Approximately(model.PaddingBottom, 0))
 2322                element.style.paddingBottom = new Length(model.PaddingBottom, GetUnit(model.PaddingBottomUnit));
 2323            if (!Mathf.Approximately(model.PaddingLeft, 0))
 2324                element.style.paddingLeft = new Length(model.PaddingLeft, GetUnit(model.PaddingLeftUnit));
 2325            if (!Mathf.Approximately(model.PaddingRight, 0))
 2326                element.style.paddingRight = new Length(model.PaddingRight, GetUnit(model.PaddingRightUnit));
 2327            if (!Mathf.Approximately(model.PaddingTop, 0))
 2328                element.style.paddingTop = new Length(model.PaddingTop, GetUnit(model.PaddingTopUnit));
 329
 330            // Margins
 2331            if (!Mathf.Approximately(model.MarginLeft, 0))
 2332                element.style.marginLeft = new Length(model.MarginLeft, GetUnit(model.MarginLeftUnit));
 2333            if (!Mathf.Approximately(model.MarginRight, 0))
 2334                element.style.marginRight = new Length(model.MarginRight, GetUnit(model.MarginRightUnit));
 2335            if (!Mathf.Approximately(model.MarginBottom, 0))
 2336                element.style.marginBottom = new Length(model.MarginBottom, GetUnit(model.MarginBottomUnit));
 2337            if (!Mathf.Approximately(model.MarginTop, 0))
 2338                element.style.marginTop = new Length(model.MarginTop, GetUnit(model.MarginTopUnit));
 339
 340            // Borders
 2341            element.style.borderBottomWidth = model.BorderBottom;
 2342            element.style.borderLeftWidth = model.BorderLeft;
 2343            element.style.borderRightWidth = model.BorderRight;
 2344            element.style.borderTopWidth = model.BorderTop;
 345
 346            // Position
 2347            element.style.position = GetPosition(model.PositionType);
 348
 349            // This is for debugging purposes, we will change this to a proper approach so devs can debug the UI easily.
 2350            if (randomColor)
 2351                element.style.backgroundColor = Random.ColorHSV();
 352
 2353            return element;
 354        }
 355
 356        internal LengthUnit GetUnit(YGUnit unit)
 357        {
 358            switch (unit)
 359            {
 360                case YGUnit.Point:
 0361                    return LengthUnit.Pixel;
 362                case YGUnit.Percent:
 0363                    return LengthUnit.Percent;
 364                default:
 0365                    return LengthUnit.Pixel;
 366            }
 367        }
 368
 369        internal StyleEnum<Overflow> GetOverflow (YGOverflow overflow)
 370        {
 371            switch (overflow)
 372            {
 373                case YGOverflow.Visible:
 0374                    return Overflow.Visible;
 375                case YGOverflow.Hidden:
 3376                    return Overflow.Hidden;
 377                default:
 0378                    return Overflow.Visible;
 379            }
 380        }
 381
 382        internal StyleEnum<DisplayStyle> GetDisplay (YGDisplay display)
 383        {
 384            switch (display)
 385            {
 386                case YGDisplay.Flex:
 3387                    return DisplayStyle.Flex;
 388                    break;
 389                case YGDisplay.None:
 0390                    return DisplayStyle.None;
 391                default:
 0392                    return DisplayStyle.Flex;
 393            }
 394        }
 395
 396        internal StyleEnum<Justify> GetJustify (YGJustify justify)
 397        {
 398            switch (justify)
 399            {
 400                case YGJustify.FlexStart:
 0401                    return Justify.FlexStart;
 402                case YGJustify.Center:
 3403                    return Justify.Center;
 404                case YGJustify.FlexEnd:
 0405                    return Justify.FlexEnd;
 406                case YGJustify.SpaceBetween:
 0407                    return Justify.SpaceBetween;
 408                case YGJustify.SpaceAround:
 0409                    return Justify.SpaceAround;
 410                default:
 0411                    return Justify.FlexStart;
 412            }
 413        }
 414
 415        internal StyleEnum<Wrap> GetWrap (YGWrap wrap)
 416        {
 417            switch (wrap)
 418            {
 419                case YGWrap.NoWrap:
 0420                    return Wrap.NoWrap;
 421                case YGWrap.Wrap:
 3422                    return Wrap.Wrap;
 423                case YGWrap.WrapReverse:
 0424                    return Wrap.WrapReverse;
 425                default:
 0426                    return Wrap.Wrap;
 427            }
 428        }
 429
 430        internal StyleEnum<FlexDirection> GetFlexDirection (YGFlexDirection direction)
 431        {
 432            switch (direction)
 433            {
 434                case YGFlexDirection.Column:
 3435                    return FlexDirection.Column;
 436                case YGFlexDirection.ColumnReverse:
 0437                    return FlexDirection.ColumnReverse;
 438                case YGFlexDirection.Row:
 0439                    return FlexDirection.Row;
 440                case YGFlexDirection.RowReverse:
 0441                    return FlexDirection.RowReverse;
 442                default:
 0443                    return FlexDirection.Row;
 444            }
 445        }
 446
 447        internal StyleEnum<Position> GetPosition(YGPositionType  positionType)
 448        {
 449            switch (positionType)
 450            {
 451                case YGPositionType.Relative:
 0452                    return Position.Relative;
 453                case YGPositionType.Absolute:
 4454                    return Position.Absolute;
 455                default:
 0456                    return Position.Relative;
 457            }
 458        }
 459
 460        internal StyleEnum<Align> GetAlign(YGAlign align)
 461        {
 462            switch (align)
 463            {
 464                case YGAlign.Auto:
 0465                    return Align.Auto;
 466                case YGAlign.FlexStart:
 0467                    return Align.FlexStart;
 468                case YGAlign.Center:
 9469                    return Align.Center;
 470                case YGAlign.FlexEnd:
 0471                    return Align.FlexEnd;
 472                case YGAlign.Stretch:
 0473                    return Align.Stretch;
 474                default:
 0475                    return Align.Auto;
 476            }
 477        }
 478    }
 479}