ProjectDDD/Assets/_Datas/SLShared/SLUnity/SLUI/SLUIList.cs
2025-06-17 20:47:57 +09:00

356 lines
10 KiB (Stored with Git LFS)
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Superlazy.UI
{
public class SLUIList : SLUIObjectOnOff
{
public bool useSort = false;
public bool descending = false;
public string sortKey;
public string bindMaxCount = "";
public string bindMinCount = "";
public SLValueComparison comparison;
public float addDelay = 0.0f;
[NonSerialized]
public SLUIEntity listElement;
private readonly Queue<SLUIEntity> unused = new Queue<SLUIEntity>();
private readonly List<string> removeKeys = new List<string>();
private Dictionary<SLUIEntity, string> sortKeys;
protected Dictionary<SLUIEntity, SLEntity> sortValues;
private int emptyObjectHandle = 0;
protected Dictionary<string, SLUIEntity> bindObjects = new Dictionary<string, SLUIEntity>();
protected override bool ActiveSelf => isActiveAndEnabled;
protected override void Validate()
{
listElement = GetComponentInChildren<SLUIEntity>(true);
}
protected override void Init()
{
listElement.inherit = true;
listElement.bindParent = this;
listElement.useOnOff = false;
listElement.gameObject.SetActive(false);
if (useSort)
{
sortKeys = new Dictionary<SLUIEntity, string>();
sortValues = new Dictionary<SLUIEntity, SLEntity>();
}
}
protected override void Enable()
{
if (addDelay > 0)
{
StartCoroutine(UpdateList());
}
else
{
SLGame.AddNotify(base.BindPath, OnChange);
}
if (bindParent == null || bindParent.Active == false) return;
}
protected override void Disable()
{
foreach (var obj in bindObjects.Values)
{
if (useSort)
{
var key = sortKeys[obj];
SLGame.RemoveNotify(key, SortList);
sortKeys.Remove(obj);
sortValues.Remove(obj);
}
obj.gameObject.SetActive(false);
obj.name = "Unused";
unused.Enqueue(obj);
}
bindObjects.Clear();
if (unused.Count > 20)
{
foreach (var unusedItem in unused)
{
Destroy(unusedItem.gameObject);
}
unused.Clear();
}
emptyObjectHandle = 0;
if (addDelay <= 0)
{
SLGame.RemoveNotify(base.BindPath, OnChange);
}
}
private void DisableItem(SLUIEntity obj)
{
if (useSort)
{
var key = sortKeys[obj];
SLGame.RemoveNotify(key, SortList);
sortKeys.Remove(obj);
}
if (obj.OnRemoveByParent((_) => { obj.gameObject.SetActive(false); unused.Enqueue(obj); if (useSort) { sortValues.Remove(obj); } })) return;
obj.gameObject.SetActive(false);
unused.Enqueue(obj);
if (useSort)
{
sortValues.Remove(obj);
}
}
private void EnableItem(string key, SLUIEntity child)
{
child.SetupByParent(key);
child.gameObject.SetActive(true);
if (useSort)
{
var sort = base.BindPath.CombinePath(key).CombinePath(sortKey);
sortKeys.Add(child, sort);
sortValues[child] = 0; // sortList에서 업데이트
SLGame.AddNotify(sort, SortList);
}
}
protected void RemoveEntity(string key)
{
DisableItem(bindObjects[key]);
bindObjects.Remove(key);
}
protected void AddEntity(string key)
{
SLUIEntity child;
if (unused != null && unused.Count != 0)
{
child = unused.Dequeue();
child.transform.SetAsLastSibling();
}
else
{
child = Instantiate(listElement.gameObject, transform, false).GetComponent<SLUIEntity>();
}
EnableItem(key, child);
bindObjects[key] = child;
}
private void OnChange()
{
if (bindParent == null || bindParent.Active == false) return;
// base를 강제하는 이유는 이후 상속된 이벤트 등에서 다형성으로 적용되면 안되기 때문
var session = SLGame.SessionGet(base.BindPath);
var needSort = false;
removeKeys.Clear();
foreach (var key in bindObjects.Keys)
{
if (CheckRemoveKey(session, key))
{
removeKeys.Add(key);
needSort = true;
}
}
foreach (var key in removeKeys)
{
OnRemoveKey(session, key);
}
foreach (var item in session)
{
if (CheckAddKey(session, item.ID))
{
OnAddKey(session, item.ID);
needSort = true;
}
}
if (bindMinCount != string.Empty)
{
int minCount = SLGame.SessionGet(bindMinCount);
if (bindObjects.Count < minCount)
{
var emptyCount = minCount - bindObjects.Count;
for (var i = 0; i < emptyCount; ++i)
{
OnAddKey(session, $"__Empty{emptyObjectHandle}");
emptyObjectHandle += 1;
}
}
}
if (useSort && sortKey != null && needSort)
{
SortList();
}
}
private IEnumerator UpdateList()
{
do
{
if (bindParent == null || bindParent.Active == false) yield break;
// 삭제는 전체를 진행
// base를 강제하는 이유는 이후 상속된 이벤트 등에서 다형성으로 적용되면 안되기 때문
var session = SLGame.SessionGet(base.BindPath);
var added = false;
removeKeys.Clear();
foreach (var key in bindObjects.Keys)
{
if (CheckRemoveKey(session, key))
{
removeKeys.Add(key);
}
}
foreach (var key in removeKeys)
{
OnRemoveKey(session, key);
}
var items = session.Where(s => CheckAddKey(session, s.ID));
if (items.Count() > 0)
{
SLEntity item;
if (useSort && sortKey != null)
{
if (descending)
{
item = items.MaxBy(s => s[sortKey]);
}
else
{
item = items.MinBy(s => s[sortKey]);
}
}
else
{
item = items.First();
}
OnAddKey(session, item.ID);
added = true;
}
if (bindMinCount != string.Empty)
{
int minCount = SLGame.SessionGet(bindMinCount);
if (bindObjects.Count < minCount)
{
var emptyCount = minCount - bindObjects.Count;
for (var i = 0; i < emptyCount; ++i)
{
OnAddKey(session, $"__Empty{emptyObjectHandle}");
emptyObjectHandle += 1;
}
}
}
if (added)
{
yield return new WaitForSeconds(addDelay);
}
else
{
yield return null;
}
} while (true);
}
private void SortList()
{
if (ActiveSelf == false) return;
foreach (var obj in sortKeys)
{
var newData = SLGame.SessionGet(obj.Value);
if (newData == false) continue; // 소트키가 삭제됨
sortValues[obj.Key] = newData;
}
IOrderedEnumerable<KeyValuePair<SLUIEntity, SLEntity>> orderedList;
if (descending)
{
orderedList = sortValues.OrderByDescending((u) => u.Value);
}
else
{
orderedList = sortValues.OrderBy((u) => u.Value);
}
var i = 0;
foreach (var item in orderedList)
{
item.Key.transform.SetSiblingIndex(i);
++i;
}
}
protected virtual bool CheckAddKey(SLEntity session, string key)
{
// TODO: Min,Max가 둘다 있는 상황에서 빈오브젝트가 있으면 추가가 안되거나 할 수 있음.
if (bindMaxCount != string.Empty)
{
int maxCount = SLGame.SessionGet(bindMaxCount);
if (bindObjects.Count >= maxCount) return false;
}
if (comparison.CheckPath(BindPath.CombinePath(key)) == false) return false;
if (bindObjects.ContainsKey(key) == false)
{
return true;
}
return false;
}
protected virtual bool CheckRemoveKey(SLEntity session, string key)
{
if (session.HasChild(key) == false)
{
return true;
}
if (comparison.CheckPath(BindPath.CombinePath(key)) == false) return true;
return false;
}
protected virtual void OnRemoveKey(SLEntity session, string key)
{
RemoveEntity(key);
}
protected virtual void OnAddKey(SLEntity session, string key)
{
AddEntity(key);
}
}
}