탐색기, 라이브러리, 화면 객체 배치

This commit is contained in:
logonkhi
2025-12-18 20:38:38 +09:00
parent beca5f0da5
commit deeaa9a7ad
102 changed files with 4956 additions and 387 deletions

View File

@@ -135,6 +135,7 @@ namespace UVC.UI.List.Accordion
public int? CaptionSize;
/// <summary>임의의 태그/키 값.</summary>
public string? Tag;
public object? Data;
}
/// <summary>

View File

@@ -23,6 +23,7 @@ namespace UVC.UI.List.Accordion
private Image? _dragGhost;
private AccordionGridItemData? _boundData;
private AccordionList? _list;
private RectTransform? _listRectTransform;
/// <summary>
/// 에디터에서 컴포넌트를 추가할 때 기본 레이아웃을 보장합니다.
@@ -99,8 +100,9 @@ namespace UVC.UI.List.Accordion
{
_boundData = data;
_list = list;
_listRectTransform = list != null ? list.GetComponent<RectTransform>() : null;
caption.text = data.Caption;
if (data.Image != null) image.sprite = await ResourceManager.LoadOnlyAsync<Sprite>(data.Image);
if (data.Image != null) image.sprite = await ResourceManager.LoadSpriteAsync(data.Image);
if (data.CaptionColor.HasValue) caption.color = data.CaptionColor.Value;
if (data.CaptionSize.HasValue) caption.fontSize = data.CaptionSize.Value;
button.onClick.RemoveAllListeners();
@@ -119,23 +121,33 @@ namespace UVC.UI.List.Accordion
{
if (image == null || image.sprite == null || _canvas == null) return;
// 고스트 생성 (이미지 스프라이트 복사)
var go = new GameObject("DragGhost", typeof(RectTransform), typeof(CanvasGroup), typeof(Image));
go.transform.SetParent(_canvas.transform, false);
go.transform.SetAsLastSibling();
var ghostImg = go.GetComponent<Image>();
ghostImg.sprite = image.sprite;
ghostImg.preserveAspect = true;
ghostImg.raycastTarget = false; // 드래그 중에는 이벤트 차단하지 않도록
ghostImg.color = new Color(1f, 1f, 1f, 0.6f);
// AccordionList의 이벤트를 통해 드래그 시작 처리
if (_boundData != null && _list != null)
{
_list.HandleGridItemBeginDrag(_boundData, eventData.position);
}
// 크기/위치 초기화
var srcRT = image.rectTransform;
var dstRT = (RectTransform)go.transform;
dstRT.sizeDelta = srcRT.rect.size;
SetGhostPosition(eventData, dstRT);
// DragImageFollowCursor가 true일 때만 고스트 이미지 생성
if (_list != null && _list.DragImageFollowCursor)
{
// 고스트 생성 (이미지 스프라이트 복사)
var go = new GameObject("DragGhost", typeof(RectTransform), typeof(CanvasGroup), typeof(Image));
go.transform.SetParent(_canvas.transform, false);
go.transform.SetAsLastSibling();
var ghostImg = go.GetComponent<Image>();
ghostImg.sprite = image.sprite;
ghostImg.preserveAspect = true;
ghostImg.raycastTarget = false; // 드래그 중에는 이벤트 차단하지 않도록
ghostImg.color = new Color(1f, 1f, 1f, 0.6f);
_dragGhost = ghostImg;
// 크기/위치 초기화
var srcRT = image.rectTransform;
var dstRT = (RectTransform)go.transform;
dstRT.sizeDelta = srcRT.rect.size;
SetGhostPosition(eventData, dstRT);
_dragGhost = ghostImg;
}
}
/// <summary>
@@ -144,7 +156,25 @@ namespace UVC.UI.List.Accordion
/// <param name="eventData">포인터 이벤트 데이터.</param>
public void OnDrag(PointerEventData eventData)
{
// AccordionList의 이벤트를 통해 드래그 중 처리
if (_boundData != null && _list != null)
{
_list.HandleGridItemDrag(_boundData, eventData.position);
}
if (_dragGhost == null || _canvas == null) return;
// AccordionList 영역 체크하여 고스트 표시/숨김
if (_listRectTransform != null)
{
bool isInsideList = RectTransformUtility.RectangleContainsScreenPoint(
_listRectTransform,
eventData.position,
_canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : _canvas.worldCamera
);
_dragGhost.enabled = isInsideList;
}
SetGhostPosition(eventData, (RectTransform)_dragGhost.transform);
}
@@ -164,6 +194,7 @@ namespace UVC.UI.List.Accordion
// AccordionList의 이벤트를 통해 드롭 처리
if (_boundData != null && _list != null)
{
_list.HandleGridItemEndDrag(_boundData, eventData.position);
_list.HandleGridItemDrop(_boundData);
}
}

