< Summary

Class:DCL.Components.UIShape
Assembly:DCL.Components.UI
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/UI/UIShape.cs
Covered lines:158
Uncovered lines:49
Coverable lines:207
Total lines:528
Line coverage:76.3% (158 of 207)
Covered branches:0
Total branches:0
Covered methods:30
Total methods:34
Method coverage:88.2% (30 of 34)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Model()0%110100%
GetDataFromJSON(...)0%110100%
GetDataFromPb(...)0%14063700%
UIShape()0%110100%
OnScreenResize(...)0%220100%
GetClassId()0%2100%
GetDebugName()0%220100%
ApplyChanges(...)0%2100%
InstantiateUIGameObject[T](...)0%6.016092.86%
RequestRefresh()0%4.014090.91%
RefreshRecursively()0%110100%
MarkLayoutDirty(...)0%3.023087.5%
RefreshDCLLayout(...)0%330100%
RefreshDCLSize(...)0%2.022083.33%
RefreshDCLAlignmentAndPosition(...)0%22092.31%
RefreshDCLLayoutRecursively(...)0%110100%
RefreshDCLLayoutRecursively_Internal(...)0%220100%
FixMaxStretchRecursively()0%330100%
ReparentComponent(...)0%880100%
GetRootParent()0%330100%
ConfigureAlignment(...)0%990100%
SetComponentDebugName()0%3.143075%
Dispose()0%220100%
OnChildAttached(...)0%110100%
OnChildDetached(...)0%110100%
Refresh()0%220100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/UI/UIShape.cs

