356 lines
10 KiB
C#
356 lines
10 KiB
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);
|
|
}
|
|
}
|
|
} |