View File

@@ -152,7 +152,7 @@ namespace UVC.UI.List.Accordion
return;
}
headerImage.gameObject.SetActive(true);
headerImage.sprite = await ResourceManager.LoadOnlyAsync<Sprite>(sprite);
headerImage.sprite = await ResourceManager.LoadSpriteAsync(sprite);
headerImage.color = color;
}
@@ -172,7 +172,7 @@ namespace UVC.UI.List.Accordion
button.onClick.AddListener(() => _list.HandleHorizontalItemClick(_data, contentSpec));
}
var img = go.GetComponent<Image>();
img.sprite = await ResourceManager.LoadOnlyAsync<Sprite>(icon);
img.sprite = await ResourceManager.LoadSpriteAsync(icon);
}
private void OnDestroy()

View File

@@ -37,6 +37,16 @@ namespace UVC.UI.List.Accordion
[Header("Root")]
[SerializeField] private RectTransform contentRoot = default!; // VerticalLayoutGroup 권장
[Header("Options")]
[Tooltip("드래그 시 그리드 아이템 이미지가 커서를 따라다니도록 설정합니다.")]
[SerializeField]
public bool dragImageFollowCursor = true;
/// <summary>
/// 드래그 시 그리드 아이템 이미지가 커서를 따라다니도록 설정합니다.
/// </summary>
public bool DragImageFollowCursor => dragImageFollowCursor;
#region (Events)
/// <summary>
@@ -55,6 +65,24 @@ namespace UVC.UI.List.Accordion
/// </summary>
public Action<AccordionGridItemData>? OnGridItemDrop;
/// <summary>
/// 그리드 아이템 드래그 시작 이벤트.
/// (itemData, screenPosition)
/// </summary>
public Action<AccordionGridItemData, Vector2>? OnGridItemBeginDrag;
/// <summary>
/// 그리드 아이템 드래그 중 이벤트.
/// (itemData, screenPosition)
/// </summary>
public Action<AccordionGridItemData, Vector2>? OnGridItemDrag;
/// <summary>
/// 그리드 아이템 드래그 종료 이벤트 (위치 포함).
/// (itemData, screenPosition)
/// </summary>
public Action<AccordionGridItemData, Vector2>? OnGridItemEndDrag;
#endregion
/// <summary>
@@ -144,6 +172,9 @@ namespace UVC.UI.List.Accordion
OnHorizontalItemClick = null;
OnGridItemClick = null;
OnGridItemDrop = null;
OnGridItemBeginDrag = null;
OnGridItemDrag = null;
OnGridItemEndDrag = null;
}
#region (Internal Event Handlers)
@@ -172,6 +203,30 @@ namespace UVC.UI.List.Accordion
OnGridItemDrop?.Invoke(itemData);
}
/// <summary>
/// 그리드 아이템 드래그 시작 처리 (AccordionGridItemView에서 호출)
/// </summary>
internal void HandleGridItemBeginDrag(AccordionGridItemData itemData, Vector2 screenPosition)
{
OnGridItemBeginDrag?.Invoke(itemData, screenPosition);
}
/// <summary>
/// 그리드 아이템 드래그 중 처리 (AccordionGridItemView에서 호출)
/// </summary>
internal void HandleGridItemDrag(AccordionGridItemData itemData, Vector2 screenPosition)
{
OnGridItemDrag?.Invoke(itemData, screenPosition);
}
/// <summary>
/// 그리드 아이템 드래그 종료 처리 (AccordionGridItemView에서 호출)
/// </summary>
internal void HandleGridItemEndDrag(AccordionGridItemData itemData, Vector2 screenPosition)
{
OnGridItemEndDrag?.Invoke(itemData, screenPosition);
}
#endregion
}
}

