249 lines
8.6 KiB
C#
249 lines
8.6 KiB
C#
using System.Collections.Generic;
|
|
using DG.Tweening;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// 打孔带收藏夹视图。侧栏(<see cref="favoritesUI"/> 上的 RectTransform)做滑入滑出;
|
|
/// 可选的 <see cref="punchTapePanel"/> 仅显隐,与侧栏同开同关。数据来自 <see cref="PunchTapeManager"/>。
|
|
/// 固定 3 个槽位,打孔带按顺序放入槽位。
|
|
/// </summary>
|
|
namespace AibisDream.FixSystem
|
|
{
|
|
public class PunchTapeFavoritesPanel : MonoBehaviour
|
|
{
|
|
private const float PanelSlideDuration = 0.5f;
|
|
|
|
[Header("收藏夹 UI")]
|
|
[Tooltip("3 个固定槽位,按顺序放置打孔带")]
|
|
[SerializeField] private RectTransform[] punchTapeSlots = new RectTransform[3];
|
|
|
|
[SerializeField] private GameObject punchTapeItemPrefab;
|
|
|
|
[Tooltip("配套列表面板")]
|
|
[SerializeField] private RectTransform punchTapePanel;
|
|
|
|
[Header("定位信息")]
|
|
[SerializeField] private Vector2 panelHiddenAnchoredPos;
|
|
[SerializeField] private Vector2 panelVisibleAnchoredPos;
|
|
|
|
private readonly Dictionary<string, GameObject> _rowByTapeKey = new();
|
|
private readonly Dictionary<string, int> _slotIndexByKey = new();
|
|
|
|
private PunchTapeManager PunchTapeManager => PunchTapeManager.Instance;
|
|
|
|
private void Start()
|
|
{
|
|
if (punchTapeSlots == null || punchTapeSlots.Length != 3)
|
|
{
|
|
Debug.LogError("[PunchTapeFavoritesPanel] 必须指定 3 个 punchTapeSlots。", this);
|
|
enabled = false;
|
|
return;
|
|
}
|
|
for (var i = 0; i < punchTapeSlots.Length; i++)
|
|
{
|
|
if (punchTapeSlots[i] == null)
|
|
{
|
|
Debug.LogError($"[PunchTapeFavoritesPanel] punchTapeSlots[{i}] 未指定。", this);
|
|
enabled = false;
|
|
return;
|
|
}
|
|
}
|
|
if (punchTapeItemPrefab == null)
|
|
{
|
|
Debug.LogError("[PunchTapeFavoritesPanel] 未指定 punchTapeItemPrefab。", this);
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
PunchTapeManager.OnCollectionChanged += HandleCollectionChanged;
|
|
ApplyClosedLayout();
|
|
SyncListToData();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (PunchTapeManager != null)
|
|
PunchTapeManager.OnCollectionChanged -= HandleCollectionChanged;
|
|
|
|
punchTapePanel?.DOKill();
|
|
|
|
ClearAllRows();
|
|
_rowByTapeKey.Clear();
|
|
_slotIndexByKey.Clear();
|
|
}
|
|
|
|
private void HandleCollectionChanged() => SyncListToData();
|
|
|
|
private void ClearAllRows()
|
|
{
|
|
foreach (var kvp in _rowByTapeKey)
|
|
{
|
|
if (kvp.Value != null)
|
|
Destroy(kvp.Value);
|
|
}
|
|
_rowByTapeKey.Clear();
|
|
_slotIndexByKey.Clear();
|
|
}
|
|
|
|
private void SyncListToData()
|
|
{
|
|
var favorites = PunchTapeManager.GetFavorites();
|
|
|
|
// 回收不在前 3 个的项
|
|
var keepKeys = new HashSet<string>();
|
|
for (var i = 0; i < favorites.Count && i < punchTapeSlots.Length; i++)
|
|
keepKeys.Add(favorites[i].GetRowKey());
|
|
|
|
var staleKeys = new List<string>();
|
|
foreach (var key in _rowByTapeKey.Keys)
|
|
{
|
|
if (!keepKeys.Contains(key))
|
|
staleKeys.Add(key);
|
|
}
|
|
foreach (var key in staleKeys)
|
|
{
|
|
if (_rowByTapeKey.TryGetValue(key, out var go) && go != null)
|
|
Destroy(go);
|
|
_rowByTapeKey.Remove(key);
|
|
}
|
|
|
|
// 确保前 3 个槽位有对应项
|
|
for (var i = 0; i < punchTapeSlots.Length; i++)
|
|
{
|
|
if (i < favorites.Count)
|
|
{
|
|
var tape = favorites[i];
|
|
var key = tape.GetRowKey();
|
|
_slotIndexByKey[key] = i;
|
|
|
|
// 如果该 key 已经在别的槽位,先移过来
|
|
if (_rowByTapeKey.TryGetValue(key, out var existingGo) && existingGo != null)
|
|
{
|
|
existingGo.transform.SetParent(punchTapeSlots[i], false);
|
|
ResetRowRectTransform(existingGo);
|
|
existingGo.transform.SetSiblingIndex(i);
|
|
}
|
|
else
|
|
{
|
|
var rowGo = Instantiate(punchTapeItemPrefab, punchTapeSlots[i], false);
|
|
ResetRowRectTransform(rowGo);
|
|
_rowByTapeKey[key] = rowGo;
|
|
BindRow(rowGo, tape);
|
|
rowGo.SetActive(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 清空该槽位上不再需要的项
|
|
ClearSlot(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClearSlot(int slotIndex)
|
|
{
|
|
var slot = punchTapeSlots[slotIndex];
|
|
if (slot == null || slot.childCount == 0)
|
|
return;
|
|
|
|
// 槽位下应该只有一个打孔带子对象
|
|
for (var i = slot.childCount - 1; i >= 0; i--)
|
|
{
|
|
var child = slot.GetChild(i).gameObject;
|
|
if (child == null) continue;
|
|
|
|
var item = child.GetComponent<PunchTapeItem>();
|
|
if (item != null && item.GetPunchTapeData() != null)
|
|
{
|
|
var key = item.GetPunchTapeData().GetRowKey();
|
|
_rowByTapeKey.Remove(key);
|
|
_slotIndexByKey.Remove(key);
|
|
}
|
|
|
|
Destroy(child);
|
|
}
|
|
}
|
|
|
|
private static void ResetRowRectTransform(GameObject rowGo)
|
|
{
|
|
var rt = rowGo.GetComponent<RectTransform>();
|
|
if (rt == null) return;
|
|
|
|
rt.anchorMin = Vector2.zero;
|
|
rt.anchorMax = Vector2.one;
|
|
rt.offsetMin = Vector2.zero;
|
|
rt.offsetMax = Vector2.zero;
|
|
rt.pivot = new Vector2(0.5f, 0.5f);
|
|
rt.anchoredPosition = Vector2.zero;
|
|
rt.localRotation = Quaternion.identity;
|
|
rt.localScale = Vector3.one;
|
|
}
|
|
|
|
private void BindRow(GameObject rowGo, PunchTape tape)
|
|
{
|
|
var interaction = rowGo.GetComponent<PunchTapeInteraction>();
|
|
if (interaction != null)
|
|
{
|
|
interaction.SetIsLock(false);
|
|
interaction.SetIsFavorited(true);
|
|
}
|
|
|
|
var item = rowGo.GetComponent<PunchTapeItem>();
|
|
if (item != null && PunchTapeManager.TryGetDefinition(tape, out var definition))
|
|
item.Setup(tape, definition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将拖拽回弹的打孔带重新放置到它所属的槽位中。
|
|
/// </summary>
|
|
public void ReturnToSlot(PunchTapeInteraction interaction)
|
|
{
|
|
if (interaction == null) return;
|
|
|
|
var item = interaction.GetComponent<PunchTapeItem>();
|
|
if (item == null) return;
|
|
|
|
var tape = item.GetPunchTapeData();
|
|
if (tape == null) return;
|
|
|
|
var key = tape.GetRowKey();
|
|
if (_slotIndexByKey.TryGetValue(key, out var slotIndex) && slotIndex >= 0 && slotIndex < punchTapeSlots.Length)
|
|
{
|
|
interaction.transform.SetParent(punchTapeSlots[slotIndex], false);
|
|
ResetRowRectTransform(interaction.gameObject);
|
|
}
|
|
}
|
|
|
|
private void ApplyClosedLayout()
|
|
{
|
|
punchTapePanel.anchoredPosition = panelHiddenAnchoredPos;
|
|
punchTapePanel.gameObject.SetActive(false);
|
|
}
|
|
|
|
/// <summary>侧栏滑入并显示;<see cref="punchTapePanel"/> 若有则一并显示。</summary>
|
|
public void OpenPanel()
|
|
{
|
|
SyncListToData();
|
|
|
|
punchTapePanel.gameObject.SetActive(true);
|
|
|
|
punchTapePanel.DOKill();
|
|
punchTapePanel.DOAnchorPos(panelVisibleAnchoredPos, PanelSlideDuration);
|
|
}
|
|
|
|
/// <summary>侧栏滑出,结束后侧栏与 <see cref="punchTapePanel"/> 一并隐藏。</summary>
|
|
public void ClosePanel()
|
|
{
|
|
punchTapePanel.DOKill();
|
|
punchTapePanel
|
|
.DOAnchorPos(panelHiddenAnchoredPos, PanelSlideDuration)
|
|
.OnComplete(OnSidebarClosed);
|
|
}
|
|
|
|
private void OnSidebarClosed()
|
|
{
|
|
punchTapePanel.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|