#LineLine coverage
 1using DCL.Helpers;
 2using DCL.Models;
 3using System.Collections;
 4using System.Collections.Generic;
 5using DCL.Components.Interfaces;
 6using UnityEngine;
 7using UnityEngine.Assertions;
 8using UnityEngine.UI;
 9using Object = UnityEngine.Object;
 10using Decentraland.Sdk.Ecs6;
 11using MainScripts.DCL.Components;
 12
 13namespace DCL.Components
 14{
 15    public class UIShape<ReferencesContainerType, ModelType> : UIShape
 16        where ReferencesContainerType : UIReferencesContainer
 17        where ModelType : UIShape.Model
 18    {
 19        public const float RAYCAST_ALPHA_THRESHOLD = 0.01f;
 20
 21        public UIShape() { }
 22
 23        new public ModelType model { get { return base.model as ModelType; } set { base.model = value; } }
 24
 25        new public ReferencesContainerType referencesContainer { get { return base.referencesContainer as ReferencesCont
 26
 27        public override ComponentUpdateHandler CreateUpdateHandler() { return new UIShapeUpdateHandler<ReferencesContain
 28
 29        bool raiseOnAttached;
 30        bool firstApplyChangesCall;
 31
 32
 33        /// <summary>
 34        /// This is called by UIShapeUpdateHandler before calling ApplyChanges.
 35        /// </summary>
 36        public void PreApplyChanges(BaseModel newModel)
 37        {
 38            model = (ModelType) newModel;
 39
 40            raiseOnAttached = false;
 41            firstApplyChangesCall = false;
 42
 43            if (referencesContainer == null)
 44            {
 45                referencesContainer = InstantiateUIGameObject<ReferencesContainerType>(referencesContainerPrefabName);
 46
 47                raiseOnAttached = true;
 48                firstApplyChangesCall = true;
 49            }
 50            else if (ReparentComponent(referencesContainer.rectTransform, model.parentComponent))
 51            {
 52                raiseOnAttached = true;
 53            }
 54        }
 55
 56        public override void RaiseOnAppliedChanges()
 57        {
 58            RefreshDCLLayout();
 59
 60#if UNITY_EDITOR
 61            SetComponentDebugName();
 62#endif
 63
 64            // We hide the component visibility when it's created (first applychanges)
 65            // as it has default values and appears in the middle of the screen
 66            if (firstApplyChangesCall)
 67                referencesContainer.canvasGroup.alpha = 0f;
 68            else
 69                referencesContainer.canvasGroup.alpha = model.visible ? model.opacity : 0f;
 70
 71            referencesContainer.canvasGroup.blocksRaycasts = model.visible && model.isPointerBlocker;
 72
 73            base.RaiseOnAppliedChanges();
 74
 75            if (raiseOnAttached && parentUIComponent != null)
 76            {
 77                UIReferencesContainer[] parents = referencesContainer.GetComponentsInParent<UIReferencesContainer>(true)
 78
 79                for (int i = 0; i < parents.Length; i++)
 80                {
 81                    UIReferencesContainer parent = parents[i];
 82                    if (parent.owner != null)
 83                    {
 84                        parent.owner.OnChildAttached(parentUIComponent, this);
 85                    }
 86                }
 87            }
 88        }
 89    }
 90
 91    public class UIShape : BaseDisposable, IUIRefreshable
 92    {
 93        [System.Serializable]
 94        public class Model : BaseModel
 95        {
 96            public string name;
 97            public string parentComponent;
 64398            public bool visible = true;
 64399            public float opacity = 1f;
 643100            public string hAlign = "center";
 643101            public string vAlign = "center";
 643102            public UIValue width = new (100f);
 643103            public UIValue height = new (50f);
 643104            public UIValue positionX = new (0f);
 643105            public UIValue positionY = new (0f);
 643106            public bool isPointerBlocker = true;
 107            public string onClick;
 108
 109            public override BaseModel GetDataFromJSON(string json) =>
 54110                Utils.SafeFromJson<Model>(json);
 111
 112            public override BaseModel GetDataFromPb(ComponentBodyPayload pbModel)
 113            {
 114
 0115                switch (pbModel.PayloadCase)
 116                {
 117                    case ComponentBodyPayload.PayloadOneofCase.UiShape:
 0118                        var uiShapeModel = new Model();
 0119                        if (pbModel.UiShape.HasName) uiShapeModel.name = pbModel.UiShape.Name;
 0120                        if (pbModel.UiShape.HasParentComponent) uiShapeModel.parentComponent = pbModel.UiShape.ParentCom
 0121                        if (pbModel.UiShape.HasVisible) uiShapeModel.visible = pbModel.UiShape.Visible;
 0122                        if (pbModel.UiShape.HasOpacity) uiShapeModel.opacity = pbModel.UiShape.Opacity;
 0123                        if (pbModel.UiShape.HasHAlign) uiShapeModel.hAlign = pbModel.UiShape.HAlign;
 0124                        if (pbModel.UiShape.HasVAlign) uiShapeModel.vAlign = pbModel.UiShape.VAlign;
 0125                        if (pbModel.UiShape.Width != null) uiShapeModel.width = SDK6DataMapExtensions.FromProtobuf(uiSha
 0126                        if (pbModel.UiShape.Height != null) uiShapeModel.height = SDK6DataMapExtensions.FromProtobuf(uiS
 0127                        if (pbModel.UiShape.PositionX != null) uiShapeModel.positionX = SDK6DataMapExtensions.FromProtob
 0128                        if (pbModel.UiShape.PositionY != null) uiShapeModel.positionY = SDK6DataMapExtensions.FromProtob
 0129                        if (pbModel.UiShape.HasIsPointerBlocker) uiShapeModel.isPointerBlocker = pbModel.UiShape.IsPoint
 0130                        return uiShapeModel;
 131
 132                    case ComponentBodyPayload.PayloadOneofCase.UiScreenSpaceShape:
 0133                        var screenSpaceModel = new Model();
 0134                        if (pbModel.UiScreenSpaceShape.HasName) screenSpaceModel.name = pbModel.UiScreenSpaceShape.Name;
 0135                        if (pbModel.UiScreenSpaceShape.HasParentComponent) screenSpaceModel.parentComponent = pbModel.Ui
 0136                        if (pbModel.UiScreenSpaceShape.HasVisible) screenSpaceModel.visible = pbModel.UiScreenSpaceShape
 0137                        if (pbModel.UiScreenSpaceShape.HasOpacity) screenSpaceModel.opacity = pbModel.UiScreenSpaceShape
 0138                        if (pbModel.UiScreenSpaceShape.HasHAlign) screenSpaceModel.hAlign = pbModel.UiScreenSpaceShape.H
 0139                        if (pbModel.UiScreenSpaceShape.HasVAlign) screenSpaceModel.vAlign = pbModel.UiScreenSpaceShape.V
 0140                        if (pbModel.UiScreenSpaceShape.Width != null) screenSpaceModel.width = SDK6DataMapExtensions.Fro
 0141                        if (pbModel.UiScreenSpaceShape.Height != null) screenSpaceModel.height = SDK6DataMapExtensions.F
 0142                        if (pbModel.UiScreenSpaceShape.PositionX != null) screenSpaceModel.positionX = SDK6DataMapExtens
 0143                        if (pbModel.UiScreenSpaceShape.PositionY != null) screenSpaceModel.positionY = SDK6DataMapExtens
 0144                        if (pbModel.UiScreenSpaceShape.HasIsPointerBlocker) screenSpaceModel.isPointerBlocker = pbModel.
 0145                        return screenSpaceModel;
 146
 147                    case ComponentBodyPayload.PayloadOneofCase.UiFullScreenShape:
 0148                        var fullScreenModel = new Model();
 0149                        if (pbModel.UiFullScreenShape.HasName) fullScreenModel.name = pbModel.UiFullScreenShape.Name;
 0150                        if (pbModel.UiFullScreenShape.HasParentComponent) fullScreenModel.parentComponent = pbModel.UiFu
 0151                        if (pbModel.UiFullScreenShape.HasVisible) fullScreenModel.visible = pbModel.UiFullScreenShape.Vi
 0152                        if (pbModel.UiFullScreenShape.HasOpacity) fullScreenModel.opacity = pbModel.UiFullScreenShape.Op
 0153                        if (pbModel.UiFullScreenShape.HasHAlign) fullScreenModel.hAlign = pbModel.UiFullScreenShape.HAli
 0154                        if (pbModel.UiFullScreenShape.HasVAlign) fullScreenModel.vAlign = pbModel.UiFullScreenShape.VAli
 0155                        if (pbModel.UiFullScreenShape.Width != null) fullScreenModel.width = SDK6DataMapExtensions.FromP
 0156                        if (pbModel.UiFullScreenShape.Height != null) fullScreenModel.height = SDK6DataMapExtensions.Fro
 0157                        if (pbModel.UiFullScreenShape.PositionX != null) fullScreenModel.positionX = SDK6DataMapExtensio
 0158                        if (pbModel.UiFullScreenShape.PositionY != null) fullScreenModel.positionY = SDK6DataMapExtensio
 0159                        if (pbModel.UiFullScreenShape.HasIsPointerBlocker) fullScreenModel.isPointerBlocker = pbModel.Ui
 0160                        return fullScreenModel;
 161
 162                    default:
 0163                        return Utils.SafeUnimplemented<UIShape, Model>(expected: ComponentBodyPayload.PayloadOneofCase.U
 164                }
 165            }
 166        }
 167
 149168        public override string componentName => GetDebugName();
 0169        public virtual string referencesContainerPrefabName => "";
 170        public UIReferencesContainer referencesContainer;
 171        public RectTransform childHookRectTransform;
 172
 213173        public bool isLayoutDirty { get; private set; }
 174        protected System.Action OnLayoutRefresh;
 175
 138176        private BaseVariable<Vector2Int> screenSize => DataStore.i.screen.size;
 55177        private BaseVariable<Dictionary<int, Queue<IUIRefreshable>>> dirtyShapesBySceneVariable => DataStore.i.HUDs.dirt
 1265178        public UIShape parentUIComponent { get; protected set; }
 179
 130180        public UIShape()
 181        {
 130182            screenSize.OnChange += OnScreenResize;
 130183            model = new Model();
 130184        }
 185
 186        private void OnScreenResize(Vector2Int current, Vector2Int previous)
 187        {
 2188            if (GetRootParent() == this)
 2189                RequestRefresh();
 2190        }
 191
 0192        public override int GetClassId() { return (int) CLASS_ID.UI_IMAGE_SHAPE; }
 193
 194        public string GetDebugName()
 195        {
 149196            Model model = (Model) this.model;
 197
 149198            if (string.IsNullOrEmpty(model.name))
 199            {
 142200                return GetType().Name;
 201            }
 202            else
 203            {
 7204                return GetType().Name + " - " + model.name;
 205            }
 206        }
 207
 0208        public override IEnumerator ApplyChanges(BaseModel newJson) { return null; }
 209
 210
 211        internal T InstantiateUIGameObject<T>(string prefabPath) where T : UIReferencesContainer
 212        {
 80213            Model model = (Model) this.model;
 214
 80215            GameObject uiGameObject = null;
 216
 80217            bool targetParentExists = !string.IsNullOrEmpty(model.parentComponent) &&
 218                                      scene.componentsManagerLegacy.HasSceneSharedComponent(model.parentComponent);
 219
 80220            if (targetParentExists)
 221            {
 23222                if (scene.componentsManagerLegacy.HasSceneSharedComponent(model.parentComponent))
 223                {
 23224                    parentUIComponent = (scene.componentsManagerLegacy.GetSceneSharedComponent(model.parentComponent) as
 225                }
 226                else
 227                {
 0228                    parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent<UIScreenSpace>();
 229                }
 230            }
 231            else
 232            {
 57233                parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent<UIScreenSpace>();
 234            }
 235
 80236            uiGameObject =
 237                Object.Instantiate(
 238                    Resources.Load(prefabPath),
 239                    parentUIComponent?.childHookRectTransform) as GameObject;
 240
 80241            referencesContainer = uiGameObject.GetComponent<T>();
 242
 80243            referencesContainer.rectTransform.SetToMaxStretch();
 244
 80245            childHookRectTransform = referencesContainer.childHookRectTransform;
 246
 80247            referencesContainer.owner = this;
 248
 80249            return referencesContainer as T;
 250        }
 251
 252        public virtual void RequestRefresh()
 253        {
 135254            if (isLayoutDirty) return;
 255
 55256            isLayoutDirty = true;
 257
 55258            var dirtyShapesByScene = dirtyShapesBySceneVariable.Get();
 259
 55260            int sceneDataSceneNumber = scene.sceneData.sceneNumber;
 55261            if (sceneDataSceneNumber <= 0) sceneDataSceneNumber = 666;
 262
 55263            if (!dirtyShapesByScene.ContainsKey(sceneDataSceneNumber))
 264            {
 19265                dirtyShapesByScene.Add(sceneDataSceneNumber, new Queue<IUIRefreshable>());
 266            }
 267
 55268            dirtyShapesByScene[sceneDataSceneNumber].Enqueue(this);
 55269        }
 270
 271        private void RefreshRecursively()
 272        {
 273            // We are not using the _Internal here because the method is overridden
 274            // by some UI shapes.
 55275            RefreshDCLLayoutRecursively(refreshSize: true, refreshAlignmentAndPosition: false);
 55276            FixMaxStretchRecursively();
 55277            RefreshDCLLayoutRecursively_Internal(refreshSize: false, refreshAlignmentAndPosition: true);
 55278        }
 279
 280        public virtual void MarkLayoutDirty( System.Action OnRefresh = null )
 281        {
 93282            UIShape rootParent = GetRootParent();
 283
 93284            Assert.IsTrue(rootParent != null, "root parent must never be null");
 285
 93286            if (rootParent.referencesContainer == null)
 0287                return;
 288
 93289            rootParent.RequestRefresh();
 290
 93291            if ( OnRefresh != null )
 24292                rootParent.OnLayoutRefresh += OnRefresh;
 93293        }
 294
 295        public void RefreshDCLLayout(bool refreshSize = true, bool refreshAlignmentAndPosition = true)
 296        {
 365297            RectTransform parentRT = referencesContainer.GetComponentInParent<RectTransform>();
 298
 365299            if (refreshSize)
 300            {
 245301                RefreshDCLSize(parentRT);
 302            }
 303
 365304            if (refreshAlignmentAndPosition)
 305            {
 306                // Alignment (Alignment uses size so we should always align AFTER resizing)
 269307                RefreshDCLAlignmentAndPosition(parentRT);
 308            }
 365309        }
 310
 311        protected virtual void RefreshDCLSize(RectTransform parentTransform = null)
 312        {
 229313            if (parentTransform == null)
 0314                parentTransform = referencesContainer.GetComponentInParent<RectTransform>();
 315
 229316            Model model = (Model) this.model;
 317
 229318            referencesContainer.layoutElementRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,
 319                model.width.GetScaledValue(parentTransform.rect.width));
 229320            referencesContainer.layoutElementRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical,
 321                model.height.GetScaledValue(parentTransform.rect.height));
 229322        }
 323
 324        public void RefreshDCLAlignmentAndPosition(RectTransform parentTransform = null)
 325        {
 269326            if (parentTransform == null)
 0327                parentTransform = referencesContainer.GetComponentInParent<RectTransform>();
 328
 269329            referencesContainer.layoutElement.ignoreLayout = false;
 269330            ConfigureAlignment(referencesContainer.layoutGroup);
 269331            Utils.ForceRebuildLayoutImmediate(parentTransform);
 269332            referencesContainer.layoutElement.ignoreLayout = true;
 333
 269334            Model model = (Model) this.model;
 335
 336            // Reposition
 269337            Vector3 position = Vector3.zero;
 269338            position.x = model.positionX.GetScaledValue(parentTransform.rect.width);
 269339            position.y = model.positionY.GetScaledValue(parentTransform.rect.height);
 340
 269341            position = Utils.Sanitize(position);
 269342            referencesContainer.layoutElementRT.localPosition += position;
 269343        }
 344
 345        public virtual void RefreshDCLLayoutRecursively(bool refreshSize = true,
 346            bool refreshAlignmentAndPosition = true)
 347        {
 79348            RefreshDCLLayoutRecursively_Internal(refreshSize, refreshAlignmentAndPosition);
 79349        }
 350
 351        public void RefreshDCLLayoutRecursively_Internal(bool refreshSize = true,
 352            bool refreshAlignmentAndPosition = true)
 353        {
 134354            UIShape rootParent = GetRootParent();
 355
 134356            Assert.IsTrue(rootParent != null, "root parent must never be null");
 357
 134358            if (rootParent.referencesContainer == null)
 2359                return;
 360
 132361            Utils.InverseTransformChildTraversal<UIReferencesContainer>(
 362                (x) =>
 363                {
 300364                    if (x.owner != null)
 216365                        x.owner.RefreshDCLLayout(refreshSize, refreshAlignmentAndPosition);
 300366                },
 367                rootParent.referencesContainer.transform);
 132368        }
 369
 370        public void FixMaxStretchRecursively()
 371        {
 55372            UIShape rootParent = GetRootParent();
 373
 55374            Assert.IsTrue(rootParent != null, "root parent must never be null");
 375
 55376            if (rootParent.referencesContainer == null)
 1377                return;
 378
 54379            Utils.InverseTransformChildTraversal<UIReferencesContainer>(
 380                (x) =>
 381                {
 138382                    if (x.owner != null)
 383                    {
 96384                        x.rectTransform.SetToMaxStretch();
 385                    }
 138386                },
 387                rootParent.referencesContainer.transform);
 54388        }
 389
 390        protected bool ReparentComponent(RectTransform targetTransform, string targetParent)
 391        {
 69392            bool targetParentExists = !string.IsNullOrEmpty(targetParent) &&
 393                                      scene.componentsManagerLegacy.HasSceneSharedComponent(targetParent);
 394
 69395            if (targetParentExists && parentUIComponent == scene.componentsManagerLegacy.GetSceneSharedComponent(targetP
 396            {
 22397                return false;
 398            }
 399
 47400            if (parentUIComponent != null)
 401            {
 47402                UIReferencesContainer[] parents = referencesContainer.GetComponentsInParent<UIReferencesContainer>(true)
 403
 190404                foreach (var parent in parents)
 405                {
 48406                    if (parent.owner != null)
 407                    {
 48408                        parent.owner.OnChildDetached(parentUIComponent, this);
 409                    }
 410                }
 411            }
 412
 47413            if (targetParentExists)
 414            {
 22415                parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent(targetParent) as UIShape;
 416            }
 417            else
 418            {
 25419                parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent<UIScreenSpace>();
 420            }
 421
 47422            targetTransform.SetParent(parentUIComponent.childHookRectTransform, false);
 47423            return true;
 424        }
 425
 426        public UIShape GetRootParent()
 427        {
 290428            UIShape parent = null;
 429
 290430            if (parentUIComponent != null && !(parentUIComponent is UIScreenSpace))
 431            {
 6432                parent = parentUIComponent.GetRootParent();
 433            }
 434            else
 435            {
 284436                parent = this;
 437            }
 438
 290439            return parent;
 440        }
 441
 442        protected void ConfigureAlignment(LayoutGroup layout)
 443        {
 269444            Model model = (Model) this.model;
 269445            switch (model.vAlign)
 446            {
 447                case "top":
 7448                    switch (model.hAlign)
 449                    {
 450                        case "left":
 2451                            layout.childAlignment = TextAnchor.UpperLeft;
 2452                            break;
 453                        case "right":
 2454                            layout.childAlignment = TextAnchor.UpperRight;
 2455                            break;
 456                        default:
 3457                            layout.childAlignment = TextAnchor.UpperCenter;
 3458                            break;
 459                    }
 460
 461                    break;
 462                case "bottom":
 26463                    switch (model.hAlign)
 464                    {
 465                        case "left":
 8466                            layout.childAlignment = TextAnchor.LowerLeft;
 8467                            break;
 468                        case "right":
 16469                            layout.childAlignment = TextAnchor.LowerRight;
 16470                            break;
 471                        default:
 2472                            layout.childAlignment = TextAnchor.LowerCenter;
 2473                            break;
 474                    }
 475
 476                    break;
 477                default: // center
 236478                    switch (model.hAlign)
 479                    {
 480                        case "left":
 2481                            layout.childAlignment = TextAnchor.MiddleLeft;
 2482                            break;
 483                        case "right":
 2484                            layout.childAlignment = TextAnchor.MiddleRight;
 2485                            break;
 486                        default:
 232487                            layout.childAlignment = TextAnchor.MiddleCenter;
 488                            break;
 489                    }
 490
 491                    break;
 492            }
 232493        }
 494
 495        protected void SetComponentDebugName()
 496        {
 149497            if (referencesContainer == null || model == null)
 498            {
 0499                return;
 500            }
 501
 149502            referencesContainer.name = componentName;
 149503        }
 504
 505        public override void Dispose()
 506        {
 507
 8508            if (childHookRectTransform)
 8509                Utils.SafeDestroy(childHookRectTransform.gameObject);
 510
 8511            screenSize.OnChange -= OnScreenResize;
 512
 8513            base.Dispose();
 8514        }
 515
 127516        public virtual void OnChildAttached(UIShape parentComponent, UIShape childComponent) { }
 517
 43518        public virtual void OnChildDetached(UIShape parentComponent, UIShape childComponent) { }
 519        public void Refresh()
 520        {
 55521            RefreshRecursively();
 55522            isLayoutDirty = false;
 523
 55524            OnLayoutRefresh?.Invoke();
 55525            OnLayoutRefresh = null;
 55526        }
 527    }
 528}