View File

@@ -64,7 +64,7 @@ namespace UVC.UI.List.Accordion
else
{
bg.color = expandColor;
bg.enabled = false;
// bg.enabled = false;
}
if (titleText == null)
@@ -112,7 +112,7 @@ namespace UVC.UI.List.Accordion
expandButton.onClick.RemoveAllListeners();
expandButton.onClick.AddListener(() => Toggle());
bg.enabled = _data.IsExpanded;
// bg.enabled = _data.IsExpanded;
expandButton.transform.DOKill(); // 이전 애니메이션이 있으면 종료
expandButton.transform.DORotate(new Vector3(0, 0, !_data.IsExpanded ? 0 : 180), 0.0f);
@@ -141,7 +141,7 @@ namespace UVC.UI.List.Accordion
if (_data == null) return;
_data.IsExpanded = !_data.IsExpanded;
bg.enabled = _data.IsExpanded;
//bg.enabled = _data.IsExpanded;
expandButton.transform.DOKill(); // 이전 애니메이션이 있으면 종료
expandButton.transform.DORotate(new Vector3(0, 0, !_data.IsExpanded ? 0 : 180), 0.0f);

View File

@@ -23,6 +23,15 @@ namespace UVC.UI.List
[SerializeField]
protected TextMeshProUGUI searchResultText;
[Tooltip("드래그 시 그리드 아이템 이미지가 커서를 따라다니도록 설정합니다.")]
[SerializeField]
public bool dragImageFollowCursor = true;
/// <summary>
/// 드래그 시 그리드 아이템 이미지가 커서를 따라다니도록 설정합니다.
/// </summary>
public bool DragImageFollowCursor => dragImageFollowCursor;
// InfiniteScroll에 표시될 원본 데이터 리스트입니다.
protected List<PrefabGridItemData>? data;
@@ -42,6 +51,24 @@ namespace UVC.UI.List
/// </summary>
public Action<PrefabGridItemData>? OnItemDrop;
/// <summary>
/// 아이템 드래그 시작 이벤트.
/// (itemData, screenPosition)
/// </summary>
public Action<PrefabGridItemData, Vector2>? OnItemBeginDrag;
/// <summary>
/// 아이템 드래그 중 이벤트.
/// (itemData, screenPosition)
/// </summary>
public Action<PrefabGridItemData, Vector2>? OnItemDrag;
/// <summary>
/// 아이템 드래그 종료 이벤트.
/// (itemData, screenPosition)
/// </summary>
public Action<PrefabGridItemData, Vector2>? OnItemEndDrag;
#endregion
/// <summary>
@@ -93,6 +120,9 @@ namespace UVC.UI.List
// 이벤트 핸들러 정리
OnItemClick = null;
OnItemDrop = null;
OnItemBeginDrag = null;
OnItemDrag = null;
OnItemEndDrag = null;
// 데이터 참조 정리
data?.Clear();
@@ -119,6 +149,30 @@ namespace UVC.UI.List
OnItemDrop?.Invoke(itemData);
}
/// <summary>
/// 아이템 드래그 시작 처리 (PrefabGridItem에서 호출)
/// </summary>
internal void HandleItemBeginDrag(PrefabGridItemData itemData, Vector2 screenPosition)
{
OnItemBeginDrag?.Invoke(itemData, screenPosition);
}
/// <summary>
/// 아이템 드래그 중 처리 (PrefabGridItem에서 호출)
/// </summary>
internal void HandleItemDrag(PrefabGridItemData itemData, Vector2 screenPosition)
{
OnItemDrag?.Invoke(itemData, screenPosition);
}
/// <summary>
/// 아이템 드래그 종료 처리 (PrefabGridItem에서 호출)
/// </summary>
internal void HandleItemEndDrag(PrefabGridItemData itemData, Vector2 screenPosition)
{
OnItemEndDrag?.Invoke(itemData, screenPosition);
}
#endregion
/// <summary>

