using System; using UnityEditor.U2D.Layout; using UnityEditor.U2D.Common; using UnityEngine; using UnityEngine.UIElements; namespace UnityEditor.U2D.Animation { internal class PivotInspectorPanel : VisualElement { EnumField m_PivotAlignment; Vector2Field m_PivotPosition; public class CustomUxmlFactory : UxmlFactory { } internal static PivotInspectorPanel CreateFromUxml() { var visualTree = ResourceLoader.Load("SkinningModule/PivotInspectorPanel.uxml"); var ve = (PivotInspectorPanel)visualTree.CloneTree().Q("PivotInspectorPanel"); ve.styleSheets.Add(ResourceLoader.Load("SkinningModule/PivotInspectorPanelStyle.uss")); if (EditorGUIUtility.isProSkin) ve.AddToClassList("Dark"); ve.LocalizeTextInChildren(); ve.BindElements(); return ve; } private void BindElements() { m_PivotPosition = this.Q("PivotPositionField"); m_PivotAlignment = this.Q("pivotField"); m_PivotAlignment.Init(SpriteAlignment.Center); m_PivotAlignment.label = TextContent.pivot; } public EnumField pivotAlignment { get => m_PivotAlignment; set => m_PivotAlignment = value; } public Vector2Field pivotPosition { get => m_PivotPosition; set => m_PivotPosition = value; } } internal class PivotTool : SkeletonToolWrapper { static class Styles { public static GUIStyle pivotdotactive = "U2D.pivotDotActive"; public static GUIStyle pivotdot = "U2D.pivotDot"; } Vector2 m_Pivot = Vector2.zero; Vector2 m_CurrentMousePosition; Vector2 m_DragScreenOffset; Vector2 m_DragStartScreenPosition; SpriteCache m_LastSelectedSprite; PivotInspectorPanel m_InspectorPanel; int m_SlideHashCode = "PivotTool_Slider1D".GetHashCode(); readonly Rect k_PivotNormalizedRect = Rect.MinMaxRect(0, 0, 1, 1); Rect m_PivotRect = Rect.zero; static bool CanSelectWhileInPivotTool() => false; public override void Initialize(LayoutOverlay layout) { base.Initialize(layout); m_InspectorPanel = PivotInspectorPanel.CreateFromUxml(); layout.rightOverlay.Add(m_InspectorPanel); m_InspectorPanel.SetHiddenFromLayout(true); m_InspectorPanel.pivotAlignment.RegisterValueChangedCallback(PivotAlignmentValueChange); m_InspectorPanel.pivotPosition.RegisterValueChangedCallback(PivotPositionValueChange); } void PivotAlignmentValueChange(ChangeEvent evt) { m_Pivot = GetPivotPoint((SpriteAlignment)m_InspectorPanel.pivotAlignment.value, m_Pivot); m_InspectorPanel.pivotPosition.SetValueWithoutNotify(m_Pivot); UpdateCharacterPivot(); } void PivotPositionValueChange(ChangeEvent evt) { m_Pivot = m_InspectorPanel.pivotPosition.value; m_InspectorPanel.pivotAlignment.SetValueWithoutNotify(SpriteAlignment.Custom); UpdateCharacterPivot(); } protected override void OnGUI() { base.OnGUI(); var pivot = PivotSlider(m_PivotRect, m_Pivot, Styles.pivotdot, Styles.pivotdotactive); if (m_Pivot != pivot) { UpdateViewFields(); m_Pivot = pivot; UpdateCharacterPivot(); } } void UpdateCharacterPivot() { using (skinningCache.UndoScope(TextContent.pivotChanged)) { skinningCache.character.pivot = m_Pivot; skinningCache.events.pivotChange.Invoke(); } } protected override void OnActivate() { if (skinningCache.hasCharacter) { base.OnActivate(); m_PivotRect = new Rect(0, 0, skinningCache.character.dimension.x, skinningCache.character.dimension.y); m_LastSelectedSprite = skinningCache.selectedSprite; m_InspectorPanel.SetHiddenFromLayout(false); m_Pivot = skinningCache.character.pivot; UpdateViewFields(); skinningCache.selectionTool.CanSelect += CanSelectWhileInPivotTool; skinningCache.selectedSprite = null; } else { m_InspectorPanel.SetHiddenFromLayout(true); } } void UpdateViewFields() { SpriteAlignment alignment; TranslatePivotPoint(m_Pivot, out alignment); m_InspectorPanel.pivotAlignment.SetValueWithoutNotify(alignment); m_InspectorPanel.pivotPosition.SetValueWithoutNotify(m_Pivot); } protected override void OnDeactivate() { if (skinningCache.hasCharacter) { base.OnDeactivate(); m_InspectorPanel.SetHiddenFromLayout(true); skinningCache.selectionTool.CanSelect -= CanSelectWhileInPivotTool; if (isActive) skinningCache.selectedSprite = m_LastSelectedSprite; } } void TranslatePivotPoint(Vector2 pivot, out SpriteAlignment alignment) { if (new Vector2(k_PivotNormalizedRect.xMin, k_PivotNormalizedRect.yMax) == pivot) alignment = SpriteAlignment.TopLeft; else if(new Vector2(k_PivotNormalizedRect.center.x, k_PivotNormalizedRect.yMax) == pivot) alignment = SpriteAlignment.TopCenter; else if(new Vector2(k_PivotNormalizedRect.xMax, k_PivotNormalizedRect.yMax) == pivot) alignment = SpriteAlignment.TopRight; else if(new Vector2(k_PivotNormalizedRect.xMin, k_PivotNormalizedRect.center.y) == pivot) alignment = SpriteAlignment.LeftCenter; else if(new Vector2(k_PivotNormalizedRect.center.x, k_PivotNormalizedRect.center.y) == pivot) alignment = SpriteAlignment.Center; else if(new Vector2(k_PivotNormalizedRect.xMax, k_PivotNormalizedRect.center.y) == pivot) alignment = SpriteAlignment.RightCenter; else if(new Vector2(k_PivotNormalizedRect.xMin, k_PivotNormalizedRect.yMin) == pivot) alignment = SpriteAlignment.BottomLeft; else if(new Vector2(k_PivotNormalizedRect.center.x, k_PivotNormalizedRect.yMin) == pivot) alignment = SpriteAlignment.BottomCenter; else if(new Vector2(k_PivotNormalizedRect.xMax, k_PivotNormalizedRect.yMin) == pivot) alignment = SpriteAlignment.BottomRight; else alignment = SpriteAlignment.Custom; } Vector2 GetPivotPoint(SpriteAlignment alignment, Vector2 customPivot) { switch (alignment) { case SpriteAlignment.TopLeft: return new Vector2(k_PivotNormalizedRect.xMin, k_PivotNormalizedRect.yMax); case SpriteAlignment.TopCenter: return new Vector2(k_PivotNormalizedRect.center.x, k_PivotNormalizedRect.yMax); case SpriteAlignment.TopRight: return new Vector2(k_PivotNormalizedRect.xMax, k_PivotNormalizedRect.yMax); case SpriteAlignment.LeftCenter: return new Vector2(k_PivotNormalizedRect.xMin, k_PivotNormalizedRect.center.y); case SpriteAlignment.Center: return new Vector2(k_PivotNormalizedRect.center.x, k_PivotNormalizedRect.center.y); case SpriteAlignment.RightCenter: return new Vector2(k_PivotNormalizedRect.xMax, k_PivotNormalizedRect.center.y); case SpriteAlignment.BottomLeft: return new Vector2(k_PivotNormalizedRect.xMin, k_PivotNormalizedRect.yMin); case SpriteAlignment.BottomCenter: return new Vector2(k_PivotNormalizedRect.center.x, k_PivotNormalizedRect.yMin); case SpriteAlignment.BottomRight: return new Vector2(k_PivotNormalizedRect.xMax, k_PivotNormalizedRect.yMin); case SpriteAlignment.Custom: return new Vector2(customPivot.x * k_PivotNormalizedRect.width, customPivot.y * k_PivotNormalizedRect.height); } return Vector2.zero; } Vector2 PivotSlider(Rect sprite, Vector2 pos, GUIStyle pivotDot, GUIStyle pivotDotActive) { int id = GUIUtility.GetControlID(m_SlideHashCode, FocusType.Keyboard); // Convert from normalized space to texture space pos = new Vector2(sprite.xMin + sprite.width * pos.x, sprite.yMin + sprite.height * pos.y); Vector2 screenVal = Handles.matrix.MultiplyPoint(pos); Rect handleScreenPos = new Rect( screenVal.x - pivotDot.fixedWidth * .5f, screenVal.y - pivotDot.fixedHeight * .5f, pivotDotActive.fixedWidth, pivotDotActive.fixedHeight ); var evt = Event.current; switch (evt.GetTypeForControl(id)) { case EventType.MouseDown: // am I closest to the thingy? if (evt.button == 0 && handleScreenPos.Contains(Event.current.mousePosition) && !evt.alt) { GUIUtility.hotControl = GUIUtility.keyboardControl = id; // Grab mouse focus m_CurrentMousePosition = evt.mousePosition; m_DragStartScreenPosition = evt.mousePosition; Vector2 rectScreenCenter = Handles.matrix.MultiplyPoint(pos); m_DragScreenOffset = m_CurrentMousePosition - rectScreenCenter; evt.Use(); EditorGUIUtility.SetWantsMouseJumping(1); } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { m_CurrentMousePosition += evt.delta; Vector2 oldPos = pos; Vector3 scrPos = Handles.inverseMatrix.MultiplyPoint(m_CurrentMousePosition - m_DragScreenOffset); pos = new Vector2(scrPos.x, scrPos.y); if (!Mathf.Approximately((oldPos - pos).magnitude, 0f)) GUI.changed = true; evt.Use(); } break; case EventType.MouseUp: if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2)) { GUIUtility.hotControl = 0; evt.Use(); EditorGUIUtility.SetWantsMouseJumping(0); } break; case EventType.KeyDown: if (GUIUtility.hotControl == id) { if (evt.keyCode == KeyCode.Escape) { pos = Handles.inverseMatrix.MultiplyPoint(m_DragStartScreenPosition - m_DragScreenOffset); GUIUtility.hotControl = 0; GUI.changed = true; evt.Use(); } } break; case EventType.Repaint: EditorGUIUtility.AddCursorRect(handleScreenPos, MouseCursor.Arrow, id); if (GUIUtility.hotControl == id) pivotDotActive.Draw(handleScreenPos, GUIContent.none, id); else pivotDot.Draw(handleScreenPos, GUIContent.none, id); break; } // Convert from texture space back to normalized space pos = new Vector2((pos.x - sprite.xMin) / sprite.width, (pos.y - sprite.yMin) / sprite.height); return pos; } } }