< 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:8
Coverable lines:166
Total lines:512
Line coverage:95.1% (158 of 166)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Model()0%110100%
GetDataFromJSON(...)0%110100%
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;
 10
 11namespace DCL.Components
 12{
 13    [System.Serializable]
 14    public struct UIValue
 15    {
 16        public enum Unit
 17        {
 18            PERCENT,
 19            PIXELS
 20        }
 21
 22        public float value;
 23        public Unit type;
 24
 25        public void SetPixels(float value)
 26        {
 27            this.type = Unit.PIXELS;
 28            this.value = value;
 29        }
 30
 31        public void SetPercent(float value)
 32        {
 33            this.type = Unit.PERCENT;
 34            this.value = value;
 35        }
 36
 37        public UIValue(float value, Unit unitType = Unit.PIXELS)
 38        {
 39            this.value = value;
 40            this.type = unitType;
 41        }
 42
 43        public float GetScaledValue(float parentSize)
 44        {
 45            if (type == Unit.PIXELS)
 46                return value;
 47
 48            // Prevent division by zero
 49            if (parentSize <= Mathf.Epsilon)
 50                parentSize = 1;
 51
 52            return value / 100 * parentSize;
 53        }
 54    }
 55
 56    public class UIShape<ReferencesContainerType, ModelType> : UIShape
 57        where ReferencesContainerType : UIReferencesContainer
 58        where ModelType : UIShape.Model
 59    {
 60        public const float RAYCAST_ALPHA_THRESHOLD = 0.01f;
 61
 62        public UIShape() { }
 63
 64        new public ModelType model { get { return base.model as ModelType; } set { base.model = value; } }
 65
 66        new public ReferencesContainerType referencesContainer { get { return base.referencesContainer as ReferencesCont
 67
 68        public override ComponentUpdateHandler CreateUpdateHandler() { return new UIShapeUpdateHandler<ReferencesContain
 69
 70        bool raiseOnAttached;
 71        bool firstApplyChangesCall;
 72
 73        /// <summary>
 74        /// This is called by UIShapeUpdateHandler before calling ApplyChanges.
 75        /// </summary>
 76        public void PreApplyChanges(BaseModel newModel)
 77        {
 78            model = (ModelType) newModel;
 79
 80            raiseOnAttached = false;
 81            firstApplyChangesCall = false;
 82
 83            if (referencesContainer == null)
 84            {
 85                referencesContainer = InstantiateUIGameObject<ReferencesContainerType>(referencesContainerPrefabName);
 86
 87                raiseOnAttached = true;
 88                firstApplyChangesCall = true;
 89            }
 90            else if (ReparentComponent(referencesContainer.rectTransform, model.parentComponent))
 91            {
 92                raiseOnAttached = true;
 93            }
 94        }
 95
 96        public override void RaiseOnAppliedChanges()
 97        {
 98            RefreshDCLLayout();
 99
 100#if UNITY_EDITOR
 101            SetComponentDebugName();
 102#endif
 103
 104            // We hide the component visibility when it's created (first applychanges)
 105            // as it has default values and appears in the middle of the screen
 106            if (firstApplyChangesCall)
 107                referencesContainer.canvasGroup.alpha = 0f;
 108            else
 109                referencesContainer.canvasGroup.alpha = model.visible ? model.opacity : 0f;
 110
 111            referencesContainer.canvasGroup.blocksRaycasts = model.visible && model.isPointerBlocker;
 112
 113            base.RaiseOnAppliedChanges();
 114
 115            if (raiseOnAttached && parentUIComponent != null)
 116            {
 117                UIReferencesContainer[] parents = referencesContainer.GetComponentsInParent<UIReferencesContainer>(true)
 118
 119                for (int i = 0; i < parents.Length; i++)
 120                {
 121                    UIReferencesContainer parent = parents[i];
 122                    if (parent.owner != null)
 123                    {
 124                        parent.owner.OnChildAttached(parentUIComponent, this);
 125                    }
 126                }
 127            }
 128        }
 129    }
 130
 131    public class UIShape : BaseDisposable, IUIRefreshable
 132    {
 133
 134        [System.Serializable]
 135        public class Model : BaseModel
 136        {
 137            public string name;
 138            public string parentComponent;
 612139            public bool visible = true;
 612140            public float opacity = 1f;
 612141            public string hAlign = "center";
 612142            public string vAlign = "center";
 612143            public UIValue width = new UIValue(100f);
 612144            public UIValue height = new UIValue(50f);
 612145            public UIValue positionX = new UIValue(0f);
 612146            public UIValue positionY = new UIValue(0f);
 612147            public bool isPointerBlocker = true;
 148            public string onClick;
 149
 46150            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 151        }
 152
 146153        public override string componentName => GetDebugName();
 0154        public virtual string referencesContainerPrefabName => "";
 155        public UIReferencesContainer referencesContainer;
 156        public RectTransform childHookRectTransform;
 157
 213158        public bool isLayoutDirty { get; private set; }
 159        protected System.Action OnLayoutRefresh;
 160
 129161        private BaseVariable<Vector2Int> screenSize => DataStore.i.screen.size;
 55162        private BaseVariable<Dictionary<int, Queue<IUIRefreshable>>> dirtyShapesBySceneVariable => DataStore.i.HUDs.dirt
 1329163        public UIShape parentUIComponent { get; protected set; }
 164
 122165        public UIShape()
 166        {
 122167            screenSize.OnChange += OnScreenResize;
 122168            model = new Model();
 122169        }
 170
 171        private void OnScreenResize(Vector2Int current, Vector2Int previous)
 172        {
 2173            if (GetRootParent() == this)
 2174                RequestRefresh();
 2175        }
 176
 0177        public override int GetClassId() { return (int) CLASS_ID.UI_IMAGE_SHAPE; }
 178
 179        public string GetDebugName()
 180        {
 146181            Model model = (Model) this.model;
 182
 146183            if (string.IsNullOrEmpty(model.name))
 184            {
 139185                return GetType().Name;
 186            }
 187            else
 188            {
 7189                return GetType().Name + " - " + model.name;
 190            }
 191        }
 192
 0193        public override IEnumerator ApplyChanges(BaseModel newJson) { return null; }
 194
 195        internal T InstantiateUIGameObject<T>(string prefabPath) where T : UIReferencesContainer
 196        {
 78197            Model model = (Model) this.model;
 198
 78199            GameObject uiGameObject = null;
 200
 78201            bool targetParentExists = !string.IsNullOrEmpty(model.parentComponent) &&
 202                                      scene.componentsManagerLegacy.HasSceneSharedComponent(model.parentComponent);
 203
 78204            if (targetParentExists)
 205            {
 23206                if (scene.componentsManagerLegacy.HasSceneSharedComponent(model.parentComponent))
 207                {
 23208                    parentUIComponent = (scene.componentsManagerLegacy.GetSceneSharedComponent(model.parentComponent) as
 209                }
 210                else
 211                {
 0212                    parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent<UIScreenSpace>();
 213                }
 214            }
 215            else
 216            {
 55217                parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent<UIScreenSpace>();
 218            }
 219
 78220            uiGameObject =
 221                Object.Instantiate(
 222                    Resources.Load(prefabPath),
 223                    parentUIComponent != null ? parentUIComponent.childHookRectTransform : null) as GameObject;
 224
 78225            referencesContainer = uiGameObject.GetComponent<T>();
 226
 78227            referencesContainer.rectTransform.SetToMaxStretch();
 228
 78229            childHookRectTransform = referencesContainer.childHookRectTransform;
 230
 78231            referencesContainer.owner = this;
 232
 78233            return referencesContainer as T;
 234        }
 235
 236        public virtual void RequestRefresh()
 237        {
 135238            if (isLayoutDirty) return;
 239
 55240            isLayoutDirty = true;
 241
 55242            var dirtyShapesByScene = dirtyShapesBySceneVariable.Get();
 243
 55244            int sceneDataSceneNumber = scene.sceneData.sceneNumber;
 55245            if (sceneDataSceneNumber <= 0) sceneDataSceneNumber = 666;
 246
 55247            if (!dirtyShapesByScene.ContainsKey(sceneDataSceneNumber))
 248            {
 19249                dirtyShapesByScene.Add(sceneDataSceneNumber, new Queue<IUIRefreshable>());
 250            }
 251
 55252            dirtyShapesByScene[sceneDataSceneNumber].Enqueue(this);
 55253        }
 254
 255        private void RefreshRecursively()
 256        {
 257            // We are not using the _Internal here because the method is overridden
 258            // by some UI shapes.
 55259            RefreshDCLLayoutRecursively(refreshSize: true, refreshAlignmentAndPosition: false);
 55260            FixMaxStretchRecursively();
 55261            RefreshDCLLayoutRecursively_Internal(refreshSize: false, refreshAlignmentAndPosition: true);
 55262        }
 263
 264        public virtual void MarkLayoutDirty( System.Action OnRefresh = null )
 265        {
 93266            UIShape rootParent = GetRootParent();
 267
 93268            Assert.IsTrue(rootParent != null, "root parent must never be null");
 269
 93270            if (rootParent.referencesContainer == null)
 0271                return;
 272
 93273            rootParent.RequestRefresh();
 274
 93275            if ( OnRefresh != null )
 24276                rootParent.OnLayoutRefresh += OnRefresh;
 93277        }
 278
 279        public void RefreshDCLLayout(bool refreshSize = true, bool refreshAlignmentAndPosition = true)
 280        {
 362281            RectTransform parentRT = referencesContainer.GetComponentInParent<RectTransform>();
 282
 362283            if (refreshSize)
 284            {
 242285                RefreshDCLSize(parentRT);
 286            }
 287
 362288            if (refreshAlignmentAndPosition)
 289            {
 290                // Alignment (Alignment uses size so we should always align AFTER resizing)
 266291                RefreshDCLAlignmentAndPosition(parentRT);
 292            }
 362293        }
 294
 295        protected virtual void RefreshDCLSize(RectTransform parentTransform = null)
 296        {
 226297            if (parentTransform == null)
 0298                parentTransform = referencesContainer.GetComponentInParent<RectTransform>();
 299
 226300            Model model = (Model) this.model;
 301
 226302            referencesContainer.layoutElementRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,
 303                model.width.GetScaledValue(parentTransform.rect.width));
 226304            referencesContainer.layoutElementRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical,
 305                model.height.GetScaledValue(parentTransform.rect.height));
 226306        }
 307
 308        public void RefreshDCLAlignmentAndPosition(RectTransform parentTransform = null)
 309        {
 266310            if (parentTransform == null)
 0311                parentTransform = referencesContainer.GetComponentInParent<RectTransform>();
 312
 266313            referencesContainer.layoutElement.ignoreLayout = false;
 266314            ConfigureAlignment(referencesContainer.layoutGroup);
 266315            Utils.ForceRebuildLayoutImmediate(parentTransform);
 266316            referencesContainer.layoutElement.ignoreLayout = true;
 317
 266318            Model model = (Model) this.model;
 319
 320            // Reposition
 266321            Vector3 position = Vector3.zero;
 266322            position.x = model.positionX.GetScaledValue(parentTransform.rect.width);
 266323            position.y = model.positionY.GetScaledValue(parentTransform.rect.height);
 324
 266325            position = Utils.Sanitize(position);
 266326            referencesContainer.layoutElementRT.localPosition += position;
 266327        }
 328
 329        public virtual void RefreshDCLLayoutRecursively(bool refreshSize = true,
 330            bool refreshAlignmentAndPosition = true)
 331        {
 79332            RefreshDCLLayoutRecursively_Internal(refreshSize, refreshAlignmentAndPosition);
 79333        }
 334
 335        public void RefreshDCLLayoutRecursively_Internal(bool refreshSize = true,
 336            bool refreshAlignmentAndPosition = true)
 337        {
 134338            UIShape rootParent = GetRootParent();
 339
 134340            Assert.IsTrue(rootParent != null, "root parent must never be null");
 341
 134342            if (rootParent.referencesContainer == null)
 2343                return;
 344
 132345            Utils.InverseTransformChildTraversal<UIReferencesContainer>(
 346                (x) =>
 347                {
 300348                    if (x.owner != null)
 216349                        x.owner.RefreshDCLLayout(refreshSize, refreshAlignmentAndPosition);
 300350                },
 351                rootParent.referencesContainer.transform);
 132352        }
 353
 354        public void FixMaxStretchRecursively()
 355        {
 55356            UIShape rootParent = GetRootParent();
 357
 55358            Assert.IsTrue(rootParent != null, "root parent must never be null");
 359
 55360            if (rootParent.referencesContainer == null)
 1361                return;
 362
 54363            Utils.InverseTransformChildTraversal<UIReferencesContainer>(
 364                (x) =>
 365                {
 138366                    if (x.owner != null)
 367                    {
 96368                        x.rectTransform.SetToMaxStretch();
 369                    }
 138370                },
 371                rootParent.referencesContainer.transform);
 54372        }
 373
 374        protected bool ReparentComponent(RectTransform targetTransform, string targetParent)
 375        {
 68376            bool targetParentExists = !string.IsNullOrEmpty(targetParent) &&
 377                                      scene.componentsManagerLegacy.HasSceneSharedComponent(targetParent);
 378
 68379            if (targetParentExists && parentUIComponent == scene.componentsManagerLegacy.GetSceneSharedComponent(targetP
 380            {
 22381                return false;
 382            }
 383
 46384            if (parentUIComponent != null)
 385            {
 46386                UIReferencesContainer[] parents = referencesContainer.GetComponentsInParent<UIReferencesContainer>(true)
 387
 186388                foreach (var parent in parents)
 389                {
 47390                    if (parent.owner != null)
 391                    {
 47392                        parent.owner.OnChildDetached(parentUIComponent, this);
 393                    }
 394                }
 395            }
 396
 46397            if (targetParentExists)
 398            {
 22399                parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent(targetParent) as UIShape;
 400            }
 401            else
 402            {
 24403                parentUIComponent = scene.componentsManagerLegacy.GetSceneSharedComponent<UIScreenSpace>();
 404            }
 405
 46406            targetTransform.SetParent(parentUIComponent.childHookRectTransform, false);
 46407            return true;
 408        }
 409
 410        public UIShape GetRootParent()
 411        {
 290412            UIShape parent = null;
 413
 290414            if (parentUIComponent != null && !(parentUIComponent is UIScreenSpace))
 415            {
 6416                parent = parentUIComponent.GetRootParent();
 417            }
 418            else
 419            {
 284420                parent = this;
 421            }
 422
 290423            return parent;
 424        }
 425
 426        protected void ConfigureAlignment(LayoutGroup layout)
 427        {
 266428            Model model = (Model) this.model;
 266429            switch (model.vAlign)
 430            {
 431                case "top":
 7432                    switch (model.hAlign)
 433                    {
 434                        case "left":
 2435                            layout.childAlignment = TextAnchor.UpperLeft;
 2436                            break;
 437                        case "right":
 2438                            layout.childAlignment = TextAnchor.UpperRight;
 2439                            break;
 440                        default:
 3441                            layout.childAlignment = TextAnchor.UpperCenter;
 3442                            break;
 443                    }
 444
 445                    break;
 446                case "bottom":
 26447                    switch (model.hAlign)
 448                    {
 449                        case "left":
 8450                            layout.childAlignment = TextAnchor.LowerLeft;
 8451                            break;
 452                        case "right":
 16453                            layout.childAlignment = TextAnchor.LowerRight;
 16454                            break;
 455                        default:
 2456                            layout.childAlignment = TextAnchor.LowerCenter;
 2457                            break;
 458                    }
 459
 460                    break;
 461                default: // center
 233462                    switch (model.hAlign)
 463                    {
 464                        case "left":
 2465                            layout.childAlignment = TextAnchor.MiddleLeft;
 2466                            break;
 467                        case "right":
 2468                            layout.childAlignment = TextAnchor.MiddleRight;
 2469                            break;
 470                        default:
 229471                            layout.childAlignment = TextAnchor.MiddleCenter;
 472                            break;
 473                    }
 474
 475                    break;
 476            }
 229477        }
 478
 479        protected void SetComponentDebugName()
 480        {
 146481            if (referencesContainer == null || model == null)
 482            {
 0483                return;
 484            }
 485
 146486            referencesContainer.name = componentName;
 146487        }
 488
 489        public override void Dispose()
 490        {
 491
 7492            if (childHookRectTransform)
 7493                Utils.SafeDestroy(childHookRectTransform.gameObject);
 494
 7495            screenSize.OnChange -= OnScreenResize;
 496
 7497            base.Dispose();
 7498        }
 499
 124500        public virtual void OnChildAttached(UIShape parentComponent, UIShape childComponent) { }
 501
 42502        public virtual void OnChildDetached(UIShape parentComponent, UIShape childComponent) { }
 503        public void Refresh()
 504        {
 55505            RefreshRecursively();
 55506            isLayoutDirty = false;
 507
 55508            OnLayoutRefresh?.Invoke();
 55509            OnLayoutRefresh = null;
 55510        }
 511    }
 512}