177 lines
5.4 KiB
C#
177 lines
5.4 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace UVC.UIToolkit
|
|
{
|
|
/// <summary>
|
|
/// 모달 뒤의 배경을 차단하고 클릭 시 닫힘 처리를 담당하는 컴포넌트
|
|
/// </summary>
|
|
[UxmlElement]
|
|
public partial class UTKModalBlocker : VisualElement, IDisposable
|
|
{
|
|
#region Constants
|
|
private const string USS_CLASS = "utk-modal-blocker";
|
|
private const float DEFAULT_OPACITY = 0.7f;
|
|
private const int FADE_DURATION_MS = 200;
|
|
#endregion
|
|
|
|
#region Fields
|
|
private bool _disposed;
|
|
private bool _closeOnClick;
|
|
private float _targetOpacity;
|
|
#endregion
|
|
|
|
#region Events
|
|
/// <summary>
|
|
/// 블로커가 클릭되었을 때 발생
|
|
/// </summary>
|
|
public event Action? OnBlockerClicked;
|
|
#endregion
|
|
|
|
#region Constructor
|
|
/// <summary>
|
|
/// UTKModalBlocker 생성자
|
|
/// </summary>
|
|
public UTKModalBlocker()
|
|
{
|
|
AddToClassList(USS_CLASS);
|
|
|
|
// 기본 스타일 설정
|
|
style.position = Position.Absolute;
|
|
style.left = 0;
|
|
style.top = 0;
|
|
style.right = 0;
|
|
style.bottom = 0;
|
|
style.backgroundColor = new Color(0, 0, 0, 0);
|
|
|
|
// 클릭 이벤트 등록
|
|
RegisterCallback<ClickEvent>(OnClick);
|
|
}
|
|
#endregion
|
|
|
|
#region Static Factory
|
|
/// <summary>
|
|
/// 모달 블로커를 생성하고 표시합니다.
|
|
/// parent의 panel.visualTree에 블로커를 추가합니다.
|
|
/// </summary>
|
|
/// <param name="parent">panel 접근용 부모 요소</param>
|
|
/// <param name="opacity">배경 투명도 (0~1)</param>
|
|
/// <param name="closeOnClick">클릭 시 자동 닫힘 여부</param>
|
|
/// <returns>생성된 UTKModalBlocker 인스턴스</returns>
|
|
public static UTKModalBlocker Show(VisualElement parent, float opacity = DEFAULT_OPACITY, bool closeOnClick = false)
|
|
{
|
|
var blocker = new UTKModalBlocker
|
|
{
|
|
_closeOnClick = closeOnClick,
|
|
_targetOpacity = Mathf.Clamp01(opacity)
|
|
};
|
|
|
|
var root = parent.panel?.visualTree ?? parent;
|
|
root.Add(blocker);
|
|
blocker.FadeIn();
|
|
|
|
return blocker;
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
/// <summary>
|
|
/// 블로커를 숨기고 제거합니다.
|
|
/// </summary>
|
|
public void Hide()
|
|
{
|
|
FadeOut(() =>
|
|
{
|
|
style.display = DisplayStyle.None;
|
|
RemoveFromHierarchy();
|
|
Dispose();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 모달 콘텐츠를 블로커 앞에 추가합니다.
|
|
/// </summary>
|
|
/// <param name="modalContent">모달 콘텐츠 요소</param>
|
|
public void ShowModal(VisualElement modalContent)
|
|
{
|
|
if (modalContent == null)
|
|
return;
|
|
|
|
// 모달 콘텐츠를 블로커 다음에 추가
|
|
var parent = this.parent;
|
|
if (parent != null)
|
|
{
|
|
int blockerIndex = parent.IndexOf(this);
|
|
parent.Insert(blockerIndex + 1, modalContent);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 블로커를 맨 위로 가져옵니다.
|
|
/// </summary>
|
|
public void BringToFront()
|
|
{
|
|
this.BringToFront();
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
private void OnClick(ClickEvent evt)
|
|
{
|
|
// 블로커 자체가 클릭된 경우에만 처리 (버블링된 이벤트 무시)
|
|
if (evt.target != this)
|
|
return;
|
|
|
|
OnBlockerClicked?.Invoke();
|
|
|
|
if (_closeOnClick)
|
|
{
|
|
Hide();
|
|
}
|
|
|
|
evt.StopPropagation();
|
|
}
|
|
|
|
private void FadeIn()
|
|
{
|
|
// 시작 투명도
|
|
style.backgroundColor = new Color(0, 0, 0, 0);
|
|
|
|
// 애니메이션
|
|
schedule.Execute(() =>
|
|
{
|
|
style.transitionDuration = new StyleList<TimeValue>(new List<TimeValue> { new TimeValue(FADE_DURATION_MS, TimeUnit.Millisecond) });
|
|
style.transitionProperty = new StyleList<StylePropertyName>(new List<StylePropertyName> { new StylePropertyName("background-color") });
|
|
style.backgroundColor = new Color(0, 0, 0, _targetOpacity);
|
|
});
|
|
}
|
|
|
|
private void FadeOut(Action? onComplete = null)
|
|
{
|
|
style.transitionDuration = new StyleList<TimeValue>(new List<TimeValue> { new TimeValue(FADE_DURATION_MS, TimeUnit.Millisecond) });
|
|
style.transitionProperty = new StyleList<StylePropertyName>(new List<StylePropertyName> { new StylePropertyName("background-color") });
|
|
style.backgroundColor = new Color(0, 0, 0, 0);
|
|
|
|
// 페이드 아웃 완료 후 콜백
|
|
schedule.Execute(() => onComplete?.Invoke()).ExecuteLater(FADE_DURATION_MS);
|
|
}
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
_disposed = true;
|
|
|
|
UnregisterCallback<ClickEvent>(OnClick);
|
|
OnBlockerClicked = null;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|