A game about forced loneliness, made by TACStudios
1using System;
2using UnityEngine;
3using System.Collections.Generic;
4using System.IO;
5using UnityTexture2D = UnityEngine.Texture2D;
6using System.Linq;
7using System.Reflection;
8using UnityEditor.Overlays;
9using UnityEngine.UIElements;
10
11namespace UnityEditor.U2D.Sprites
12{
13 /// <summary>
14 /// Interface for providing a ISpriteEditorDataProvider instance.
15 /// </summary>
16 /// <typeparam name="T">The object type the implemented interface is interested in.</typeparam>
17 public interface ISpriteDataProviderFactory<T>
18 {
19 /// <summary>
20 /// Implement the method to provide an instance of ISpriteEditorDataProvider for a given object.
21 /// </summary>
22 /// <param name="obj">The object that requires an instance of ISpriteEditorDataProvider.</param>
23 /// <returns>An instance of ISpriteEditorDataProvider or null if not supported by the interface.</returns>
24 ISpriteEditorDataProvider CreateDataProvider(T obj);
25 }
26
27 [AttributeUsage(AttributeTargets.Method)]
28 internal class SpriteEditorAssetPathProviderAttribute : Attribute
29 {
30 [RequiredSignature]
31 private static string GetAssetPath(UnityEngine.Object obj)
32 {
33 return null;
34 }
35 }
36
37 [AttributeUsage(AttributeTargets.Method)]
38 internal class SpriteObjectProviderAttribute : Attribute
39 {
40 [RequiredSignature]
41 private static Sprite GetSpriteObject(UnityEngine.Object obj)
42 {
43 return null;
44 }
45 }
46
47 /// <summary>
48 /// Utility class that collects methods with SpriteDataProviderFactoryAttribute and SpriteDataProviderAssetPathProviderAttribute.
49 /// </summary>
50 public class SpriteDataProviderFactories
51 {
52 struct SpriteDataProviderFactory
53 {
54 public object instance;
55 public MethodInfo method;
56 public Type methodType;
57 }
58
59 static SpriteDataProviderFactory[] s_Factories;
60 static TypeCache.MethodCollection s_AssetPathProvider;
61 static TypeCache.MethodCollection s_SpriteObjectProvider;
62
63 static SpriteDataProviderFactory[] GetFactories()
64 {
65 CacheDataProviders();
66 return s_Factories;
67 }
68
69 static TypeCache.MethodCollection GetAssetPathProvider()
70 {
71 CacheDataProviders();
72 return s_AssetPathProvider;
73 }
74
75 static TypeCache.MethodCollection GetSpriteObjectProvider()
76 {
77 CacheDataProviders();
78 return s_SpriteObjectProvider;
79 }
80
81 static void CacheDataProviders()
82 {
83 if (s_Factories != null)
84 return;
85
86 var factories = TypeCache.GetTypesDerivedFrom(typeof(ISpriteDataProviderFactory<>));
87 var factoryList = new List<SpriteDataProviderFactory>();
88 foreach (var factory in factories)
89 {
90 try
91 {
92 var ins = Activator.CreateInstance(factory);
93 foreach (var i in factory.GetInterfaces())
94 {
95 var genericArguments = i.GetGenericArguments();
96 if (genericArguments.Length == 1)
97 {
98 var s = new SpriteDataProviderFactory();
99 s.instance = ins;
100 var method = i.GetMethod("CreateDataProvider");
101 s.method = method;
102 s.methodType = genericArguments[0];
103 factoryList.Add(s);
104 }
105 }
106 }
107 catch (Exception ex)
108 {
109 Debug.LogAssertion(ex);
110 }
111 }
112 s_Factories = factoryList.ToArray();
113 s_AssetPathProvider = TypeCache.GetMethodsWithAttribute<SpriteEditorAssetPathProviderAttribute>();
114 s_SpriteObjectProvider = TypeCache.GetMethodsWithAttribute<SpriteObjectProviderAttribute>();
115 }
116
117 /// <summary>
118 /// Initialized and collect methods with SpriteDataProviderFactoryAttribute and SpriteDataProviderAssetPathProviderAttribute.
119 /// </summary>
120 public void Init()
121 {
122 CacheDataProviders();
123 }
124
125 /// <summary>
126 /// Given a UnityEngine.Object, determine the ISpriteEditorDataProvider associate with the object by going
127 /// going through the methods with SpriteDataProviderFactoryAttribute.
128 /// </summary>
129 /// <remarks>When none of the methods is able to provide ISpriteEditorDataProvider for the object, the method will
130 /// try to cast the AssetImporter of the object to ISpriteEditorDataProvider.</remarks>
131 /// <param name="obj">The UnityEngine.Object to query.</param>
132 /// <returns>The ISpriteEditorDataProvider associated with the object.</returns>
133 public ISpriteEditorDataProvider GetSpriteEditorDataProviderFromObject(UnityEngine.Object obj)
134 {
135 if (obj != null)
136 {
137 var objType = obj.GetType();
138 foreach (var factory in GetFactories())
139 {
140 try
141 {
142 if (factory.methodType == objType)
143 {
144 var dataProvider = factory.method.Invoke(factory.instance, new[] { obj }) as ISpriteEditorDataProvider;
145 if (dataProvider != null && !dataProvider.Equals(null))
146 return dataProvider;
147 }
148 }
149 catch (Exception ex)
150 {
151 Debug.LogAssertion(ex);
152 }
153 }
154
155 if (obj is ISpriteEditorDataProvider)
156 return (ISpriteEditorDataProvider)obj;
157 // now we try the importer
158 var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(obj));
159 return importer as ISpriteEditorDataProvider;
160 }
161
162 return null;
163 }
164
165 /// <summary>
166 /// Given a UnityEngine.Object, determine the asset path associate with the object by going
167 /// going through the methods with SpriteDataProviderAssetPathProviderAttribute.
168 /// </summary>
169 /// <remarks>When none of the methods is able to provide the asset path for the object, the method will return null</remarks>
170 /// <param name="obj">The UnityEngine.Object to query</param>
171 /// <returns>The asset path for the object</returns>
172 internal string GetAssetPath(UnityEngine.Object obj)
173 {
174 foreach (var assetPathProvider in GetAssetPathProvider())
175 {
176 try
177 {
178 var path = assetPathProvider.Invoke(null, new object[] { obj }) as string;
179 if (!string.IsNullOrEmpty(path))
180 return path;
181 }
182 catch (Exception ex)
183 {
184 Debug.LogException(ex);
185 }
186 }
187 return null;
188 }
189
190 /// <summary>
191 /// Given a UnityEngine.Object, determine the Sprite object associate with the object by going
192 /// going through the methods with SpriteObjectProviderAttribute.
193 /// </summary>
194 /// <remarks>When none of the methods is able to provide a Sprite object, the method will return null</remarks>
195 /// <param name="obj">The UnityEngine.Object to query</param>
196 /// <returns>The Sprite object</returns>
197 internal Sprite GetSpriteObject(UnityEngine.Object obj)
198 {
199 if (obj is Sprite)
200 return (Sprite)obj;
201 foreach (var spriteObjectProvider in GetSpriteObjectProvider())
202 {
203 try
204 {
205 var sprite = spriteObjectProvider.Invoke(null, new object[] { obj }) as Sprite;
206 if (sprite != null && !sprite.Equals(null))
207 return sprite;
208 }
209 catch (Exception ex)
210 {
211 Debug.LogException(ex);
212 }
213 }
214 return null;
215 }
216 }
217
218 [InitializeOnLoad]
219 internal class SpriteEditorWindow : SpriteUtilityWindow, ISpriteEditor, ISupportsOverlays
220 {
221 static SpriteEditorWindow()
222 {
223 UnityEditor.SpriteUtilityWindow.SetShowSpriteEditorWindowWithObject((x) =>
224 {
225 SpriteEditorWindow.GetWindow(x);
226 return true;
227 });
228 }
229
230 private class SpriteEditorWindowStyles
231 {
232 public static readonly GUIContent editingDisableMessageBecausePlaymodeLabel = EditorGUIUtility.TrTextContent("Editing is disabled during play mode");
233 public static readonly GUIContent editingDisableMessageBecauseNonEditableLabel = EditorGUIUtility.TrTextContent("Editing is disabled because the asset is not editable.");
234 public static readonly GUIContent revertButtonLabel = EditorGUIUtility.TrTextContent("Revert");
235 public static readonly GUIContent applyButtonLabel = EditorGUIUtility.TrTextContent("Apply");
236
237 public static readonly GUIContent pendingChangesDialogContent = EditorGUIUtility.TrTextContent("The asset was modified outside of Sprite Editor Window.\nDo you want to apply pending changes?");
238
239 public static readonly GUIContent applyRevertDialogTitle = EditorGUIUtility.TrTextContent("Sprite Editor Window");
240 public static readonly GUIContent applyRevertDialogContent = EditorGUIUtility.TrTextContent("'{0}' has unapplied changes.\nDo you want to apply or revert them?");
241
242 public static readonly GUIContent noSelectionWarning = EditorGUIUtility.TrTextContent("No Texture or Sprite selected.\nPlease select one from your project to start.");
243 public static readonly GUIContent selectionNotEditableBySpriteEditor = EditorGUIUtility.TrTextContent("Selected object is not supported by Sprite Editor Window.");
244 public static readonly GUIContent noModuleWarning = EditorGUIUtility.TrTextContent("No Sprite Editor module available");
245 public static readonly GUIContent applyRevertModuleDialogTitle = EditorGUIUtility.TrTextContent("Sprite Editor Window");
246 public static readonly GUIContent applyRevertModuleDialogContent = EditorGUIUtility.TrTextContent("You have unapplied changes from the current module. Do you want to apply or revert them?");
247
248 public static readonly GUIContent revertConfirmationDialogTitle = EditorGUIUtility.TrTextContent("Revert Changes");
249 public static readonly GUIContent revertConfirmationDialogContent = EditorGUIUtility.TrTextContent("Are you sure you want to revert the changes?");
250 public static readonly GUIContent applyConfirmationDialogTitle = EditorGUIUtility.TrTextContent("Apply Changes");
251 public static readonly GUIContent applyConfirmationDialogContent = EditorGUIUtility.TrTextContent("Are you sure you want to apply the changes?");
252 public static readonly GUIContent yesLabel = EditorGUIUtility.TrTextContent("Yes");
253 public static readonly GUIContent noLabel = EditorGUIUtility.TrTextContent("No");
254 public static readonly string styleSheetPath = "Packages/com.unity.2d.sprite/Editor/UI/SpriteEditor/SpriteEditor.uss";
255 public static readonly string toolBarStyleSheetPath = "Packages/com.unity.2d.sprite/Editor/UI/SpriteEditor/SpriteEditorToolbar.uss";
256 }
257
258 class CurrentResetContext
259 {
260 public string assetPath;
261 }
262
263 private const float k_MarginForFraming = 0.05f;
264 private const float k_WarningMessageWidth = 250f;
265 private const float k_WarningMessageHeight = 40f;
266 private const float k_ModuleListWidth = 90f;
267 private const string k_RefreshOnNextRepaintCommandEvent = "RefreshOnNextRepaintCommand";
268 bool m_ResetOnNextRepaint;
269 bool m_ResetCommandSent;
270
271 private List<SpriteRect> m_RectsCache;
272 ISpriteEditorDataProvider m_SpriteDataProvider;
273
274 private bool m_RequestRepaint = false;
275
276 public static bool s_OneClickDragStarted = false;
277 string m_SelectedAssetPath;
278 bool m_AssetNotEditable;
279
280 private IEventSystem m_EventSystem;
281 private IUndoSystem m_UndoSystem;
282 private IAssetDatabase m_AssetDatabase;
283 private IGUIUtility m_GUIUtility;
284 private UnityTexture2D m_OutlineTexture;
285 private UnityTexture2D m_ReadableTexture;
286 private Dictionary<Type, RequireSpriteDataProviderAttribute> m_ModuleRequireSpriteDataProvider = new ();
287 private Dictionary<Type, List<Type>> m_ModuleMode = new ();
288
289 VisualElement m_ToolbarContainer;
290 VisualElement m_ModuleToolbarContainer;
291 VisualElement m_AlphaZoomToolbarElement;
292 VisualElement m_ModuleDropDownUI;
293 private IMGUIContainer m_ModuleToolbarIMGUIElement;
294 private IMGUIContainer m_MainViewIMGUIElement;
295 private VisualElement m_ModuleViewElement;
296 private VisualElement m_MainViewElement;
297 SpriteDataProviderFactories m_SpriteDataProviderFactories;
298
299 [SerializeField]
300 private UnityEngine.Object m_SelectedObject;
301
302 [SerializeField]
303 private string m_SelectedSpriteRectGUID;
304
305 internal Func<string, string, bool> onHandleApplyRevertDialog = ShowHandleApplyRevertDialog;
306
307 private CurrentResetContext m_CurrentResetContext = null;
308
309 EditorGUIUtility.EditorLockTracker m_LockTracker = new EditorGUIUtility.EditorLockTracker();
310
311 public static void GetWindow(UnityEngine.Object obj)
312 {
313 var window = EditorWindow.GetWindow<SpriteEditorWindow>();
314 window.selectedObject = obj;
315 }
316
317 public SpriteEditorWindow()
318 {
319 m_EventSystem = new EventSystem();
320 m_UndoSystem = new UndoSystem();
321 m_AssetDatabase = new AssetDatabaseSystem();
322 m_GUIUtility = new GUIUtilitySystem();
323 }
324
325 void ModifierKeysChanged()
326 {
327 if (EditorWindow.focusedWindow == this)
328 {
329 Repaint();
330 }
331 }
332
333 private void OnFocus()
334 {
335 if (selectedObject != Selection.activeObject)
336 OnSelectionChange();
337 if (selectedProviderChanged)
338 RefreshSpriteEditorWindow();
339 }
340
341 internal UnityEngine.Object selectedObject
342 {
343 get { return m_SelectedObject; }
344 set
345 {
346 m_SelectedObject = value;
347 RefreshSpriteEditorWindow();
348 }
349 }
350
351 string selectedAssetPath
352 {
353 get => m_SelectedAssetPath;
354 set
355 {
356 m_SelectedAssetPath = value;
357 m_AssetNotEditable = !AssetDatabase.IsOpenForEdit(m_SelectedAssetPath);
358 }
359 }
360
361 public void RefreshPropertiesCache()
362 {
363 Undo.ClearUndo(this);
364 m_Texture = null;
365 var obj = AssetDatabase.LoadMainAssetAtPath(selectedAssetPath);
366 m_SpriteDataProvider = spriteDataProviderFactories.GetSpriteEditorDataProviderFromObject(obj);
367 if (!IsSpriteDataProviderValid())
368 {
369 selectedAssetPath = "";
370 var s = m_SpriteDataProviderFactories.GetSpriteObject(Selection.activeObject);
371 if(s != null)
372 {
373 var t = SpriteInspector.BuildPreviewTexture(s, null, false, (int)s.rect.width, (int)s.rect.height);
374 SetPreviewTexture(t, t.width, t.height);
375 }
376 return;
377 }
378
379
380 m_SpriteDataProvider.InitSpriteEditorDataProvider();
381
382 var textureProvider = m_SpriteDataProvider.GetDataProvider<ITextureDataProvider>();
383 if (textureProvider != null)
384 {
385 int width = 0, height = 0;
386 textureProvider.GetTextureActualWidthAndHeight(out width, out height);
387 m_Texture = textureProvider.previewTexture == null ? null : new PreviewTexture2D(textureProvider.previewTexture, width, height);
388 }
389 }
390
391 internal string GetSelectionAssetPath()
392 {
393 var path = spriteDataProviderFactories.GetAssetPath(selectedObject);
394 if (string.IsNullOrEmpty(path))
395 path = m_AssetDatabase.GetAssetPath(selectedObject);
396 return path;
397 }
398
399 public void InvalidatePropertiesCache()
400 {
401 spriteRects = null;
402 m_SpriteDataProvider = null;
403 }
404
405 private Rect spriteEditorMessageRect
406 {
407 get
408 {
409 return new Rect(
410 k_InspectorWindowMargin,
411 k_InspectorWindowMargin,
412 k_WarningMessageWidth,
413 k_WarningMessageHeight);
414 }
415 }
416
417 public SpriteImportMode spriteImportMode
418 {
419 get { return !IsSpriteDataProviderValid() ? SpriteImportMode.None : m_SpriteDataProvider.spriteImportMode; }
420 }
421
422 bool activeDataProviderSelected
423 {
424 get { return m_SpriteDataProvider != null; }
425 }
426
427 public bool textureIsDirty
428 {
429 get
430 {
431 return hasUnsavedChanges;
432 }
433 set
434 {
435 hasUnsavedChanges = value;
436 }
437 }
438
439 public bool selectedProviderChanged
440 {
441 get
442 {
443 var assetPath = GetSelectionAssetPath();
444 var dataProvider = spriteDataProviderFactories.GetSpriteEditorDataProviderFromObject(selectedObject);
445 return dataProvider != null && selectedAssetPath != assetPath;
446 }
447 }
448
449 void OnSelectionChange()
450 {
451 if(m_LockTracker.isLocked)
452 return;
453
454 selectedObject = Selection.activeObject;
455 RefreshSpriteEditorWindow();
456 }
457
458 void RefreshSpriteEditorWindow()
459 {
460 // In case of changed of texture/sprite or selected on non texture object
461 bool updateModules = false;
462 var selectedSprite = spriteDataProviderFactories.GetSpriteObject(selectedObject);
463 var assetPath = GetSelectionAssetPath();
464 var dataProvider = spriteDataProviderFactories.GetSpriteEditorDataProviderFromObject(selectedObject);
465 if ((dataProvider != null && selectedAssetPath != assetPath) ||
466 (dataProvider == null && selectedSprite !=null))
467 {
468 HandleApplyRevertDialog(SpriteEditorWindowStyles.applyRevertDialogTitle.text,
469 String.Format(SpriteEditorWindowStyles.applyRevertDialogContent.text, selectedAssetPath));
470 selectedAssetPath = assetPath;
471 ResetWindow();
472 ResetZoomAndScroll();
473 RefreshPropertiesCache();
474 RefreshRects();
475 updateModules = true;
476 }
477
478 if (m_RectsCache != null)
479 {
480 UpdateSelectedSpriteRectFromSelection();
481 }
482
483 // We only update modules when data provider changed
484 if (updateModules)
485 UpdateAvailableModules();
486 if (m_ModuleDropDownUI != null)
487 m_ModuleDropDownUI.style.display = m_Texture == null || m_RegisteredModules?.Count <= 0 ? DisplayStyle.None : DisplayStyle.Flex;
488 Repaint();
489 }
490
491 private void UpdateSelectedSpriteRectFromSelection()
492 {
493 if (Selection.activeObject is UnityEngine.Sprite)
494 {
495 UpdateSelectedSpriteRect(Selection.activeObject as UnityEngine.Sprite);
496 }
497 else
498 {
499 var sprite = spriteDataProviderFactories.GetSpriteObject(Selection.activeObject);
500 UpdateSelectedSpriteRect(sprite);
501 }
502 }
503
504 public void ResetWindow()
505 {
506 InvalidatePropertiesCache();
507 textureIsDirty = false;
508 saveChangesMessage = SpriteEditorWindowStyles.applyRevertModuleDialogContent.text;
509 }
510
511 public void ResetZoomAndScroll()
512 {
513 m_Zoom = -1;
514 m_ScrollPosition = Vector2.zero;
515 }
516
517 SpriteDataProviderFactories spriteDataProviderFactories
518 {
519 get
520 {
521 if (m_SpriteDataProviderFactories == null)
522 {
523 m_SpriteDataProviderFactories = new SpriteDataProviderFactories();
524 m_SpriteDataProviderFactories.Init();
525 }
526 return m_SpriteDataProviderFactories;
527 }
528 }
529
530 protected void SetTitleContent(string windowTitle, string windowIconPath)
531 {
532 if (string.IsNullOrEmpty(windowTitle))
533 return;
534
535 Texture2D iconTex = null;
536 if (!string.IsNullOrEmpty(windowIconPath))
537 {
538 if (EditorGUIUtility.isProSkin)
539 {
540 var newName = "d_" + Path.GetFileName(windowIconPath);
541 var iconDirName = Path.GetDirectoryName(windowIconPath);
542 if (!string.IsNullOrEmpty(iconDirName))
543 newName = $"{iconDirName}/{newName}";
544
545 windowIconPath = newName;
546 }
547
548 if (EditorGUIUtility.pixelsPerPoint > 1)
549 windowIconPath = $"{windowIconPath}@2x";
550
551 iconTex = EditorGUIUtility.Load(windowIconPath + ".png") as Texture2D;
552 }
553
554 titleContent = new GUIContent(windowTitle, iconTex);
555 }
556
557 void OnEnable()
558 {
559 name = "SpriteEditorWindow";
560 SetTitleContent(L10n.Tr("Sprite Editor"), "Packages/com.unity.2d.sprite/Editor/Assets/SpriteEditor");
561 selectedObject = Selection.activeObject;
562 minSize = new Vector2(360, 200);
563 m_UndoSystem.RegisterUndoCallback(UndoRedoPerformed);
564 EditorApplication.modifierKeysChanged += ModifierKeysChanged;
565 EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
566 EditorApplication.quitting += OnEditorApplicationQuit;
567 AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
568
569 if (selectedProviderChanged)
570 selectedAssetPath = GetSelectionAssetPath();
571
572 ResetWindow();
573 RefreshPropertiesCache();
574 bool noSelectedSprite = string.IsNullOrEmpty(m_SelectedSpriteRectGUID);
575 RefreshRects();
576 if (noSelectedSprite)
577 UpdateSelectedSpriteRectFromSelection();
578 UnityEditor.SpriteUtilityWindow.SetApplySpriteEditorWindow(RebuildCache);
579 }
580
581 void CreateGUI()
582 {
583 if (m_MainViewElement == null)
584 {
585 if (SetupVisualElements())
586 InitModules();
587 }
588 }
589
590 private void ShowButton(Rect r)
591 {
592 EditorGUI.BeginChangeCheck();
593 this.m_LockTracker.ShowButton(r, (GUIStyle) "IN LockButton", false);
594 if (!EditorGUI.EndChangeCheck())
595 return;
596 OnSelectionChange();
597 }
598
599 private bool SetupVisualElements()
600 {
601 var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(SpriteEditorWindowStyles.styleSheetPath);
602 if (styleSheet != null && rootVisualElement != null && rootVisualElement.styleSheetList != null)
603 {
604 m_ToolbarContainer = new VisualElement()
605 {
606 name = "spriteEditorWindowToolbarContainer"
607 };
608 m_ModuleToolbarContainer = new VisualElement()
609 {
610 name = "spriteEditorWindowModuleToolbarContainer"
611 };
612 m_ModuleDropDownUI = new IMGUIContainer(DoModuleDropDownGUI)
613 {
614 name = "spriteEditorWindowModuleDropDown",
615 };
616 m_ModuleDropDownUI.style.display = m_Texture == null || m_RegisteredModules?.Count <= 0 ? DisplayStyle.None : DisplayStyle.Flex;
617 var moduleApplyRevertGUI = new IMGUIContainer(DoApplyRevertGUI)
618 {
619 name = "spriteEditorWindowApplyRevert"
620 };
621 m_AlphaZoomToolbarElement = new IMGUIContainer(DoAlphaZoomToolbarGUI)
622 {
623 name = "spriteEditorWindowAlphaZoom"
624 };
625 m_ModuleToolbarIMGUIElement = new IMGUIContainer(DoToolbarGUI)
626 {
627 name = "spriteEditorWindowModuleToolbarIMGUI",
628 };
629 m_ToolbarContainer.Add(m_ModuleDropDownUI);
630 m_ToolbarContainer.Add(m_ModuleToolbarIMGUIElement);
631 m_ToolbarContainer.Add(m_ModuleToolbarContainer);
632 m_ToolbarContainer.Add(moduleApplyRevertGUI);
633 m_ToolbarContainer.Add(m_AlphaZoomToolbarElement);
634 m_MainViewIMGUIElement = new IMGUIContainer(DoTextureAndModulesGUI)
635 {
636 name = "mainViewIMGUIElement"
637 };
638 m_MainViewElement = new VisualElement()
639 {
640 name = "spriteEditorWindowMainView",
641 };
642 m_ModuleViewElement = new VisualElement()
643 {
644 name = "moduleViewElement",
645 pickingMode = PickingMode.Ignore
646 };
647 m_MainViewElement.Add(m_MainViewIMGUIElement);
648 m_MainViewElement.Add(m_ModuleViewElement);
649 var root = rootVisualElement;
650 root.styleSheetList.Add(styleSheet);
651 m_ToolbarContainer.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(SpriteEditorWindowStyles.toolBarStyleSheetPath));
652 baseRootVisualElement.Insert(0, m_ToolbarContainer);
653 root.Add(m_MainViewElement);
654
655 TryGetOverlay("Overlays/OverlayMenu", out Overlay overlay);
656 if(overlay != null)
657 overlay.displayed = false;
658 return true;
659 }
660 return false;
661 }
662
663 private void UndoRedoPerformed()
664 {
665 // Was selected texture changed by undo?
666 if (selectedProviderChanged)
667 RefreshSpriteEditorWindow();
668
669 InitSelectedSpriteRect();
670
671 Repaint();
672 }
673
674 private void InitSelectedSpriteRect()
675 {
676 SpriteRect newSpriteRect = null;
677 if (m_RectsCache != null && m_RectsCache.Count > 0)
678 {
679 if (selectedSpriteRect != null)
680 newSpriteRect = m_RectsCache.FirstOrDefault(x => x.spriteID == selectedSpriteRect.spriteID) != null ? selectedSpriteRect : m_RectsCache[0];
681 else
682 newSpriteRect = m_RectsCache[0];
683 }
684
685 selectedSpriteRect = newSpriteRect;
686 }
687
688 void OnGUI()
689 {
690 CreateGUI();
691 }
692
693 public override void SaveChanges()
694 {
695 var oldDelegate = onHandleApplyRevertDialog;
696 onHandleApplyRevertDialog = (x, y) => true;
697 HandleApplyRevertDialog(SpriteEditorWindowStyles.applyRevertDialogTitle.text,
698 String.Format(SpriteEditorWindowStyles.applyRevertDialogContent.text, selectedAssetPath));
699 onHandleApplyRevertDialog = oldDelegate;
700 base.SaveChanges();
701 }
702
703 private void OnDisable()
704 {
705 Undo.undoRedoPerformed -= UndoRedoPerformed;
706 InvalidatePropertiesCache();
707 EditorApplication.modifierKeysChanged -= ModifierKeysChanged;
708 EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
709 EditorApplication.quitting -= OnEditorApplicationQuit;
710 AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
711
712 if (m_OutlineTexture != null)
713 {
714 DestroyImmediate(m_OutlineTexture);
715 m_OutlineTexture = null;
716 }
717
718 if (m_ReadableTexture)
719 {
720 DestroyImmediate(m_ReadableTexture);
721 m_ReadableTexture = null;
722 }
723
724 if (m_CurrentModule != null)
725 m_CurrentModule.OnModuleDeactivate();
726 UnityEditor.SpriteUtilityWindow.SetApplySpriteEditorWindow(null);
727 if (m_MainViewElement != null && rootVisualElement.Contains(m_MainViewElement))
728 {
729 rootVisualElement.Remove(m_MainViewElement);
730 m_MainViewElement = null;
731 }
732 }
733
734 void OnPlayModeStateChanged(PlayModeStateChange playModeState)
735 {
736 if (PlayModeStateChange.EnteredPlayMode == playModeState || PlayModeStateChange.EnteredEditMode == playModeState)
737 {
738 RebuildCache();
739 }
740 }
741
742 void OnEditorApplicationQuit()
743 {
744 HandleApplyRevertDialog(SpriteEditorWindowStyles.applyRevertDialogTitle.text,
745 String.Format(SpriteEditorWindowStyles.applyRevertDialogContent.text, selectedAssetPath));
746 }
747
748 void OnBeforeAssemblyReload()
749 {
750 HandleApplyRevertDialog(SpriteEditorWindowStyles.applyRevertDialogTitle.text,
751 String.Format(SpriteEditorWindowStyles.applyRevertDialogContent.text, selectedAssetPath));
752 }
753
754 static bool ShowHandleApplyRevertDialog(string dialogTitle, string dialogContent)
755 {
756 return EditorUtility.DisplayDialog(dialogTitle, dialogContent,
757 SpriteEditorWindowStyles.applyButtonLabel.text, SpriteEditorWindowStyles.revertButtonLabel.text);
758 }
759
760 void HandleApplyRevertDialog(string dialogTitle, string dialogContent)
761 {
762 if (textureIsDirty && IsSpriteDataProviderValid())
763 {
764 if (onHandleApplyRevertDialog(dialogTitle, dialogContent))
765 DoApply();
766 else
767 DoRevert();
768
769 SetupModule(m_CurrentModuleIndex);
770 }
771 }
772
773 bool IsSpriteDataProviderValid()
774 {
775 return m_SpriteDataProvider != null && !m_SpriteDataProvider.Equals(null);
776 }
777
778 void RefreshRects()
779 {
780 spriteRects = null;
781 if (IsSpriteDataProviderValid())
782 {
783 m_RectsCache = m_SpriteDataProvider.GetSpriteRects().ToList();
784 }
785
786 InitSelectedSpriteRect();
787 }
788
789 private void UpdateAssetSelectionChange()
790 {
791 if (selectedProviderChanged)
792 {
793 ResetOnNextRepaint();
794 }
795
796 if (m_ResetCommandSent || (UnityEngine.Event.current.type == EventType.ExecuteCommand && UnityEngine.Event.current.commandName == k_RefreshOnNextRepaintCommandEvent))
797 {
798 m_ResetCommandSent = false;
799 if (selectedProviderChanged || !IsSpriteDataProviderValid())
800 selectedAssetPath = GetSelectionAssetPath();
801 RebuildCache();
802 }
803 }
804
805 internal void ResetOnNextRepaint()
806 {
807 //Because we can't show dialog in a repaint/layout event, we need to send event to IMGUI to trigger this.
808 //The event is now sent through the Update loop.
809 m_ResetOnNextRepaint = true;
810 if (textureIsDirty)
811 {
812 // We can't depend on the existing data provider to set data because a reimport might cause
813 // the data provider to be invalid. We store up the current asset path so that in DoApply()
814 // the modified data can be set correctly to correct asset.
815 if (m_CurrentResetContext != null)
816 Debug.LogError("Existing reset not completed for " + m_CurrentResetContext.assetPath);
817 m_CurrentResetContext = new CurrentResetContext()
818 {
819 assetPath = selectedAssetPath
820 };
821 }
822 }
823
824 void Update()
825 {
826 if (m_ResetOnNextRepaint)
827 {
828 m_ResetOnNextRepaint = false;
829 m_ResetCommandSent = true;
830 var e = EditorGUIUtility.CommandEvent(k_RefreshOnNextRepaintCommandEvent);
831 this.SendEvent(e);
832 }
833 }
834
835 private void RebuildCache()
836 {
837 HandleApplyRevertDialog(SpriteEditorWindowStyles.applyRevertDialogTitle.text, SpriteEditorWindowStyles.pendingChangesDialogContent.text);
838 ResetWindow();
839 RefreshPropertiesCache();
840 RefreshRects();
841 UpdateAvailableModules();
842 }
843
844 void ShowEditorMessage(string message, MessageType messageType)
845 {
846 GUILayout.BeginArea(spriteEditorMessageRect);
847 EditorGUILayout.HelpBox(message, messageType);
848 GUILayout.EndArea();
849 }
850
851 private void DoTextureAndModulesGUI()
852 {
853 // Don't do anything until reset event is sent
854 if (m_ResetOnNextRepaint)
855 return;
856 InitStyles();
857 UpdateAssetSelectionChange();
858 if (m_ResetCommandSent)
859 return;
860 textureViewRect = new Rect(0f, 0f, m_MainViewIMGUIElement.layout.width - k_ScrollbarMargin, m_MainViewIMGUIElement.layout.height - k_ScrollbarMargin);
861 if (!activeDataProviderSelected)
862 {
863 if (m_Texture != null)
864 {
865 DoTextureGUI();
866 ShowEditorMessage(SpriteEditorWindowStyles.selectionNotEditableBySpriteEditor.text, MessageType.Info);
867 }
868 else
869 {
870 ShowEditorMessage(SpriteEditorWindowStyles.noSelectionWarning.text, MessageType.Info);
871 }
872 return;
873 }
874 if (m_CurrentModule == null)
875 {
876 using (new EditorGUI.DisabledScope(true))
877 {
878 GUILayout.Label(SpriteEditorWindowStyles.noModuleWarning);
879 }
880 return;
881 }
882 textureViewRect = new Rect(0f, 0f, m_MainViewIMGUIElement.layout.width - k_ScrollbarMargin, m_MainViewIMGUIElement.layout.height - k_ScrollbarMargin);
883 Matrix4x4 oldHandlesMatrix = Handles.matrix;
884 DoTextureGUI();
885 // Warning message if applicable
886 DoEditingDisabledMessage();
887 m_CurrentModule.DoPostGUI();
888 Handles.matrix = oldHandlesMatrix;
889 if (m_RequestRepaint)
890 {
891 Repaint();
892 m_RequestRepaint = false;
893 }
894 }
895
896 protected override void DoTextureGUIExtras()
897 {
898 if (activeDataProviderSelected)
899 {
900 HandleFrameSelected();
901
902 if (m_EventSystem.current.type == EventType.Repaint)
903 {
904 SpriteEditorUtility.BeginLines(new Color(1f, 1f, 1f, 0.5f));
905 var selectedRect = selectedSpriteRect != null ? selectedSpriteRect.spriteID : new GUID();
906 for (int i = 0; i < m_RectsCache.Count; i++)
907 {
908 if (m_RectsCache[i].spriteID != selectedRect)
909 SpriteEditorUtility.DrawBox(m_RectsCache[i].rect);
910 }
911
912 SpriteEditorUtility.EndLines();
913 }
914
915 m_CurrentModule.DoMainGUI();
916 }
917 }
918
919 private void DoModuleDropDownGUI()
920 {
921 InitStyles();
922
923 if (!activeDataProviderSelected || m_CurrentModule == null)
924 return;
925
926 if (m_RegisteredModules.Count > 1)
927 {
928 int module = EditorGUILayout.Popup(m_CurrentModuleIndex, m_RegisteredModuleNames, EditorStyles.toolbarPopup);
929 if (module != m_CurrentModuleIndex)
930 {
931 if (textureIsDirty)
932 {
933 // Have pending module edit changes. Ask user if they want to apply or revert
934 if (EditorUtility.DisplayDialog(SpriteEditorWindowStyles.applyRevertModuleDialogTitle.text,
935 SpriteEditorWindowStyles.applyRevertModuleDialogContent.text,
936 SpriteEditorWindowStyles.applyButtonLabel.text, SpriteEditorWindowStyles.revertButtonLabel.text))
937 DoApply();
938 else
939 DoRevert();
940 }
941 SetupModule(module);
942 }
943 }
944 }
945
946 private void DoApplyRevertGUI()
947 {
948 InitStyles();
949 GUILayout.BeginHorizontal();
950 using (new EditorGUI.DisabledScope(!textureIsDirty))
951 {
952 if (GUILayout.Button(SpriteEditorWindowStyles.applyButtonLabel, EditorStyles.toolbarButton))
953 {
954 var apply = true;
955 if (SpriteEditorWindowSettings.showApplyConfirmation)
956 {
957 apply = EditorUtility.DisplayDialog(SpriteEditorWindowStyles.applyConfirmationDialogTitle.text, SpriteEditorWindowStyles.applyConfirmationDialogContent.text,
958 SpriteEditorWindowStyles.yesLabel.text, SpriteEditorWindowStyles.noLabel.text);
959 }
960 if (apply)
961 {
962 DoApply();
963 SetupModule(m_CurrentModuleIndex);
964 }
965 }
966
967 if (GUILayout.Button(SpriteEditorWindowStyles.revertButtonLabel, EditorStyles.toolbarButton))
968 {
969 var revert = true;
970 if (SpriteEditorWindowSettings.showRevertConfirmation)
971 {
972 revert = EditorUtility.DisplayDialog(SpriteEditorWindowStyles.revertConfirmationDialogTitle.text, SpriteEditorWindowStyles.revertConfirmationDialogContent.text,
973 SpriteEditorWindowStyles.yesLabel.text, SpriteEditorWindowStyles.noLabel.text);
974 }
975 if (revert)
976 {
977 DoRevert();
978 SetupModule(m_CurrentModuleIndex);
979 }
980 }
981 }
982 GUILayout.EndHorizontal();
983 }
984
985 void DoAlphaZoomToolbarGUI()
986 {
987 InitStyles();
988 Rect toolbarRect = new Rect(0, 0, m_AlphaZoomToolbarElement.resolvedStyle.width, m_AlphaZoomToolbarElement.resolvedStyle.height);
989 DoAlphaZoomToolbarGUI(toolbarRect);
990 }
991
992 private void DoToolbarGUI()
993 {
994 InitStyles();
995 Rect toolbarRect = new Rect(0, 0, m_ModuleToolbarIMGUIElement.resolvedStyle.width, m_ModuleToolbarIMGUIElement.resolvedStyle.height);
996 m_CurrentModule?.DoToolbarGUI(toolbarRect);
997 }
998
999 private void DoEditingDisabledMessage()
1000 {
1001 if (editingDisabled)
1002 {
1003 var disableMessage = m_AssetNotEditable ? SpriteEditorWindowStyles.editingDisableMessageBecauseNonEditableLabel.text : SpriteEditorWindowStyles.editingDisableMessageBecausePlaymodeLabel.text;
1004 ShowEditorMessage(disableMessage, MessageType.Warning);
1005 }
1006 }
1007
1008 private void DoApply()
1009 {
1010 textureIsDirty = false;
1011 bool reimport = true;
1012 var dataProvider = m_SpriteDataProvider;
1013 if (m_CurrentResetContext != null)
1014 {
1015 m_SpriteDataProvider =
1016 m_SpriteDataProviderFactories.GetSpriteEditorDataProviderFromObject(
1017 AssetDatabase.LoadMainAssetAtPath(m_CurrentResetContext.assetPath));
1018 m_SpriteDataProvider.InitSpriteEditorDataProvider();
1019 m_CurrentResetContext = null;
1020 }
1021
1022 if (m_SpriteDataProvider != null)
1023 {
1024 if (m_CurrentModule != null)
1025 reimport = m_CurrentModule.ApplyRevert(true);
1026 m_SpriteDataProvider.Apply();
1027 }
1028
1029 m_SpriteDataProvider = dataProvider;
1030 // Do this so that asset change save dialog will not show
1031 var originalValue = EditorPrefs.GetBool("VerifySavingAssets", false);
1032 EditorPrefs.SetBool("VerifySavingAssets", false);
1033 AssetDatabase.ForceReserializeAssets(new[] {selectedAssetPath}, ForceReserializeAssetsOptions.ReserializeMetadata);
1034 EditorPrefs.SetBool("VerifySavingAssets", originalValue);
1035
1036 if (reimport)
1037 DoTextureReimport(selectedAssetPath);
1038 Repaint();
1039 RefreshRects();
1040 }
1041
1042 private void DoRevert()
1043 {
1044 textureIsDirty = false;
1045 RefreshRects();
1046 GUI.FocusControl("");
1047 if (m_CurrentModule != null)
1048 m_CurrentModule.ApplyRevert(false);
1049 }
1050
1051 public bool HandleSpriteSelection()
1052 {
1053 bool changed = false;
1054
1055 if (m_EventSystem.current.type == EventType.MouseDown && m_EventSystem.current.button == 0 && GUIUtility.hotControl == 0 && !m_EventSystem.current.alt)
1056 {
1057 var oldSelected = selectedSpriteRect;
1058
1059 var triedRect = TrySelect(m_EventSystem.current.mousePosition);
1060 if (triedRect != oldSelected)
1061 {
1062 Undo.RegisterCompleteObjectUndo(this, "Sprite Selection");
1063
1064 selectedSpriteRect = triedRect;
1065 changed = true;
1066 }
1067
1068 if (selectedSpriteRect != null)
1069 s_OneClickDragStarted = true;
1070 else
1071 RequestRepaint();
1072
1073 if (changed && selectedSpriteRect != null)
1074 {
1075 m_EventSystem.current.Use();
1076 }
1077 }
1078
1079 return changed;
1080 }
1081
1082 private void HandleFrameSelected()
1083 {
1084 var evt = m_EventSystem.current;
1085
1086 if ((evt.type == EventType.ValidateCommand || evt.type == EventType.ExecuteCommand)
1087 && evt.commandName == EventCommandNames.FrameSelected)
1088 {
1089 if (evt.type == EventType.ExecuteCommand)
1090 {
1091 // Do not do frame if there is none selected
1092 if (selectedSpriteRect == null)
1093 return;
1094
1095 Rect rect = selectedSpriteRect.rect;
1096
1097 // Calculate the require pixel to display the frame, then get the zoom needed.
1098 float targetZoom = m_Zoom;
1099 if (rect.width < rect.height)
1100 targetZoom = textureViewRect.height / (rect.height + textureViewRect.height * k_MarginForFraming);
1101 else
1102 targetZoom = textureViewRect.width / (rect.width + textureViewRect.width * k_MarginForFraming);
1103
1104 // Apply the zoom
1105 zoomLevel = targetZoom;
1106
1107 // Calculate the scroll values to center the frame
1108 m_ScrollPosition.x = (rect.center.x - (m_Texture.width * 0.5f)) * m_Zoom;
1109 m_ScrollPosition.y = (rect.center.y - (m_Texture.height * 0.5f)) * m_Zoom * -1.0f;
1110
1111 Repaint();
1112 }
1113
1114 evt.Use();
1115 }
1116 }
1117
1118 void UpdateSelectedSpriteRect(UnityEngine.Sprite sprite)
1119 {
1120 if (m_RectsCache == null || sprite == null || sprite.Equals(null))
1121 return;
1122
1123 var spriteGUID = sprite.GetSpriteID();
1124 for (int i = 0; i < m_RectsCache.Count; i++)
1125 {
1126 if (spriteGUID == m_RectsCache[i].spriteID)
1127 {
1128 selectedSpriteRect = m_RectsCache[i];
1129 return;
1130 }
1131 }
1132 selectedSpriteRect = null;
1133 }
1134
1135 private SpriteRect TrySelect(Vector2 mousePosition)
1136 {
1137 float selectedSize = float.MaxValue;
1138 SpriteRect currentRect = null;
1139 mousePosition = Handles.inverseMatrix.MultiplyPoint(mousePosition);
1140
1141 for (int i = 0; i < m_RectsCache.Count; i++)
1142 {
1143 var sr = m_RectsCache[i];
1144 if (sr.rect.Contains(mousePosition))
1145 {
1146 // If we clicked inside an already selected spriterect, always persist that selection
1147 if (sr == selectedSpriteRect)
1148 return sr;
1149
1150 float width = sr.rect.width;
1151 float height = sr.rect.height;
1152 float newSize = width * height;
1153 if (width > 0f && height > 0f && newSize < selectedSize)
1154 {
1155 currentRect = sr;
1156 selectedSize = newSize;
1157 }
1158 }
1159 }
1160
1161 return currentRect;
1162 }
1163
1164 public void DoTextureReimport(string path)
1165 {
1166 if (m_SpriteDataProvider != null)
1167 {
1168 try
1169 {
1170 AssetDatabase.StartAssetEditing();
1171 AssetDatabase.ImportAsset(path);
1172 }
1173 finally
1174 {
1175 AssetDatabase.StopAssetEditing();
1176 }
1177 }
1178 }
1179
1180 GUIContent[] m_RegisteredModuleNames;
1181 List<SpriteEditorModuleBase> m_AllRegisteredModules;
1182 List<SpriteEditorModuleBase> m_RegisteredModules;
1183 SpriteEditorModuleBase m_CurrentModule = null;
1184 int m_CurrentModuleIndex = 0;
1185 [SerializeField]
1186 string m_LastUsedModuleTypeName;
1187
1188 internal void SetupModule(int newModuleIndex)
1189 {
1190 if (m_CurrentModule != null)
1191 m_CurrentModule.OnModuleDeactivate();
1192
1193 m_CurrentModule = null;
1194 m_ModuleViewElement.Clear();
1195 m_ModuleToolbarContainer.Clear();
1196
1197 if (m_RegisteredModules.Count > newModuleIndex && newModuleIndex >= 0)
1198 {
1199 m_CurrentModuleIndex = newModuleIndex;
1200 m_CurrentModule = m_RegisteredModules[newModuleIndex];
1201 // if there are any modes for the module
1202 List<Type> modes;
1203 if (m_CurrentModule is SpriteEditorModuleModeSupportBase moduleWithModes &&
1204 m_ModuleMode.TryGetValue(moduleWithModes.GetType(), out modes))
1205 {
1206 moduleWithModes.SetModuleModes(modes);
1207 }
1208 m_LastUsedModuleTypeName = m_CurrentModule.GetType().FullName;
1209 m_CurrentModule.OnModuleActivate();
1210 }
1211
1212 if (m_MainViewElement != null)
1213 m_MainViewElement.MarkDirtyRepaint();
1214 if (m_ModuleViewElement != null)
1215 m_ModuleViewElement.MarkDirtyRepaint();
1216 }
1217
1218 void UpdateAvailableModules()
1219 {
1220 if (m_AllRegisteredModules == null)
1221 return;
1222 m_RegisteredModules = new List<SpriteEditorModuleBase>();
1223 int lastUsedModuleIndex = -1;
1224 if (activeDataProviderSelected)
1225 {
1226 foreach (var module in m_AllRegisteredModules)
1227 {
1228 if (module.CanBeActivated())
1229 {
1230 RequireSpriteDataProviderAttribute attribute = null;
1231 m_ModuleRequireSpriteDataProvider.TryGetValue(module.GetType(), out attribute);
1232 if (attribute == null || attribute.ContainsAllType(m_SpriteDataProvider))
1233 {
1234 m_RegisteredModules.Add(module);
1235 }
1236 }
1237 }
1238
1239 lastUsedModuleIndex = 0;
1240 m_RegisteredModuleNames = new GUIContent[m_RegisteredModules.Count];
1241 for (int i = 0; i < m_RegisteredModules.Count; i++)
1242 {
1243 m_RegisteredModuleNames[i] = new GUIContent(m_RegisteredModules[i].moduleName);
1244 if (m_RegisteredModules[i].GetType().FullName.Equals(m_LastUsedModuleTypeName))
1245 {
1246 lastUsedModuleIndex = i;
1247 }
1248 }
1249 }
1250
1251 if (m_ModuleDropDownUI != null)
1252 {
1253 m_ModuleDropDownUI.style.display = m_RegisteredModuleNames?.Length > 1 ? DisplayStyle.Flex : DisplayStyle.None;
1254 m_ModuleDropDownUI.style.position = m_RegisteredModuleNames?.Length > 1 ? Position.Relative : Position.Absolute;
1255 }
1256
1257 SetupModule(lastUsedModuleIndex);
1258 }
1259
1260 void InitModules()
1261 {
1262 m_AllRegisteredModules = new List<SpriteEditorModuleBase>();
1263 m_ModuleRequireSpriteDataProvider.Clear();
1264 m_ModuleMode.Clear();
1265
1266 if (m_OutlineTexture == null)
1267 {
1268 m_OutlineTexture = new UnityTexture2D(1, 16, TextureFormat.RGBA32, false);
1269 m_OutlineTexture.SetPixels(new Color[]
1270 {
1271 new Color(0.5f, 0.5f, 0.5f, 0.5f), new Color(0.5f, 0.5f, 0.5f, 0.5f), new Color(0.8f, 0.8f, 0.8f, 0.8f), new Color(0.8f, 0.8f, 0.8f, 0.8f),
1272 Color.white, Color.white, Color.white, Color.white,
1273 new Color(.8f, .8f, .8f, 1f), new Color(.5f, .5f, .5f, .8f), new Color(0.3f, 0.3f, 0.3f, 0.5f), new Color(0.3f, .3f, 0.3f, 0.5f),
1274 new Color(0.3f, .3f, 0.3f, 0.3f), new Color(0.3f, .3f, 0.3f, 0.3f), new Color(0.1f, 0.1f, 0.1f, 0.1f), new Color(0.1f, .1f, 0.1f, 0.1f)
1275 });
1276 m_OutlineTexture.Apply();
1277 m_OutlineTexture.hideFlags = HideFlags.HideAndDontSave;
1278 }
1279 var outlineTexture = new Texture2DWrapper(m_OutlineTexture);
1280
1281 // Add your modules here
1282 RegisterModule(new SpriteFrameModule(this, m_EventSystem, m_UndoSystem, m_AssetDatabase));
1283 RegisterModule(new SpritePolygonModeModule(this, m_EventSystem, m_UndoSystem, m_AssetDatabase));
1284 RegisterModule(new SpriteOutlineModule(this, m_EventSystem, m_UndoSystem, m_AssetDatabase, m_GUIUtility, new ShapeEditorFactory(), outlineTexture));
1285 RegisterModule(new SpritePhysicsShapeModule(this, m_EventSystem, m_UndoSystem, m_AssetDatabase, m_GUIUtility, new ShapeEditorFactory(), outlineTexture));
1286 RegisterCustomModules();
1287 UpdateAvailableModules();
1288 }
1289
1290 void RegisterModule(SpriteEditorModuleBase module)
1291 {
1292 var type = module.GetType();
1293 bool showAsModule = true;
1294 if (module is SpriteEditorModeBase)
1295 {
1296 var moduleMode = type.GetCustomAttributes(typeof(SpriteEditorModuleModeAttribute), false);
1297 if (moduleMode.Length == 1)
1298 {
1299 var modeAttribute = (SpriteEditorModuleModeAttribute)moduleMode[0];
1300 showAsModule = modeAttribute.showAsModule;
1301 foreach (var modeForModule in modeAttribute.GetModuleTypes())
1302 {
1303 List<Type> modulesList;
1304 if (!m_ModuleMode.TryGetValue(modeForModule, out modulesList))
1305 modulesList = new List<Type>();
1306 modulesList.Add(module.GetType());
1307 m_ModuleMode[modeForModule] = modulesList;
1308 }
1309 }
1310 }
1311
1312 if(showAsModule)
1313 {
1314 var attributes = type.GetCustomAttributes(typeof(RequireSpriteDataProviderAttribute), false);
1315 if (attributes.Length == 1)
1316 m_ModuleRequireSpriteDataProvider.Add(type, (RequireSpriteDataProviderAttribute)attributes[0]);
1317 m_AllRegisteredModules.Add(module);
1318 }
1319 }
1320
1321 void RegisterCustomModules()
1322 {
1323 foreach (var moduleClassType in TypeCache.GetTypesDerivedFrom<SpriteEditorModuleBase>())
1324 {
1325 if (!moduleClassType.IsAbstract)
1326 {
1327 bool moduleFound = false;
1328 foreach (var module in m_AllRegisteredModules)
1329 {
1330 if (module.GetType() == moduleClassType)
1331 {
1332 moduleFound = true;
1333 break;
1334 }
1335 }
1336 if (!moduleFound)
1337 {
1338 var constructorType = new Type[0];
1339 // Get the public instance constructor that takes ISpriteEditorModule parameter.
1340 var constructorInfoObj = moduleClassType.GetConstructor(
1341 BindingFlags.Instance | BindingFlags.Public, null,
1342 CallingConventions.HasThis, constructorType, null);
1343 if (constructorInfoObj != null)
1344 {
1345 try
1346 {
1347 var newInstance = constructorInfoObj.Invoke(new object[0]) as SpriteEditorModuleBase;
1348 if (newInstance != null)
1349 {
1350 newInstance.spriteEditor = this;
1351 RegisterModule(newInstance);
1352 }
1353 }
1354 catch (Exception ex)
1355 {
1356 Debug.LogWarning("Unable to instantiate custom module " + moduleClassType.FullName + ". Exception:" + ex);
1357 }
1358 }
1359 else
1360 Debug.LogWarning(moduleClassType.FullName + " does not have a parameterless constructor");
1361 }
1362 }
1363 }
1364 }
1365
1366 internal List<SpriteEditorModuleBase> activatedModules
1367 {
1368 get { return m_RegisteredModules; }
1369 }
1370
1371 public List<SpriteRect> spriteRects
1372 {
1373 set
1374 {
1375 m_RectsCache = value;
1376 m_CachedSelectedSpriteRect = null;
1377 }
1378 }
1379
1380 private SpriteRect m_CachedSelectedSpriteRect;
1381
1382 public SpriteRect selectedSpriteRect
1383 {
1384 get
1385 {
1386 // Always return null if editing is disabled to prevent all possible action to selected frame.
1387 if (editingDisabled || m_RectsCache == null || string.IsNullOrEmpty(m_SelectedSpriteRectGUID))
1388 return null;
1389
1390 var guid = new GUID(m_SelectedSpriteRectGUID);
1391 if (m_CachedSelectedSpriteRect == null || m_CachedSelectedSpriteRect.spriteID != guid)
1392 {
1393 m_CachedSelectedSpriteRect = m_RectsCache.FirstOrDefault(x => x.spriteID == guid);
1394 }
1395 return m_CachedSelectedSpriteRect;
1396 }
1397 set
1398 {
1399 if (editingDisabled)
1400 return;
1401
1402 var oldSelected = m_SelectedSpriteRectGUID;
1403 m_SelectedSpriteRectGUID = value != null ? value.spriteID.ToString() : new GUID().ToString();
1404 if (oldSelected != m_SelectedSpriteRectGUID)
1405 {
1406 if (m_MainViewIMGUIElement != null)
1407 m_MainViewIMGUIElement.MarkDirtyRepaint();
1408 if (m_MainViewElement != null)
1409 {
1410 m_MainViewElement.MarkDirtyRepaint();
1411 using (var e = SpriteSelectionChangeEvent.GetPooled())
1412 {
1413 e.target = m_ModuleViewElement;
1414 m_MainViewElement.SendEvent(e);
1415 }
1416 }
1417 }
1418 }
1419 }
1420
1421 public ISpriteEditorDataProvider spriteEditorDataProvider
1422 {
1423 get { return m_SpriteDataProvider; }
1424 }
1425
1426 public bool enableMouseMoveEvent
1427 {
1428 set { wantsMouseMove = value; }
1429 }
1430
1431 public void RequestRepaint()
1432 {
1433 if (focusedWindow != this)
1434 Repaint();
1435 else
1436 m_RequestRepaint = true;
1437 }
1438
1439 public void SetDataModified()
1440 {
1441 textureIsDirty = true;
1442 }
1443
1444 public Rect windowDimension
1445 {
1446 get { return textureViewRect; }
1447 }
1448
1449 public ITexture2D previewTexture
1450 {
1451 get { return m_Texture; }
1452 }
1453
1454 public bool editingDisabled => EditorApplication.isPlayingOrWillChangePlaymode || m_AssetNotEditable;
1455
1456 public void SetPreviewTexture(UnityTexture2D texture, int width, int height)
1457 {
1458 m_Texture = new PreviewTexture2D(texture, width, height);
1459 }
1460
1461 public void ApplyOrRevertModification(bool apply)
1462 {
1463 if (apply)
1464 DoApply();
1465 else
1466 DoRevert();
1467 }
1468
1469 internal class PreviewTexture2D : Texture2DWrapper
1470 {
1471 private int m_ActualWidth = 0;
1472 private int m_ActualHeight = 0;
1473
1474 public PreviewTexture2D(UnityTexture2D t, int width, int height)
1475 : base(t)
1476 {
1477 m_ActualWidth = width;
1478 m_ActualHeight = height;
1479 }
1480
1481 public override int width
1482 {
1483 get { return m_ActualWidth; }
1484 }
1485
1486 public override int height
1487 {
1488 get { return m_ActualHeight; }
1489 }
1490 }
1491
1492 public T GetDataProvider<T>() where T : class
1493 {
1494 return m_SpriteDataProvider != null ? m_SpriteDataProvider.GetDataProvider<T>() : null;
1495 }
1496
1497 public VisualElement GetMainVisualContainer()
1498 {
1499 return m_ModuleViewElement;
1500 }
1501
1502 public VisualElement GetToolbarRootElement()
1503 {
1504 return m_ModuleToolbarContainer;
1505 }
1506
1507 static internal void OnTextureReimport(SpriteEditorWindow win, string path)
1508 {
1509 if (win.selectedAssetPath == path)
1510 {
1511 win.ResetOnNextRepaint();
1512 }
1513 }
1514
1515 [MenuItem("Window/2D/Sprite Editor", priority = 0)]
1516 static private void OpenSpriteEditorWindow()
1517 {
1518 SpriteEditorWindow.GetWindow(Selection.activeObject);
1519 }
1520 }
1521
1522 internal class SpriteEditorTexturePostprocessor : AssetPostprocessor
1523 {
1524 public override int GetPostprocessOrder()
1525 {
1526 return 1;
1527 }
1528
1529 static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
1530 {
1531 UnityEngine.Object[] wins = Resources.FindObjectsOfTypeAll(typeof(SpriteEditorWindow));
1532 SpriteEditorWindow win = wins.Length > 0 ? (EditorWindow)(wins[0]) as SpriteEditorWindow : null;
1533 if (win != null)
1534 {
1535 foreach (var deletedAsset in deletedAssets)
1536 SpriteEditorWindow.OnTextureReimport(win, deletedAsset);
1537
1538 bool rebuildTracker = false;
1539 var assetPaths = ActiveEditorTracker.sharedTracker.activeEditors.Where(x => x.target is AssetImporter).Select(y => ((AssetImporter)y.target).assetPath);
1540 foreach (var importedAsset in importedAssets)
1541 {
1542 SpriteEditorWindow.OnTextureReimport(win, importedAsset);
1543 if (!rebuildTracker)
1544 {
1545 rebuildTracker = assetPaths.Contains(importedAsset);
1546 }
1547 }
1548 // Since Inspector Window doesn't rebuild anymore, we need to do it manually (https://github.cds.internal.unity3d.com/unity/unity/pull/13828)
1549 if(rebuildTracker)
1550 ActiveEditorTracker.sharedTracker.ForceRebuild();
1551 }
1552 }
1553 }
1554
1555 internal class SpriteSelectionChangeEvent : EventBase<SpriteSelectionChangeEvent>
1556 {
1557 }
1558}