164 lines
6.4 KiB
C#
164 lines
6.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace AibisDream.FrameAnimation.Editor
|
|
{
|
|
internal sealed class FrameAnimationPreviewElement : VisualElement
|
|
{
|
|
private static Texture2D checkerTexture;
|
|
private readonly Image image;
|
|
private readonly Label emptyLabel;
|
|
private Sprite sprite;
|
|
private FrameAnimationPreviewZoomMode zoomMode = FrameAnimationPreviewZoomMode.Fit;
|
|
private float manualZoom = 1f;
|
|
private Vector2 pan;
|
|
private Vector2 dragStart;
|
|
private Vector2 panStart;
|
|
private bool dragging;
|
|
|
|
public FrameAnimationPreviewElement()
|
|
{
|
|
AddToClassList("fa-preview");
|
|
focusable = true;
|
|
image = new Image { scaleMode = ScaleMode.ScaleToFit, pickingMode = PickingMode.Ignore };
|
|
image.AddToClassList("fa-preview__image");
|
|
emptyLabel = new Label("Empty Frame") { pickingMode = PickingMode.Ignore };
|
|
emptyLabel.AddToClassList("fa-preview__empty");
|
|
Add(image);
|
|
Add(emptyLabel);
|
|
RegisterCallback<GeometryChangedEvent>(_ => UpdateImageLayout());
|
|
RegisterCallback<PointerDownEvent>(OnPointerDown);
|
|
RegisterCallback<PointerMoveEvent>(OnPointerMove);
|
|
RegisterCallback<PointerUpEvent>(OnPointerUp);
|
|
SetBackground(FrameAnimationPreviewBackground.Checkerboard);
|
|
}
|
|
|
|
public void SetDisplay(Sprite value, bool isEmpty, string emptyText = "Empty Frame")
|
|
{
|
|
sprite = value;
|
|
image.sprite = value;
|
|
image.style.display = value != null ? DisplayStyle.Flex : DisplayStyle.None;
|
|
emptyLabel.text = string.IsNullOrEmpty(emptyText) ? "Empty Frame" : emptyText;
|
|
emptyLabel.style.display = value == null || isEmpty ? DisplayStyle.Flex : DisplayStyle.None;
|
|
UpdateImageLayout();
|
|
}
|
|
|
|
public void SetBackground(FrameAnimationPreviewBackground background)
|
|
{
|
|
switch (background)
|
|
{
|
|
case FrameAnimationPreviewBackground.Dark:
|
|
style.backgroundImage = StyleKeyword.None;
|
|
style.backgroundColor = new StyleColor(new Color(0.08f, 0.08f, 0.08f));
|
|
break;
|
|
case FrameAnimationPreviewBackground.Light:
|
|
style.backgroundImage = StyleKeyword.None;
|
|
style.backgroundColor = new StyleColor(new Color(0.72f, 0.72f, 0.72f));
|
|
break;
|
|
default:
|
|
style.backgroundImage = new StyleBackground(GetCheckerTexture());
|
|
style.backgroundColor = StyleKeyword.Null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void SetZoom(FrameAnimationPreviewZoomMode mode, float value)
|
|
{
|
|
zoomMode = mode;
|
|
manualZoom = Mathf.Clamp(value, 0.25f, 8f);
|
|
if (mode == FrameAnimationPreviewZoomMode.Fit)
|
|
{
|
|
pan = Vector2.zero;
|
|
}
|
|
UpdateImageLayout();
|
|
}
|
|
|
|
private void UpdateImageLayout()
|
|
{
|
|
if (sprite == null || resolvedStyle.width <= 0f || resolvedStyle.height <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
var sourceSize = sprite.rect.size;
|
|
if (sourceSize.x <= 0f || sourceSize.y <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
var scale = zoomMode == FrameAnimationPreviewZoomMode.Fit
|
|
? Mathf.Min(resolvedStyle.width / sourceSize.x, resolvedStyle.height / sourceSize.y)
|
|
: GetScale();
|
|
var size = sourceSize * Mathf.Max(0.0001f, scale);
|
|
image.style.width = size.x;
|
|
image.style.height = size.y;
|
|
image.style.left = (resolvedStyle.width - size.x) * 0.5f + pan.x;
|
|
image.style.top = (resolvedStyle.height - size.y) * 0.5f + pan.y;
|
|
}
|
|
|
|
private float GetScale()
|
|
{
|
|
return zoomMode switch
|
|
{
|
|
FrameAnimationPreviewZoomMode.One => 1f,
|
|
FrameAnimationPreviewZoomMode.Two => 2f,
|
|
FrameAnimationPreviewZoomMode.Three => 3f,
|
|
FrameAnimationPreviewZoomMode.Four => 4f,
|
|
FrameAnimationPreviewZoomMode.Eight => 8f,
|
|
FrameAnimationPreviewZoomMode.Manual => manualZoom,
|
|
_ => 1f
|
|
};
|
|
}
|
|
|
|
private void OnPointerDown(PointerDownEvent evt)
|
|
{
|
|
if (zoomMode == FrameAnimationPreviewZoomMode.Fit || evt.button != 0) return;
|
|
dragging = true;
|
|
dragStart = new Vector2(evt.position.x, evt.position.y);
|
|
panStart = pan;
|
|
this.CapturePointer(evt.pointerId);
|
|
evt.StopPropagation();
|
|
}
|
|
|
|
private void OnPointerMove(PointerMoveEvent evt)
|
|
{
|
|
if (!dragging || !this.HasPointerCapture(evt.pointerId)) return;
|
|
var position = new Vector2(evt.position.x, evt.position.y);
|
|
pan = panStart + position - dragStart;
|
|
UpdateImageLayout();
|
|
evt.StopPropagation();
|
|
}
|
|
|
|
private void OnPointerUp(PointerUpEvent evt)
|
|
{
|
|
if (!dragging || !this.HasPointerCapture(evt.pointerId)) return;
|
|
dragging = false;
|
|
this.ReleasePointer(evt.pointerId);
|
|
evt.StopPropagation();
|
|
}
|
|
|
|
private static Texture2D GetCheckerTexture()
|
|
{
|
|
if (checkerTexture != null) return checkerTexture;
|
|
const int textureSize = 128;
|
|
const int cellSize = 8;
|
|
checkerTexture = new Texture2D(textureSize, textureSize, TextureFormat.RGBA32, false)
|
|
{
|
|
name = "Frame Animation Preview Checker",
|
|
hideFlags = HideFlags.HideAndDontSave,
|
|
wrapMode = TextureWrapMode.Repeat,
|
|
filterMode = FilterMode.Point
|
|
};
|
|
var light = new Color32(106, 106, 106, 255);
|
|
var dark = new Color32(78, 78, 78, 255);
|
|
var pixels = new Color32[textureSize * textureSize];
|
|
for (var y = 0; y < textureSize; y++)
|
|
for (var x = 0; x < textureSize; x++)
|
|
{
|
|
pixels[y * textureSize + x] = ((x / cellSize) + (y / cellSize)) % 2 == 0 ? light : dark;
|
|
}
|
|
checkerTexture.SetPixels32(pixels);
|
|
checkerTexture.Apply(false, true);
|
|
return checkerTexture;
|
|
}
|
|
}
|
|
}
|