using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
namespace RosettaUI.UIToolkit
{
///
/// GradientEditorのキーを編集するクラス
/// Color/Alphaのキーを編集する
///
public class GradientKeysEditor
{
public static int MaxKeyNum { get; set; } = 8;
private readonly VisualElement _container;
private readonly List _showedSwatches = new();
private readonly bool _isAlpha;
private Gradient _gradient;
private GradientKeysSwatch _selectedSwatch;
private readonly Action _onAddSwatch;
private readonly Action _onRemoveSwatch;
private readonly Action _onSelectedSwatchChanged;
private readonly Action _onSwatchValueChanged;
public IReadOnlyList ShowedSwatches => _showedSwatches;
public GradientKeysEditor(VisualElement container,
Action onAddSwatch,
Action onRemoveSwatch,
Action onSelectedSwatchChanged,
Action onSwatchValueChanged,
bool isAlpha
)
{
_container = container;
_onAddSwatch = onAddSwatch;
_onRemoveSwatch = onRemoveSwatch;
_onSelectedSwatchChanged = onSelectedSwatchChanged;
_onSwatchValueChanged = onSwatchValueChanged;
_isAlpha = isAlpha;
container.RegisterCallback(OnPointerDownOnContainer);
PointerDrag.RegisterCallback(container, OnPointerMoveOnContainer, OnPointerupOnContainer);
}
public void Initialize(Gradient gradient, IEnumerable showedSwatches)
{
_gradient = gradient;
_container.Clear();
_showedSwatches.Clear();
_showedSwatches.AddRange(showedSwatches);
foreach(var swatch in _showedSwatches)
{
_container.Add(swatch.visualElement);
}
}
private void OnPointerDownOnContainer(PointerDownEvent evt)
{
var localPos = _container.WorldToLocal(evt.position);
_showedSwatches.Sort((a, b) => a.TimePercent.CompareTo(b.TimePercent));
var swatch = _showedSwatches.FirstOrDefault(s => s.visualElement.localBound.Contains(localPos));
// 無かったら新規追加
if (swatch == null)
{
swatch = CreateSwatch(localPos.x);
if (swatch != null)
{
ShowSwatch(swatch);
OnAddSwatch(swatch);
}
}
// セレクト
if (swatch != null)
{
SelectSwatch(swatch);
OnSelectedSwatchChanged();
}
evt.StopPropagation();
}
private void OnPointerMoveOnContainer(PointerMoveEvent evt)
{
if (_selectedSwatch == null) return;
var localPos = _container.WorldToLocal(evt.position);
var rect = _container.contentRect;
var updateTime = false;
// 左右ではみ出てもカーソル表示はキープ
if (rect.min.y <= localPos.y && localPos.y <= rect.max.y)
{
// なかったら復活
if (!_showedSwatches.Contains(_selectedSwatch))
{
ShowSwatch(_selectedSwatch);
}
updateTime = true;
}
// 範囲外ではGradient的には削除するがセレクト状態は継続
// 再度範囲内に戻った場合にColor/Alphaの値が復活する
// ただし、1つしかない場合は削除せずTimeを更新する
else
{
if ( _showedSwatches.Count > 1)
{
HideSwatch(_selectedSwatch);
}
else
{
updateTime = true;
}
}
if (updateTime)
{
_selectedSwatch.Time = Mathf.Clamp01(localPos.x / rect.width);
}
OnSwatchValueChanged();
evt.StopPropagation();
}
private void OnPointerupOnContainer(PointerUpEvent evt)
{
if (_selectedSwatch == null) return;
// 非表示状態でドラッグ終了したら削除
if (!_showedSwatches.Contains(_selectedSwatch))
{
var swatch = _selectedSwatch;
UnselectSwatch();
OnRemoveSwatch(swatch);
}
evt.StopPropagation();
}
private void ShowSwatch(GradientKeysSwatch swatch)
{
_showedSwatches.Add(swatch);
_container.Add(swatch.visualElement);
}
private void HideSwatch(GradientKeysSwatch swatch)
{
_showedSwatches.Remove(swatch);
swatch.visualElement.RemoveFromHierarchy();
}
private bool IsShowedSwatch(GradientKeysSwatch swatch) => _showedSwatches.Contains(swatch);
private GradientKeysSwatch CreateSwatch(float localPosX)
{
if (_showedSwatches.Count >= MaxKeyNum)
{
Debug.LogWarning("Max 8 color keys and 8 alpha keys are allowed in a gradient.");
return null;
}
var t = Mathf.Clamp01(localPosX / _container.resolvedStyle.width);
var color = _gradient.Evaluate(t);
var newSwatch = new GradientKeysSwatch()
{
Time = t
};
if (_isAlpha)
{
newSwatch.Alpha = color.a;
}
else
{
newSwatch.Color = color;
}
return newSwatch;
}
private void SelectSwatch(GradientKeysSwatch swatch)
{
UnselectSwatch();
_selectedSwatch = swatch;
_selectedSwatch.visualElement.Focus();
_selectedSwatch.visualElement.BringToFront();
}
private void UnselectSwatch()
{
if (_selectedSwatch == null)
return;
_selectedSwatch.visualElement.Blur();
_selectedSwatch = null;
}
private void OnAddSwatch(GradientKeysSwatch swatch)
{
_onAddSwatch?.Invoke(swatch);
}
private void OnRemoveSwatch(GradientKeysSwatch swatch)
{
_onRemoveSwatch?.Invoke(swatch);
}
private void OnSelectedSwatchChanged()
{
_onSelectedSwatchChanged?.Invoke(_selectedSwatch);
}
private void OnSwatchValueChanged()
{
// 非表示中の値の変化は通知しない
if ( IsShowedSwatch(_selectedSwatch))
{
_onSwatchValueChanged?.Invoke(_selectedSwatch);
}
}
}
}