2026-01-08 20:15:57 +09:00
|
|
|
#nullable enable
|
|
|
|
|
using System;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UIElements;
|
|
|
|
|
|
|
|
|
|
namespace UVC.UIToolkit
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 프로그레스 바 컴포넌트.
|
|
|
|
|
/// Unity ProgressBar를 래핑하여 커스텀 스타일을 적용합니다.
|
2026-01-21 20:43:54 +09:00
|
|
|
/// 작업의 진행 상황을 시각적으로 표시합니다.
|
2026-01-08 20:15:57 +09:00
|
|
|
/// </summary>
|
2026-01-21 20:43:54 +09:00
|
|
|
/// <remarks>
|
|
|
|
|
/// <para><b>ProgressBar(진행률 표시줄)란?</b></para>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// ProgressBar는 작업의 완료 정도를 시각적으로 표시하는 UI 컨트롤입니다.
|
|
|
|
|
/// 파일 다운로드, 로딩, 설치 진행률 등 사용자에게 대기 시간과 완료 상태를 알려줍니다.
|
|
|
|
|
/// </para>
|
|
|
|
|
///
|
|
|
|
|
/// <para><b>주요 속성:</b></para>
|
|
|
|
|
/// <list type="bullet">
|
|
|
|
|
/// <item><description><c>Value</c> - 현재 진행 값</description></item>
|
|
|
|
|
/// <item><description><c>MinValue</c> - 최소값 (보통 0)</description></item>
|
|
|
|
|
/// <item><description><c>MaxValue</c> - 최대값 (보통 100)</description></item>
|
|
|
|
|
/// <item><description><c>ShowValue</c> - 값 텍스트 표시 여부</description></item>
|
|
|
|
|
/// <item><description><c>ShowPercentage</c> - 퍼센트로 표시 (true) 또는 값/최대값으로 표시 (false)</description></item>
|
|
|
|
|
/// <item><description><c>IsIndeterminate</c> - 불확정 상태 (진행률을 알 수 없을 때 애니메이션)</description></item>
|
|
|
|
|
/// <item><description><c>Variant</c> - 스타일 변형 (Default, Success, Warning, Error)</description></item>
|
|
|
|
|
/// </list>
|
|
|
|
|
///
|
|
|
|
|
/// <para><b>Variant 스타일:</b></para>
|
|
|
|
|
/// <list type="bullet">
|
|
|
|
|
/// <item><description><c>Default</c> - 기본 파란색 (일반 진행)</description></item>
|
|
|
|
|
/// <item><description><c>Success</c> - 녹색 (완료, 성공)</description></item>
|
|
|
|
|
/// <item><description><c>Warning</c> - 주황색 (주의 필요)</description></item>
|
|
|
|
|
/// <item><description><c>Error</c> - 빨간색 (오류, 실패)</description></item>
|
|
|
|
|
/// </list>
|
|
|
|
|
///
|
|
|
|
|
/// <para><b>불확정 상태(Indeterminate):</b></para>
|
|
|
|
|
/// <para>
|
|
|
|
|
/// 작업 완료 시점을 알 수 없을 때 사용합니다 (예: 서버 응답 대기).
|
|
|
|
|
/// 바가 좌우로 움직이는 애니메이션으로 "작업 중"임을 표시합니다.
|
|
|
|
|
/// </para>
|
|
|
|
|
///
|
|
|
|
|
/// <para><b>실제 활용 예시:</b></para>
|
|
|
|
|
/// <list type="bullet">
|
|
|
|
|
/// <item><description>파일 다운로드/업로드 진행률</description></item>
|
|
|
|
|
/// <item><description>게임 로딩 화면</description></item>
|
|
|
|
|
/// <item><description>설치/업데이트 진행 상태</description></item>
|
|
|
|
|
/// <item><description>데이터 처리 진행률</description></item>
|
|
|
|
|
/// <item><description>퀘스트/미션 달성도</description></item>
|
|
|
|
|
/// </list>
|
|
|
|
|
/// </remarks>
|
2026-01-13 20:39:45 +09:00
|
|
|
/// <example>
|
|
|
|
|
/// <para><b>C# 코드에서 사용:</b></para>
|
|
|
|
|
/// <code>
|
2026-01-21 20:43:54 +09:00
|
|
|
/// // 다운로드 진행률 표시
|
|
|
|
|
/// var downloadProgress = new UTKProgressBar("다운로드 중...", 0, 100);
|
|
|
|
|
/// downloadProgress.ShowPercentage = true;
|
|
|
|
|
///
|
|
|
|
|
/// // 비동기 다운로드 시뮬레이션
|
|
|
|
|
/// async UniTask DownloadFile()
|
|
|
|
|
/// {
|
|
|
|
|
/// for (int i = 0; i <= 100; i++)
|
|
|
|
|
/// {
|
|
|
|
|
/// downloadProgress.Value = i;
|
|
|
|
|
/// await UniTask.Delay(50);
|
|
|
|
|
/// }
|
|
|
|
|
/// downloadProgress.Variant = UTKProgressBar.ProgressBarVariant.Success;
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// // 불확정 상태 (서버 응답 대기)
|
|
|
|
|
/// var loadingBar = new UTKProgressBar();
|
|
|
|
|
/// loadingBar.IsIndeterminate = true; // 무한 애니메이션
|
|
|
|
|
///
|
|
|
|
|
/// // 상태별 색상 변경
|
|
|
|
|
/// progressBar.Variant = UTKProgressBar.ProgressBarVariant.Warning; // 주의
|
|
|
|
|
/// progressBar.Variant = UTKProgressBar.ProgressBarVariant.Error; // 실패
|
|
|
|
|
///
|
|
|
|
|
/// // 값/최대값 형식 (예: 50/100)
|
|
|
|
|
/// var itemProgress = new UTKProgressBar("수집한 아이템", 0, 100);
|
|
|
|
|
/// itemProgress.ShowPercentage = false; // "50/100" 형식으로 표시
|
|
|
|
|
/// itemProgress.Value = 50;
|
2026-01-13 20:39:45 +09:00
|
|
|
/// </code>
|
|
|
|
|
/// <para><b>UXML에서 사용:</b></para>
|
|
|
|
|
/// <code>
|
2026-01-21 20:43:54 +09:00
|
|
|
/// <!-- 기본 프로그레스 바 -->
|
|
|
|
|
/// <utk:UTKProgressBar title="진행률" low-value="0" high-value="100" value="30" />
|
|
|
|
|
///
|
|
|
|
|
/// <!-- 퍼센트 표시 -->
|
|
|
|
|
/// <utk:UTKProgressBar show-percentage="true" value="75" />
|
|
|
|
|
///
|
|
|
|
|
/// <!-- 값/최대값 표시 -->
|
|
|
|
|
/// <utk:UTKProgressBar show-percentage="false" show-value="true" value="50" />
|
|
|
|
|
///
|
|
|
|
|
/// <!-- 성공 스타일 -->
|
|
|
|
|
/// <utk:UTKProgressBar variant="Success" value="100" />
|
|
|
|
|
///
|
|
|
|
|
/// <!-- 경고 스타일 -->
|
|
|
|
|
/// <utk:UTKProgressBar variant="Warning" value="80" />
|
|
|
|
|
///
|
|
|
|
|
/// <!-- 불확정 상태 (로딩 애니메이션) -->
|
|
|
|
|
/// <utk:UTKProgressBar is-indeterminate="true" />
|
2026-01-13 20:39:45 +09:00
|
|
|
/// </code>
|
|
|
|
|
/// </example>
|
2026-01-08 20:15:57 +09:00
|
|
|
[UxmlElement]
|
|
|
|
|
public partial class UTKProgressBar : ProgressBar, IDisposable
|
|
|
|
|
{
|
|
|
|
|
#region Constants
|
|
|
|
|
private const string USS_PATH = "UIToolkit/Slider/UTKProgressBar";
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Fields
|
|
|
|
|
private bool _disposed;
|
|
|
|
|
private bool _showValue = true;
|
|
|
|
|
private bool _showPercentage = true;
|
|
|
|
|
private bool _isIndeterminate;
|
|
|
|
|
private ProgressBarVariant _variant = ProgressBarVariant.Default;
|
2026-01-21 20:43:54 +09:00
|
|
|
private string _baseTitle = "";
|
2026-01-08 20:15:57 +09:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
/// <summary>현재 값</summary>
|
|
|
|
|
public float Value
|
|
|
|
|
{
|
|
|
|
|
get => value;
|
|
|
|
|
set => SetValue(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>최소값</summary>
|
|
|
|
|
public float MinValue
|
|
|
|
|
{
|
|
|
|
|
get => lowValue;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
lowValue = value;
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>최대값</summary>
|
|
|
|
|
public float MaxValue
|
|
|
|
|
{
|
|
|
|
|
get => highValue;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
highValue = value;
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>값 표시 여부</summary>
|
2026-01-20 20:18:47 +09:00
|
|
|
[UxmlAttribute("show-value")]
|
2026-01-08 20:15:57 +09:00
|
|
|
public bool ShowValue
|
|
|
|
|
{
|
|
|
|
|
get => _showValue;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_showValue = value;
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>퍼센트로 표시 여부</summary>
|
2026-01-20 20:18:47 +09:00
|
|
|
[UxmlAttribute("show-percentage")]
|
2026-01-08 20:15:57 +09:00
|
|
|
public bool ShowPercentage
|
|
|
|
|
{
|
|
|
|
|
get => _showPercentage;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_showPercentage = value;
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>불확정 상태 (애니메이션)</summary>
|
2026-01-20 20:18:47 +09:00
|
|
|
[UxmlAttribute("is-indeterminate")]
|
2026-01-08 20:15:57 +09:00
|
|
|
public bool IsIndeterminate
|
|
|
|
|
{
|
|
|
|
|
get => _isIndeterminate;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_isIndeterminate = value;
|
|
|
|
|
EnableInClassList("utk-progress--indeterminate", value);
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>스타일 변형</summary>
|
2026-01-20 20:18:47 +09:00
|
|
|
[UxmlAttribute("variant")]
|
2026-01-08 20:15:57 +09:00
|
|
|
public ProgressBarVariant Variant
|
|
|
|
|
{
|
|
|
|
|
get => _variant;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_variant = value;
|
|
|
|
|
UpdateVariant();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Enums
|
|
|
|
|
public enum ProgressBarVariant
|
|
|
|
|
{
|
|
|
|
|
Default,
|
|
|
|
|
Success,
|
|
|
|
|
Warning,
|
|
|
|
|
Error
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructor
|
|
|
|
|
public UTKProgressBar() : base()
|
|
|
|
|
{
|
|
|
|
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
|
|
|
|
|
|
|
|
|
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
|
|
|
|
if (uss != null)
|
|
|
|
|
{
|
|
|
|
|
styleSheets.Add(uss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetupStyles();
|
|
|
|
|
SubscribeToThemeChanges();
|
2026-01-21 20:43:54 +09:00
|
|
|
|
|
|
|
|
// UXML에서 value가 설정된 후 title 갱신
|
|
|
|
|
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnAttachToPanel(AttachToPanelEvent evt)
|
|
|
|
|
{
|
|
|
|
|
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanel);
|
|
|
|
|
UpdateValueLabel();
|
2026-01-08 20:15:57 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public UTKProgressBar(string text, float value = 0f, float maxValue = 100f) : this()
|
|
|
|
|
{
|
|
|
|
|
title = text;
|
|
|
|
|
highValue = maxValue;
|
|
|
|
|
SetValue(value);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Setup
|
|
|
|
|
private void SetupStyles()
|
|
|
|
|
{
|
|
|
|
|
AddToClassList("utk-progress");
|
|
|
|
|
UpdateVariant();
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SubscribeToThemeChanges()
|
|
|
|
|
{
|
|
|
|
|
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
|
|
|
|
RegisterCallback<DetachFromPanelEvent>(_ =>
|
|
|
|
|
{
|
|
|
|
|
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnThemeChanged(UTKTheme theme)
|
|
|
|
|
{
|
|
|
|
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 값 설정
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void SetValue(float newValue)
|
|
|
|
|
{
|
|
|
|
|
value = Mathf.Clamp(newValue, lowValue, highValue);
|
|
|
|
|
UpdateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateValueLabel()
|
|
|
|
|
{
|
|
|
|
|
if (_isIndeterminate || !_showValue)
|
|
|
|
|
{
|
2026-01-21 20:43:54 +09:00
|
|
|
title = _baseTitle;
|
2026-01-08 20:15:57 +09:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_showPercentage)
|
|
|
|
|
{
|
|
|
|
|
float range = highValue - lowValue;
|
|
|
|
|
float percent = range > 0 ? (value - lowValue) / range * 100 : 0;
|
2026-01-21 20:43:54 +09:00
|
|
|
title = $"{percent:F0}%";
|
2026-01-08 20:15:57 +09:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-01-21 20:43:54 +09:00
|
|
|
title = $"{value:F0}/{highValue:F0}";
|
2026-01-08 20:15:57 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateVariant()
|
|
|
|
|
{
|
|
|
|
|
RemoveFromClassList("utk-progress--default");
|
|
|
|
|
RemoveFromClassList("utk-progress--success");
|
|
|
|
|
RemoveFromClassList("utk-progress--warning");
|
|
|
|
|
RemoveFromClassList("utk-progress--error");
|
|
|
|
|
|
|
|
|
|
var variantClass = _variant switch
|
|
|
|
|
{
|
|
|
|
|
ProgressBarVariant.Success => "utk-progress--success",
|
|
|
|
|
ProgressBarVariant.Warning => "utk-progress--warning",
|
|
|
|
|
ProgressBarVariant.Error => "utk-progress--error",
|
|
|
|
|
_ => "utk-progress--default"
|
|
|
|
|
};
|
|
|
|
|
AddToClassList(variantClass);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region IDisposable
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (_disposed) return;
|
|
|
|
|
_disposed = true;
|
|
|
|
|
|
|
|
|
|
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|