testss
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditorInternal;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityTexture2D = UnityEngine.Texture2D;
|
||||
using UnityEditor.ShortcutManagement;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
[RequireSpriteDataProvider(typeof(ITextureDataProvider))]
|
||||
internal partial class SpriteFrameModule : SpriteFrameModuleBase
|
||||
{
|
||||
public enum AutoSlicingMethod
|
||||
{
|
||||
DeleteAll = 0,
|
||||
Smart = 1,
|
||||
Safe = 2
|
||||
}
|
||||
|
||||
private bool[] m_AlphaPixelCache;
|
||||
SpriteFrameModuleContext m_SpriteFrameModuleContext;
|
||||
|
||||
private const float kOverlapTolerance = 0.00001f;
|
||||
private StringBuilder m_SpriteNameStringBuilder;
|
||||
|
||||
private List<Rect> m_PotentialRects;
|
||||
public List<Rect> potentialRects
|
||||
{
|
||||
set => m_PotentialRects = value;
|
||||
}
|
||||
|
||||
public SpriteFrameModule(ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad) :
|
||||
base("Sprite Editor", sw, es, us, ad)
|
||||
{}
|
||||
|
||||
class SpriteFrameModuleContext : IShortcutToolContext
|
||||
{
|
||||
SpriteFrameModule m_SpriteFrameModule;
|
||||
|
||||
public SpriteFrameModuleContext(SpriteFrameModule spriteFrame)
|
||||
{
|
||||
m_SpriteFrameModule = spriteFrame;
|
||||
}
|
||||
|
||||
public bool active
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public SpriteFrameModule spriteFrameModule
|
||||
{
|
||||
get { return m_SpriteFrameModule; }
|
||||
}
|
||||
}
|
||||
|
||||
[FormerlyPrefKeyAs("Sprite Editor/Trim", "#t")]
|
||||
[Shortcut("Sprite Editor/Trim", typeof(SpriteFrameModuleContext), KeyCode.T, ShortcutModifiers.Shift)]
|
||||
static void ShortcutTrim(ShortcutArguments args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(GUI.GetNameOfFocusedControl()))
|
||||
return;
|
||||
var spriteFrameContext = (SpriteFrameModuleContext)args.context;
|
||||
spriteFrameContext.spriteFrameModule.TrimAlpha();
|
||||
spriteFrameContext.spriteFrameModule.spriteEditor.RequestRepaint();
|
||||
}
|
||||
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
base.OnModuleActivate();
|
||||
spriteEditor.enableMouseMoveEvent = true;
|
||||
m_SpriteFrameModuleContext = new SpriteFrameModuleContext(this);
|
||||
ShortcutIntegration.instance.contextManager.RegisterToolContext(m_SpriteFrameModuleContext);
|
||||
m_SpriteNameStringBuilder = new StringBuilder(GetSpriteNamePrefix() + "_");
|
||||
m_PotentialRects = null;
|
||||
}
|
||||
|
||||
public override void OnModuleDeactivate()
|
||||
{
|
||||
base.OnModuleDeactivate();
|
||||
ShortcutIntegration.instance.contextManager.DeregisterToolContext(m_SpriteFrameModuleContext);
|
||||
m_PotentialRects = null;
|
||||
m_AlphaPixelCache = null;
|
||||
}
|
||||
|
||||
public static SpriteImportMode GetSpriteImportMode(ISpriteEditorDataProvider dataProvider)
|
||||
{
|
||||
return dataProvider == null ? SpriteImportMode.None : dataProvider.spriteImportMode;
|
||||
}
|
||||
|
||||
public override bool CanBeActivated()
|
||||
{
|
||||
return GetSpriteImportMode(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>()) != SpriteImportMode.Polygon;
|
||||
}
|
||||
|
||||
private string GenerateSpriteNameWithIndex(int startIndex)
|
||||
{
|
||||
int originalLength = m_SpriteNameStringBuilder.Length;
|
||||
m_SpriteNameStringBuilder.Append(startIndex);
|
||||
var name = m_SpriteNameStringBuilder.ToString();
|
||||
m_SpriteNameStringBuilder.Length = originalLength;
|
||||
return name;
|
||||
}
|
||||
|
||||
// 1. Find top-most rectangle
|
||||
// 2. Sweep it vertically to find out all rects from that "row"
|
||||
// 3. goto 1.
|
||||
// This will give us nicely sorted left->right top->down list of rectangles
|
||||
// Works for most sprite sheets pretty nicely
|
||||
private List<Rect> SortRects(List<Rect> rects)
|
||||
{
|
||||
List<Rect> result = new List<Rect>();
|
||||
|
||||
while (rects.Count > 0)
|
||||
{
|
||||
// Because the slicing algorithm works from bottom-up, the topmost rect is the last one in the array
|
||||
Rect r = rects[rects.Count - 1];
|
||||
Rect sweepRect = new Rect(0, r.yMin, textureActualWidth, r.height);
|
||||
|
||||
List<Rect> rowRects = RectSweep(rects, sweepRect);
|
||||
|
||||
if (rowRects.Count > 0)
|
||||
result.AddRange(rowRects);
|
||||
else
|
||||
{
|
||||
// We didn't find any rects, just dump the remaining rects and continue
|
||||
result.AddRange(rects);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Rect> RectSweep(List<Rect> rects, Rect sweepRect)
|
||||
{
|
||||
if (rects == null || rects.Count == 0)
|
||||
return new List<Rect>();
|
||||
|
||||
List<Rect> containedRects = new List<Rect>();
|
||||
|
||||
foreach (Rect rect in rects)
|
||||
{
|
||||
if (rect.Overlaps(sweepRect))
|
||||
containedRects.Add(rect);
|
||||
}
|
||||
|
||||
// Remove found rects from original list
|
||||
foreach (Rect rect in containedRects)
|
||||
rects.Remove(rect);
|
||||
|
||||
// Sort found rects by x position
|
||||
containedRects.Sort((a, b) => a.x.CompareTo(b.x));
|
||||
|
||||
return containedRects;
|
||||
}
|
||||
|
||||
private int AddSprite(Rect frame, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod, int originalCount, ref int nameIndex)
|
||||
{
|
||||
int outSprite = -1;
|
||||
switch (slicingMethod)
|
||||
{
|
||||
case AutoSlicingMethod.DeleteAll:
|
||||
{
|
||||
while (outSprite == -1)
|
||||
{
|
||||
outSprite = AddSprite(frame, alignment, pivot, GenerateSpriteNameWithIndex(nameIndex++), Vector4.zero, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AutoSlicingMethod.Smart:
|
||||
{
|
||||
outSprite = GetExistingOverlappingSprite(frame, originalCount, true);
|
||||
if (outSprite != -1)
|
||||
{
|
||||
var existingRect = m_RectsCache.spriteRects[outSprite];
|
||||
existingRect.rect = frame;
|
||||
existingRect.alignment = (SpriteAlignment)alignment;
|
||||
existingRect.pivot = pivot;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (outSprite == -1)
|
||||
{
|
||||
outSprite = AddSprite(frame, alignment, pivot, GenerateSpriteNameWithIndex(nameIndex++), Vector4.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AutoSlicingMethod.Safe:
|
||||
{
|
||||
outSprite = GetExistingOverlappingSprite(frame, originalCount);
|
||||
while (outSprite == -1)
|
||||
{
|
||||
outSprite = AddSprite(frame, alignment, pivot, GenerateSpriteNameWithIndex(nameIndex++), Vector4.zero);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return outSprite;
|
||||
}
|
||||
|
||||
private int GetExistingOverlappingSprite(Rect rect, int originalCount, bool bestFit = false)
|
||||
{
|
||||
var count = Math.Min(originalCount, m_RectsCache.spriteRects.Count);
|
||||
int bestRect = -1;
|
||||
float rectArea = rect.width * rect.height;
|
||||
if (rectArea < kOverlapTolerance)
|
||||
return bestRect;
|
||||
|
||||
float bestRatio = float.MaxValue;
|
||||
float bestArea = float.MaxValue;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Rect existingRect = m_RectsCache.spriteRects[i].rect;
|
||||
if (existingRect.Overlaps(rect))
|
||||
{
|
||||
if (bestFit)
|
||||
{
|
||||
float dx = Math.Min(rect.xMax, existingRect.xMax) - Math.Max(rect.xMin, existingRect.xMin);
|
||||
float dy = Math.Min(rect.yMax, existingRect.yMax) - Math.Max(rect.yMin, existingRect.yMin);
|
||||
float overlapArea = dx * dy;
|
||||
float overlapRatio = Math.Abs((overlapArea / rectArea) - 1.0f);
|
||||
float existingArea = existingRect.width * existingRect.height;
|
||||
if (overlapRatio < bestRatio || (overlapRatio < kOverlapTolerance && existingArea < bestArea))
|
||||
{
|
||||
bestRatio = overlapRatio;
|
||||
if (overlapRatio < kOverlapTolerance)
|
||||
bestArea = existingArea;
|
||||
bestRect = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bestRect = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestRect;
|
||||
}
|
||||
|
||||
private bool PixelHasAlpha(int x, int y, UnityTexture2D texture)
|
||||
{
|
||||
if (m_AlphaPixelCache == null)
|
||||
{
|
||||
m_AlphaPixelCache = new bool[texture.width * texture.height];
|
||||
Color32[] pixels = texture.GetPixels32();
|
||||
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
m_AlphaPixelCache[i] = pixels[i].a != 0;
|
||||
}
|
||||
int index = y * (int)texture.width + x;
|
||||
return m_AlphaPixelCache[index];
|
||||
}
|
||||
|
||||
private int AddSprite(Rect rect, int alignment, Vector2 pivot, string name, Vector4 border, bool uniqueNameCheck = true)
|
||||
{
|
||||
var sed = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
long internalID = AssetImporter.MakeLocalFileIDWithHash(spriteType.persistentTypeID, name, 0);
|
||||
if (m_RectsCache.HasName(name))
|
||||
return -1;
|
||||
if (m_RectsCache.HasInternalID(internalID))
|
||||
return -1;
|
||||
|
||||
SpriteRect spriteRect = new SpriteRect();
|
||||
spriteRect.rect = rect;
|
||||
spriteRect.alignment = (SpriteAlignment)alignment;
|
||||
spriteRect.pivot = pivot;
|
||||
spriteRect.name = name;
|
||||
spriteRect.originalName = spriteRect.name;
|
||||
spriteRect.border = border;
|
||||
|
||||
spriteRect.internalID = internalID;
|
||||
spriteRect.spriteID = GUID.CreateGUIDFromSInt64(internalID);
|
||||
|
||||
// check if someone is using the internal id, if so, we change it to us.
|
||||
// Only TextureImporter needs this now.
|
||||
var ai = sed.targetObject as TextureImporter;
|
||||
var oldName = "";
|
||||
if (ai != null && ai.GetNameFromInternalIDMap(internalID, ref oldName))
|
||||
{
|
||||
if (string.IsNullOrEmpty(oldName))
|
||||
return -1;
|
||||
spriteRect.originalName = oldName;
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteRect.m_RegisterInternalID = true;
|
||||
}
|
||||
|
||||
m_RectsCache.Add(spriteRect);
|
||||
spriteEditor.SetDataModified();
|
||||
|
||||
return m_RectsCache.spriteRects.Count - 1;
|
||||
}
|
||||
|
||||
private string GetSpriteNamePrefix()
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(spriteAssetPath);
|
||||
}
|
||||
|
||||
public void DoAutomaticSlicing(int minimumSpriteSize, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Automatic Slicing");
|
||||
|
||||
if (slicingMethod == AutoSlicingMethod.DeleteAll)
|
||||
m_RectsCache.Clear();
|
||||
|
||||
var textureToUse = GetTextureToSlice();
|
||||
List<Rect> frames = new List<Rect>(InternalSpriteUtility.GenerateAutomaticSpriteRectangles((UnityTexture2D)textureToUse, minimumSpriteSize, 0));
|
||||
frames = SortRects(frames);
|
||||
int index = 0;
|
||||
int originalCount = m_RectsCache.spriteRects.Count;
|
||||
|
||||
foreach (Rect frame in frames)
|
||||
AddSprite(frame, alignment, pivot, slicingMethod, originalCount, ref index);
|
||||
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
UnityTexture2D GetTextureToSlice()
|
||||
{
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
var readableTexture = m_TextureDataProvider.GetReadableTexture2D();
|
||||
if (readableTexture == null || (readableTexture.width == width && readableTexture.height == height))
|
||||
return readableTexture;
|
||||
// we want to slice based on the original texture slice. Upscale the imported texture
|
||||
var texture = UnityEditor.SpriteUtility.CreateTemporaryDuplicate(readableTexture, width, height);
|
||||
return texture;
|
||||
}
|
||||
|
||||
public IEnumerable<Rect> GetGridRects(Vector2 size, Vector2 offset, Vector2 padding, bool keepEmptyRects)
|
||||
{
|
||||
var textureToUse = GetTextureToSlice();
|
||||
return InternalSpriteUtility.GenerateGridSpriteRectangles((UnityTexture2D)textureToUse, offset, size, padding, keepEmptyRects);
|
||||
}
|
||||
|
||||
public void DoGridSlicing(Vector2 size, Vector2 offset, Vector2 padding, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod, bool keepEmptyRects = false)
|
||||
{
|
||||
var frames = GetGridRects(size, offset, padding, keepEmptyRects);
|
||||
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Grid Slicing");
|
||||
if (slicingMethod == AutoSlicingMethod.DeleteAll)
|
||||
m_RectsCache.Clear();
|
||||
|
||||
int index = 0;
|
||||
int originalCount = m_RectsCache.spriteRects.Count;
|
||||
foreach (Rect frame in frames)
|
||||
AddSprite(frame, alignment, pivot, slicingMethod, originalCount, ref index);
|
||||
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public IEnumerable<Rect> GetIsometricRects(Vector2 size, Vector2 offset, bool isAlternate, bool keepEmptyRects)
|
||||
{
|
||||
var textureToUse = GetTextureToSlice();
|
||||
var gradient = (size.x / 2) / (size.y / 2);
|
||||
bool isAlt = isAlternate;
|
||||
float x = offset.x;
|
||||
if (isAlt)
|
||||
x += size.x / 2;
|
||||
float y = textureToUse.height - offset.y;
|
||||
while (y - size.y >= 0)
|
||||
{
|
||||
while (x + size.x <= textureToUse.width)
|
||||
{
|
||||
var rect = new Rect(x, y - size.y, size.x, size.y);
|
||||
if (!keepEmptyRects)
|
||||
{
|
||||
int sx = (int)rect.x;
|
||||
int sy = (int)rect.y;
|
||||
int width = (int)size.x;
|
||||
int odd = ((int)size.y) % 2;
|
||||
int topY = ((int)size.y / 2) - 1;
|
||||
int bottomY = topY + odd;
|
||||
int totalPixels = 0;
|
||||
int alphaPixels = 0;
|
||||
{
|
||||
for (int ry = 0; ry <= topY; ry++)
|
||||
{
|
||||
var pixelOffset = Mathf.CeilToInt(gradient * ry);
|
||||
for (int rx = pixelOffset; rx < width - pixelOffset; ++rx)
|
||||
{
|
||||
if (PixelHasAlpha(sx + rx, sy + topY - ry, textureToUse))
|
||||
alphaPixels++;
|
||||
if (PixelHasAlpha(sx + rx, sy + bottomY + ry, textureToUse))
|
||||
alphaPixels++;
|
||||
totalPixels += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (odd > 0)
|
||||
{
|
||||
int ry = topY + 1;
|
||||
for (int rx = 0; rx < size.x; ++rx)
|
||||
{
|
||||
if (PixelHasAlpha(sx + rx, sy + ry, textureToUse))
|
||||
alphaPixels++;
|
||||
totalPixels++;
|
||||
}
|
||||
}
|
||||
if (totalPixels > 0 && ((float)alphaPixels) / totalPixels > 0.01f)
|
||||
yield return rect;
|
||||
}
|
||||
else
|
||||
yield return rect;
|
||||
x += size.x;
|
||||
}
|
||||
isAlt = !isAlt;
|
||||
x = offset.x;
|
||||
if (isAlt)
|
||||
x += size.x / 2;
|
||||
y -= size.y / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void DoIsometricGridSlicing(Vector2 size, Vector2 offset, int alignment, Vector2 pivot, AutoSlicingMethod slicingMethod, bool keepEmptyRects = false, bool isAlternate = false)
|
||||
{
|
||||
var frames = GetIsometricRects(size, offset, isAlternate, keepEmptyRects);
|
||||
|
||||
List<Vector2[]> outlines = new List<Vector2[]>(4);
|
||||
outlines.Add(new[] { new Vector2(0.0f, -size.y / 2)
|
||||
, new Vector2(size.x / 2, 0.0f)
|
||||
, new Vector2(0.0f, size.y / 2)
|
||||
, new Vector2(-size.x / 2, 0.0f)});
|
||||
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Isometric Grid Slicing");
|
||||
if (slicingMethod == AutoSlicingMethod.DeleteAll)
|
||||
m_RectsCache.Clear();
|
||||
|
||||
int index = 0;
|
||||
var spriteRects = m_RectsCache.GetSpriteRects();
|
||||
int originalCount = spriteRects.Count;
|
||||
foreach (var frame in frames)
|
||||
{
|
||||
var spriteIndex = AddSprite(frame, alignment, pivot, slicingMethod, originalCount, ref index);
|
||||
var outlineRect = new OutlineSpriteRect(spriteRects[spriteIndex]);
|
||||
outlineRect.outlines = outlines;
|
||||
spriteRects[spriteIndex] = outlineRect;
|
||||
}
|
||||
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public void ScaleSpriteRect(Rect r)
|
||||
{
|
||||
if (selected != null)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Scale sprite");
|
||||
selected.rect = ClampSpriteRect(r, textureActualWidth, textureActualHeight);
|
||||
selected.border = ClampSpriteBorderToRect(selected.border, selected.rect);
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
}
|
||||
|
||||
public void TrimAlpha()
|
||||
{
|
||||
var texture = GetTextureToSlice();
|
||||
if (texture == null)
|
||||
return;
|
||||
|
||||
Rect rect = selected.rect;
|
||||
|
||||
int xMin = (int)rect.xMax;
|
||||
int xMax = (int)rect.xMin;
|
||||
int yMin = (int)rect.yMax;
|
||||
int yMax = (int)rect.yMin;
|
||||
|
||||
for (int y = (int)rect.yMin; y < (int)rect.yMax; y++)
|
||||
{
|
||||
for (int x = (int)rect.xMin; x < (int)rect.xMax; x++)
|
||||
{
|
||||
if (PixelHasAlpha(x, y, texture))
|
||||
{
|
||||
xMin = Mathf.Min(xMin, x);
|
||||
xMax = Mathf.Max(xMax, x);
|
||||
yMin = Mathf.Min(yMin, y);
|
||||
yMax = Mathf.Max(yMax, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Case 582309: Return an empty rectangle if no pixel has an alpha
|
||||
if (xMin > xMax || yMin > yMax)
|
||||
rect = new Rect(0, 0, 0, 0);
|
||||
else
|
||||
rect = new Rect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
|
||||
|
||||
if (rect.width <= 0 && rect.height <= 0)
|
||||
{
|
||||
m_RectsCache.Remove(selected);
|
||||
spriteEditor.SetDataModified();
|
||||
selected = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = ClampSpriteRect(rect, texture.width, texture.height);
|
||||
if (selected.rect != rect)
|
||||
spriteEditor.SetDataModified();
|
||||
|
||||
selected.rect = rect;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
public void DuplicateSprite()
|
||||
{
|
||||
if (selected != null)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Duplicate sprite");
|
||||
var index = 0;
|
||||
var createdIndex = -1;
|
||||
while (createdIndex == -1)
|
||||
{
|
||||
createdIndex = AddSprite(selected.rect, (int)selected.alignment, selected.pivot, GenerateSpriteNameWithIndex(index++), selected.border);
|
||||
}
|
||||
selected = m_RectsCache.spriteRects[createdIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateSprite(Rect rect)
|
||||
{
|
||||
rect = ClampSpriteRect(rect, textureActualWidth, textureActualHeight);
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Create sprite");
|
||||
var index = 0;
|
||||
var createdIndex = -1;
|
||||
while (createdIndex == -1)
|
||||
{
|
||||
createdIndex = AddSprite(rect, 0, Vector2.zero, GenerateSpriteNameWithIndex(index++), Vector4.zero);
|
||||
}
|
||||
selected = m_RectsCache.spriteRects[createdIndex];
|
||||
}
|
||||
|
||||
public void DeleteSprite()
|
||||
{
|
||||
if (selected != null)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Delete sprite");
|
||||
m_RectsCache.Remove(selected);
|
||||
selected = null;
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,524 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using UnityEngine;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal class SpriteRectModel : ScriptableObject, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField]
|
||||
private List<SpriteRect> m_SpriteRects;
|
||||
private HashSet<string> m_Names;
|
||||
private HashSet<long> m_InternalIds;
|
||||
|
||||
private IReadOnlyList<SpriteRect> m_SpriteReadOnlyList;
|
||||
public IReadOnlyList<SpriteRect> spriteRects
|
||||
{
|
||||
get { return m_SpriteReadOnlyList; }
|
||||
}
|
||||
|
||||
private SpriteRectModel()
|
||||
{
|
||||
m_Names = new HashSet<string>();
|
||||
m_InternalIds = new HashSet<long>();
|
||||
}
|
||||
|
||||
public void SetSpriteRects(List<SpriteRect> newSpriteRects)
|
||||
{
|
||||
m_SpriteRects = newSpriteRects;
|
||||
foreach (var spriteRect in m_SpriteRects)
|
||||
{
|
||||
m_Names.Add(spriteRect.name);
|
||||
m_InternalIds.Add(spriteRect.internalID);
|
||||
}
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
public int FindIndex(Predicate<SpriteRect> match)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var spriteRect in m_SpriteRects)
|
||||
{
|
||||
if (match.Invoke(spriteRect))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_SpriteRects = new List<SpriteRect>();
|
||||
m_InternalIds.Clear();
|
||||
m_Names.Clear();
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
public bool Add(SpriteRect spriteRect)
|
||||
{
|
||||
if (m_Names.Contains(spriteRect.name))
|
||||
return false;
|
||||
if (spriteRect.internalID != 0 && m_InternalIds.Contains(spriteRect.internalID))
|
||||
return false;
|
||||
m_Names.Add(spriteRect.name);
|
||||
if (spriteRect.internalID != 0)
|
||||
m_InternalIds.Add(spriteRect.internalID);
|
||||
m_SpriteRects.Add(spriteRect);
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Remove(SpriteRect spriteRect)
|
||||
{
|
||||
m_Names.Remove(spriteRect.name);
|
||||
if (spriteRect.internalID != 0)
|
||||
m_InternalIds.Remove(spriteRect.internalID);
|
||||
m_SpriteRects.Remove(spriteRect);
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
public bool HasName(string rectName)
|
||||
{
|
||||
return m_Names.Contains(rectName);
|
||||
}
|
||||
|
||||
public bool HasInternalID(long internalID)
|
||||
{
|
||||
return m_InternalIds.Contains(internalID);
|
||||
}
|
||||
|
||||
public List<SpriteRect> GetSpriteRects()
|
||||
{
|
||||
return m_SpriteRects;
|
||||
}
|
||||
|
||||
public void Rename(string oldName, string newName)
|
||||
{
|
||||
m_Names.Remove(oldName);
|
||||
m_Names.Add(newName);
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{}
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
m_SpriteReadOnlyList = m_SpriteRects.AsReadOnly();
|
||||
m_Names.Clear();
|
||||
m_InternalIds.Clear();
|
||||
foreach (var sprite in m_SpriteReadOnlyList)
|
||||
{
|
||||
m_Names.Add(sprite.name);
|
||||
m_InternalIds.Add(sprite.internalID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class OutlineSpriteRect : SpriteRect
|
||||
{
|
||||
public List<Vector2[]> outlines;
|
||||
|
||||
public OutlineSpriteRect(SpriteRect rect)
|
||||
{
|
||||
this.name = rect.name;
|
||||
this.originalName = rect.originalName;
|
||||
this.pivot = rect.pivot;
|
||||
this.alignment = rect.alignment;
|
||||
this.border = rect.border;
|
||||
this.rect = rect.rect;
|
||||
this.spriteID = rect.spriteID;
|
||||
this.internalID = rect.internalID;
|
||||
outlines = new List<Vector2[]>();
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract partial class SpriteFrameModuleBase : SpriteEditorModuleBase
|
||||
{
|
||||
protected static UnityType spriteType = UnityType.FindTypeByName("Sprite");
|
||||
|
||||
protected SpriteRectModel m_RectsCache;
|
||||
protected ITextureDataProvider m_TextureDataProvider;
|
||||
protected ISpriteEditorDataProvider m_SpriteDataProvider;
|
||||
string m_ModuleName;
|
||||
|
||||
internal enum PivotUnitMode
|
||||
{
|
||||
Normalized,
|
||||
Pixels
|
||||
}
|
||||
|
||||
private PivotUnitMode m_PivotUnitMode = PivotUnitMode.Normalized;
|
||||
|
||||
protected SpriteFrameModuleBase(string name, ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad)
|
||||
{
|
||||
spriteEditor = sw;
|
||||
eventSystem = es;
|
||||
undoSystem = us;
|
||||
assetDatabase = ad;
|
||||
m_ModuleName = name;
|
||||
}
|
||||
|
||||
// implements ISpriteEditorModule
|
||||
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
spriteImportMode = SpriteFrameModule.GetSpriteImportMode(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>());
|
||||
m_TextureDataProvider = spriteEditor.GetDataProvider<ITextureDataProvider>();
|
||||
m_SpriteDataProvider = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
|
||||
int width, height;
|
||||
m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height);
|
||||
textureActualWidth = width;
|
||||
textureActualHeight = height;
|
||||
m_RectsCache = ScriptableObject.CreateInstance<SpriteRectModel>();
|
||||
m_RectsCache.hideFlags = HideFlags.HideAndDontSave;
|
||||
var spriteList = m_SpriteDataProvider.GetSpriteRects().ToList();
|
||||
m_RectsCache.SetSpriteRects(spriteList);
|
||||
spriteEditor.spriteRects = spriteList;
|
||||
if (spriteEditor.selectedSpriteRect != null)
|
||||
spriteEditor.selectedSpriteRect = m_RectsCache.spriteRects.FirstOrDefault(x => x.spriteID == spriteEditor.selectedSpriteRect.spriteID);
|
||||
AddMainUI(spriteEditor.GetMainVisualContainer());
|
||||
undoSystem.RegisterUndoCallback(UndoCallback);
|
||||
}
|
||||
|
||||
public override void OnModuleDeactivate()
|
||||
{
|
||||
if (m_RectsCache != null)
|
||||
{
|
||||
undoSystem.ClearUndo(m_RectsCache);
|
||||
ScriptableObject.DestroyImmediate(m_RectsCache);
|
||||
m_RectsCache = null;
|
||||
}
|
||||
undoSystem.UnregisterUndoCallback(UndoCallback);
|
||||
RemoveMainUI(spriteEditor.GetMainVisualContainer());
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
if (containsMultipleSprites)
|
||||
{
|
||||
var oldNames = new List<string>();
|
||||
var newNames = new List<string>();
|
||||
var ids = new List<long>();
|
||||
var names = new List<string>();
|
||||
|
||||
foreach (var spriteRect in m_RectsCache.spriteRects)
|
||||
{
|
||||
if (string.IsNullOrEmpty(spriteRect.name))
|
||||
spriteRect.name = "Empty";
|
||||
|
||||
if (!string.IsNullOrEmpty(spriteRect.originalName))
|
||||
{
|
||||
oldNames.Add(spriteRect.originalName);
|
||||
newNames.Add(spriteRect.name);
|
||||
}
|
||||
|
||||
if (spriteRect.m_RegisterInternalID)
|
||||
{
|
||||
ids.Add(spriteRect.internalID);
|
||||
names.Add(spriteRect.name);
|
||||
}
|
||||
spriteRect.m_RegisterInternalID = false;
|
||||
}
|
||||
var so = new SerializedObject(m_SpriteDataProvider.targetObject);
|
||||
if (so.isValid && ids.Count > 0)
|
||||
{
|
||||
ImportSettingInternalID.RegisterInternalID(so, spriteType, ids, names);
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
|
||||
AssetImporter assetImporter = m_SpriteDataProvider.targetObject as AssetImporter;
|
||||
if (oldNames.Count > 0 && assetImporter != null)
|
||||
{
|
||||
assetImporter.RenameSubAssets(spriteType.persistentTypeID, oldNames.ToArray(), newNames.ToArray());
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
}
|
||||
}
|
||||
var array = m_RectsCache != null ? m_RectsCache.spriteRects.ToArray() : null;
|
||||
m_SpriteDataProvider.SetSpriteRects(array);
|
||||
|
||||
var outlineDataProvider = m_SpriteDataProvider.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
var physicsDataProvider = m_SpriteDataProvider.GetDataProvider<ISpritePhysicsOutlineDataProvider>();
|
||||
foreach (var rect in array)
|
||||
{
|
||||
if (rect is OutlineSpriteRect outlineRect)
|
||||
{
|
||||
if (outlineRect.outlines.Count > 0)
|
||||
{
|
||||
outlineDataProvider.SetOutlines(outlineRect.spriteID, outlineRect.outlines);
|
||||
physicsDataProvider.SetOutlines(outlineRect.spriteID, outlineRect.outlines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RectsCache != null)
|
||||
undoSystem.ClearUndo(m_RectsCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_RectsCache != null)
|
||||
{
|
||||
undoSystem.ClearUndo(m_RectsCache);
|
||||
var spriteList = m_SpriteDataProvider.GetSpriteRects().ToList();
|
||||
m_RectsCache.SetSpriteRects(spriteList);
|
||||
spriteEditor.spriteRects = spriteList;
|
||||
if (spriteEditor.selectedSpriteRect != null)
|
||||
spriteEditor.selectedSpriteRect = m_RectsCache.spriteRects.FirstOrDefault(x => x.spriteID == spriteEditor.selectedSpriteRect.spriteID);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string moduleName
|
||||
{
|
||||
get { return m_ModuleName; }
|
||||
}
|
||||
|
||||
// injected interfaces
|
||||
protected IEventSystem eventSystem
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected IUndoSystem undoSystem
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected IAssetDatabase assetDatabase
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected SpriteRect selected
|
||||
{
|
||||
get { return spriteEditor.selectedSpriteRect; }
|
||||
set { spriteEditor.selectedSpriteRect = value; }
|
||||
}
|
||||
|
||||
protected SpriteImportMode spriteImportMode
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
protected string spriteAssetPath
|
||||
{
|
||||
get { return assetDatabase.GetAssetPath(m_TextureDataProvider.texture); }
|
||||
}
|
||||
|
||||
public bool hasSelected
|
||||
{
|
||||
get { return spriteEditor.selectedSpriteRect != null; }
|
||||
}
|
||||
|
||||
public SpriteAlignment selectedSpriteAlignment
|
||||
{
|
||||
get { return selected.alignment; }
|
||||
}
|
||||
|
||||
public Vector2 selectedSpritePivot
|
||||
{
|
||||
get { return selected.pivot; }
|
||||
}
|
||||
|
||||
private Vector2 selectedSpritePivotInCurUnitMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_PivotUnitMode == PivotUnitMode.Pixels
|
||||
? ConvertFromNormalizedToRectSpace(selectedSpritePivot, selectedSpriteRect)
|
||||
: selectedSpritePivot;
|
||||
}
|
||||
}
|
||||
|
||||
public int CurrentSelectedSpriteIndex()
|
||||
{
|
||||
if (m_RectsCache != null && selected != null)
|
||||
return m_RectsCache.FindIndex(x => x.spriteID == selected.spriteID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Vector4 selectedSpriteBorder
|
||||
{
|
||||
get { return ClampSpriteBorderToRect(selected.border, selected.rect); }
|
||||
set
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite Border");
|
||||
spriteEditor.SetDataModified();
|
||||
selected.border = ClampSpriteBorderToRect(value, selected.rect);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect selectedSpriteRect
|
||||
{
|
||||
get { return selected.rect; }
|
||||
set
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite rect");
|
||||
spriteEditor.SetDataModified();
|
||||
selected.rect = ClampSpriteRect(value, textureActualWidth, textureActualHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public string selectedSpriteName
|
||||
{
|
||||
get { return selected.name; }
|
||||
set
|
||||
{
|
||||
if (selected.name == value)
|
||||
return;
|
||||
if (m_RectsCache.HasName(value))
|
||||
return;
|
||||
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite Name");
|
||||
spriteEditor.SetDataModified();
|
||||
|
||||
string oldName = selected.name;
|
||||
string newName = InternalEditorUtility.RemoveInvalidCharsFromFileName(value, true);
|
||||
|
||||
// These can only be changed in sprite multiple mode
|
||||
if (string.IsNullOrEmpty(selected.originalName) && (newName != oldName))
|
||||
selected.originalName = oldName;
|
||||
|
||||
// Is the name empty?
|
||||
if (string.IsNullOrEmpty(newName))
|
||||
newName = oldName;
|
||||
|
||||
m_RectsCache.Rename(oldName, newName);
|
||||
selected.name = newName;
|
||||
}
|
||||
}
|
||||
|
||||
public int spriteCount
|
||||
{
|
||||
get { return m_RectsCache.spriteRects.Count; }
|
||||
}
|
||||
|
||||
public Vector4 GetSpriteBorderAt(int i)
|
||||
{
|
||||
return m_RectsCache.spriteRects[i].border;
|
||||
}
|
||||
|
||||
public Rect GetSpriteRectAt(int i)
|
||||
{
|
||||
return m_RectsCache.spriteRects[i].rect;
|
||||
}
|
||||
|
||||
public int textureActualWidth { get; private set; }
|
||||
public int textureActualHeight { get; private set; }
|
||||
|
||||
public void SetSpritePivotAndAlignment(Vector2 pivot, SpriteAlignment alignment)
|
||||
{
|
||||
undoSystem.RegisterCompleteObjectUndo(m_RectsCache, "Change Sprite Pivot");
|
||||
spriteEditor.SetDataModified();
|
||||
selected.alignment = alignment;
|
||||
selected.pivot = SpriteEditorUtility.GetPivotValue(alignment, pivot);
|
||||
}
|
||||
|
||||
public bool containsMultipleSprites
|
||||
{
|
||||
get { return spriteImportMode == SpriteImportMode.Multiple; }
|
||||
}
|
||||
|
||||
protected void SnapPivotToSnapPoints(Vector2 pivot, out Vector2 outPivot, out SpriteAlignment outAlignment)
|
||||
{
|
||||
Rect rect = selectedSpriteRect;
|
||||
|
||||
// Convert from normalized space to texture space
|
||||
Vector2 texturePos = new Vector2(rect.xMin + rect.width * pivot.x, rect.yMin + rect.height * pivot.y);
|
||||
|
||||
Vector2[] snapPoints = GetSnapPointsArray(rect);
|
||||
|
||||
// Snapping is now a firm action, it will always snap to one of the snapping points.
|
||||
SpriteAlignment snappedAlignment = SpriteAlignment.Custom;
|
||||
float nearestDistance = float.MaxValue;
|
||||
for (int alignment = 0; alignment < snapPoints.Length; alignment++)
|
||||
{
|
||||
float distance = (texturePos - snapPoints[alignment]).magnitude * m_Zoom;
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
snappedAlignment = (SpriteAlignment)alignment;
|
||||
nearestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
outAlignment = snappedAlignment;
|
||||
outPivot = ConvertFromTextureToNormalizedSpace(snapPoints[(int)snappedAlignment], rect);
|
||||
}
|
||||
|
||||
protected void SnapPivotToPixels(Vector2 pivot, out Vector2 outPivot, out SpriteAlignment outAlignment)
|
||||
{
|
||||
outAlignment = SpriteAlignment.Custom;
|
||||
|
||||
Rect rect = selectedSpriteRect;
|
||||
float unitsPerPixelX = 1.0f / rect.width;
|
||||
float unitsPerPixelY = 1.0f / rect.height;
|
||||
outPivot.x = Mathf.Round(pivot.x / unitsPerPixelX) * unitsPerPixelX;
|
||||
outPivot.y = Mathf.Round(pivot.y / unitsPerPixelY) * unitsPerPixelY;
|
||||
}
|
||||
|
||||
private void UndoCallback()
|
||||
{
|
||||
UIUndoCallback();
|
||||
}
|
||||
|
||||
protected static Rect ClampSpriteRect(Rect rect, float maxX, float maxY)
|
||||
{
|
||||
// Clamp rect to width height
|
||||
rect = FlipNegativeRect(rect);
|
||||
Rect newRect = new Rect();
|
||||
|
||||
newRect.xMin = Mathf.Clamp(rect.xMin, 0, maxX - 1);
|
||||
newRect.yMin = Mathf.Clamp(rect.yMin, 0, maxY - 1);
|
||||
newRect.xMax = Mathf.Clamp(rect.xMax, 1, maxX);
|
||||
newRect.yMax = Mathf.Clamp(rect.yMax, 1, maxY);
|
||||
|
||||
// Prevent width and height to be 0 value after clamping.
|
||||
if (Mathf.RoundToInt(newRect.width) == 0)
|
||||
newRect.width = 1;
|
||||
if (Mathf.RoundToInt(newRect.height) == 0)
|
||||
newRect.height = 1;
|
||||
|
||||
return SpriteEditorUtility.RoundedRect(newRect);
|
||||
}
|
||||
|
||||
protected static Rect FlipNegativeRect(Rect rect)
|
||||
{
|
||||
Rect newRect = new Rect();
|
||||
|
||||
newRect.xMin = Mathf.Min(rect.xMin, rect.xMax);
|
||||
newRect.yMin = Mathf.Min(rect.yMin, rect.yMax);
|
||||
newRect.xMax = Mathf.Max(rect.xMin, rect.xMax);
|
||||
newRect.yMax = Mathf.Max(rect.yMin, rect.yMax);
|
||||
|
||||
return newRect;
|
||||
}
|
||||
|
||||
protected static Vector4 ClampSpriteBorderToRect(Vector4 border, Rect rect)
|
||||
{
|
||||
Rect flipRect = FlipNegativeRect(rect);
|
||||
float w = flipRect.width;
|
||||
float h = flipRect.height;
|
||||
|
||||
Vector4 newBorder = new Vector4();
|
||||
|
||||
// Make sure borders are within the width/height and left < right and top < bottom
|
||||
newBorder.x = Mathf.RoundToInt(Mathf.Clamp(border.x, 0, Mathf.Min(Mathf.Abs(w - border.z), w))); // Left
|
||||
newBorder.z = Mathf.RoundToInt(Mathf.Clamp(border.z, 0, Mathf.Min(Mathf.Abs(w - newBorder.x), w))); // Right
|
||||
|
||||
newBorder.y = Mathf.RoundToInt(Mathf.Clamp(border.y, 0, Mathf.Min(Mathf.Abs(h - border.w), h))); // Bottom
|
||||
newBorder.w = Mathf.RoundToInt(Mathf.Clamp(border.w, 0, Mathf.Min(Mathf.Abs(h - newBorder.y), h))); // Top
|
||||
|
||||
return newBorder;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,744 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal abstract partial class SpriteFrameModuleBase : SpriteEditorModuleBase
|
||||
{
|
||||
protected enum GizmoMode
|
||||
{
|
||||
BorderEditing,
|
||||
RectEditing
|
||||
}
|
||||
|
||||
protected class Styles
|
||||
{
|
||||
public readonly GUIStyle dragdot = "U2D.dragDot";
|
||||
public readonly GUIStyle dragdotactive = "U2D.dragDotActive";
|
||||
public readonly GUIStyle createRect = "U2D.createRect";
|
||||
public readonly GUIStyle pivotdotactive = "U2D.pivotDotActive";
|
||||
public readonly GUIStyle pivotdot = "U2D.pivotDot";
|
||||
|
||||
public readonly GUIStyle dragBorderdot = new GUIStyle();
|
||||
public readonly GUIStyle dragBorderDotActive = new GUIStyle();
|
||||
|
||||
public readonly GUIStyle toolbar;
|
||||
|
||||
public Styles()
|
||||
{
|
||||
toolbar = new GUIStyle(EditorStyles.inspectorBig);
|
||||
toolbar.margin.top = 0;
|
||||
toolbar.margin.bottom = 0;
|
||||
createRect.border = new RectOffset(3, 3, 3, 3);
|
||||
|
||||
dragBorderdot.fixedHeight = 5f;
|
||||
dragBorderdot.fixedWidth = 5f;
|
||||
dragBorderdot.normal.background = EditorGUIUtility.whiteTexture;
|
||||
|
||||
dragBorderDotActive.fixedHeight = dragBorderdot.fixedHeight;
|
||||
dragBorderDotActive.fixedWidth = dragBorderdot.fixedWidth;
|
||||
dragBorderDotActive.normal.background = EditorGUIUtility.whiteTexture;
|
||||
}
|
||||
}
|
||||
|
||||
private static Styles s_Styles;
|
||||
|
||||
protected static Styles styles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Styles == null)
|
||||
s_Styles = new Styles();
|
||||
return s_Styles;
|
||||
}
|
||||
}
|
||||
|
||||
private const float kInspectorWidth = 330f;
|
||||
private const float kInspectorHeight = 170;
|
||||
private const float kPivotFieldPrecision = 0.0001f;
|
||||
private float m_Zoom = 1.0f;
|
||||
private GizmoMode m_GizmoMode;
|
||||
|
||||
private VisualElement m_NameElement;
|
||||
private TextField m_NameField;
|
||||
private VisualElement m_PositionElement;
|
||||
private IntegerField m_PositionFieldX;
|
||||
private IntegerField m_PositionFieldY;
|
||||
private IntegerField m_PositionFieldW;
|
||||
private IntegerField m_PositionFieldH;
|
||||
private IntegerField m_BorderFieldL;
|
||||
private IntegerField m_BorderFieldT;
|
||||
private IntegerField m_BorderFieldR;
|
||||
private IntegerField m_BorderFieldB;
|
||||
private EnumField m_PivotField;
|
||||
private EnumField m_PivotUnitModeField;
|
||||
private VisualElement m_CustomPivotElement;
|
||||
private FloatField m_CustomPivotFieldX;
|
||||
private FloatField m_CustomPivotFieldY;
|
||||
private VisualElement m_SelectedFrameInspector;
|
||||
|
||||
private bool ShouldShowRectScaling()
|
||||
{
|
||||
return hasSelected && m_GizmoMode == GizmoMode.RectEditing;
|
||||
}
|
||||
|
||||
private static Rect inspectorRect
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Rect(
|
||||
0, 0,
|
||||
kInspectorWidth,
|
||||
kInspectorHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveMainUI(VisualElement mainView)
|
||||
{
|
||||
if (mainView.Contains(m_SelectedFrameInspector))
|
||||
mainView.Remove(m_SelectedFrameInspector);
|
||||
mainView.UnregisterCallback<SpriteSelectionChangeEvent>(SelectionChange);
|
||||
}
|
||||
|
||||
protected void UpdatePositionField(FocusOutEvent evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_PositionFieldX.SetValueWithoutNotify((int)selectedSpriteRect.x);
|
||||
m_PositionFieldY.SetValueWithoutNotify((int)selectedSpriteRect.y);
|
||||
m_PositionFieldW.SetValueWithoutNotify((int)selectedSpriteRect.width);
|
||||
m_PositionFieldH.SetValueWithoutNotify((int)selectedSpriteRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBorderField(FocusOutEvent evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_BorderFieldL.SetValueWithoutNotify((int)selectedSpriteBorder.x);
|
||||
m_BorderFieldB.SetValueWithoutNotify((int)selectedSpriteBorder.y);
|
||||
m_BorderFieldR.SetValueWithoutNotify((int)selectedSpriteBorder.z);
|
||||
m_BorderFieldT.SetValueWithoutNotify((int)selectedSpriteBorder.w);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupIntegerField(IntegerField field, EventCallback<FocusOutEvent> onFocusOutEvent, EventCallback<ChangeEvent<int>> onChangeEvent)
|
||||
{
|
||||
field.RegisterCallback(onFocusOutEvent);
|
||||
field.RegisterValueChangedCallback(onChangeEvent);
|
||||
}
|
||||
|
||||
void SetDragFieldLimit(IntegerField field, int value)
|
||||
{
|
||||
// The only way to know if value change is due to dragger or text input
|
||||
var t = field.Q("unity-text-input");
|
||||
if (!t.focusController.IsFocused(t))
|
||||
{
|
||||
// Value changed due to drag. We set back the field so to show the drag limit
|
||||
field.SetValueWithoutNotify(value);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntXChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.x = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldX, (int)selectedSpriteRect.x);
|
||||
m_PositionFieldW.SetValueWithoutNotify((int)selectedSpriteRect.width);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntYChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.y = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldY, (int)selectedSpriteRect.y);
|
||||
m_PositionFieldH.SetValueWithoutNotify((int)selectedSpriteRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntWChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.width = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldW, (int)selectedSpriteRect.width);
|
||||
m_PositionFieldX.SetValueWithoutNotify((int)selectedSpriteRect.x);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPositionIntHChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var rect = selectedSpriteRect;
|
||||
rect.height = evt.newValue;
|
||||
selectedSpriteRect = rect;
|
||||
SetDragFieldLimit(m_PositionFieldH, (int)selectedSpriteRect.height);
|
||||
m_PositionFieldY.SetValueWithoutNotify((int)selectedSpriteRect.y);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntLChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.x = evt.newValue;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldL, (int)selectedSpriteBorder.x);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntBChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.y = evt.newValue;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldB, (int)selectedSpriteBorder.y);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntRChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.z = (evt.newValue + border.x) <= selectedSpriteRect.width ? evt.newValue : selectedSpriteRect.width - border.x;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldR, (int)selectedSpriteBorder.z);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBorderIntTChange(ChangeEvent<int> evt)
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
var border = selectedSpriteBorder;
|
||||
border.w = (evt.newValue + border.y) <= selectedSpriteRect.height ? evt.newValue : selectedSpriteRect.height - border.y;
|
||||
selectedSpriteBorder = border;
|
||||
SetDragFieldLimit(m_BorderFieldT, (int)selectedSpriteBorder.w);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMainUI(VisualElement mainView)
|
||||
{
|
||||
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Packages/com.unity.2d.sprite/Editor/UI/SpriteEditor/SpriteFrameModuleInspector.uxml") as VisualTreeAsset;
|
||||
m_SelectedFrameInspector = visualTree.CloneTree().Q("spriteFrameModuleInspector");
|
||||
|
||||
m_NameElement = m_SelectedFrameInspector.Q("name");
|
||||
m_NameField = m_SelectedFrameInspector.Q<TextField>("spriteName");
|
||||
m_NameField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
selectedSpriteName = evt.newValue;
|
||||
}
|
||||
});
|
||||
|
||||
m_NameField.RegisterCallback<FocusOutEvent>((focus) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_NameField.SetValueWithoutNotify(selectedSpriteName);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
m_PositionElement = m_SelectedFrameInspector.Q("position");
|
||||
m_PositionFieldX = m_PositionElement.Q<IntegerField>("positionX");
|
||||
SetupIntegerField(m_PositionFieldX, UpdatePositionField, OnPositionIntXChange);
|
||||
|
||||
m_PositionFieldY = m_PositionElement.Q<IntegerField>("positionY");
|
||||
SetupIntegerField(m_PositionFieldY, UpdatePositionField, OnPositionIntYChange);
|
||||
|
||||
m_PositionFieldW = m_PositionElement.Q<IntegerField>("positionW");
|
||||
SetupIntegerField(m_PositionFieldW, UpdatePositionField, OnPositionIntWChange);
|
||||
|
||||
m_PositionFieldH = m_PositionElement.Q<IntegerField>("positionH");
|
||||
SetupIntegerField(m_PositionFieldH, UpdatePositionField, OnPositionIntHChange);
|
||||
|
||||
var borderElement = m_SelectedFrameInspector.Q("border");
|
||||
m_BorderFieldL = borderElement.Q<IntegerField>("borderL");
|
||||
SetupIntegerField(m_BorderFieldL, UpdateBorderField, OnBorderIntLChange);
|
||||
|
||||
m_BorderFieldT = borderElement.Q<IntegerField>("borderT");
|
||||
SetupIntegerField(m_BorderFieldT, UpdateBorderField, OnBorderIntTChange);
|
||||
|
||||
m_BorderFieldR = borderElement.Q<IntegerField>("borderR");
|
||||
SetupIntegerField(m_BorderFieldR, UpdateBorderField, OnBorderIntRChange);
|
||||
|
||||
m_BorderFieldB = borderElement.Q<IntegerField>("borderB");
|
||||
SetupIntegerField(m_BorderFieldB, UpdateBorderField, OnBorderIntBChange);
|
||||
|
||||
m_PivotField = m_SelectedFrameInspector.Q<EnumField>("pivotField");
|
||||
m_PivotField.Init(SpriteAlignment.Center);
|
||||
m_PivotField.label = L10n.Tr("Pivot");
|
||||
m_PivotField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
SpriteAlignment alignment = (SpriteAlignment)evt.newValue;
|
||||
SetSpritePivotAndAlignment(selectedSpritePivot, alignment);
|
||||
m_CustomPivotElement.SetEnabled(selectedSpriteAlignment == SpriteAlignment.Custom);
|
||||
Vector2 pivot = selectedSpritePivotInCurUnitMode;
|
||||
m_CustomPivotFieldX.SetValueWithoutNotify(pivot.x);
|
||||
m_CustomPivotFieldY.SetValueWithoutNotify(pivot.y);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
m_PivotUnitModeField = m_SelectedFrameInspector.Q<EnumField>("pivotUnitModeField");
|
||||
m_PivotUnitModeField.Init(PivotUnitMode.Normalized);
|
||||
m_PivotUnitModeField.label = L10n.Tr("Pivot Unit Mode");
|
||||
m_PivotUnitModeField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
m_PivotUnitMode = (PivotUnitMode)evt.newValue;
|
||||
|
||||
Vector2 pivot = selectedSpritePivotInCurUnitMode;
|
||||
m_CustomPivotFieldX.SetValueWithoutNotify(pivot.x);
|
||||
m_CustomPivotFieldY.SetValueWithoutNotify(pivot.y);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
m_CustomPivotElement = m_SelectedFrameInspector.Q("customPivot");
|
||||
m_CustomPivotFieldX = m_CustomPivotElement.Q<FloatField>("customPivotX");
|
||||
m_CustomPivotFieldX.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
float newValue = (float)evt.newValue;
|
||||
float pivotX = m_PivotUnitMode == PivotUnitMode.Pixels
|
||||
? ConvertFromRectToNormalizedSpace(new Vector2(newValue, 0.0f), selectedSpriteRect).x
|
||||
: newValue;
|
||||
|
||||
var pivot = selectedSpritePivot;
|
||||
pivot.x = pivotX;
|
||||
SetSpritePivotAndAlignment(pivot, selectedSpriteAlignment);
|
||||
}
|
||||
});
|
||||
|
||||
m_CustomPivotFieldY = m_CustomPivotElement.Q<FloatField>("customPivotY");
|
||||
m_CustomPivotFieldY.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
if (hasSelected)
|
||||
{
|
||||
float newValue = (float)evt.newValue;
|
||||
float pivotY = m_PivotUnitMode == PivotUnitMode.Pixels
|
||||
? ConvertFromRectToNormalizedSpace(new Vector2(0.0f, newValue), selectedSpriteRect).y
|
||||
: newValue;
|
||||
|
||||
var pivot = selectedSpritePivot;
|
||||
pivot.y = pivotY;
|
||||
SetSpritePivotAndAlignment(pivot, selectedSpriteAlignment);
|
||||
}
|
||||
});
|
||||
|
||||
//// Force an update of all the fields.
|
||||
PopulateSpriteFrameInspectorField();
|
||||
|
||||
mainView.RegisterCallback<SpriteSelectionChangeEvent>(SelectionChange);
|
||||
|
||||
// Stop mouse events from reaching the main view.
|
||||
m_SelectedFrameInspector.pickingMode = PickingMode.Ignore;
|
||||
m_SelectedFrameInspector.RegisterCallback<MouseDownEvent>((e) => { e.StopPropagation(); });
|
||||
m_SelectedFrameInspector.RegisterCallback<MouseUpEvent>((e) => { e.StopPropagation(); });
|
||||
m_SelectedFrameInspector.AddToClassList("moduleWindow");
|
||||
m_SelectedFrameInspector.AddToClassList("bottomRightFloating");
|
||||
mainView.Add(m_SelectedFrameInspector);
|
||||
}
|
||||
|
||||
private void SelectionChange(SpriteSelectionChangeEvent evt)
|
||||
{
|
||||
m_SelectedFrameInspector.style.display = hasSelected ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
|
||||
private void UIUndoCallback()
|
||||
{
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
|
||||
protected void PopulateSpriteFrameInspectorField()
|
||||
{
|
||||
m_SelectedFrameInspector.style.display = hasSelected ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (!hasSelected)
|
||||
return;
|
||||
m_NameElement.SetEnabled(containsMultipleSprites);
|
||||
m_NameField.SetValueWithoutNotify(selectedSpriteName);
|
||||
m_PositionElement.SetEnabled(containsMultipleSprites);
|
||||
var spriteRect = selectedSpriteRect;
|
||||
m_PositionFieldX.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.x));
|
||||
m_PositionFieldY.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.y));
|
||||
m_PositionFieldW.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.width));
|
||||
m_PositionFieldH.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.height));
|
||||
var spriteBorder = selectedSpriteBorder;
|
||||
m_BorderFieldL.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.x));
|
||||
m_BorderFieldT.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.w));
|
||||
m_BorderFieldR.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.z));
|
||||
m_BorderFieldB.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.y));
|
||||
m_PivotField.SetValueWithoutNotify(selectedSpriteAlignment);
|
||||
m_PivotUnitModeField.SetValueWithoutNotify(m_PivotUnitMode);
|
||||
Vector2 pivot = selectedSpritePivotInCurUnitMode;
|
||||
m_CustomPivotFieldX.SetValueWithoutNotify(pivot.x);
|
||||
m_CustomPivotFieldY.SetValueWithoutNotify(pivot.y);
|
||||
|
||||
m_CustomPivotElement.SetEnabled(hasSelected && selectedSpriteAlignment == SpriteAlignment.Custom);
|
||||
}
|
||||
|
||||
private static Vector2 ApplySpriteAlignmentToPivot(Vector2 pivot, Rect rect, SpriteAlignment alignment)
|
||||
{
|
||||
if (alignment != SpriteAlignment.Custom)
|
||||
{
|
||||
Vector2[] snapPoints = GetSnapPointsArray(rect);
|
||||
Vector2 texturePos = snapPoints[(int)alignment];
|
||||
return ConvertFromTextureToNormalizedSpace(texturePos, rect);
|
||||
}
|
||||
return pivot;
|
||||
}
|
||||
|
||||
private static Vector2 ConvertFromTextureToNormalizedSpace(Vector2 texturePos, Rect rect)
|
||||
{
|
||||
return new Vector2((texturePos.x - rect.xMin) / rect.width, (texturePos.y - rect.yMin) / rect.height);
|
||||
}
|
||||
|
||||
private static Vector2 ConvertFromNormalizedToRectSpace(Vector2 normalizedPos, Rect rect)
|
||||
{
|
||||
Vector2 rectPos = new Vector2(rect.width * normalizedPos.x, rect.height * normalizedPos.y);
|
||||
|
||||
// This is to combat the lack of precision formating on the UI controls.
|
||||
rectPos.x = Mathf.Round(rectPos.x / kPivotFieldPrecision) * kPivotFieldPrecision;
|
||||
rectPos.y = Mathf.Round(rectPos.y / kPivotFieldPrecision) * kPivotFieldPrecision;
|
||||
|
||||
return rectPos;
|
||||
}
|
||||
|
||||
private static Vector2 ConvertFromRectToNormalizedSpace(Vector2 rectPos, Rect rect)
|
||||
{
|
||||
return new Vector2(rectPos.x / rect.width, rectPos.y / rect.height);
|
||||
}
|
||||
|
||||
private static Vector2[] GetSnapPointsArray(Rect rect)
|
||||
{
|
||||
Vector2[] snapPoints = new Vector2[9];
|
||||
snapPoints[(int)SpriteAlignment.TopLeft] = new Vector2(rect.xMin, rect.yMax);
|
||||
snapPoints[(int)SpriteAlignment.TopCenter] = new Vector2(rect.center.x, rect.yMax);
|
||||
snapPoints[(int)SpriteAlignment.TopRight] = new Vector2(rect.xMax, rect.yMax);
|
||||
snapPoints[(int)SpriteAlignment.LeftCenter] = new Vector2(rect.xMin, rect.center.y);
|
||||
snapPoints[(int)SpriteAlignment.Center] = new Vector2(rect.center.x, rect.center.y);
|
||||
snapPoints[(int)SpriteAlignment.RightCenter] = new Vector2(rect.xMax, rect.center.y);
|
||||
snapPoints[(int)SpriteAlignment.BottomLeft] = new Vector2(rect.xMin, rect.yMin);
|
||||
snapPoints[(int)SpriteAlignment.BottomCenter] = new Vector2(rect.center.x, rect.yMin);
|
||||
snapPoints[(int)SpriteAlignment.BottomRight] = new Vector2(rect.xMax, rect.yMin);
|
||||
return snapPoints;
|
||||
}
|
||||
|
||||
protected void Repaint()
|
||||
{
|
||||
spriteEditor.RequestRepaint();
|
||||
}
|
||||
|
||||
protected void HandleGizmoMode()
|
||||
{
|
||||
GizmoMode oldGizmoMode = m_GizmoMode;
|
||||
var evt = eventSystem.current;
|
||||
if (evt.control)
|
||||
m_GizmoMode = GizmoMode.BorderEditing;
|
||||
else
|
||||
m_GizmoMode = GizmoMode.RectEditing;
|
||||
|
||||
if (oldGizmoMode != m_GizmoMode && (evt.type == EventType.KeyDown || evt.type == EventType.KeyUp) && (evt.keyCode == KeyCode.LeftControl || evt.keyCode == KeyCode.RightControl || evt.keyCode == KeyCode.LeftAlt || evt.keyCode == KeyCode.RightAlt))
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected bool MouseOnTopOfInspector()
|
||||
{
|
||||
if (hasSelected == false)
|
||||
return false;
|
||||
|
||||
var point = GUIClip.Unclip(eventSystem.current.mousePosition);
|
||||
point = m_SelectedFrameInspector.parent.LocalToWorld(point);
|
||||
|
||||
var selectedElement = m_SelectedFrameInspector.panel.Pick(point);
|
||||
if (selectedElement != null
|
||||
&& selectedElement.pickingMode != PickingMode.Ignore
|
||||
&& selectedElement.FindCommonAncestor(m_SelectedFrameInspector) == m_SelectedFrameInspector)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void HandlePivotHandle()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
SpriteAlignment alignment = selectedSpriteAlignment;
|
||||
Vector2 pivot = selectedSpritePivot;
|
||||
Rect rect = selectedSpriteRect;
|
||||
pivot = ApplySpriteAlignmentToPivot(pivot, rect, alignment);
|
||||
Vector2 pivotHandlePosition = SpriteEditorHandles.PivotSlider(rect, pivot, styles.pivotdot, styles.pivotdotactive);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
// Pivot snapping only happen when ctrl is press. Same as scene view snapping move
|
||||
if (eventSystem.current.control)
|
||||
SnapPivotToSnapPoints(pivotHandlePosition, out pivot, out alignment);
|
||||
else if (m_PivotUnitMode == PivotUnitMode.Pixels)
|
||||
SnapPivotToPixels(pivotHandlePosition, out pivot, out alignment);
|
||||
else
|
||||
{
|
||||
pivot = pivotHandlePosition;
|
||||
alignment = SpriteAlignment.Custom;
|
||||
}
|
||||
SetSpritePivotAndAlignment(pivot, alignment);
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderSidePointScalingSliders()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
GUIStyle dragDot = styles.dragBorderdot;
|
||||
GUIStyle dragDotActive = styles.dragBorderDotActive;
|
||||
var color = new Color(0f, 1f, 0f);
|
||||
|
||||
Rect rect = selectedSpriteRect;
|
||||
Vector4 border = selectedSpriteBorder;
|
||||
|
||||
float left = rect.xMin + border.x;
|
||||
float right = rect.xMax - border.z;
|
||||
float top = rect.yMax - border.w;
|
||||
float bottom = rect.yMin + border.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
float horizontal = bottom - (bottom - top) / 2;
|
||||
float vertical = left - (left - right) / 2;
|
||||
|
||||
float center = horizontal;
|
||||
HandleBorderPointSlider(ref left, ref center, MouseCursor.ResizeHorizontal, false, dragDot, dragDotActive, color);
|
||||
|
||||
center = horizontal;
|
||||
HandleBorderPointSlider(ref right, ref center, MouseCursor.ResizeHorizontal, false, dragDot, dragDotActive, color);
|
||||
|
||||
center = vertical;
|
||||
HandleBorderPointSlider(ref center, ref top, MouseCursor.ResizeVertical, false, dragDot, dragDotActive, color);
|
||||
|
||||
center = vertical;
|
||||
HandleBorderPointSlider(ref center, ref bottom, MouseCursor.ResizeVertical, false, dragDot, dragDotActive, color);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
border.x = left - rect.xMin;
|
||||
border.z = rect.xMax - right;
|
||||
border.w = rect.yMax - top;
|
||||
border.y = bottom - rect.yMin;
|
||||
selectedSpriteBorder = border;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderCornerScalingHandles()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
GUIStyle dragDot = styles.dragBorderdot;
|
||||
GUIStyle dragDotActive = styles.dragBorderDotActive;
|
||||
var color = new Color(0f, 1f, 0f);
|
||||
|
||||
Rect rect = selectedSpriteRect;
|
||||
Vector4 border = selectedSpriteBorder;
|
||||
|
||||
float left = rect.xMin + border.x;
|
||||
float right = rect.xMax - border.z;
|
||||
float top = rect.yMax - border.w;
|
||||
float bottom = rect.yMin + border.y;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
// Handle corner points, but hide them if border values are below 1
|
||||
HandleBorderPointSlider(ref left, ref top, MouseCursor.ResizeUpLeft, border.x < 1 && border.w < 1, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref top, MouseCursor.ResizeUpRight, border.z < 1 && border.w < 1, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref left, ref bottom, MouseCursor.ResizeUpRight, border.x < 1 && border.y < 1, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref bottom, MouseCursor.ResizeUpLeft, border.z < 1 && border.y < 1, dragDot, dragDotActive, color);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
border.x = left - rect.xMin;
|
||||
border.z = rect.xMax - right;
|
||||
border.w = rect.yMax - top;
|
||||
border.y = bottom - rect.yMin;
|
||||
selectedSpriteBorder = border;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderSideScalingHandles()
|
||||
{
|
||||
if (hasSelected == false)
|
||||
return;
|
||||
|
||||
Rect rect = new Rect(selectedSpriteRect);
|
||||
Vector4 border = selectedSpriteBorder;
|
||||
|
||||
float left = rect.xMin + border.x;
|
||||
float right = rect.xMax - border.z;
|
||||
float top = rect.yMax - border.w;
|
||||
float bottom = rect.yMin + border.y;
|
||||
|
||||
Vector2 screenRectTopLeft = Handles.matrix.MultiplyPoint(new Vector3(rect.xMin, rect.yMin));
|
||||
Vector2 screenRectBottomRight = Handles.matrix.MultiplyPoint(new Vector3(rect.xMax, rect.yMax));
|
||||
|
||||
float screenRectWidth = Mathf.Abs(screenRectBottomRight.x - screenRectTopLeft.x);
|
||||
float screenRectHeight = Mathf.Abs(screenRectBottomRight.y - screenRectTopLeft.y);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
left = HandleBorderScaleSlider(left, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
right = HandleBorderScaleSlider(right, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
|
||||
top = HandleBorderScaleSlider(rect.xMin, top, screenRectWidth, screenRectHeight, false);
|
||||
bottom = HandleBorderScaleSlider(rect.xMin, bottom, screenRectWidth, screenRectHeight, false);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
border.x = left - rect.xMin;
|
||||
border.z = rect.xMax - right;
|
||||
border.w = rect.yMax - top;
|
||||
border.y = bottom - rect.yMin;
|
||||
|
||||
selectedSpriteBorder = border;
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleBorderPointSlider(ref float x, ref float y, MouseCursor mouseCursor, bool isHidden, GUIStyle dragDot, GUIStyle dragDotActive, Color color)
|
||||
{
|
||||
var originalColor = GUI.color;
|
||||
|
||||
if (isHidden)
|
||||
GUI.color = new Color(0, 0, 0, 0);
|
||||
else
|
||||
GUI.color = color;
|
||||
|
||||
Vector2 point = SpriteEditorHandles.PointSlider(new Vector2(x, y), mouseCursor, dragDot, dragDotActive);
|
||||
x = point.x;
|
||||
y = point.y;
|
||||
|
||||
GUI.color = originalColor;
|
||||
}
|
||||
|
||||
protected float HandleBorderScaleSlider(float x, float y, float width, float height, bool isHorizontal)
|
||||
{
|
||||
float handleSize = styles.dragBorderdot.fixedWidth;
|
||||
Vector2 point = Handles.matrix.MultiplyPoint(new Vector2(x, y));
|
||||
float result;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
if (isHorizontal)
|
||||
{
|
||||
Rect newRect = new Rect(point.x - handleSize * .5f, point.y, handleSize, height);
|
||||
result = SpriteEditorHandles.ScaleSlider(point, MouseCursor.ResizeHorizontal, newRect).x;
|
||||
}
|
||||
else
|
||||
{
|
||||
Rect newRect = new Rect(point.x, point.y - handleSize * .5f, width, handleSize);
|
||||
result = SpriteEditorHandles.ScaleSlider(point, MouseCursor.ResizeVertical, newRect).y;
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
return result;
|
||||
|
||||
return isHorizontal ? x : y;
|
||||
}
|
||||
|
||||
protected void DrawSpriteRectGizmos()
|
||||
{
|
||||
if (eventSystem.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
SpriteEditorUtility.BeginLines(new Color(0f, 1f, 0f, 0.7f));
|
||||
var selectedGUID = selected != null ? selected.spriteID : new GUID();
|
||||
for (int i = 0; i < spriteCount; i++)
|
||||
{
|
||||
Vector4 border = GetSpriteBorderAt(i);
|
||||
if (m_GizmoMode != GizmoMode.BorderEditing && (m_RectsCache != null && m_RectsCache.spriteRects[i].spriteID != selectedGUID))
|
||||
{
|
||||
// border does not contain negative values
|
||||
if (border.sqrMagnitude < Mathf.Epsilon * 8)
|
||||
continue;
|
||||
}
|
||||
|
||||
var rect = GetSpriteRectAt(i);
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin + border.x, rect.yMin), new Vector3(rect.xMin + border.x, rect.yMax));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMax - border.z, rect.yMin), new Vector3(rect.xMax - border.z, rect.yMax));
|
||||
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMin + border.y), new Vector3(rect.xMax, rect.yMin + border.y));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMax - border.w), new Vector3(rect.xMax, rect.yMax - border.w));
|
||||
}
|
||||
SpriteEditorUtility.EndLines();
|
||||
|
||||
if (ShouldShowRectScaling())
|
||||
{
|
||||
Rect r = selectedSpriteRect;
|
||||
SpriteEditorUtility.BeginLines(new Color(0f, 0.1f, 0.3f, 0.25f));
|
||||
SpriteEditorUtility.DrawBox(new Rect(r.xMin + 1f / m_Zoom, r.yMin + 1f / m_Zoom, r.width, r.height));
|
||||
SpriteEditorUtility.EndLines();
|
||||
SpriteEditorUtility.BeginLines(new Color(0.25f, 0.5f, 1f, 0.75f));
|
||||
SpriteEditorUtility.DrawBox(r);
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawRectGizmos(IEnumerable<Rect> rects, Color color)
|
||||
{
|
||||
if (eventSystem.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
SpriteEditorUtility.BeginLines(color);
|
||||
foreach (var rect in rects)
|
||||
{
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMin), new Vector3(rect.xMin, rect.yMax));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMax, rect.yMin), new Vector3(rect.xMax, rect.yMax));
|
||||
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMin), new Vector3(rect.xMax, rect.yMin));
|
||||
SpriteEditorUtility.DrawLine(new Vector3(rect.xMin, rect.yMax), new Vector3(rect.xMax, rect.yMax));
|
||||
}
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
|
||||
// implements ISpriteEditorModule
|
||||
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
m_Zoom = Handles.matrix.GetColumn(0).magnitude;
|
||||
}
|
||||
|
||||
public override void DoPostGUI()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,220 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal partial class SpriteFrameModule : SpriteFrameModuleBase
|
||||
{
|
||||
private static class SpriteFrameModuleStyles
|
||||
{
|
||||
public static readonly GUIContent sliceButtonLabel = EditorGUIUtility.TrTextContent("Slice");
|
||||
public static readonly GUIContent trimButtonLabel = EditorGUIUtility.TrTextContent("Trim", "Trims selected rectangle (T)");
|
||||
}
|
||||
|
||||
// overrides for SpriteFrameModuleBase
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
base.DoMainGUI();
|
||||
DrawSpriteRectGizmos();
|
||||
DrawPotentialSpriteRectGizmos();
|
||||
|
||||
HandleGizmoMode();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
HandleRectCornerScalingHandles();
|
||||
|
||||
HandleBorderCornerScalingHandles();
|
||||
HandleBorderSidePointScalingSliders();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
HandleRectSideScalingHandles();
|
||||
|
||||
HandleBorderSideScalingHandles();
|
||||
HandlePivotHandle();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
HandleDragging();
|
||||
|
||||
spriteEditor.HandleSpriteSelection();
|
||||
|
||||
if (containsMultipleSprites)
|
||||
{
|
||||
HandleCreate();
|
||||
HandleDelete();
|
||||
HandleDuplicate();
|
||||
}
|
||||
spriteEditor.spriteRects = m_RectsCache.GetSpriteRects();
|
||||
}
|
||||
|
||||
private void DrawPotentialSpriteRectGizmos()
|
||||
{
|
||||
if (m_PotentialRects != null && m_PotentialRects.Count > 0)
|
||||
DrawRectGizmos(m_PotentialRects, Color.red);
|
||||
}
|
||||
|
||||
public override void DoToolbarGUI(Rect toolbarRect)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(!containsMultipleSprites || spriteEditor.editingDisabled || m_TextureDataProvider.GetReadableTexture2D() == null))
|
||||
{
|
||||
GUIStyle skin = EditorStyles.toolbarPopup;
|
||||
|
||||
Rect drawArea = toolbarRect;
|
||||
|
||||
drawArea.width = skin.CalcSize(SpriteFrameModuleStyles.sliceButtonLabel).x;
|
||||
SpriteUtilityWindow.DrawToolBarWidget(ref drawArea, ref toolbarRect, (adjustedDrawArea) =>
|
||||
{
|
||||
if (GUI.Button(adjustedDrawArea, SpriteFrameModuleStyles.sliceButtonLabel, skin))
|
||||
{
|
||||
if (SpriteEditorMenu.ShowAtPosition(adjustedDrawArea, this, m_TextureDataProvider))
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
});
|
||||
|
||||
using (new EditorGUI.DisabledScope(!hasSelected))
|
||||
{
|
||||
drawArea.x += drawArea.width;
|
||||
drawArea.width = skin.CalcSize(SpriteFrameModuleStyles.trimButtonLabel).x;
|
||||
SpriteUtilityWindow.DrawToolBarWidget(ref drawArea, ref toolbarRect, (adjustedDrawArea) =>
|
||||
{
|
||||
if (GUI.Button(adjustedDrawArea, SpriteFrameModuleStyles.trimButtonLabel, EditorStyles.toolbarButton))
|
||||
{
|
||||
TrimAlpha();
|
||||
Repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRectCornerScalingHandles()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
GUIStyle dragDot = styles.dragdot;
|
||||
GUIStyle dragDotActive = styles.dragdotactive;
|
||||
var color = Color.white;
|
||||
|
||||
Rect rect = new Rect(selectedSpriteRect);
|
||||
|
||||
float left = rect.xMin;
|
||||
float right = rect.xMax;
|
||||
float top = rect.yMax;
|
||||
float bottom = rect.yMin;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
HandleBorderPointSlider(ref left, ref top, MouseCursor.ResizeUpLeft, false, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref top, MouseCursor.ResizeUpRight, false, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref left, ref bottom, MouseCursor.ResizeUpRight, false, dragDot, dragDotActive, color);
|
||||
HandleBorderPointSlider(ref right, ref bottom, MouseCursor.ResizeUpLeft, false, dragDot, dragDotActive, color);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
rect.xMin = left;
|
||||
rect.xMax = right;
|
||||
rect.yMax = top;
|
||||
rect.yMin = bottom;
|
||||
ScaleSpriteRect(rect);
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRectSideScalingHandles()
|
||||
{
|
||||
if (!hasSelected)
|
||||
return;
|
||||
|
||||
Rect rect = new Rect(selectedSpriteRect);
|
||||
|
||||
float left = rect.xMin;
|
||||
float right = rect.xMax;
|
||||
float top = rect.yMax;
|
||||
float bottom = rect.yMin;
|
||||
|
||||
Vector2 screenRectTopLeft = Handles.matrix.MultiplyPoint(new Vector3(rect.xMin, rect.yMin));
|
||||
Vector2 screenRectBottomRight = Handles.matrix.MultiplyPoint(new Vector3(rect.xMax, rect.yMax));
|
||||
|
||||
float screenRectWidth = Mathf.Abs(screenRectBottomRight.x - screenRectTopLeft.x);
|
||||
float screenRectHeight = Mathf.Abs(screenRectBottomRight.y - screenRectTopLeft.y);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
left = HandleBorderScaleSlider(left, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
right = HandleBorderScaleSlider(right, rect.yMax, screenRectWidth, screenRectHeight, true);
|
||||
|
||||
top = HandleBorderScaleSlider(rect.xMin, top, screenRectWidth, screenRectHeight, false);
|
||||
bottom = HandleBorderScaleSlider(rect.xMin, bottom, screenRectWidth, screenRectHeight, false);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
rect.xMin = left;
|
||||
rect.xMax = right;
|
||||
rect.yMax = top;
|
||||
rect.yMin = bottom;
|
||||
|
||||
ScaleSpriteRect(rect);
|
||||
PopulateSpriteFrameInspectorField();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDragging()
|
||||
{
|
||||
if (hasSelected && !MouseOnTopOfInspector())
|
||||
{
|
||||
Rect textureBounds = new Rect(0, 0, textureActualWidth, textureActualHeight);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
Rect oldRect = selectedSpriteRect;
|
||||
Rect newRect = SpriteEditorUtility.ClampedRect(SpriteEditorUtility.RoundedRect(SpriteEditorHandles.SliderRect(oldRect)), textureBounds, true);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
selectedSpriteRect = newRect;
|
||||
UpdatePositionField(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCreate()
|
||||
{
|
||||
if (!MouseOnTopOfInspector() && !eventSystem.current.alt)
|
||||
{
|
||||
// Create new rects via dragging in empty space
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Rect newRect = SpriteEditorHandles.RectCreator(textureActualWidth, textureActualHeight, styles.createRect);
|
||||
if (EditorGUI.EndChangeCheck() && newRect.width > 0f && newRect.height > 0f)
|
||||
{
|
||||
CreateSprite(newRect);
|
||||
GUIUtility.keyboardControl = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDuplicate()
|
||||
{
|
||||
IEvent evt = eventSystem.current;
|
||||
if ((evt.type == EventType.ValidateCommand || evt.type == EventType.ExecuteCommand)
|
||||
&& evt.commandName == EventCommandNames.Duplicate)
|
||||
{
|
||||
if (evt.type == EventType.ExecuteCommand)
|
||||
DuplicateSprite();
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDelete()
|
||||
{
|
||||
IEvent evt = eventSystem.current;
|
||||
|
||||
if ((evt.type == EventType.ValidateCommand || evt.type == EventType.ExecuteCommand)
|
||||
&& (evt.commandName == EventCommandNames.SoftDelete || evt.commandName == EventCommandNames.Delete))
|
||||
{
|
||||
if (evt.type == EventType.ExecuteCommand && hasSelected)
|
||||
DeleteSprite();
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
[RequireSpriteDataProvider(typeof(ISpriteOutlineDataProvider), typeof(ITextureDataProvider))]
|
||||
internal partial class SpritePolygonModeModule : SpriteFrameModuleBase
|
||||
{
|
||||
List<List<Vector2[]>> m_Outline;
|
||||
|
||||
public SpritePolygonModeModule(ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad) :
|
||||
base("Sprite Polygon Mode Editor", sw, es, us, ad)
|
||||
{}
|
||||
|
||||
// ISpriteEditorModule implemenation
|
||||
public override void OnModuleActivate()
|
||||
{
|
||||
base.OnModuleActivate();
|
||||
AddMainUI(spriteEditor.GetMainVisualContainer());
|
||||
m_Outline = new List<List<Vector2[]>>();
|
||||
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count; ++i)
|
||||
{
|
||||
var rect = m_RectsCache.spriteRects[i];
|
||||
m_Outline.Add(spriteEditor.GetDataProvider<ISpriteOutlineDataProvider>().GetOutlines(rect.spriteID));
|
||||
}
|
||||
|
||||
showChangeShapeWindow = polygonSprite;
|
||||
if (polygonSprite)
|
||||
DeterminePolygonSides();
|
||||
}
|
||||
|
||||
public override bool CanBeActivated()
|
||||
{
|
||||
return SpriteFrameModule.GetSpriteImportMode(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>()) == SpriteImportMode.Polygon;
|
||||
}
|
||||
|
||||
private bool polygonSprite
|
||||
{
|
||||
get { return spriteImportMode == SpriteImportMode.Polygon; }
|
||||
}
|
||||
|
||||
private void DeterminePolygonSides()
|
||||
{
|
||||
if (polygonSprite && m_RectsCache.spriteRects.Count == 1 && m_Outline.Count == 1 && m_Outline[0].Count == 1)
|
||||
{
|
||||
polygonSides = m_Outline[0][0].Length;
|
||||
}
|
||||
else
|
||||
// If for reasons we cannot determine the sides of the polygon, fall back to 0 (Square)
|
||||
polygonSides = 0;
|
||||
ViewUpdateSideCountField();
|
||||
}
|
||||
|
||||
public int GetPolygonSideCount()
|
||||
{
|
||||
DeterminePolygonSides();
|
||||
return polygonSides;
|
||||
}
|
||||
|
||||
public int polygonSides
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public List<Vector2[]> GetSpriteOutlineAt(int i)
|
||||
{
|
||||
return m_Outline[i];
|
||||
}
|
||||
|
||||
public void GeneratePolygonOutline()
|
||||
{
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count; i++)
|
||||
{
|
||||
SpriteRect currentRect = m_RectsCache.spriteRects[i];
|
||||
|
||||
var result = UnityEditor.Sprites.SpriteUtility.GeneratePolygonOutlineVerticesOfSize(polygonSides, (int)currentRect.rect.width, (int)currentRect.rect.height);
|
||||
|
||||
m_Outline.Clear();
|
||||
var newOutlineList = new List<Vector2[]>();
|
||||
newOutlineList.Add(result);
|
||||
m_Outline.Add(newOutlineList);
|
||||
|
||||
spriteEditor.SetDataModified();
|
||||
}
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public override bool ApplyRevert(bool apply)
|
||||
{
|
||||
var outlineProvider = spriteEditor.GetDataProvider<ISpriteOutlineDataProvider>();
|
||||
if (apply)
|
||||
{
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count && i < m_Outline.Count; ++i)
|
||||
outlineProvider.SetOutlines(m_RectsCache.spriteRects[i].spriteID, m_Outline[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Outline.Clear();
|
||||
for (int i = 0; i < m_RectsCache.spriteRects.Count; ++i)
|
||||
m_Outline.Add(outlineProvider.GetOutlines(m_RectsCache.spriteRects[i].spriteID));
|
||||
DeterminePolygonSides();
|
||||
ViewUpdateSideCountField();
|
||||
}
|
||||
return base.ApplyRevert(apply);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UIElementButton = UnityEngine.UIElements.Button;
|
||||
|
||||
namespace UnityEditor.U2D.Sprites
|
||||
{
|
||||
internal partial class SpritePolygonModeModule : SpriteFrameModuleBase
|
||||
{
|
||||
private static class SpritePolygonModeStyles
|
||||
{
|
||||
public static readonly GUIContent changeShapeLabel = EditorGUIUtility.TrTextContent("Change Shape");
|
||||
}
|
||||
|
||||
private VisualElement m_PolygonShapeView;
|
||||
private UIElementButton m_ChangeButton;
|
||||
private VisualElement m_WarningMessage;
|
||||
|
||||
// overrides for SpriteFrameModuleViewBase
|
||||
private void AddMainUI(VisualElement element)
|
||||
{
|
||||
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Packages/com.unity.2d.sprite/Editor/UI/SpriteEditor/PolygonChangeShapeWindow.uxml") as VisualTreeAsset;
|
||||
m_PolygonShapeView = visualTree.CloneTree().Q<VisualElement>("polygonShapeWindow");
|
||||
m_PolygonShapeView.RegisterCallback<MouseDownEvent>((e) => { e.StopPropagation(); });
|
||||
m_PolygonShapeView.RegisterCallback<MouseUpEvent>((e) => { e.StopPropagation(); });
|
||||
SetupPolygonChangeShapeWindowElements(m_PolygonShapeView);
|
||||
element.Add(m_PolygonShapeView);
|
||||
}
|
||||
|
||||
public override void DoMainGUI()
|
||||
{
|
||||
base.DoMainGUI();
|
||||
DrawGizmos();
|
||||
|
||||
HandleGizmoMode();
|
||||
|
||||
HandleBorderCornerScalingHandles();
|
||||
HandleBorderSidePointScalingSliders();
|
||||
|
||||
HandleBorderSideScalingHandles();
|
||||
HandlePivotHandle();
|
||||
|
||||
if (!MouseOnTopOfInspector())
|
||||
spriteEditor.HandleSpriteSelection();
|
||||
}
|
||||
|
||||
public override void DoToolbarGUI(Rect toolbarRect)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(spriteEditor.editingDisabled))
|
||||
{
|
||||
GUIStyle skin = EditorStyles.toolbarPopup;
|
||||
Rect drawArea = toolbarRect;
|
||||
drawArea.width = skin.CalcSize(SpritePolygonModeStyles.changeShapeLabel).x;
|
||||
SpriteUtilityWindow.DrawToolBarWidget(ref drawArea, ref toolbarRect, (adjustedDrawArea) =>
|
||||
{
|
||||
showChangeShapeWindow = GUI.Toggle(adjustedDrawArea, showChangeShapeWindow, SpritePolygonModeStyles.changeShapeLabel, EditorStyles.toolbarButton);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGizmos()
|
||||
{
|
||||
if (eventSystem.current.type != EventType.Repaint)
|
||||
return;
|
||||
for (int i = 0; i < spriteCount; i++)
|
||||
{
|
||||
List<Vector2[]> outline = GetSpriteOutlineAt(i);
|
||||
Vector2 offset = GetSpriteRectAt(i).size * 0.5f;
|
||||
if (outline.Count > 0)
|
||||
{
|
||||
SpriteEditorUtility.BeginLines(new Color(0.75f, 0.75f, 0.75f, 0.75f));
|
||||
for (int j = 0; j < outline.Count; ++j)
|
||||
{
|
||||
for (int k = 0, last = outline[j].Length - 1; k < outline[j].Length; last = k, ++k)
|
||||
SpriteEditorUtility.DrawLine(outline[j][last] + offset, outline[j][k] + offset);
|
||||
}
|
||||
SpriteEditorUtility.EndLines();
|
||||
}
|
||||
}
|
||||
DrawSpriteRectGizmos();
|
||||
}
|
||||
|
||||
private void ViewUpdateSideCountField()
|
||||
{
|
||||
var sidesField = m_PolygonShapeView.Q<IntegerField>("labelIntegerField");
|
||||
sidesField.value = polygonSides;
|
||||
}
|
||||
|
||||
private void SetupPolygonChangeShapeWindowElements(VisualElement moduleView)
|
||||
{
|
||||
var sidesField = moduleView.Q<IntegerField>("labelIntegerField");
|
||||
sidesField.SetValueWithoutNotify(polygonSides);
|
||||
sidesField.RegisterValueChangedCallback((evt) =>
|
||||
{
|
||||
polygonSides = (int)evt.newValue;
|
||||
ShowHideWarningMessage();
|
||||
});
|
||||
m_ChangeButton = moduleView.Q<UIElementButton>("changeButton");
|
||||
m_ChangeButton.RegisterCallback<MouseUpEvent>((e) =>
|
||||
{
|
||||
if (isSidesValid)
|
||||
{
|
||||
GeneratePolygonOutline();
|
||||
showChangeShapeWindow = false;
|
||||
}
|
||||
});
|
||||
m_WarningMessage = moduleView.Q("warning");
|
||||
ShowHideWarningMessage();
|
||||
}
|
||||
|
||||
void ShowHideWarningMessage()
|
||||
{
|
||||
m_WarningMessage.style.display = !isSidesValid ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
m_WarningMessage.style.position = m_WarningMessage.style.display == DisplayStyle.Flex ? Position.Relative : Position.Absolute;
|
||||
|
||||
m_ChangeButton.style.display = isSidesValid ? DisplayStyle.Flex : DisplayStyle.None;;
|
||||
m_ChangeButton.style.position = m_ChangeButton.style.display == DisplayStyle.Flex ? Position.Relative : Position.Absolute;
|
||||
}
|
||||
|
||||
private bool isSidesValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return polygonSides == 0 || (polygonSides >= 3 && polygonSides <= 128);
|
||||
}
|
||||
}
|
||||
|
||||
public bool showChangeShapeWindow
|
||||
{
|
||||
get { return m_PolygonShapeView.style.display == DisplayStyle.Flex; }
|
||||
set
|
||||
{
|
||||
var displayValue = value ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
if (m_PolygonShapeView.style.display == displayValue)
|
||||
return;
|
||||
m_PolygonShapeView.style.display = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user