From d2ab30e7a69bfe7efda63ae75812207377917bd3 Mon Sep 17 00:00:00 2001
From: miepzerino <o.skotnik@gmail.com>
Date: Sun, 30 Mar 2025 18:50:27 +0000
Subject: [PATCH] Merge branch 'Flexalon-UI-Layouts' into develop

---
 Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs |  188 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 188 insertions(+), 0 deletions(-)

diff --git a/Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs b/Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs
new file mode 100644
index 0000000..a4cceb5
--- /dev/null
+++ b/Assets/Flexalon/Runtime/Cloner/FlexalonCloner.cs
@@ -0,0 +1,188 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Flexalon
+{
+    /// <summary>
+    /// Sometimes, it's useful to generate child objects instead of defining them statically.
+    /// The Flexalon Cloner can generate objects from a set of prefabs iteratively or randomly,
+    /// and can optionally bind to a data source.
+    /// </summary>
+    [AddComponentMenu("Flexalon/Flexalon Cloner"), HelpURL("https://www.flexalon.com/docs/cloner")]
+    public class FlexalonCloner : MonoBehaviour
+    {
+        [SerializeField]
+        private List<GameObject> _objects;
+        /// <summary> Prefabs which should be cloned as children. </summary>
+        public List<GameObject> Objects
+        {
+            get => _objects;
+            set { _objects = value; MarkDirty(); }
+        }
+
+        /// <summary> In which order should prefabs be cloned. </summary>
+        public enum CloneTypes
+        {
+            /// <summary> Clone prefabs in the order they are assigned. </summary>
+            Iterative,
+
+            /// <summary> Clone prefabs in a random order. </summary>
+            Random
+        }
+
+        [SerializeField]
+        private CloneTypes _cloneType = CloneTypes.Iterative;
+        /// <summary> In which order should prefabs be cloned. </summary>
+        public CloneTypes CloneType
+        {
+            get => _cloneType;
+            set { _cloneType = value; MarkDirty(); }
+        }
+
+        [SerializeField]
+        private uint _count;
+        /// <summary> How many clones should be generated. </summary>
+        public uint Count
+        {
+            get => _count;
+            set { _count = value; MarkDirty(); }
+        }
+
+        [SerializeField]
+        private int _randomSeed;
+        /// <summary> Seed used for the Random clone type, to ensure results remain consistent. </summary>
+        public int RandomSeed
+        {
+                get => _randomSeed;
+                set { _randomSeed = value; MarkDirty(); }
+        }
+
+        [SerializeField]
+        private GameObject _dataSource = null;
+        /// <summary> Can be an gameObject with a component that implements FlexalonDataSource.
+        /// The number of objects cloned is set to the number of items in the Data property. </summary>
+        public GameObject DataSource
+        {
+            get => _dataSource;
+            set
+            {
+                UnhookDataSource();
+                _dataSource = value;
+                HookDataSource();
+                MarkDirty();
+            }
+        }
+
+        [SerializeField, HideInInspector]
+        private List<GameObject> _clones = new List<GameObject>();
+
+        void OnEnable()
+        {
+            HookDataSource();
+            MarkDirty();
+        }
+
+        private void HookDataSource()
+        {
+            if (isActiveAndEnabled && _dataSource != null && _dataSource)
+            {
+                if (_dataSource.TryGetComponent<DataSource>(out var component))
+                {
+                    component.DataChanged += MarkDirty;
+                }
+            }
+        }
+
+        private void UnhookDataSource()
+        {
+            if (_dataSource != null && _dataSource)
+            {
+                if (_dataSource.TryGetComponent<DataSource>(out var component))
+                {
+                    component.DataChanged -= MarkDirty;
+                }
+            }
+        }
+
+        void OnDisable()
+        {
+            UnhookDataSource();
+            MarkDirty();
+        }
+
+        /// <summary> Forces the cloner to regenerate its clones. </summary>
+        public void MarkDirty()
+        {
+            foreach(var clone in _clones)
+            {
+                if (Application.isPlaying)
+                {
+                    Destroy(clone);
+                }
+                else
+                {
+                    DestroyImmediate(clone);
+                }
+            }
+
+            _clones.Clear();
+
+            if (isActiveAndEnabled && _objects != null && _objects.Count > 0)
+            {
+                switch (_cloneType)
+                {
+                    case CloneTypes.Iterative:
+                        GenerateIterativeClones();
+                        break;
+                    case CloneTypes.Random:
+                        GenerateRandomClones();
+                        break;
+                }
+            }
+        }
+
+        private IReadOnlyList<object> GetData()
+        {
+            if (_dataSource != null && _dataSource)
+            {
+                return _dataSource.GetComponent<DataSource>()?.Data;
+            }
+
+            return null;
+        }
+
+        private void GenerateIterativeClones()
+        {
+            int i = 0;
+            var data = GetData();
+            var count = data?.Count ?? (int)_count;
+            while (_clones.Count < count)
+            {
+                GenerateClone(i, data);
+                i = (i + 1) % _objects.Count;
+            }
+        }
+
+        private void GenerateRandomClones()
+        {
+            var random = new System.Random(_randomSeed);
+            var data = GetData();
+            var count = data?.Count ?? (int)_count;
+            while (_clones.Count < count)
+            {
+                GenerateClone(random.Next(_objects.Count), data);
+            }
+        }
+
+        private void GenerateClone(int index, IReadOnlyList<object> data)
+        {
+            var clone = Instantiate(_objects[index], Vector3.zero, Quaternion.identity, transform);
+            _clones.Add(clone);
+
+            if (data != null && clone.TryGetComponent<DataBinding>(out var dataBinding))
+            {
+                dataBinding.SetData(data[_clones.Count - 1]);
+            }
+        }
+    }
+}
\ No newline at end of file

--
Gitblit v1.9.3