< Summary

Class:DCL.Components.UIShape
Assembly:MainScripts
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/UI/UIShape.cs
Covered lines:134
Uncovered lines:10
Coverable lines:144
Total lines:455
Line coverage:93% (134 of 144)
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%110100%
GetClassId()0%2100%
GetDebugName()0%220100%
ApplyChanges(...)0%2100%
InstantiateUIGameObject[T](...)0%6.076087.5%
RefreshAll()0%110100%
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%13130100%
SetComponentDebugName()0%3.143075%
Dispose()0%220100%
OnChildAttached(...)0%110100%
OnChildDetached(...)0%110100%

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 UnityEngine;
 5using UnityEngine.Assertions;
 6using UnityEngine.UI;
 7
 8namespace DCL.Components
 9{
 10    [System.Serializable]
 11    public struct UIValue
 12    {
 13        public enum Unit
 14        {
 15            PERCENT,
 16            PIXELS
 17        }
 18
 19        public float value;
 20        public Unit type;
 21
 22        public void SetPixels(float value)
 23        {
 24            this.type = Unit.PIXELS;
 25            this.value = value;
 26        }
 27
 28        public void SetPercent(float value)
 29        {
 30            this.type = Unit.PERCENT;
 31            this.value = value;
 32        }
 33
 34        public UIValue(float value, Unit unitType = Unit.PIXELS)
 35        {
 36            this.value = value;
 37            this.type = unitType;
 38        }
 39
 40        public float GetScaledValue(float parentSize)
 41        {
 42            if (type == Unit.PIXELS)
 43                return value;
 44
 45            // Prevent division by zero
 46            if (parentSize <= Mathf.Epsilon)
 47                parentSize = 1;
 48
 49            return value / 100 * parentSize;
 50        }
 51    }
 52
 53    public class UIShape<ReferencesContainerType, ModelType> : UIShape
 54        where ReferencesContainerType : UIReferencesContainer
 55        where ModelType : UIShape.Model
 56    {
 57        public UIShape() { }
 58
 59        new public ModelType model { get { return base.model as ModelType; } set { base.model = value; } }
 60
 61        new public ReferencesContainerType referencesContainer { get { return base.referencesContainer as ReferencesCont
 62
 63        public override ComponentUpdateHandler CreateUpdateHandler() { return new UIShapeUpdateHandler<ReferencesContain
 64
 65        bool raiseOnAttached;
 66        bool firstApplyChangesCall;
 67
 68        /// <summary>
 69        /// This is called by UIShapeUpdateHandler before calling ApplyChanges.
 70        /// </summary>
 71        public void PreApplyChanges(BaseModel newModel)
 72        {
 73            model = (ModelType) newModel;
 74
 75            raiseOnAttached = false;
 76            firstApplyChangesCall = false;
 77
 78            if (referencesContainer == null)
 79            {
 80                referencesContainer = InstantiateUIGameObject<ReferencesContainerType>(referencesContainerPrefabName);
 81
 82                raiseOnAttached = true;
 83                firstApplyChangesCall = true;
 84            }
 85            else if (ReparentComponent(referencesContainer.rectTransform, model.parentComponent))
 86            {
 87                raiseOnAttached = true;
 88            }
 89        }
 90
 91        public override void RaiseOnAppliedChanges()
 92        {
 93            RefreshDCLLayout();
 94
 95#if UNITY_EDITOR
 96            SetComponentDebugName();
 97#endif
 98
 99            // We hide the component visibility when it's created (first applychanges)
 100            // as it has default values and appears in the middle of the screen
 101            if (firstApplyChangesCall)
 102                referencesContainer.canvasGroup.alpha = 0f;
 103            else
 104                referencesContainer.canvasGroup.alpha = model.visible ? model.opacity : 0f;
 105
 106            referencesContainer.canvasGroup.blocksRaycasts = model.visible && model.isPointerBlocker;
 107
 108            base.RaiseOnAppliedChanges();
 109
 110            if (raiseOnAttached && parentUIComponent != null)
 111            {
 112                UIReferencesContainer[] parents = referencesContainer.GetComponentsInParent<UIReferencesContainer>(true)
 113
 114                for (int i = 0; i < parents.Length; i++)
 115                {
 116                    UIReferencesContainer parent = parents[i];
 117                    if (parent.owner != null)
 118                    {
 119                        parent.owner.OnChildAttached(parentUIComponent, this);
 120                    }
 121                }
 122            }
 123        }
 124    }
 125
 126    public class UIShape : BaseDisposable
 127    {
 128        [System.Serializable]
 129        public class Model : BaseModel
 130        {
 131            public string name;
 132            public string parentComponent;
 610133            public bool visible = true;
 610134            public float opacity = 1f;
 610135            public string hAlign = "center";
 610136            public string vAlign = "center";
 610137            public UIValue width = new UIValue(100f);
 610138            public UIValue height = new UIValue(50f);
 610139            public UIValue positionX = new UIValue(0f);
 610140            public UIValue positionY = new UIValue(0f);
 610141            public bool isPointerBlocker = true;
 142            public string onClick;
 143
 51144            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 145        }
 146
 145147        public override string componentName => GetDebugName();
 0148        public virtual string referencesContainerPrefabName => "";
 149        public UIReferencesContainer referencesContainer;
 150        public RectTransform childHookRectTransform;
 151
 0152        private BaseVariable<Vector2Int> screenSize => DataStore.i.screen.size;
 153
 0154        public UIShape parentUIComponent { get; protected set; }
 155
 120156        public UIShape()
 157        {
 120158            screenSize.OnChange += OnScreenResize;
 120159            model = new Model();
 120160        }
 161
 37162        private void OnScreenResize(Vector2Int current, Vector2Int previous) => RefreshAll();
 163
 0164        public override int GetClassId() { return (int) CLASS_ID.UI_IMAGE_SHAPE; }
 165
 166        public string GetDebugName()
 167        {
 145168            Model model = (Model) this.model;
 145169            if (string.IsNullOrEmpty(model.name))
 170            {
 138171                return GetType().Name;
 172            }
 173            else
 174            {
 7175                return GetType().Name + " - " + model.name;
 176            }
 177        }
 178
 0179        public override IEnumerator ApplyChanges(BaseModel newJson) { return null; }
 180
 181        internal T InstantiateUIGameObject<T>(string prefabPath) where T : UIReferencesContainer
 182        {
 77183            Model model = (Model) this.model;
 77184            GameObject uiGameObject = null;
 77185            bool targetParentExists = !string.IsNullOrEmpty(model.parentComponent) &&
 186                                      scene.disposableComponents.ContainsKey(model.parentComponent);
 187
 77188            if (targetParentExists)
 189            {
 23190                if (scene.disposableComponents.ContainsKey(model.parentComponent))
 191                {
 23192                    parentUIComponent = (scene.disposableComponents[model.parentComponent] as UIShape);
 23193                }
 194                else
 195                {
 0196                    parentUIComponent = scene.GetSharedComponent<UIScreenSpace>();
 197                }
 0198            }
 199            else
 200            {
 54201                parentUIComponent = scene.GetSharedComponent<UIScreenSpace>();
 202            }
 203
 77204            uiGameObject =
 205                UnityEngine.Object.Instantiate(
 206                    Resources.Load(prefabPath),
 207                    parentUIComponent != null ? parentUIComponent.childHookRectTransform : null) as GameObject;
 77208            referencesContainer = uiGameObject.GetComponent<T>();
 209
 77210            referencesContainer.rectTransform.SetToMaxStretch();
 211
 77212            childHookRectTransform = referencesContainer.childHookRectTransform;
 213
 77214            referencesContainer.owner = this;
 215
 77216            return referencesContainer as T;
 217        }
 218
 219        public virtual void RefreshAll()
 220        {
 130221            RefreshDCLLayoutRecursively(refreshSize: true, refreshAlignmentAndPosition: false);
 130222            FixMaxStretchRecursively();
 130223            RefreshDCLLayoutRecursively_Internal(refreshSize: false, refreshAlignmentAndPosition: true);
 130224        }
 225
 226        public void RefreshDCLLayout(bool refreshSize = true, bool refreshAlignmentAndPosition = true)
 227        {
 511228            RectTransform parentRT = referencesContainer.GetComponentInParent<RectTransform>();
 229
 511230            if (refreshSize)
 231            {
 316232                RefreshDCLSize(parentRT);
 233            }
 234
 511235            if (refreshAlignmentAndPosition)
 236            {
 237                // Alignment (Alignment uses size so we should always align AFTER resizing)
 340238                RefreshDCLAlignmentAndPosition(parentRT);
 239            }
 511240        }
 241
 242        protected virtual void RefreshDCLSize(RectTransform parentTransform = null)
 243        {
 300244            if (parentTransform == null)
 245            {
 0246                parentTransform = referencesContainer.GetComponentInParent<RectTransform>();
 247            }
 248
 300249            Model model = (Model) this.model;
 250
 300251            referencesContainer.layoutElementRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,
 252                model.width.GetScaledValue(parentTransform.rect.width));
 300253            referencesContainer.layoutElementRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical,
 254                model.height.GetScaledValue(parentTransform.rect.height));
 300255        }
 256
 257        public void RefreshDCLAlignmentAndPosition(RectTransform parentTransform = null)
 258        {
 340259            if (parentTransform == null)
 260            {
 0261                parentTransform = referencesContainer.GetComponentInParent<RectTransform>();
 262            }
 263
 340264            referencesContainer.layoutElement.ignoreLayout = false;
 340265            ConfigureAlignment(referencesContainer.layoutGroup);
 340266            Utils.ForceRebuildLayoutImmediate(parentTransform);
 340267            referencesContainer.layoutElement.ignoreLayout = true;
 268
 340269            Model model = (Model) this.model;
 270            // Reposition
 340271            Vector3 position = Vector3.zero;
 340272            position.x = model.positionX.GetScaledValue(parentTransform.rect.width);
 340273            position.y = model.positionY.GetScaledValue(parentTransform.rect.height);
 274
 340275            position = Utils.Sanitize(position);
 340276            referencesContainer.layoutElementRT.localPosition += position;
 340277        }
 278
 279        public virtual void RefreshDCLLayoutRecursively(bool refreshSize = true,
 280            bool refreshAlignmentAndPosition = true)
 281        {
 154282            RefreshDCLLayoutRecursively_Internal(refreshSize, refreshAlignmentAndPosition);
 154283        }
 284
 285        public void RefreshDCLLayoutRecursively_Internal(bool refreshSize = true,
 286            bool refreshAlignmentAndPosition = true)
 287        {
 284288            UIShape rootParent = GetRootParent();
 289
 284290            Assert.IsTrue(rootParent != null, "root parent must never be null");
 291
 284292            if (rootParent.referencesContainer == null)
 68293                return;
 294
 216295            Utils.InverseTransformChildTraversal<UIReferencesContainer>(
 296                (x) =>
 297                {
 510298                    if (x.owner != null)
 299                    {
 366300                        x.owner.RefreshDCLLayout(refreshSize, refreshAlignmentAndPosition);
 301                    }
 510302                },
 303                rootParent.referencesContainer.transform);
 216304        }
 305
 306        public void FixMaxStretchRecursively()
 307        {
 130308            UIShape rootParent = GetRootParent();
 309
 130310            Assert.IsTrue(rootParent != null, "root parent must never be null");
 311
 130312            if (rootParent.referencesContainer == null)
 34313                return;
 314
 96315            Utils.InverseTransformChildTraversal<UIReferencesContainer>(
 316                (x) =>
 317                {
 243318                    if (x.owner != null)
 319                    {
 171320                        x.rectTransform.SetToMaxStretch();
 321                    }
 243322                },
 323                rootParent.referencesContainer.transform);
 96324        }
 325
 326        protected bool ReparentComponent(RectTransform targetTransform, string targetParent)
 327        {
 68328            bool targetParentExists = !string.IsNullOrEmpty(targetParent) &&
 329                                      scene.disposableComponents.ContainsKey(targetParent);
 330
 68331            if (targetParentExists && parentUIComponent == scene.disposableComponents[targetParent])
 332            {
 22333                return false;
 334            }
 335
 46336            if (parentUIComponent != null)
 337            {
 46338                UIReferencesContainer[] parents = referencesContainer.GetComponentsInParent<UIReferencesContainer>(true)
 339
 186340                foreach (var parent in parents)
 341                {
 47342                    if (parent.owner != null)
 343                    {
 47344                        parent.owner.OnChildDetached(parentUIComponent, this);
 345                    }
 346                }
 347            }
 348
 46349            if (targetParentExists)
 350            {
 22351                parentUIComponent = scene.disposableComponents[targetParent] as UIShape;
 22352            }
 353            else
 354            {
 24355                parentUIComponent = scene.GetSharedComponent<UIScreenSpace>();
 356            }
 357
 46358            targetTransform.SetParent(parentUIComponent.childHookRectTransform, false);
 46359            return true;
 360        }
 361
 362        public UIShape GetRootParent()
 363        {
 432364            UIShape parent = null;
 365
 432366            if (parentUIComponent != null && !(parentUIComponent is UIScreenSpace))
 367            {
 18368                parent = parentUIComponent.GetRootParent();
 18369            }
 370            else
 371            {
 414372                parent = this;
 373            }
 374
 432375            return parent;
 376        }
 377
 378        protected void ConfigureAlignment(LayoutGroup layout)
 379        {
 340380            Model model = (Model) this.model;
 340381            switch (model.vAlign)
 382            {
 383                case "top":
 7384                    switch (model.hAlign)
 385                    {
 386                        case "left":
 2387                            layout.childAlignment = TextAnchor.UpperLeft;
 2388                            break;
 389                        case "right":
 2390                            layout.childAlignment = TextAnchor.UpperRight;
 2391                            break;
 392                        default:
 3393                            layout.childAlignment = TextAnchor.UpperCenter;
 3394                            break;
 395                    }
 396
 397                    break;
 398                case "bottom":
 29399                    switch (model.hAlign)
 400                    {
 401                        case "left":
 8402                            layout.childAlignment = TextAnchor.LowerLeft;
 8403                            break;
 404                        case "right":
 19405                            layout.childAlignment = TextAnchor.LowerRight;
 19406                            break;
 407                        default:
 2408                            layout.childAlignment = TextAnchor.LowerCenter;
 2409                            break;
 410                    }
 411
 412                    break;
 413                default: // center
 304414                    switch (model.hAlign)
 415                    {
 416                        case "left":
 2417                            layout.childAlignment = TextAnchor.MiddleLeft;
 2418                            break;
 419                        case "right":
 2420                            layout.childAlignment = TextAnchor.MiddleRight;
 2421                            break;
 422                        default:
 300423                            layout.childAlignment = TextAnchor.MiddleCenter;
 424                            break;
 425                    }
 426
 427                    break;
 428            }
 300429        }
 430
 431        protected void SetComponentDebugName()
 432        {
 145433            if (referencesContainer == null || model == null)
 434            {
 0435                return;
 436            }
 437
 145438            referencesContainer.name = componentName;
 145439        }
 440
 441        public override void Dispose()
 442        {
 84443            if (childHookRectTransform)
 51444                Utils.SafeDestroy(childHookRectTransform.gameObject);
 445
 84446            screenSize.OnChange -= OnScreenResize;
 447
 84448            base.Dispose();
 84449        }
 450
 114451        public virtual void OnChildAttached(UIShape parentComponent, UIShape childComponent) { }
 452
 38453        public virtual void OnChildDetached(UIShape parentComponent, UIShape childComponent) { }
 454    }
 455}