View File

@@ -83,7 +83,7 @@ namespace UVC.UI.List
PrefabGridItemData data = (PrefabGridItemData)scrollData;
text.text = data.ItemName;
image.sprite = await ResourceManager.LoadOnlyAsync<Sprite>(data.ImagePrefabPath);
image.sprite = await ResourceManager.LoadSpriteAsync(data.ImagePrefabPath);
_boundData = data;
}
@@ -112,23 +112,33 @@ namespace UVC.UI.List
{
if (image == null || image.sprite == null || _canvas == null) return;
// 고스트 생성 (이미지 스프라이트 복사)
var go = new GameObject("DragGhost", typeof(RectTransform), typeof(CanvasGroup), typeof(Image));
go.transform.SetParent(_canvas.transform, false);
go.transform.SetAsLastSibling();
var ghostImg = go.GetComponent<Image>();
ghostImg.sprite = image.sprite;
ghostImg.preserveAspect = true;
ghostImg.raycastTarget = false; // 드래그 중에는 이벤트 차단하지 않도록
ghostImg.color = new Color(1f, 1f, 1f, 0.6f);
// PrefabGrid의 이벤트를 통해 드래그 시작 처리
if (_boundData != null && _prefabGrid != null)
{
_prefabGrid.HandleItemBeginDrag(_boundData, eventData.position);
}
// 크기/위치 초기화
var srcRT = image.rectTransform;
var dstRT = (RectTransform)go.transform;
dstRT.sizeDelta = srcRT.rect.size;
SetGhostPosition(eventData, dstRT);
// DragImageFollowCursor가 true일 때만 고스트 이미지 생성
if (_prefabGrid != null && _prefabGrid.DragImageFollowCursor)
{
// 고스트 생성 (이미지 스프라이트 복사)
var go = new GameObject("DragGhost", typeof(RectTransform), typeof(CanvasGroup), typeof(Image));
go.transform.SetParent(_canvas.transform, false);
go.transform.SetAsLastSibling();
var ghostImg = go.GetComponent<Image>();
ghostImg.sprite = image.sprite;
ghostImg.preserveAspect = true;
ghostImg.raycastTarget = false; // 드래그 중에는 이벤트 차단하지 않도록
ghostImg.color = new Color(1f, 1f, 1f, 0.6f);
_dragGhost = ghostImg;
// 크기/위치 초기화
var srcRT = image.rectTransform;
var dstRT = (RectTransform)go.transform;
dstRT.sizeDelta = srcRT.rect.size;
SetGhostPosition(eventData, dstRT);
_dragGhost = ghostImg;
}
}
/// <summary>
@@ -137,6 +147,12 @@ namespace UVC.UI.List
/// <param name="eventData">포인터 이벤트 데이터.</param>
public void OnDrag(PointerEventData eventData)
{
// PrefabGrid의 이벤트를 통해 드래그 중 처리
if (_boundData != null && _prefabGrid != null)
{
_prefabGrid.HandleItemDrag(_boundData, eventData.position);
}
if (_dragGhost == null || _canvas == null) return;
SetGhostPosition(eventData, (RectTransform)_dragGhost.transform);
}
@@ -157,6 +173,7 @@ namespace UVC.UI.List
// PrefabGrid의 이벤트를 통해 드롭 처리
if (_boundData != null && _prefabGrid != null)
{
_prefabGrid.HandleItemEndDrag(_boundData, eventData.position);
_prefabGrid.HandleItemDrop(_boundData);
}
}