using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace UVC.UI.Buttons
{
///
/// Unity 기본 Toggle을 상속하지 않고 직접 토글 기능을 구현한 이미지 토글.
/// isOn 상태에 따라 targetImage의 Sprite를 교체합니다.
///
///
///
/// ();
/// }
///
/// void Start()
/// {
/// // 필드 연결
/// imageToggle.SetIsOnWithoutNotify(false); // 초기 상태 강제 설정(이벤트 없이)
///
/// // targetImage 및 Sprite 지정
/// var so = new SerializedObject(imageToggle); // (직접 참조 가능하지만 예시로 표시)
/// imageToggle.GetType(); // 의미 없는 호출(예시 컴파일 방지 목적) -> 실제 코드는 제거 가능
/// imageToggle.gameObject.name = "SampleImageToggle";
/// // 에디터에서는 인스펙터에서 설정하는 것이 일반적
///
/// // 리스너 등록
/// imageToggle.OnValueChanged.AddListener(OnToggleChanged);
///
/// // 초기 텍스트 표시
/// if (stateText != null)
/// stateText.text = imageToggle.isOn ? "ON" : "OFF";
/// }
/// }
///
/// private void OnToggleChanged(bool value)
/// {
/// if (stateText != null)
/// stateText.text = value ? "ON" : "OFF";
/// }
///
/// // 외부에서 호출하여 상태를 강제로 변경하는 메서드 예시
/// public void ForceOn()
/// {
/// imageToggle.isOn = true;
/// }
/// }
/// ]]>
///
///
public class ImageToggle : Selectable, IPointerClickHandler, ISubmitHandler
{
#region 이벤트 정의
///
/// 토글 값 변경 시(bool) 호출되는 UnityEvent.
///
[System.Serializable]
public class ImageToggleEvent : UnityEvent { }
#endregion
[Header("Sprite 교체 대상")]
[Tooltip("Sprite를 교체할 대상 Image 컴포넌트")]
[SerializeField] private Image targetImage;
[Header("상태별 Sprite")]
[Tooltip("토글이 켜짐(isOn=true) 상태일 때 사용할 Sprite")]
[SerializeField] private Sprite onSprite;
[Tooltip("토글이 꺼짐(isOn=false) 상태일 때 사용할 Sprite")]
[SerializeField] private Sprite offSprite;
[Header("이벤트")]
[Tooltip("토글 값이 변경될 때 호출되는 이벤트")]
public ImageToggleEvent OnValueChanged = new ImageToggleEvent();
[Header("초기 상태")]
[Tooltip("시작 시 토글이 켜짐 상태인지 여부")]
[SerializeField] private bool m_IsOn = false;
///
/// 현재 토글 상태(읽기/쓰기). 값을 설정하면 시각과 이벤트를 갱신합니다.
///
public bool isOn
{
get => m_IsOn;
set => Set(value);
}
///
/// isOn 값을 이벤트 없이 변경합니다.
///
public void SetIsOnWithoutNotify(bool value)
{
Set(value, false);
}
///
/// isOn 값을 이벤트 없이 강제로 설정하고 시각을 업데이트합니다.
/// 풀에서 재사용 시 동일 값이어도 시각 갱신이 필요할 때 사용합니다.
///
public void ForceSetIsOnWithoutNotify(bool value)
{
m_IsOn = value;
UpdateVisual();
}
///
/// 내부 설정 로직. 상태가 변경되면 비주얼과 콜백을 처리합니다.
///
private void Set(bool value, bool sendCallback = true)
{
if (m_IsOn == value) // 동일 값이면 무시
return;
m_IsOn = value;
UpdateVisual();
if (sendCallback)
{
OnValueChanged.Invoke(m_IsOn);
}
}
protected override void Start()
{
base.Start();
UpdateVisual();
}
protected override void OnEnable()
{
base.OnEnable();
UpdateVisual();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
UpdateVisual();
}
#endif
///
/// 현재 isOn 상태에 맞게 targetImage Sprite를 교체.
///
private void UpdateVisual()
{
if (targetImage == null)
return;
targetImage.sprite = m_IsOn ? onSprite : offSprite;
}
///
/// 토글 상태를 반전시킵니다.
///
private void Toggle()
{
if (!IsActive() || !IsInteractable())
return;
isOn = !isOn; // Set 통해 처리
}
///
/// 마우스 클릭 처리(왼쪽 버튼).
///
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
return;
Toggle();
}
///
/// 키보드/게임패드 Submit 처리.
///
public void OnSubmit(BaseEventData eventData)
{
Toggle();
}
}
}