123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047 |
- using UnityEngine;
- using UnityEditor;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- namespace UnityVersionControl
- {
- /// <summary>
- /// Advanced File Diff Viewer for Unity Assets
- /// Supports text diffs, visual comparisons, and property changes
- /// </summary>
- public class FileDiffViewer : EditorWindow
- {
- #region Fields
- private Vector2 scrollPosition;
- private int selectedViewMode = 0;
- private readonly string[] viewModeNames = { "Split View", "Unified Diff", "Visual Compare" };
-
- private string currentFilePath = "";
- private DiffData currentDiff;
- private Texture2D leftTexture;
- private Texture2D rightTexture;
-
- // UI state
- private bool showLineNumbers = true;
- private bool showWhitespace = false;
- private bool wordWrap = false;
- private int contextLines = 3;
- private string searchText = "";
-
- // Visual constants
- private static readonly Color backgroundColor = new Color(0.22f, 0.22f, 0.22f);
- private static readonly Color cardColor = new Color(0.28f, 0.28f, 0.28f);
- private static readonly Color addedColor = new Color(0.2f, 0.6f, 0.2f, 0.3f);
- private static readonly Color removedColor = new Color(0.6f, 0.2f, 0.2f, 0.3f);
- private static readonly Color modifiedColor = new Color(0.6f, 0.4f, 0.2f, 0.3f);
- private static readonly Color lineNumberColor = new Color(0.5f, 0.5f, 0.5f);
- private static readonly Color contextColor = new Color(0.8f, 0.8f, 0.8f);
-
- // Caches
- private readonly Dictionary<string, Texture2D> textureCache = new Dictionary<string, Texture2D>();
- private readonly Dictionary<string, DiffData> diffCache = new Dictionary<string, DiffData>();
- #endregion
- #region Unity Lifecycle
- [MenuItem("Window/Version Control/File Diff Viewer", false, 3)]
- public static void ShowWindow()
- {
- var window = GetWindow<FileDiffViewer>();
- window.titleContent = new GUIContent("File Diff");
- window.minSize = new Vector2(800, 600);
- }
- public static void ShowDiff(string filePath)
- {
- var window = GetWindow<FileDiffViewer>();
- window.titleContent = new GUIContent($"Diff: {System.IO.Path.GetFileName(filePath)}");
- window.minSize = new Vector2(800, 600);
- window.LoadFile(filePath);
- }
- private void OnEnable()
- {
- LoadTestData();
- }
- private void OnDisable()
- {
- CleanupTextures();
- }
- private void OnGUI()
- {
- DrawBackground();
- DrawToolbar();
- DrawDiffContent();
- }
- #endregion
- #region File Loading
- public void LoadFile(string filePath)
- {
- currentFilePath = filePath;
-
- if (diffCache.TryGetValue(filePath, out var cachedDiff))
- {
- currentDiff = cachedDiff;
- }
- else
- {
- currentDiff = GenerateDiff(filePath);
- diffCache[filePath] = currentDiff;
- }
-
- LoadAssetTextures(filePath);
- Repaint();
- }
- private void LoadTestData()
- {
- // Load a test diff for demonstration
- LoadFile("Assets/Scripts/PlayerController.cs");
- }
- private DiffData GenerateDiff(string filePath)
- {
- var extension = System.IO.Path.GetExtension(filePath).ToLower();
-
- switch (extension)
- {
- case ".cs":
- case ".js":
- case ".shader":
- case ".hlsl":
- case ".cginc":
- return GenerateCodeDiff(filePath);
-
- case ".unity":
- case ".prefab":
- case ".asset":
- case ".mat":
- return GenerateAssetDiff(filePath);
-
- case ".png":
- case ".jpg":
- case ".jpeg":
- case ".tga":
- return GenerateImageDiff(filePath);
-
- default:
- return GenerateTextDiff(filePath);
- }
- }
- private DiffData GenerateCodeDiff(string filePath)
- {
- // Mock C# code diff
- var diff = new DiffData
- {
- filePath = filePath,
- diffType = DiffType.Code,
- leftContent = GetMockCodeContent(true),
- rightContent = GetMockCodeContent(false),
- lines = new List<DiffLine>()
- };
-
- diff.lines = GenerateDiffLines(diff.leftContent, diff.rightContent);
- return diff;
- }
- private DiffData GenerateAssetDiff(string filePath)
- {
- // Mock Unity asset property diff
- var diff = new DiffData
- {
- filePath = filePath,
- diffType = DiffType.Properties,
- leftContent = GetMockAssetContent(true),
- rightContent = GetMockAssetContent(false),
- propertyChanges = GeneratePropertyChanges()
- };
-
- return diff;
- }
- private DiffData GenerateImageDiff(string filePath)
- {
- // Mock image diff
- var diff = new DiffData
- {
- filePath = filePath,
- diffType = DiffType.Image,
- leftImagePath = "Assets/Textures/PlayerTexture_Old.png",
- rightImagePath = "Assets/Textures/PlayerTexture_New.png"
- };
-
- return diff;
- }
- private DiffData GenerateTextDiff(string filePath)
- {
- // Generic text diff
- var diff = new DiffData
- {
- filePath = filePath,
- diffType = DiffType.Text,
- leftContent = "Original content line 1\nOriginal content line 2\nShared line 3\nOriginal line 4",
- rightContent = "Modified content line 1\nNew line 2\nShared line 3\nModified line 4\nNew line 5"
- };
-
- diff.lines = GenerateDiffLines(diff.leftContent, diff.rightContent);
- return diff;
- }
- private void LoadAssetTextures(string filePath)
- {
- if (currentDiff?.diffType == DiffType.Image)
- {
- leftTexture = LoadTextureFromPath(currentDiff.leftImagePath);
- rightTexture = LoadTextureFromPath(currentDiff.rightImagePath);
- }
- }
- private Texture2D LoadTextureFromPath(string path)
- {
- try
- {
- return AssetDatabase.LoadAssetAtPath<Texture2D>(path);
- }
- catch
- {
- // Return a placeholder texture
- return CreateSolidColorTexture(Color.gray, 256, 256);
- }
- }
- #endregion
- #region Mock Data Generation
- private string GetMockCodeContent(bool isOld)
- {
- if (isOld)
- {
- return @"using UnityEngine;
- public class PlayerController : MonoBehaviour
- {
- public float speed = 5.0f;
- public float jumpForce = 8.0f;
-
- private Rigidbody rb;
-
- void Start()
- {
- rb = GetComponent<Rigidbody>();
- }
-
- void Update()
- {
- float horizontal = Input.GetAxis(""Horizontal"");
- float vertical = Input.GetAxis(""Vertical"");
-
- Vector3 movement = new Vector3(horizontal, 0, vertical);
- transform.Translate(movement * speed * Time.deltaTime);
-
- if (Input.GetKeyDown(KeyCode.Space))
- {
- rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
- }
- }
- }";
- }
- else
- {
- return @"using UnityEngine;
- public class PlayerController : MonoBehaviour
- {
- [Header(""Movement Settings"")]
- public float speed = 7.0f;
- public float jumpForce = 10.0f;
- public float groundCheckDistance = 0.1f;
-
- private Rigidbody rb;
- private bool isGrounded;
-
- void Start()
- {
- rb = GetComponent<Rigidbody>();
- }
-
- void Update()
- {
- CheckGrounded();
- HandleMovement();
- HandleJumping();
- }
-
- void CheckGrounded()
- {
- isGrounded = Physics.Raycast(transform.position, Vector3.down, groundCheckDistance);
- }
-
- void HandleMovement()
- {
- float horizontal = Input.GetAxis(""Horizontal"");
- float vertical = Input.GetAxis(""Vertical"");
-
- Vector3 movement = new Vector3(horizontal, 0, vertical).normalized;
- rb.velocity = new Vector3(movement.x * speed, rb.velocity.y, movement.z * speed);
- }
-
- void HandleJumping()
- {
- if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
- {
- rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
- }
- }
- }";
- }
- }
- private string GetMockAssetContent(bool isOld)
- {
- if (isOld)
- {
- return @"Transform:
- position: {x: 0, y: 0, z: 0}
- rotation: {x: 0, y: 0, z: 0, w: 1}
- scale: {x: 1, y: 1, z: 1}
- MeshRenderer:
- material: PlayerMaterial
- receiveShadows: 1
- BoxCollider:
- size: {x: 1, y: 1, z: 1}
- center: {x: 0, y: 0, z: 0}";
- }
- else
- {
- return @"Transform:
- position: {x: 0, y: 0.5, z: 0}
- rotation: {x: 0, y: 0, z: 0, w: 1}
- scale: {x: 1.2, y: 1.2, z: 1.2}
- MeshRenderer:
- material: PlayerMaterial_Updated
- receiveShadows: 1
- shadowCastingMode: 1
- BoxCollider:
- size: {x: 1.1, y: 1.1, z: 1.1}
- center: {x: 0, y: 0, z: 0}
- isTrigger: 0
- Rigidbody:
- mass: 1
- drag: 0.5";
- }
- }
- private List<PropertyChange> GeneratePropertyChanges()
- {
- return new List<PropertyChange>
- {
- new PropertyChange
- {
- propertyName = "Transform.position.y",
- oldValue = "0",
- newValue = "0.5",
- changeType = PropertyChangeType.Modified
- },
- new PropertyChange
- {
- propertyName = "Transform.scale",
- oldValue = "{x: 1, y: 1, z: 1}",
- newValue = "{x: 1.2, y: 1.2, z: 1.2}",
- changeType = PropertyChangeType.Modified
- },
- new PropertyChange
- {
- propertyName = "MeshRenderer.material",
- oldValue = "PlayerMaterial",
- newValue = "PlayerMaterial_Updated",
- changeType = PropertyChangeType.Modified
- },
- new PropertyChange
- {
- propertyName = "MeshRenderer.shadowCastingMode",
- oldValue = "",
- newValue = "1",
- changeType = PropertyChangeType.Added
- },
- new PropertyChange
- {
- propertyName = "BoxCollider.size",
- oldValue = "{x: 1, y: 1, z: 1}",
- newValue = "{x: 1.1, y: 1.1, z: 1.1}",
- changeType = PropertyChangeType.Modified
- },
- new PropertyChange
- {
- propertyName = "Rigidbody",
- oldValue = "",
- newValue = "Component Added",
- changeType = PropertyChangeType.Added
- }
- };
- }
- #endregion
- #region GUI Drawing
- private void DrawBackground()
- {
- var rect = new Rect(0, 0, position.width, position.height);
- EditorGUI.DrawRect(rect, backgroundColor);
- }
- private void DrawToolbar()
- {
- DrawCard(() =>
- {
- using (new EditorGUILayout.HorizontalScope())
- {
- // File info
- using (new EditorGUILayout.VerticalScope())
- {
- var titleStyle = new GUIStyle(EditorStyles.boldLabel);
- titleStyle.fontSize = 14;
- titleStyle.normal.textColor = Color.white;
- EditorGUILayout.LabelField(System.IO.Path.GetFileName(currentFilePath), titleStyle);
-
- var pathStyle = new GUIStyle(EditorStyles.label);
- pathStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f);
- pathStyle.fontSize = 11;
- EditorGUILayout.LabelField(currentFilePath, pathStyle);
- }
-
- GUILayout.FlexibleSpace();
-
- // View mode selector
- EditorGUILayout.LabelField("View:", GUILayout.Width(40));
- selectedViewMode = EditorGUILayout.Popup(selectedViewMode, viewModeNames, GUILayout.Width(120));
-
- GUILayout.Space(8);
-
- // Options
- showLineNumbers = EditorGUILayout.Toggle("Lines", showLineNumbers, GUILayout.Width(60));
- showWhitespace = EditorGUILayout.Toggle("Space", showWhitespace, GUILayout.Width(60));
- wordWrap = EditorGUILayout.Toggle("Wrap", wordWrap, GUILayout.Width(60));
- }
-
- EditorGUILayout.Space(4);
-
- // Search bar
- using (new EditorGUILayout.HorizontalScope())
- {
- EditorGUILayout.LabelField("Search:", GUILayout.Width(50));
- searchText = EditorGUILayout.TextField(searchText);
-
- if (GUILayout.Button("Clear", GUILayout.Width(50)))
- searchText = "";
- }
- }, 12);
- }
- private void DrawDiffContent()
- {
- if (currentDiff == null)
- {
- DrawEmptyState();
- return;
- }
-
- EditorGUILayout.Space(8);
-
- switch (selectedViewMode)
- {
- case 0: DrawSplitView(); break;
- case 1: DrawUnifiedView(); break;
- case 2: DrawVisualCompare(); break;
- }
- }
- private void DrawEmptyState()
- {
- DrawCard(() =>
- {
- var iconRect = GUILayoutUtility.GetRect(48, 48);
- var emptyTexture = CreateSolidColorTexture(new Color(0.5f, 0.5f, 0.5f, 0.3f), 48, 48);
- GUI.DrawTexture(iconRect, emptyTexture);
-
- EditorGUILayout.Space(8);
-
- var titleStyle = new GUIStyle(EditorStyles.boldLabel);
- titleStyle.alignment = TextAnchor.MiddleCenter;
- titleStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f);
- EditorGUILayout.LabelField("No File Selected", titleStyle);
-
- var subtitleStyle = new GUIStyle(EditorStyles.label);
- subtitleStyle.alignment = TextAnchor.MiddleCenter;
- subtitleStyle.normal.textColor = new Color(0.6f, 0.6f, 0.6f);
- subtitleStyle.wordWrap = true;
- EditorGUILayout.LabelField("Select a modified file to view its diff.", subtitleStyle);
- }, 32);
- }
- private void DrawSplitView()
- {
- using (var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition))
- {
- scrollPosition = scrollView.scrollPosition;
-
- switch (currentDiff.diffType)
- {
- case DiffType.Code:
- case DiffType.Text:
- DrawCodeSplitView();
- break;
- case DiffType.Properties:
- DrawPropertySplitView();
- break;
- case DiffType.Image:
- DrawImageSplitView();
- break;
- }
- }
- }
- private void DrawCodeSplitView()
- {
- DrawCard(() =>
- {
- using (new EditorGUILayout.HorizontalScope())
- {
- // Left panel header
- using (new EditorGUILayout.VerticalScope())
- {
- var headerStyle = new GUIStyle(EditorStyles.boldLabel);
- headerStyle.normal.textColor = removedColor;
- headerStyle.alignment = TextAnchor.MiddleCenter;
- EditorGUILayout.LabelField("BEFORE", headerStyle);
-
- DrawCodePanel(currentDiff.leftContent, true);
- }
-
- GUILayout.Space(8);
-
- // Right panel header
- using (new EditorGUILayout.VerticalScope())
- {
- var headerStyle = new GUIStyle(EditorStyles.boldLabel);
- headerStyle.normal.textColor = addedColor;
- headerStyle.alignment = TextAnchor.MiddleCenter;
- EditorGUILayout.LabelField("AFTER", headerStyle);
-
- DrawCodePanel(currentDiff.rightContent, false);
- }
- }
- }, 12);
- }
- private void DrawCodePanel(string content, bool isLeft)
- {
- var lines = content.Split('\n');
- var codeStyle = new GUIStyle(EditorStyles.textArea);
- codeStyle.wordWrap = wordWrap;
- codeStyle.richText = true;
- codeStyle.font = EditorGUIUtility.Load("Consolas") as Font ?? GUI.skin.font;
-
- for (int i = 0; i < lines.Length; i++)
- {
- using (new EditorGUILayout.HorizontalScope())
- {
- if (showLineNumbers)
- {
- var lineNumStyle = new GUIStyle(EditorStyles.label);
- lineNumStyle.normal.textColor = lineNumberColor;
- lineNumStyle.fontSize = 10;
- lineNumStyle.alignment = TextAnchor.MiddleRight;
- EditorGUILayout.LabelField((i + 1).ToString(), lineNumStyle, GUILayout.Width(30));
- }
-
- var line = lines[i];
- if (!string.IsNullOrEmpty(searchText) && line.Contains(searchText))
- {
- line = line.Replace(searchText, $"<color=yellow>{searchText}</color>");
- }
-
- var lineRect = EditorGUILayout.GetControlRect();
-
- // Highlight changed lines
- var diffLine = GetDiffLineForIndex(i, isLeft);
- if (diffLine != null)
- {
- Color bgColor = diffLine.type switch
- {
- DiffLineType.Added => addedColor,
- DiffLineType.Removed => removedColor,
- DiffLineType.Modified => modifiedColor,
- _ => Color.clear
- };
-
- if (bgColor != Color.clear)
- {
- EditorGUI.DrawRect(lineRect, bgColor);
- }
- }
-
- EditorGUI.LabelField(lineRect, line, codeStyle);
- }
- }
- }
- private void DrawPropertySplitView()
- {
- DrawCard(() =>
- {
- using (new EditorGUILayout.HorizontalScope())
- {
- // Properties comparison
- using (new EditorGUILayout.VerticalScope())
- {
- var headerStyle = new GUIStyle(EditorStyles.boldLabel);
- headerStyle.alignment = TextAnchor.MiddleCenter;
- EditorGUILayout.LabelField("PROPERTY CHANGES", headerStyle);
-
- EditorGUILayout.Space(8);
-
- foreach (var change in currentDiff.propertyChanges)
- {
- DrawPropertyChange(change);
- EditorGUILayout.Space(4);
- }
- }
- }
- }, 12);
- }
- private void DrawPropertyChange(PropertyChange change)
- {
- var changeRect = EditorGUILayout.BeginHorizontal();
-
- Color bgColor = change.changeType switch
- {
- PropertyChangeType.Added => addedColor,
- PropertyChangeType.Removed => removedColor,
- PropertyChangeType.Modified => modifiedColor,
- _ => Color.clear
- };
-
- if (bgColor != Color.clear)
- {
- EditorGUI.DrawRect(changeRect, bgColor);
- }
-
- GUILayout.Space(8);
-
- // Property name
- var nameStyle = new GUIStyle(EditorStyles.boldLabel);
- nameStyle.normal.textColor = Color.white;
- nameStyle.fontSize = 11;
- EditorGUILayout.LabelField(change.propertyName, nameStyle, GUILayout.Width(200));
-
- // Change indicator
- var changeIcon = change.changeType switch
- {
- PropertyChangeType.Added => "+",
- PropertyChangeType.Removed => "-",
- PropertyChangeType.Modified => "⟳",
- _ => "?"
- };
-
- var iconStyle = new GUIStyle(EditorStyles.label);
- iconStyle.normal.textColor = change.changeType switch
- {
- PropertyChangeType.Added => Color.green,
- PropertyChangeType.Removed => Color.red,
- PropertyChangeType.Modified => Color.yellow,
- _ => Color.white
- };
- EditorGUILayout.LabelField(changeIcon, iconStyle, GUILayout.Width(20));
-
- // Values
- using (new EditorGUILayout.VerticalScope())
- {
- if (!string.IsNullOrEmpty(change.oldValue))
- {
- var oldStyle = new GUIStyle(EditorStyles.label);
- oldStyle.normal.textColor = new Color(1f, 0.7f, 0.7f);
- oldStyle.fontSize = 10;
- EditorGUILayout.LabelField($"- {change.oldValue}", oldStyle);
- }
-
- if (!string.IsNullOrEmpty(change.newValue))
- {
- var newStyle = new GUIStyle(EditorStyles.label);
- newStyle.normal.textColor = new Color(0.7f, 1f, 0.7f);
- newStyle.fontSize = 10;
- EditorGUILayout.LabelField($"+ {change.newValue}", newStyle);
- }
- }
-
- GUILayout.Space(8);
- EditorGUILayout.EndHorizontal();
- }
- private void DrawImageSplitView()
- {
- DrawCard(() =>
- {
- using (new EditorGUILayout.HorizontalScope())
- {
- // Left image
- using (new EditorGUILayout.VerticalScope())
- {
- var headerStyle = new GUIStyle(EditorStyles.boldLabel);
- headerStyle.normal.textColor = removedColor;
- headerStyle.alignment = TextAnchor.MiddleCenter;
- EditorGUILayout.LabelField("BEFORE", headerStyle);
-
- DrawImagePanel(leftTexture, "Old Version");
- }
-
- GUILayout.Space(8);
-
- // Right image
- using (new EditorGUILayout.VerticalScope())
- {
- var headerStyle = new GUIStyle(EditorStyles.boldLabel);
- headerStyle.normal.textColor = addedColor;
- headerStyle.alignment = TextAnchor.MiddleCenter;
- EditorGUILayout.LabelField("AFTER", headerStyle);
-
- DrawImagePanel(rightTexture, "New Version");
- }
- }
- }, 12);
- }
- private void DrawImagePanel(Texture2D texture, string label)
- {
- if (texture != null)
- {
- var maxSize = 300f;
- var aspect = (float)texture.width / texture.height;
- var displayWidth = aspect > 1 ? maxSize : maxSize * aspect;
- var displayHeight = aspect > 1 ? maxSize / aspect : maxSize;
-
- var imageRect = GUILayoutUtility.GetRect(displayWidth, displayHeight);
- EditorGUI.DrawRect(imageRect, Color.black);
- GUI.DrawTexture(imageRect, texture, ScaleMode.ScaleToFit);
-
- // Image info
- var infoStyle = new GUIStyle(EditorStyles.label);
- infoStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f);
- infoStyle.fontSize = 10;
- infoStyle.alignment = TextAnchor.MiddleCenter;
- EditorGUILayout.LabelField($"{texture.width}x{texture.height}", infoStyle);
- }
- else
- {
- var placeholderRect = GUILayoutUtility.GetRect(300, 200);
- EditorGUI.DrawRect(placeholderRect, new Color(0.3f, 0.3f, 0.3f));
-
- var labelStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
- labelStyle.normal.textColor = Color.white;
- GUI.Label(placeholderRect, label, labelStyle);
- }
- }
- private void DrawUnifiedView()
- {
- using (var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition))
- {
- scrollPosition = scrollView.scrollPosition;
-
- DrawCard(() =>
- {
- if (currentDiff.lines != null && currentDiff.lines.Count > 0)
- {
- DrawUnifiedDiff();
- }
- else
- {
- var noChangesStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
- noChangesStyle.normal.textColor = Color.white;
- EditorGUILayout.LabelField("No textual changes to display", noChangesStyle);
- }
- }, 12);
- }
- }
- private void DrawUnifiedDiff()
- {
- var codeStyle = new GUIStyle(EditorStyles.textArea);
- codeStyle.wordWrap = wordWrap;
- codeStyle.richText = true;
- codeStyle.font = EditorGUIUtility.Load("Consolas") as Font ?? GUI.skin.font;
-
- foreach (var line in currentDiff.lines)
- {
- using (new EditorGUILayout.HorizontalScope())
- {
- if (showLineNumbers)
- {
- var lineNumStyle = new GUIStyle(EditorStyles.label);
- lineNumStyle.normal.textColor = lineNumberColor;
- lineNumStyle.fontSize = 10;
- lineNumStyle.alignment = TextAnchor.MiddleRight;
-
- var lineNumber = line.type == DiffLineType.Removed ? line.leftLineNumber : line.rightLineNumber;
- EditorGUILayout.LabelField(lineNumber > 0 ? lineNumber.ToString() : "", lineNumStyle, GUILayout.Width(30));
- }
-
- var lineRect = EditorGUILayout.GetControlRect();
-
- // Background color based on line type
- Color bgColor = line.type switch
- {
- DiffLineType.Added => addedColor,
- DiffLineType.Removed => removedColor,
- DiffLineType.Modified => modifiedColor,
- _ => Color.clear
- };
-
- if (bgColor != Color.clear)
- {
- EditorGUI.DrawRect(lineRect, bgColor);
- }
-
- // Line prefix
- var prefix = line.type switch
- {
- DiffLineType.Added => "+ ",
- DiffLineType.Removed => "- ",
- DiffLineType.Modified => "~ ",
- _ => " "
- };
-
- var displayText = prefix + line.content;
- if (!string.IsNullOrEmpty(searchText) && line.content.Contains(searchText))
- {
- displayText = displayText.Replace(searchText, $"<color=yellow>{searchText}</color>");
- }
-
- EditorGUI.LabelField(lineRect, displayText, codeStyle);
- }
- }
- }
- private void DrawVisualCompare()
- {
- using (var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition))
- {
- scrollPosition = scrollView.scrollPosition;
-
- if (currentDiff.diffType == DiffType.Image)
- {
- DrawImageOverlay();
- }
- else
- {
- DrawSideBySideComparison();
- }
- }
- }
- private void DrawImageOverlay()
- {
- DrawCard(() =>
- {
- EditorGUILayout.LabelField("Image Overlay Comparison", EditorStyles.boldLabel);
- EditorGUILayout.Space(8);
-
- // TODO: Implement image overlay/blend comparison
- if (leftTexture != null && rightTexture != null)
- {
- var maxSize = 400f;
- var imageRect = GUILayoutUtility.GetRect(maxSize, maxSize);
-
- // For now, just show both images side by side
- var leftRect = new Rect(imageRect.x, imageRect.y, imageRect.width * 0.5f, imageRect.height);
- var rightRect = new Rect(imageRect.x + imageRect.width * 0.5f, imageRect.y, imageRect.width * 0.5f, imageRect.height);
-
- GUI.DrawTexture(leftRect, leftTexture, ScaleMode.ScaleToFit);
- GUI.DrawTexture(rightRect, rightTexture, ScaleMode.ScaleToFit);
- }
- else
- {
- EditorGUILayout.LabelField("Image overlay comparison not available", EditorStyles.centeredGreyMiniLabel);
- }
- }, 12);
- }
- private void DrawSideBySideComparison()
- {
- DrawSplitView(); // Fallback to split view for non-image assets
- }
- #endregion
- #region Helper Methods
- private void DrawCard(System.Action content, int padding = 12)
- {
- var rect = EditorGUILayout.BeginVertical();
- EditorGUI.DrawRect(rect, cardColor);
-
- GUILayout.Space(padding);
- EditorGUILayout.BeginHorizontal();
- GUILayout.Space(padding);
- EditorGUILayout.BeginVertical();
-
- content?.Invoke();
-
- EditorGUILayout.EndVertical();
- GUILayout.Space(padding);
- EditorGUILayout.EndHorizontal();
- GUILayout.Space(padding);
-
- EditorGUILayout.EndVertical();
- }
- private List<DiffLine> GenerateDiffLines(string leftContent, string rightContent)
- {
- var leftLines = leftContent.Split('\n');
- var rightLines = rightContent.Split('\n');
- var result = new List<DiffLine>();
-
- // Simple diff algorithm - in production, use a proper diff library
- var maxLines = Mathf.Max(leftLines.Length, rightLines.Length);
-
- for (int i = 0; i < maxLines; i++)
- {
- var leftLine = i < leftLines.Length ? leftLines[i] : null;
- var rightLine = i < rightLines.Length ? rightLines[i] : null;
-
- if (leftLine == null)
- {
- result.Add(new DiffLine
- {
- content = rightLine,
- type = DiffLineType.Added,
- leftLineNumber = 0,
- rightLineNumber = i + 1
- });
- }
- else if (rightLine == null)
- {
- result.Add(new DiffLine
- {
- content = leftLine,
- type = DiffLineType.Removed,
- leftLineNumber = i + 1,
- rightLineNumber = 0
- });
- }
- else if (leftLine == rightLine)
- {
- result.Add(new DiffLine
- {
- content = leftLine,
- type = DiffLineType.Context,
- leftLineNumber = i + 1,
- rightLineNumber = i + 1
- });
- }
- else
- {
- result.Add(new DiffLine
- {
- content = rightLine,
- type = DiffLineType.Modified,
- leftLineNumber = i + 1,
- rightLineNumber = i + 1
- });
- }
- }
-
- return result;
- }
- private DiffLine GetDiffLineForIndex(int index, bool isLeft)
- {
- if (currentDiff?.lines == null) return null;
-
- return currentDiff.lines.FirstOrDefault(line =>
- isLeft ? line.leftLineNumber == index + 1 : line.rightLineNumber == index + 1);
- }
- private Texture2D CreateSolidColorTexture(Color color, int width, int height)
- {
- var texture = new Texture2D(width, height);
- var pixels = new Color[width * height];
- for (int i = 0; i < pixels.Length; i++)
- pixels[i] = color;
- texture.SetPixels(pixels);
- texture.Apply();
- return texture;
- }
- private void CleanupTextures()
- {
- foreach (var texture in textureCache.Values)
- {
- if (texture != null) DestroyImmediate(texture);
- }
- textureCache.Clear();
- }
- #endregion
- }
- #region Data Models
- [Serializable]
- public class DiffData
- {
- public string filePath;
- public DiffType diffType;
- public string leftContent;
- public string rightContent;
- public string leftImagePath;
- public string rightImagePath;
- public List<DiffLine> lines;
- public List<PropertyChange> propertyChanges;
- }
- [Serializable]
- public class DiffLine
- {
- public string content;
- public DiffLineType type;
- public int leftLineNumber;
- public int rightLineNumber;
- }
- [Serializable]
- public class PropertyChange
- {
- public string propertyName;
- public string oldValue;
- public string newValue;
- public PropertyChangeType changeType;
- }
- public enum DiffType
- {
- Text,
- Code,
- Properties,
- Image,
- Binary
- }
- public enum DiffLineType
- {
- Context,
- Added,
- Removed,
- Modified
- }
- public enum PropertyChangeType
- {
- Added,
- Removed,
- Modified
- }
- #endregion
- }
|