A game about forced loneliness, made by TACStudios
1using System;
2using UnityEngine;
3using UnityEvent = UnityEngine.Event;
4
5namespace UnityEditor.U2D.Sprites
6{
7 internal class SpriteUtilityWindow : EditorWindow
8 {
9 protected class Styles
10 {
11 public readonly GUIStyle dragdot = "U2D.dragDot";
12 public readonly GUIStyle dragdotDimmed = "U2D.dragDotDimmed";
13 public readonly GUIStyle dragdotactive = "U2D.dragDotActive";
14 public readonly GUIStyle createRect = "U2D.createRect";
15 public readonly GUIStyle preToolbar = "preToolbar";
16 public readonly GUIStyle preButton = "preButton";
17 public readonly GUIStyle preLabel = "preLabel";
18 public readonly GUIStyle preSlider = "preSlider";
19 public readonly GUIStyle preSliderThumb = "preSliderThumb";
20 public readonly GUIStyle preBackground = "preBackground";
21 public readonly GUIStyle pivotdotactive = "U2D.pivotDotActive";
22 public readonly GUIStyle pivotdot = "U2D.pivotDot";
23
24 public readonly GUIStyle dragBorderdot = new GUIStyle();
25 public readonly GUIStyle dragBorderDotActive = new GUIStyle();
26
27 public readonly GUIStyle toolbar;
28 public readonly GUIContent alphaIcon;
29 public readonly GUIContent RGBIcon;
30 public readonly GUIStyle notice;
31
32 public readonly GUIContent smallMip;
33 public readonly GUIContent largeMip;
34
35 public Styles()
36 {
37 toolbar = new GUIStyle(EditorStyles.inspectorBig);
38 toolbar.margin.top = 0;
39 toolbar.margin.bottom = 0;
40 alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha");
41 RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB");
42 preToolbar.border.top = 0;
43 createRect.border = new RectOffset(3, 3, 3, 3);
44
45 notice = new GUIStyle(GUI.skin.label);
46 notice.alignment = TextAnchor.MiddleCenter;
47 notice.normal.textColor = Color.yellow;
48
49 dragBorderdot.fixedHeight = 5f;
50 dragBorderdot.fixedWidth = 5f;
51 dragBorderdot.normal.background = EditorGUIUtility.whiteTexture;
52
53 dragBorderDotActive.fixedHeight = dragBorderdot.fixedHeight;
54 dragBorderDotActive.fixedWidth = dragBorderdot.fixedWidth;
55 dragBorderDotActive.normal.background = EditorGUIUtility.whiteTexture;
56
57 smallMip = EditorGUIUtility.IconContent("PreTextureMipMapLow");
58 largeMip = EditorGUIUtility.IconContent("PreTextureMipMapHigh");
59 }
60 }
61
62 protected void InitStyles()
63 {
64 if (m_Styles == null)
65 m_Styles = new Styles();
66 }
67
68 protected Styles m_Styles;
69
70 protected const float k_BorderMargin = 10f;
71 protected const float k_ScrollbarMargin = 16f;
72 protected const float k_InspectorWindowMargin = 8f;
73 protected const float k_InspectorWidth = 330f;
74 protected const float k_MinZoomPercentage = 0.9f;
75 protected const float k_MaxZoom = 50f;
76 protected const float k_WheelZoomSpeed = 0.03f;
77 protected const float k_MouseZoomSpeed = 0.005f;
78 protected const float k_ToolbarHeight = 17f;
79
80 protected ITexture2D m_Texture;
81 protected ITexture2D m_TextureAlphaOverride;
82 Rect m_TextureViewRect;
83 protected Rect m_TextureRect;
84
85 [SerializeField]
86 protected bool m_ShowAlpha = false;
87 [SerializeField]
88 protected float m_MipLevel = 0;
89 [SerializeField]
90 protected float m_Zoom = -1f;
91 [SerializeField]
92 protected Vector2 m_ScrollPosition = new Vector2();
93
94 public float zoomLevel
95 {
96 get { return m_Zoom; }
97 set { m_Zoom = Mathf.Clamp(value, GetMinZoom(), k_MaxZoom); }
98 }
99
100 internal Rect textureViewRect
101 {
102 get => m_TextureViewRect;
103 set
104 {
105 m_TextureViewRect = value;
106 zoomLevel = m_Zoom; // update zoom level
107 }
108 }
109
110 public Vector2 scrollPosition
111 {
112 get { return m_ScrollPosition; }
113 set
114 {
115 if (m_Zoom < 0)
116 m_Zoom = GetMinZoom();
117
118 m_ScrollPosition.x = Mathf.Clamp(value.x, maxScrollRect.xMin, maxScrollRect.xMax);
119 m_ScrollPosition.y = Mathf.Clamp(value.y, maxScrollRect.yMin, maxScrollRect.yMax);
120 }
121 }
122
123 public bool showAlpha
124 {
125 get { return m_ShowAlpha; }
126 set { m_ShowAlpha = value; }
127 }
128
129 public float mipLevel
130 {
131 get { return m_MipLevel; }
132 set
133 {
134 var mipCount = 1;
135 if (m_Texture != null)
136 mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(m_Texture));
137 m_MipLevel = Mathf.Clamp(value, 0, mipCount - 1);
138 }
139 }
140
141 protected float GetMinZoom()
142 {
143 if (m_Texture == null)
144 return 1.0f;
145 // Case 654327: Add k_MaxZoom size to min check to ensure that min zoom is smaller than max zoom
146 return Mathf.Min(m_TextureViewRect.width / m_Texture.width, m_TextureViewRect.height / m_Texture.height, k_MaxZoom) * k_MinZoomPercentage;
147 }
148
149 protected void HandleZoom()
150 {
151 bool zoomMode = UnityEvent.current.alt && UnityEvent.current.button == 1;
152 if (zoomMode)
153 {
154 EditorGUIUtility.AddCursorRect(m_TextureViewRect, MouseCursor.Zoom);
155 }
156
157 if (
158 ((UnityEvent.current.type == EventType.MouseUp || UnityEvent.current.type == EventType.MouseDown) && zoomMode) ||
159 ((UnityEvent.current.type == EventType.KeyUp || UnityEvent.current.type == EventType.KeyDown) && UnityEvent.current.keyCode == KeyCode.LeftAlt)
160 )
161 {
162 Repaint();
163 }
164
165 if (UnityEvent.current.type == EventType.ScrollWheel || (UnityEvent.current.type == EventType.MouseDrag && UnityEvent.current.alt && UnityEvent.current.button == 1))
166 {
167 float zoomMultiplier = 1f - UnityEvent.current.delta.y * (UnityEvent.current.type == EventType.ScrollWheel ? k_WheelZoomSpeed : -k_MouseZoomSpeed);
168
169 // Clamp zoom
170 float wantedZoom = m_Zoom * zoomMultiplier;
171
172 float currentZoom = Mathf.Clamp(wantedZoom, GetMinZoom(), k_MaxZoom);
173
174 if (currentZoom != m_Zoom)
175 {
176 m_Zoom = currentZoom;
177
178 // We need to fix zoomMultiplier if we clamped wantedZoom != currentZoom
179 if (wantedZoom != currentZoom)
180 zoomMultiplier /= wantedZoom / currentZoom;
181
182 Vector3 textureHalfSize = new Vector2(m_Texture.width, m_Texture.height) * 0.5f;
183 Vector3 mousePositionWorld = Handles.inverseMatrix.MultiplyPoint3x4(UnityEvent.current.mousePosition + m_ScrollPosition);
184 Vector3 delta = (mousePositionWorld - textureHalfSize) * (zoomMultiplier - 1f);
185
186 m_ScrollPosition += (Vector2)Handles.matrix.MultiplyVector(delta);
187
188 UnityEvent.current.Use();
189 }
190 }
191 }
192
193 protected void HandlePanning()
194 {
195 // You can pan by holding ALT and using left button or NOT holding ALT and using right button. ALT + right is reserved for zooming.
196 bool panMode = (!UnityEvent.current.alt && UnityEvent.current.button > 0 || UnityEvent.current.alt && UnityEvent.current.button <= 0);
197 if (panMode && GUIUtility.hotControl == 0)
198 {
199 EditorGUIUtility.AddCursorRect(m_TextureViewRect, MouseCursor.Pan);
200
201 if (UnityEvent.current.type == EventType.MouseDrag)
202 {
203 m_ScrollPosition -= UnityEvent.current.delta;
204 UnityEvent.current.Use();
205 }
206 }
207
208 //We need to repaint when entering or exiting the pan mode, so the mouse cursor gets refreshed.
209 if (
210 ((UnityEvent.current.type == EventType.MouseUp || UnityEvent.current.type == EventType.MouseDown) && panMode) ||
211 (UnityEvent.current.type == EventType.KeyUp || UnityEvent.current.type == EventType.KeyDown) && UnityEvent.current.keyCode == KeyCode.LeftAlt
212 )
213 {
214 Repaint();
215 }
216 }
217
218 // Bounding values for scrollbars. Changes with zoom, because we want min/max scroll to stop at texture edges.
219 protected Rect maxScrollRect
220 {
221 get
222 {
223 float halfWidth = m_Texture.width * .5f * m_Zoom;
224 float halfHeight = m_Texture.height * .5f * m_Zoom;
225 return new Rect(-halfWidth, -halfHeight, m_TextureViewRect.width + halfWidth * 2f, m_TextureViewRect.height + halfHeight * 2f);
226 }
227 }
228
229 // Max rect in texture space that can ever be visible
230 protected Rect maxRect
231 {
232 get
233 {
234 float marginW = m_TextureViewRect.width * .5f / GetMinZoom();
235 float marginH = m_TextureViewRect.height * .5f / GetMinZoom();
236 float left = -marginW;
237 float top = -marginH;
238 float width = m_Texture.width + marginW * 2f;
239 float height = m_Texture.height + marginH * 2f;
240 return new Rect(left, top, width, height);
241 }
242 }
243
244 protected void DrawTexturespaceBackground()
245 {
246 float size = Mathf.Max(maxRect.width, maxRect.height);
247 Vector2 offset = new Vector2(maxRect.xMin, maxRect.yMin);
248
249 float halfSize = size * .5f;
250 float alpha = EditorGUIUtility.isProSkin ? 0.15f : 0.08f;
251 float gridSize = 8f;
252
253 SpriteEditorUtility.BeginLines(new Color(0f, 0f, 0f, alpha));
254 for (float v = 0; v <= size; v += gridSize)
255 SpriteEditorUtility.DrawLine(new Vector2(-halfSize + v, halfSize + v) + offset, new Vector2(halfSize + v, -halfSize + v) + offset);
256 SpriteEditorUtility.EndLines();
257 }
258
259 private float Log2(float x)
260 {
261 return (float)(System.Math.Log(x) / System.Math.Log(2));
262 }
263
264 protected void DrawTexture()
265 {
266 float mipLevel = Mathf.Min(m_MipLevel, TextureUtil.GetMipmapCount(m_Texture) - 1);
267
268 if (m_ShowAlpha)
269 {
270 // check if we have a valid alpha texture
271 if (m_TextureAlphaOverride != null)
272 EditorGUI.DrawTextureTransparent(m_TextureRect, m_TextureAlphaOverride, ScaleMode.StretchToFill, 0, mipLevel);
273 // else use the original texture and display its alpha
274 else
275 EditorGUI.DrawTextureAlpha(m_TextureRect, m_Texture, ScaleMode.StretchToFill, 0, mipLevel);
276 }
277 else
278 EditorGUI.DrawTextureTransparent(m_TextureRect, m_Texture, ScaleMode.StretchToFill, 0, mipLevel);
279 }
280
281 protected void DrawScreenspaceBackground()
282 {
283 if (UnityEvent.current.type == EventType.Repaint)
284 m_Styles.preBackground.Draw(m_TextureViewRect, false, false, false, false);
285 }
286
287 protected void HandleScrollbars()
288 {
289 Rect horizontalScrollBarPosition = new Rect(m_TextureViewRect.xMin, m_TextureViewRect.yMax, m_TextureViewRect.width, k_ScrollbarMargin);
290 m_ScrollPosition.x = GUI.HorizontalScrollbar(horizontalScrollBarPosition, m_ScrollPosition.x, m_TextureViewRect.width, maxScrollRect.xMin, maxScrollRect.xMax);
291
292 Rect verticalScrollBarPosition = new Rect(m_TextureViewRect.xMax, m_TextureViewRect.yMin, k_ScrollbarMargin, m_TextureViewRect.height);
293 m_ScrollPosition.y = GUI.VerticalScrollbar(verticalScrollBarPosition, m_ScrollPosition.y, m_TextureViewRect.height, maxScrollRect.yMin, maxScrollRect.yMax);
294 }
295
296 protected void SetupHandlesMatrix()
297 {
298 // Offset from top left to center in view space
299 Vector3 handlesPos = new Vector3(m_TextureRect.x, m_TextureRect.yMax, 0f);
300 // We flip Y-scale because Unity texture space is bottom-up
301 Vector3 handlesScale = new Vector3(zoomLevel, -zoomLevel, 1f);
302
303 // Handle matrix is for converting between view and texture space coordinates, without taking account the scroll position.
304 // Scroll position is added separately so we can use it with GUIClip.
305 Handles.matrix = Matrix4x4.TRS(handlesPos, Quaternion.identity, handlesScale);
306 }
307
308 protected Rect DoAlphaZoomToolbarGUI(Rect area)
309 {
310 int mipCount = 1;
311 if (m_Texture != null)
312 mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(m_Texture));
313
314 Rect drawArea = new Rect(area.width, 0, 0, area.height);
315 using (new EditorGUI.DisabledScope(mipCount == 1))
316 {
317 drawArea.width = m_Styles.largeMip.image.width;
318 drawArea.x -= drawArea.width;
319 GUI.Box(drawArea, m_Styles.largeMip, m_Styles.preLabel);
320
321 drawArea.width = EditorGUI.kSliderMinW;
322 drawArea.x -= drawArea.width;
323 m_MipLevel = Mathf.Round(GUI.HorizontalSlider(drawArea, m_MipLevel, mipCount - 1, 0, m_Styles.preSlider, m_Styles.preSliderThumb));
324
325 drawArea.width = m_Styles.smallMip.image.width;
326 drawArea.x -= drawArea.width;
327 GUI.Box(drawArea, m_Styles.smallMip, m_Styles.preLabel);
328 }
329
330 drawArea.width = EditorGUI.kSliderMinW;
331 drawArea.x -= drawArea.width;
332 zoomLevel = GUI.HorizontalSlider(drawArea, zoomLevel, GetMinZoom(), k_MaxZoom, m_Styles.preSlider, m_Styles.preSliderThumb);
333
334 drawArea.width = EditorGUI.kObjectFieldMiniThumbnailWidth;
335 drawArea.x -= drawArea.width + EditorGUI.kSpacing;
336 m_ShowAlpha = GUI.Toggle(drawArea, m_ShowAlpha, m_ShowAlpha ? m_Styles.alphaIcon : m_Styles.RGBIcon, "toolbarButton");
337
338 // Returns the area that is not used
339 return new Rect(area.x, area.y, drawArea.x, area.height);
340 }
341
342 protected void DoTextureGUI()
343 {
344 if (m_Texture == null)
345 return;
346
347 // zoom startup init
348 if (m_Zoom < 0f)
349 m_Zoom = GetMinZoom();
350
351 // Texture rect in view space
352 m_TextureRect = new Rect(
353 m_TextureViewRect.width / 2f - (m_Texture.width * m_Zoom / 2f),
354 m_TextureViewRect.height / 2f - (m_Texture.height * m_Zoom / 2f),
355 (m_Texture.width * m_Zoom),
356 (m_Texture.height * m_Zoom)
357 );
358
359 HandleScrollbars();
360 SetupHandlesMatrix();
361 DrawScreenspaceBackground();
362
363 GUIClip.Push(m_TextureViewRect, -m_ScrollPosition, Vector2.zero, false);
364
365 if (UnityEvent.current.type == EventType.Repaint)
366 {
367 DrawTexturespaceBackground();
368 DrawTexture();
369 DrawGizmos();
370 }
371
372 DoTextureGUIExtras();
373
374 GUIClip.Pop();
375
376 // Handle this after DoTextureGUIExtras in case user wants any event that is handled by Zoom or Panning
377 HandleZoom();
378 HandlePanning();
379 }
380
381 protected virtual void DoTextureGUIExtras()
382 {
383 }
384
385 protected virtual void DrawGizmos()
386 {
387 }
388
389 protected void SetNewTexture(Texture2D texture)
390 {
391 if (texture != m_Texture)
392 {
393 m_Texture = new Texture2DWrapper(texture);
394 m_Zoom = -1;
395 m_TextureAlphaOverride = null;
396 }
397 }
398
399 protected void SetAlphaTextureOverride(Texture2D alphaTexture)
400 {
401 if (alphaTexture != m_TextureAlphaOverride)
402 {
403 m_TextureAlphaOverride = new Texture2DWrapper(alphaTexture);
404 m_Zoom = -1;
405 }
406 }
407
408 internal override void OnResized()
409 {
410 if (m_Texture != null && UnityEvent.current != null)
411 HandleZoom();
412 base.OnResized();
413 }
414
415 internal static void DrawToolBarWidget(ref Rect drawRect, ref Rect toolbarRect, Action<Rect> drawAction)
416 {
417 toolbarRect.width -= drawRect.width;
418 if (toolbarRect.width < 0)
419 drawRect.width += toolbarRect.width;
420
421 if (drawRect.width > 0)
422 drawAction(drawRect);
423 }
424 } // class
425}