using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; using System.Linq; namespace UnityVersionControl { /// /// Enhanced Visual Conflict Resolution Window for Unity Version Control /// Provides rich visual comparison and resolution for different asset types /// public class VersionControlConflictResolver : EditorWindow { #region Fields private Vector2 conflictListScrollPosition; private Vector2 viewerScrollPosition; private Vector2 leftScrollPosition; private Vector2 rightScrollPosition; private int selectedConflictIndex = 0; private readonly List mockConflicts = new List(); private ConflictViewer currentViewer; // Enhanced visual comparison private int selectedViewMode = 0; private readonly string[] viewModeNames = { "Side by Side", "3D Preview", "Properties", "Overlay" }; private float splitRatio = 0.5f; private bool isDraggingSplitter = false; // 3D Preview system private Camera previewCameraLeft; private Camera previewCameraRight; private RenderTexture leftRenderTexture; private RenderTexture rightRenderTexture; private GameObject leftPreviewObject; private GameObject rightPreviewObject; private GameObject previewEnvironment; private Light previewLight; // UI State private bool showLineNumbers = true; private bool showWhitespace = false; private bool wordWrap = false; private string searchText = ""; private Vector3 cameraRotation = new Vector3(15f, -30f, 0f); private float cameraDistance = 3f; // Static callback for when conflicts are resolved private static System.Action onConflictsResolved; // 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 accentColor = new Color(0.3f, 0.7f, 1f); private static readonly Color successColor = new Color(0.3f, 0.8f, 0.3f); private static readonly Color warningColor = new Color(1f, 0.8f, 0.2f); private static readonly Color errorColor = new Color(1f, 0.4f, 0.4f); private static readonly Color localColor = new Color(0.4f, 0.8f, 0.4f); private static readonly Color remoteColor = new Color(0.4f, 0.6f, 1f); private static readonly Color conflictColor = new Color(1f, 0.6f, 0.2f); private static readonly Color selectedColor = new Color(0.2f, 0.5f, 0.8f, 0.3f); // Caches private readonly Dictionary textureCache = new Dictionary(); private readonly Dictionary materialCache = new Dictionary(); private readonly Dictionary prefabCache = new Dictionary(); private readonly List currentPropertyDifferences = new List(); #endregion #region Unity Lifecycle [MenuItem("Window/Version Control/Conflict Resolver", false, 1)] public static void ShowWindow() { var window = GetWindow(); window.minSize = new Vector2(1000, 700); window.Initialize(); } public static void ShowWindow(System.Action onResolved) { onConflictsResolved = onResolved; var window = GetWindow("Visual Conflict Resolver"); window.minSize = new Vector2(1000, 700); window.Initialize(); } private void OnEnable() { Initialize(); Setup3DPreviewSystem(); } private void OnDisable() { CleanupTextures(); Cleanup3DPreviewSystem(); // Clean up callback to prevent memory leaks if (onConflictsResolved != null) { Debug.LogWarning("Conflict resolver closed without resolving conflicts - cleaning up callback"); onConflictsResolved = null; } } private void OnGUI() { try { HandleEvents(); DrawBackground(); DrawEnhancedHeader(); DrawMainLayout(); } catch (Exception ex) { Debug.LogError($"Error in ConflictResolver OnGUI: {ex.Message}"); DrawFallbackUI(); } } #endregion #region Initialization private void Initialize() { CreateMockConflicts(); if (mockConflicts.Count > 0) { SelectConflict(0); } } private void CreateMockConflicts() { try { mockConflicts.Clear(); // Script conflict mockConflicts.Add(new MockConflictInfo { fileName = "PlayerController.cs", assetType = ConflictAssetType.Script, conflictType = ConflictType.PropertyValueDifference, description = "Method implementation differs", localVersion = "public float speed = 5.0f;\npublic void Move() {\n // Local implementation\n transform.Translate(Vector3.forward * speed);\n}", remoteVersion = "public float speed = 7.0f;\npublic void Move() {\n // Remote implementation\n rb.velocity = Vector3.forward * speed;\n}", resolution = ConflictResolution.Unresolved }); // Prefab conflict mockConflicts.Add(new MockConflictInfo { fileName = "PlayerPrefab.prefab", assetType = ConflictAssetType.Prefab, conflictType = ConflictType.ComponentAdded, description = "Components differ between versions", localVersion = "Components: Transform, MeshRenderer, Rigidbody, BoxCollider", remoteVersion = "Components: Transform, MeshRenderer, Rigidbody, AudioSource, ParticleSystem", resolution = ConflictResolution.Unresolved }); // Material conflict mockConflicts.Add(new MockConflictInfo { fileName = "PlayerMaterial.mat", assetType = ConflictAssetType.Material, conflictType = ConflictType.PropertyValueDifference, description = "Material properties have different values", localVersion = "Albedo: Red (1,0,0), Metallic: 0.5, Smoothness: 0.8", remoteVersion = "Albedo: Blue (0,0,1), Metallic: 0.2, Smoothness: 0.6", resolution = ConflictResolution.Unresolved }); // Scene conflict mockConflicts.Add(new MockConflictInfo { fileName = "MainScene.unity", assetType = ConflictAssetType.Scene, conflictType = ConflictType.GameObjectMoved, description = "GameObject positions and lighting differ", localVersion = "Camera: (0,10,0), Lighting: Realtime GI", remoteVersion = "Camera: (5,10,-5), Lighting: Baked GI", resolution = ConflictResolution.Unresolved }); // Texture conflict mockConflicts.Add(new MockConflictInfo { fileName = "CharacterTexture.png", assetType = ConflictAssetType.Texture, conflictType = ConflictType.AssetReplaced, description = "Different texture versions", localVersion = "Local texture: 512x512, RGB format", remoteVersion = "Remote texture: 1024x1024, RGBA format", resolution = ConflictResolution.Unresolved }); } catch (Exception ex) { Debug.LogError($"Error creating mock conflicts: {ex.Message}"); } } private void CleanupTextures() { foreach (var texture in textureCache.Values) { if (texture != null) DestroyImmediate(texture); } textureCache.Clear(); } #endregion #region 3D Preview System private void Setup3DPreviewSystem() { try { // Create preview environment previewEnvironment = new GameObject("ConflictResolver_PreviewEnvironment"); previewEnvironment.hideFlags = HideFlags.HideAndDontSave; // Setup lighting var lightGO = new GameObject("PreviewLight"); lightGO.transform.SetParent(previewEnvironment.transform); lightGO.hideFlags = HideFlags.HideAndDontSave; previewLight = lightGO.AddComponent(); previewLight.type = LightType.Directional; previewLight.intensity = 1.0f; previewLight.color = Color.white; previewLight.transform.rotation = Quaternion.Euler(50f, -30f, 0f); // Setup left camera var leftCameraGO = new GameObject("PreviewCamera_Left"); leftCameraGO.transform.SetParent(previewEnvironment.transform); leftCameraGO.hideFlags = HideFlags.HideAndDontSave; previewCameraLeft = leftCameraGO.AddComponent(); previewCameraLeft.clearFlags = CameraClearFlags.SolidColor; previewCameraLeft.backgroundColor = new Color(0.1f, 0.1f, 0.1f); previewCameraLeft.cullingMask = 1 << 30; // Layer 30 for left previews // Setup right camera var rightCameraGO = new GameObject("PreviewCamera_Right"); rightCameraGO.transform.SetParent(previewEnvironment.transform); rightCameraGO.hideFlags = HideFlags.HideAndDontSave; previewCameraRight = rightCameraGO.AddComponent(); previewCameraRight.clearFlags = CameraClearFlags.SolidColor; previewCameraRight.backgroundColor = new Color(0.1f, 0.1f, 0.1f); previewCameraRight.cullingMask = 1 << 31; // Layer 31 for right previews // Create render textures leftRenderTexture = new RenderTexture(256, 256, 16); rightRenderTexture = new RenderTexture(256, 256, 16); previewCameraLeft.targetTexture = leftRenderTexture; previewCameraRight.targetTexture = rightRenderTexture; // Position cameras UpdateCameraPositions(); } catch (Exception ex) { Debug.LogError($"Failed to setup 3D preview system: {ex.Message}"); } } private void UpdateCameraPositions() { if (previewCameraLeft != null && previewCameraRight != null) { var position = Quaternion.Euler(cameraRotation) * Vector3.back * cameraDistance; previewCameraLeft.transform.position = position + Vector3.left * 2f; previewCameraLeft.transform.LookAt(Vector3.left * 2f); previewCameraRight.transform.position = position + Vector3.right * 2f; previewCameraRight.transform.LookAt(Vector3.right * 2f); } } private void Cleanup3DPreviewSystem() { try { if (leftRenderTexture != null) { leftRenderTexture.Release(); DestroyImmediate(leftRenderTexture); leftRenderTexture = null; } if (rightRenderTexture != null) { rightRenderTexture.Release(); DestroyImmediate(rightRenderTexture); rightRenderTexture = null; } if (leftPreviewObject != null) { DestroyImmediate(leftPreviewObject); leftPreviewObject = null; } if (rightPreviewObject != null) { DestroyImmediate(rightPreviewObject); rightPreviewObject = null; } if (previewEnvironment != null) { DestroyImmediate(previewEnvironment); previewEnvironment = null; } previewCameraLeft = null; previewCameraRight = null; previewLight = null; } catch (Exception ex) { Debug.LogError($"Failed to cleanup 3D preview system: {ex.Message}"); } } private void RefreshPreviewObjects() { if (selectedViewMode == 1 && selectedConflictIndex >= 0 && selectedConflictIndex < mockConflicts.Count) { var conflict = mockConflicts[selectedConflictIndex]; LoadPreviewObjects(conflict); } } private void LoadPreviewObjects(MockConflictInfo conflict) { try { // Clean up existing preview objects if (leftPreviewObject != null) DestroyImmediate(leftPreviewObject); if (rightPreviewObject != null) DestroyImmediate(rightPreviewObject); if (conflict.assetType == ConflictAssetType.Prefab) { leftPreviewObject = CreateMockPrefab(true); rightPreviewObject = CreateMockPrefab(false); // Position objects leftPreviewObject.transform.position = Vector3.left * 2f; rightPreviewObject.transform.position = Vector3.right * 2f; // Set layers SetLayerRecursively(leftPreviewObject, 30); SetLayerRecursively(rightPreviewObject, 31); } else if (conflict.assetType == ConflictAssetType.Material) { // Create sphere previews for materials leftPreviewObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); rightPreviewObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); leftPreviewObject.hideFlags = HideFlags.HideAndDontSave; rightPreviewObject.hideFlags = HideFlags.HideAndDontSave; leftPreviewObject.transform.position = Vector3.left * 2f; rightPreviewObject.transform.position = Vector3.right * 2f; // Apply different materials var leftRenderer = leftPreviewObject.GetComponent(); var rightRenderer = rightPreviewObject.GetComponent(); leftRenderer.material.color = Color.red; rightRenderer.material.color = Color.blue; SetLayerRecursively(leftPreviewObject, 30); SetLayerRecursively(rightPreviewObject, 31); } } catch (Exception ex) { Debug.LogError($"Failed to load preview objects: {ex.Message}"); } } private GameObject CreateMockPrefab(bool isLocal) { var prefab = GameObject.CreatePrimitive(PrimitiveType.Cube); prefab.hideFlags = HideFlags.HideAndDontSave; prefab.name = isLocal ? "LocalPrefab" : "RemotePrefab"; if (isLocal) { prefab.transform.localScale = Vector3.one; var renderer = prefab.GetComponent(); renderer.material.color = Color.red; } else { prefab.transform.localScale = Vector3.one * 1.2f; var renderer = prefab.GetComponent(); renderer.material.color = Color.blue; // Add additional component to show difference var audioSource = prefab.AddComponent(); } return prefab; } private void SetLayerRecursively(GameObject obj, int layer) { obj.layer = layer; foreach (Transform child in obj.transform) { SetLayerRecursively(child.gameObject, layer); } } #endregion #region Enhanced GUI Drawing private void DrawBackground() { var rect = new Rect(0, 0, position.width, position.height); EditorGUI.DrawRect(rect, backgroundColor); } private void DrawEnhancedHeader() { DrawCard(() => { using (new EditorGUILayout.HorizontalScope()) { var iconRect = GUILayoutUtility.GetRect(24, 24); var iconTexture = CreateRoundedTexture(errorColor, 24, 4); GUI.DrawTexture(iconRect, iconTexture); GUILayout.Space(8); using (new EditorGUILayout.VerticalScope()) { var titleStyle = new GUIStyle(EditorStyles.boldLabel); titleStyle.fontSize = 14; titleStyle.normal.textColor = Color.white; EditorGUILayout.LabelField("Visual Conflict Resolution", titleStyle); var subtitleStyle = new GUIStyle(EditorStyles.label); subtitleStyle.normal.textColor = new Color(0.8f, 0.8f, 0.8f); subtitleStyle.fontSize = 11; EditorGUILayout.LabelField($"Resolve {mockConflicts.Count(c => c.resolution == ConflictResolution.Unresolved)} conflicts to continue", subtitleStyle); } GUILayout.FlexibleSpace(); DrawViewModeSelector(); GUILayout.Space(8); DrawResolutionButtons(); } }, 12); } private void DrawViewModeSelector() { using (new EditorGUILayout.VerticalScope()) { EditorGUILayout.LabelField("View Mode:", EditorStyles.miniLabel); var newViewMode = GUILayout.Toolbar(selectedViewMode, viewModeNames, GUILayout.Width(300)); if (newViewMode != selectedViewMode) { selectedViewMode = newViewMode; RefreshPreviewObjects(); GenerateCurrentPropertyDifferences(); Repaint(); } } } private void DrawResolutionButtons() { var unresolvedCount = mockConflicts.Count(c => c.resolution == ConflictResolution.Unresolved); var canProceed = unresolvedCount == 0; using (new EditorGUI.DisabledScope(!canProceed)) { var proceedStyle = new GUIStyle(GUI.skin.button); proceedStyle.normal.textColor = Color.white; proceedStyle.fixedHeight = 28; proceedStyle.fontStyle = FontStyle.Bold; if (canProceed) { proceedStyle.normal.background = CreateRoundedTexture(successColor, 28, 4); } if (GUILayout.Button("✓ APPLY RESOLUTIONS", proceedStyle, GUILayout.Width(140))) { ApplyResolutions(); } } GUILayout.Space(8); var cancelStyle = new GUIStyle(GUI.skin.button); cancelStyle.normal.textColor = errorColor; cancelStyle.fixedHeight = 28; if (GUILayout.Button("✕ CANCEL PULL", cancelStyle, GUILayout.Width(100))) { Close(); } } private void DrawMainLayout() { EditorGUILayout.Space(8); using (new EditorGUILayout.HorizontalScope()) { // Left panel - Conflict list DrawConflictList(); GUILayout.Space(8); // Right panel - Conflict viewer DrawConflictViewer(); } } private void DrawConflictList() { using (new EditorGUILayout.VerticalScope(GUILayout.Width(280))) { DrawCard(() => { DrawSectionHeader("Conflicting Files", $"{mockConflicts.Count} files need resolution"); EditorGUILayout.Space(8); using (var scrollView = new EditorGUILayout.ScrollViewScope(conflictListScrollPosition)) { conflictListScrollPosition = scrollView.scrollPosition; for (int i = 0; i < mockConflicts.Count; i++) { DrawConflictListItem(mockConflicts[i], i); if (i < mockConflicts.Count - 1) EditorGUILayout.Space(4); } } }, 12); } } private void DrawConflictListItem(MockConflictInfo conflict, int index) { var isSelected = selectedConflictIndex == index; var itemRect = EditorGUILayout.BeginVertical(); if (isSelected) { EditorGUI.DrawRect(itemRect, selectedColor); } if (GUI.Button(itemRect, "", GUIStyle.none)) { SelectConflict(index); } GUILayout.Space(8); using (new EditorGUILayout.HorizontalScope()) { GUILayout.Space(8); // Asset type icon var iconSize = 20; var iconRect = GUILayoutUtility.GetRect(iconSize, iconSize); var iconColor = GetAssetTypeColor(conflict.assetType); var iconTexture = CreateRoundedTexture(iconColor, iconSize, iconSize / 2); GUI.DrawTexture(iconRect, iconTexture); var iconStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel); iconStyle.normal.textColor = Color.white; iconStyle.fontSize = 8; iconStyle.fontStyle = FontStyle.Bold; GUI.Label(iconRect, GetAssetTypeIcon(conflict.assetType), iconStyle); GUILayout.Space(8); using (new EditorGUILayout.VerticalScope()) { var fileNameStyle = new GUIStyle(EditorStyles.boldLabel); fileNameStyle.fontSize = 12; fileNameStyle.normal.textColor = Color.white; EditorGUILayout.LabelField(conflict.fileName, fileNameStyle); var descStyle = new GUIStyle(EditorStyles.label); descStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f); descStyle.fontSize = 10; descStyle.wordWrap = true; EditorGUILayout.LabelField(conflict.description, descStyle); } GUILayout.FlexibleSpace(); // Resolution status var statusSize = 16; var statusRect = GUILayoutUtility.GetRect(statusSize, statusSize); var statusColor = conflict.resolution switch { ConflictResolution.Unresolved => errorColor, ConflictResolution.UseLocal => localColor, ConflictResolution.UseRemote => remoteColor, _ => warningColor }; var statusTexture = CreateRoundedTexture(statusColor, statusSize, statusSize / 2); GUI.DrawTexture(statusRect, statusTexture); var statusIcon = conflict.resolution switch { ConflictResolution.Unresolved => "!", ConflictResolution.UseLocal => "L", ConflictResolution.UseRemote => "R", _ => "?" }; var statusStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel); statusStyle.normal.textColor = Color.white; statusStyle.fontSize = 8; statusStyle.fontStyle = FontStyle.Bold; GUI.Label(statusRect, statusIcon, statusStyle); GUILayout.Space(8); } GUILayout.Space(8); EditorGUILayout.EndVertical(); } private void DrawConflictViewer() { using (new EditorGUILayout.VerticalScope()) { if (selectedConflictIndex >= 0 && selectedConflictIndex < mockConflicts.Count) { var selectedConflict = mockConflicts[selectedConflictIndex]; DrawCard(() => { DrawConflictViewerContent(selectedConflict); }, 12); } else { DrawCard(() => { var emptyStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel); emptyStyle.normal.textColor = new Color(0.6f, 0.6f, 0.6f); emptyStyle.fontSize = 14; EditorGUILayout.LabelField("Select a conflict to view details", emptyStyle); }, 32); } } } private void DrawConflictViewerContent(MockConflictInfo conflict) { DrawSectionHeader($"{conflict.fileName} Conflict", $"{conflict.assetType} • {conflict.conflictType}"); EditorGUILayout.Space(8); // Enhanced conflict viewer based on selected mode switch (selectedViewMode) { case 0: DrawSideBySideComparison(conflict); break; case 1: Draw3DPreviewComparison(conflict); break; case 2: DrawPropertiesComparison(conflict); break; case 3: DrawOverlayComparison(conflict); break; } EditorGUILayout.Space(16); DrawResolutionControls(conflict); } private void DrawSideBySideComparison(MockConflictInfo conflict) { var viewerRect = EditorGUILayout.BeginHorizontal(GUILayout.Height(400)); // Left panel var leftWidth = viewerRect.width * splitRatio; using (new EditorGUILayout.VerticalScope(GUILayout.Width(leftWidth))) { DrawVersionPanel(conflict, true, "LOCAL VERSION"); } // Splitter DrawSplitter(); // Right panel using (new EditorGUILayout.VerticalScope()) { DrawVersionPanel(conflict, false, "REMOTE VERSION"); } EditorGUILayout.EndHorizontal(); } private void DrawVersionPanel(MockConflictInfo conflict, bool isLocal, string title) { var headerColor = isLocal ? localColor : remoteColor; var content = isLocal ? conflict.localVersion : conflict.remoteVersion; var headerStyle = new GUIStyle(EditorStyles.boldLabel); headerStyle.normal.textColor = headerColor; headerStyle.alignment = TextAnchor.MiddleCenter; EditorGUILayout.LabelField(title, headerStyle); EditorGUILayout.Space(4); // Enhanced content based on asset type switch (conflict.assetType) { case ConflictAssetType.Material: DrawMaterialPreview(conflict, isLocal); break; case ConflictAssetType.Prefab: DrawPrefabPreview(conflict, isLocal); break; case ConflictAssetType.Texture: DrawTexturePreview(conflict, isLocal); break; case ConflictAssetType.Scene: DrawScenePreview(conflict, isLocal); break; case ConflictAssetType.Script: DrawScriptPreview(conflict, isLocal); break; default: DrawGenericPreview(content, headerColor); break; } } private void DrawMaterialPreview(MockConflictInfo conflict, bool isLocal) { // Material sphere preview var previewRect = GUILayoutUtility.GetRect(200, 150); EditorGUI.DrawRect(previewRect, new Color(0.1f, 0.1f, 0.1f)); // Mock material sphere var centerRect = new Rect(previewRect.x + 50, previewRect.y + 25, 100, 100); var sphereColor = isLocal ? new Color(0.8f, 0.2f, 0.2f) : new Color(0.2f, 0.2f, 0.8f); // Draw gradient to simulate sphere lighting for (int i = 0; i < 50; i++) { var t = i / 50f; var currentColor = Color.Lerp(sphereColor * 1.5f, sphereColor * 0.3f, t); var rect = new Rect(centerRect.x + i, centerRect.y + i * 0.5f, centerRect.width - i * 2, centerRect.height - i); EditorGUI.DrawRect(rect, currentColor); } // Material properties EditorGUILayout.Space(8); var materialData = isLocal ? "Albedo: Red (1,0,0)\nMetallic: 0.5\nSmoothness: 0.8\nEmission: Off" : "Albedo: Blue (0,0,1)\nMetallic: 0.2\nSmoothness: 0.6\nEmission: On"; var propStyle = new GUIStyle(EditorStyles.textArea); propStyle.fontSize = 10; propStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f); EditorGUILayout.TextArea(materialData, propStyle, GUILayout.Height(60)); } private void DrawPrefabPreview(MockConflictInfo conflict, bool isLocal) { // Hierarchy view var hierarchyRect = GUILayoutUtility.GetRect(200, 200); EditorGUI.DrawRect(hierarchyRect, new Color(0.15f, 0.15f, 0.15f)); GUILayout.BeginArea(hierarchyRect); GUILayout.Space(8); if (isLocal) { DrawHierarchyItem("Player", 0, true); DrawHierarchyItem("Mesh Renderer", 1, false); DrawHierarchyItem("Box Collider", 1, false); DrawHierarchyItem("Rigidbody", 1, false); DrawHierarchyItem("Player Controller", 1, false); } else { DrawHierarchyItem("Player", 0, true); DrawHierarchyItem("Mesh Renderer", 1, false); DrawHierarchyItem("Box Collider", 1, false); DrawHierarchyItem("Rigidbody", 1, false); DrawHierarchyItem("Audio Source", 1, true); // New component DrawHierarchyItem("Particle System", 1, true); // New component DrawHierarchyItem("Player Controller", 1, false); } GUILayout.EndArea(); } private void DrawHierarchyItem(string name, int indent, bool isHighlighted) { using (new EditorGUILayout.HorizontalScope()) { GUILayout.Space(indent * 16 + 8); var itemStyle = new GUIStyle(EditorStyles.label); itemStyle.fontSize = 10; itemStyle.normal.textColor = isHighlighted ? conflictColor : Color.white; var icon = indent == 0 ? "🎮" : "⚙️"; EditorGUILayout.LabelField($"{icon} {name}", itemStyle, GUILayout.Height(16)); } } private void DrawTexturePreview(MockConflictInfo conflict, bool isLocal) { var previewRect = GUILayoutUtility.GetRect(200, 150); EditorGUI.DrawRect(previewRect, new Color(0.1f, 0.1f, 0.1f)); // Mock texture preview with checkerboard pattern var textureRect = new Rect(previewRect.x + 25, previewRect.y + 25, 150, 100); var textureColor = isLocal ? new Color(0.8f, 0.6f, 0.4f) : // Brown texture new Color(0.4f, 0.6f, 0.8f); // Blue texture // Draw checkerboard pattern for (int x = 0; x < 15; x++) { for (int y = 0; y < 10; y++) { var checkerColor = ((x + y) % 2 == 0) ? textureColor : textureColor * 0.7f; var checkerRect = new Rect(textureRect.x + x * 10, textureRect.y + y * 10, 10, 10); EditorGUI.DrawRect(checkerRect, checkerColor); } } // Texture info EditorGUILayout.Space(8); var textureInfo = isLocal ? "Size: 512x512\nFormat: RGB24\nMipmaps: Yes\nFilter: Bilinear" : "Size: 1024x1024\nFormat: RGBA32\nMipmaps: Yes\nFilter: Trilinear"; var infoStyle = new GUIStyle(EditorStyles.textArea); infoStyle.fontSize = 10; infoStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f); EditorGUILayout.TextArea(textureInfo, infoStyle, GUILayout.Height(60)); } private void DrawScenePreview(MockConflictInfo conflict, bool isLocal) { var previewRect = GUILayoutUtility.GetRect(200, 150); EditorGUI.DrawRect(previewRect, new Color(0.05f, 0.05f, 0.1f)); // Dark blue background // Mock scene elements var cameraPos = isLocal ? new Vector2(50, 50) : new Vector2(100, 80); var lightPos = isLocal ? new Vector2(150, 30) : new Vector2(120, 40); // Draw ground plane var groundRect = new Rect(previewRect.x + 20, previewRect.y + 120, previewRect.width - 40, 20); EditorGUI.DrawRect(groundRect, new Color(0.3f, 0.5f, 0.3f)); // Draw camera var cameraRect = new Rect(previewRect.x + cameraPos.x, previewRect.y + cameraPos.y, 20, 20); EditorGUI.DrawRect(cameraRect, Color.yellow); // Draw light var lightRect = new Rect(previewRect.x + lightPos.x, previewRect.y + lightPos.y, 15, 15); EditorGUI.DrawRect(lightRect, Color.white); // Scene info EditorGUILayout.Space(8); var sceneInfo = isLocal ? "Camera: (0,10,0)\nLighting: Realtime GI\nSkybox: Default\nFog: Disabled" : "Camera: (5,10,-5)\nLighting: Baked GI\nSkybox: Procedural\nFog: Enabled"; var infoStyle = new GUIStyle(EditorStyles.textArea); infoStyle.fontSize = 10; infoStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f); EditorGUILayout.TextArea(sceneInfo, infoStyle, GUILayout.Height(60)); } private void DrawScriptPreview(MockConflictInfo conflict, bool isLocal) { var content = isLocal ? conflict.localVersion : conflict.remoteVersion; using (var scrollView = new EditorGUILayout.ScrollViewScope(isLocal ? leftScrollPosition : rightScrollPosition, GUILayout.Height(200))) { if (isLocal) leftScrollPosition = scrollView.scrollPosition; else rightScrollPosition = scrollView.scrollPosition; var codeStyle = new GUIStyle(EditorStyles.textArea); codeStyle.wordWrap = wordWrap; codeStyle.richText = false; codeStyle.font = EditorGUIUtility.Load("Consolas") as Font ?? GUI.skin.font; codeStyle.fontSize = 10; var lines = content.Split('\n'); for (int i = 0; i < lines.Length; i++) { using (new EditorGUILayout.HorizontalScope()) { if (showLineNumbers) { var lineNumStyle = new GUIStyle(EditorStyles.label); lineNumStyle.normal.textColor = new Color(0.5f, 0.5f, 0.5f); lineNumStyle.fontSize = 9; lineNumStyle.alignment = TextAnchor.MiddleRight; EditorGUILayout.LabelField((i + 1).ToString(), lineNumStyle, GUILayout.Width(30)); } EditorGUILayout.LabelField(lines[i], codeStyle, GUILayout.Height(16)); } } } // Code options EditorGUILayout.Space(4); using (new EditorGUILayout.HorizontalScope()) { showLineNumbers = EditorGUILayout.Toggle("Lines", showLineNumbers, GUILayout.Width(60)); wordWrap = EditorGUILayout.Toggle("Wrap", wordWrap, GUILayout.Width(60)); } } private void DrawGenericPreview(string content, Color borderColor) { var rect = EditorGUILayout.BeginVertical(); EditorGUI.DrawRect(rect, new Color(borderColor.r, borderColor.g, borderColor.b, 0.1f)); GUILayout.Space(8); using (new EditorGUILayout.HorizontalScope()) { GUILayout.Space(8); using (new EditorGUILayout.VerticalScope()) { var contentStyle = new GUIStyle(EditorStyles.textArea); contentStyle.wordWrap = true; contentStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f); EditorGUILayout.TextArea(content, contentStyle, GUILayout.Height(200)); } GUILayout.Space(8); } GUILayout.Space(8); EditorGUILayout.EndVertical(); } private void Draw3DPreviewComparison(MockConflictInfo conflict) { if (conflict.assetType == ConflictAssetType.Prefab || conflict.assetType == ConflictAssetType.Scene || conflict.assetType == ConflictAssetType.Material) { EditorGUILayout.LabelField("3D Preview Comparison", EditorStyles.boldLabel); EditorGUILayout.Space(8); using (new EditorGUILayout.HorizontalScope()) { // Left 3D preview using (new EditorGUILayout.VerticalScope()) { EditorGUILayout.LabelField("LOCAL VERSION", EditorStyles.centeredGreyMiniLabel); Draw3DPreviewPanel(true); } GUILayout.Space(8); // Right 3D preview using (new EditorGUILayout.VerticalScope()) { EditorGUILayout.LabelField("REMOTE VERSION", EditorStyles.centeredGreyMiniLabel); Draw3DPreviewPanel(false); } } // 3D Controls EditorGUILayout.Space(8); Draw3DControls(); } else { EditorGUILayout.HelpBox("3D Preview is only available for Prefabs, Scenes, and Materials.", MessageType.Info); DrawSideBySideComparison(conflict); } } private void Draw3DPreviewPanel(bool isLocal) { var previewRect = GUILayoutUtility.GetRect(300, 200); EditorGUI.DrawRect(previewRect, new Color(0.1f, 0.1f, 0.1f)); // Draw render texture if available var renderTexture = isLocal ? leftRenderTexture : rightRenderTexture; if (renderTexture != null) { GUI.DrawTexture(previewRect, renderTexture, ScaleMode.ScaleToFit); } else { // Fallback: mock 3D preview var centerX = previewRect.x + previewRect.width * 0.5f; var centerY = previewRect.y + previewRect.height * 0.5f; var cubeSize = isLocal ? 40 : 50; var cubeColor = isLocal ? new Color(0.8f, 0.2f, 0.2f) : new Color(0.2f, 0.2f, 0.8f); var cubeRect = new Rect(centerX - cubeSize/2, centerY - cubeSize/2, cubeSize, cubeSize); EditorGUI.DrawRect(cubeRect, cubeColor); // Draw simple perspective lines to simulate 3D var offset = 10; var topRect = new Rect(centerX - cubeSize/2 + offset, centerY - cubeSize/2 - offset, cubeSize, cubeSize); EditorGUI.DrawRect(topRect, new Color(cubeColor.r + 0.2f, cubeColor.g + 0.2f, cubeColor.b + 0.2f)); } // Preview info var infoStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel); infoStyle.normal.textColor = Color.white; infoStyle.fontSize = 10; var infoRect = new Rect(previewRect.x, previewRect.y + previewRect.height - 20, previewRect.width, 20); GUI.Label(infoRect, isLocal ? "Local 3D Preview" : "Remote 3D Preview", infoStyle); } private void Draw3DControls() { using (new EditorGUILayout.HorizontalScope()) { if (GUILayout.Button("Reset Camera", GUILayout.Width(100))) { cameraRotation = new Vector3(15f, -30f, 0f); cameraDistance = 3f; UpdateCameraPositions(); } GUILayout.Space(8); if (GUILayout.Button("Focus on Differences", GUILayout.Width(150))) { // Focus camera on differences Debug.Log("Focusing on differences"); } GUILayout.FlexibleSpace(); EditorGUILayout.LabelField("Wireframe:", GUILayout.Width(70)); var showWireframe = EditorGUILayout.Toggle(false, GUILayout.Width(20)); } EditorGUILayout.Space(4); // Camera controls using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.LabelField("Camera:", GUILayout.Width(60)); cameraDistance = EditorGUILayout.Slider("Distance", cameraDistance, 1f, 10f); var newRotationY = EditorGUILayout.Slider("Rotation", cameraRotation.y, -180f, 180f); if (Math.Abs(newRotationY - cameraRotation.y) > 0.1f) { cameraRotation.y = newRotationY; UpdateCameraPositions(); } } } private void DrawPropertiesComparison(MockConflictInfo conflict) { EditorGUILayout.LabelField("Property-by-Property Comparison", EditorStyles.boldLabel); EditorGUILayout.Space(8); // Search bar using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.LabelField("Search:", GUILayout.Width(50)); searchText = EditorGUILayout.TextField(searchText); if (GUILayout.Button("Clear", GUILayout.Width(50))) searchText = ""; } EditorGUILayout.Space(4); using (var scrollView = new EditorGUILayout.ScrollViewScope(viewerScrollPosition, GUILayout.Height(300))) { viewerScrollPosition = scrollView.scrollPosition; var properties = currentPropertyDifferences; if (!string.IsNullOrEmpty(searchText)) { properties = properties.Where(p => p.propertyName.ToLower().Contains(searchText.ToLower())).ToList(); } foreach (var prop in properties) { DrawPropertyDifference(prop); EditorGUILayout.Space(4); } if (properties.Count == 0) { EditorGUILayout.LabelField("No properties found", EditorStyles.centeredGreyMiniLabel); } } } private void DrawPropertyDifference(PropertyDifference prop) { var propRect = EditorGUILayout.BeginHorizontal(); // Background color based on change type Color bgColor = prop.changeType switch { PropertyChangeType.Added => new Color(0.2f, 0.6f, 0.2f, 0.3f), PropertyChangeType.Removed => new Color(0.6f, 0.2f, 0.2f, 0.3f), PropertyChangeType.Modified => new Color(0.6f, 0.4f, 0.2f, 0.3f), _ => Color.clear }; if (bgColor != Color.clear) { EditorGUI.DrawRect(propRect, bgColor); } GUILayout.Space(8); // Property name var nameStyle = new GUIStyle(EditorStyles.boldLabel); nameStyle.normal.textColor = Color.white; nameStyle.fontSize = 11; EditorGUILayout.LabelField(prop.propertyName, nameStyle, GUILayout.Width(200)); // Change type icon var changeIcon = prop.changeType switch { PropertyChangeType.Added => "+", PropertyChangeType.Removed => "-", PropertyChangeType.Modified => "⟳", _ => "?" }; var iconStyle = new GUIStyle(EditorStyles.label); iconStyle.normal.textColor = prop.changeType switch { PropertyChangeType.Added => Color.green, PropertyChangeType.Removed => Color.red, PropertyChangeType.Modified => Color.yellow, _ => Color.white }; EditorGUILayout.LabelField(changeIcon, iconStyle, GUILayout.Width(20)); // Values comparison using (new EditorGUILayout.VerticalScope()) { if (!string.IsNullOrEmpty(prop.localValue)) { var localStyle = new GUIStyle(EditorStyles.label); localStyle.normal.textColor = localColor; localStyle.fontSize = 10; EditorGUILayout.LabelField($"Local: {prop.localValue}", localStyle); } if (!string.IsNullOrEmpty(prop.remoteValue)) { var remoteStyle = new GUIStyle(EditorStyles.label); remoteStyle.normal.textColor = remoteColor; remoteStyle.fontSize = 10; EditorGUILayout.LabelField($"Remote: {prop.remoteValue}", remoteStyle); } } GUILayout.FlexibleSpace(); // Individual resolution choice if (prop.changeType == PropertyChangeType.Modified) { using (new EditorGUILayout.VerticalScope(GUILayout.Width(100))) { var localSelected = prop.resolution == ConflictResolution.UseLocal; var remoteSelected = prop.resolution == ConflictResolution.UseRemote; var localStyle = new GUIStyle(GUI.skin.button); if (localSelected) localStyle.normal.background = CreateRoundedTexture(localColor, 18, 4); if (GUILayout.Button("Use Local", localStyle, GUILayout.Height(18))) { prop.resolution = ConflictResolution.UseLocal; Debug.Log($"Using local value for {prop.propertyName}"); } var remoteStyle = new GUIStyle(GUI.skin.button); if (remoteSelected) remoteStyle.normal.background = CreateRoundedTexture(remoteColor, 18, 4); if (GUILayout.Button("Use Remote", remoteStyle, GUILayout.Height(18))) { prop.resolution = ConflictResolution.UseRemote; Debug.Log($"Using remote value for {prop.propertyName}"); } } } GUILayout.Space(8); EditorGUILayout.EndHorizontal(); } private void DrawOverlayComparison(MockConflictInfo conflict) { if (conflict.assetType == ConflictAssetType.Texture) { DrawTextureOverlay(conflict); } else if (conflict.assetType == ConflictAssetType.Material) { DrawMaterialOverlay(conflict); } else { EditorGUILayout.HelpBox("Overlay comparison is only available for Textures and Materials.", MessageType.Info); DrawSideBySideComparison(conflict); } } private void DrawTextureOverlay(MockConflictInfo conflict) { EditorGUILayout.LabelField("Texture Overlay Comparison", EditorStyles.boldLabel); EditorGUILayout.Space(8); var overlayRect = GUILayoutUtility.GetRect(400, 300); EditorGUI.DrawRect(overlayRect, new Color(0.1f, 0.1f, 0.1f)); // Mock overlay - checkerboard showing differences for (int x = 0; x < 40; x++) { for (int y = 0; y < 30; y++) { var isLocalPixel = (x + y) % 3 == 0; var isDifferent = (x + y) % 7 == 0; Color pixelColor; if (isDifferent) { pixelColor = conflictColor; // Show differences } else if (isLocalPixel) { pixelColor = new Color(0.8f, 0.2f, 0.2f, 0.7f); // Local } else { pixelColor = new Color(0.2f, 0.2f, 0.8f, 0.7f); // Remote } var pixelRect = new Rect(overlayRect.x + x * 10, overlayRect.y + y * 10, 10, 10); EditorGUI.DrawRect(pixelRect, pixelColor); } } // Overlay controls EditorGUILayout.Space(8); using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.LabelField("Blend Mode:", GUILayout.Width(80)); var blendMode = EditorGUILayout.Popup(0, new[] { "Normal", "Difference", "Overlay" }, GUILayout.Width(100)); GUILayout.Space(16); EditorGUILayout.LabelField("Opacity:", GUILayout.Width(60)); var opacity = EditorGUILayout.Slider(0.5f, 0f, 1f, GUILayout.Width(100)); } } private void DrawMaterialOverlay(MockConflictInfo conflict) { EditorGUILayout.LabelField("Material Property Overlay", EditorStyles.boldLabel); EditorGUILayout.Space(8); // Side-by-side material spheres with difference highlighting using (new EditorGUILayout.HorizontalScope()) { // Local material sphere var leftRect = GUILayoutUtility.GetRect(150, 150); EditorGUI.DrawRect(leftRect, new Color(0.1f, 0.1f, 0.1f)); var leftSphere = new Rect(leftRect.x + 25, leftRect.y + 25, 100, 100); EditorGUI.DrawRect(leftSphere, new Color(0.8f, 0.2f, 0.2f)); GUILayout.Space(16); // Remote material sphere var rightRect = GUILayoutUtility.GetRect(150, 150); EditorGUI.DrawRect(rightRect, new Color(0.1f, 0.1f, 0.1f)); var rightSphere = new Rect(rightRect.x + 25, rightRect.y + 25, 100, 100); EditorGUI.DrawRect(rightSphere, new Color(0.2f, 0.2f, 0.8f)); } // Property differences highlight EditorGUILayout.Space(8); EditorGUILayout.LabelField("Differences:", EditorStyles.boldLabel); DrawPropertyDifference(new PropertyDifference { propertyName = "Albedo Color", localValue = "Red (1,0,0)", remoteValue = "Blue (0,0,1)", changeType = PropertyChangeType.Modified }); } private void DrawSplitter() { var splitterRect = GUILayoutUtility.GetRect(4, 400); EditorGUI.DrawRect(splitterRect, new Color(0.5f, 0.5f, 0.5f, 0.5f)); EditorGUIUtility.AddCursorRect(splitterRect, MouseCursor.ResizeHorizontal); if (Event.current.type == EventType.MouseDown && splitterRect.Contains(Event.current.mousePosition)) { isDraggingSplitter = true; Event.current.Use(); } if (isDraggingSplitter) { if (Event.current.type == EventType.MouseDrag) { splitRatio = Mathf.Clamp(Event.current.mousePosition.x / position.width, 0.2f, 0.8f); Event.current.Use(); Repaint(); } else if (Event.current.type == EventType.MouseUp) { isDraggingSplitter = false; Event.current.Use(); } } } private void DrawResolutionControls(MockConflictInfo conflict) { using (new EditorGUILayout.HorizontalScope()) { GUILayout.FlexibleSpace(); var localButtonStyle = new GUIStyle(GUI.skin.button); localButtonStyle.normal.textColor = Color.white; localButtonStyle.fixedHeight = 32; localButtonStyle.fontSize = 12; localButtonStyle.fontStyle = FontStyle.Bold; if (conflict.resolution == ConflictResolution.UseLocal) { localButtonStyle.normal.background = CreateRoundedTexture(localColor, 32, 4); } if (GUILayout.Button("◀ USE LOCAL", localButtonStyle, GUILayout.Width(120))) { SetConflictResolution(selectedConflictIndex, ConflictResolution.UseLocal); } GUILayout.Space(8); var remoteButtonStyle = new GUIStyle(GUI.skin.button); remoteButtonStyle.normal.textColor = Color.white; remoteButtonStyle.fixedHeight = 32; remoteButtonStyle.fontSize = 12; remoteButtonStyle.fontStyle = FontStyle.Bold; if (conflict.resolution == ConflictResolution.UseRemote) { remoteButtonStyle.normal.background = CreateRoundedTexture(remoteColor, 32, 4); } if (GUILayout.Button("USE REMOTE ▶", remoteButtonStyle, GUILayout.Width(120))) { SetConflictResolution(selectedConflictIndex, ConflictResolution.UseRemote); } GUILayout.FlexibleSpace(); } } #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 void DrawSectionHeader(string title, string subtitle = "", Color? color = null) { var headerStyle = new GUIStyle(EditorStyles.boldLabel); headerStyle.fontSize = 13; headerStyle.normal.textColor = color ?? Color.white; EditorGUILayout.LabelField(title, headerStyle); if (!string.IsNullOrEmpty(subtitle)) { var subtitleStyle = new GUIStyle(EditorStyles.label); subtitleStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f); subtitleStyle.fontSize = 10; EditorGUILayout.LabelField(subtitle, subtitleStyle); } } private Texture2D CreateRoundedTexture(Color color, int size = 64, int cornerRadius = 8) { var key = $"{color}_{size}_{cornerRadius}"; if (textureCache.TryGetValue(key, out var cachedTexture) && cachedTexture != null) return cachedTexture; var texture = new Texture2D(size, size); var pixels = new Color[size * size]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { float distanceToCorner = float.MaxValue; if (x < cornerRadius && y < cornerRadius) distanceToCorner = Vector2.Distance(new Vector2(x, y), new Vector2(cornerRadius, cornerRadius)); else if (x >= size - cornerRadius && y < cornerRadius) distanceToCorner = Vector2.Distance(new Vector2(x, y), new Vector2(size - cornerRadius - 1, cornerRadius)); else if (x < cornerRadius && y >= size - cornerRadius) distanceToCorner = Vector2.Distance(new Vector2(x, y), new Vector2(cornerRadius, size - cornerRadius - 1)); else if (x >= size - cornerRadius && y >= size - cornerRadius) distanceToCorner = Vector2.Distance(new Vector2(x, y), new Vector2(size - cornerRadius - 1, size - cornerRadius - 1)); pixels[y * size + x] = (distanceToCorner <= cornerRadius || (x >= cornerRadius && x < size - cornerRadius) || (y >= cornerRadius && y < size - cornerRadius)) ? color : Color.clear; } } texture.SetPixels(pixels); texture.Apply(); textureCache[key] = texture; return texture; } private Color GetAssetTypeColor(ConflictAssetType assetType) { return assetType switch { ConflictAssetType.Script => new Color(0.2f, 0.6f, 0.9f), ConflictAssetType.Prefab => new Color(0.3f, 0.7f, 0.9f), ConflictAssetType.Material => new Color(0.8f, 0.4f, 0.8f), ConflictAssetType.Scene => new Color(0.9f, 0.3f, 0.3f), ConflictAssetType.Texture => new Color(0.3f, 0.8f, 0.3f), ConflictAssetType.ScriptableObject => new Color(0.9f, 0.7f, 0.2f), ConflictAssetType.Animation => new Color(1f, 0.6f, 0.2f), _ => new Color(0.6f, 0.6f, 0.6f) }; } private string GetAssetTypeIcon(ConflictAssetType assetType) { return assetType switch { ConflictAssetType.Script => "C#", ConflictAssetType.Prefab => "PF", ConflictAssetType.Material => "MT", ConflictAssetType.Scene => "SC", ConflictAssetType.Texture => "TX", ConflictAssetType.ScriptableObject => "SO", ConflictAssetType.Animation => "AN", _ => "??" }; } private void SelectConflict(int index) { selectedConflictIndex = index; GenerateCurrentPropertyDifferences(); RefreshPreviewObjects(); Repaint(); } private void SetConflictResolution(int conflictIndex, ConflictResolution resolution) { try { if (conflictIndex >= 0 && conflictIndex < mockConflicts.Count) { mockConflicts[conflictIndex].resolution = resolution; Debug.Log($"Set resolution for {mockConflicts[conflictIndex].fileName}: {resolution}"); Repaint(); } else { Debug.LogError($"Invalid conflict index: {conflictIndex}"); } } catch (Exception ex) { Debug.LogError($"Error setting conflict resolution: {ex.Message}"); } } private void GenerateCurrentPropertyDifferences() { currentPropertyDifferences.Clear(); if (selectedConflictIndex >= 0 && selectedConflictIndex < mockConflicts.Count) { var conflict = mockConflicts[selectedConflictIndex]; currentPropertyDifferences.AddRange(GeneratePropertyDifferences(conflict)); } } private List GeneratePropertyDifferences(MockConflictInfo conflict) { return conflict.assetType switch { ConflictAssetType.Material => GenerateMaterialProperties(), ConflictAssetType.Prefab => GeneratePrefabProperties(), ConflictAssetType.Texture => GenerateTextureProperties(), ConflictAssetType.Scene => GenerateSceneProperties(), ConflictAssetType.Script => GenerateScriptProperties(), _ => GenerateGenericProperties() }; } private List GenerateMaterialProperties() { return new List { new PropertyDifference { propertyName = "Albedo Color", localValue = "Red (1.0, 0.0, 0.0, 1.0)", remoteValue = "Blue (0.0, 0.0, 1.0, 1.0)", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Metallic", localValue = "0.5", remoteValue = "0.2", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Smoothness", localValue = "0.8", remoteValue = "0.6", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Emission", localValue = "", remoteValue = "Enabled", changeType = PropertyChangeType.Added } }; } private List GeneratePrefabProperties() { return new List { new PropertyDifference { propertyName = "Transform.Scale", localValue = "(1.0, 1.0, 1.0)", remoteValue = "(1.2, 1.2, 1.2)", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "BoxCollider.Size", localValue = "(1.0, 1.0, 1.0)", remoteValue = "(1.1, 1.1, 1.1)", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "AudioSource", localValue = "", remoteValue = "Component Added", changeType = PropertyChangeType.Added }, new PropertyDifference { propertyName = "ParticleSystem", localValue = "", remoteValue = "Component Added", changeType = PropertyChangeType.Added } }; } private List GenerateTextureProperties() { return new List { new PropertyDifference { propertyName = "Texture Size", localValue = "512x512", remoteValue = "1024x1024", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Format", localValue = "RGB24", remoteValue = "RGBA32", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Filter Mode", localValue = "Bilinear", remoteValue = "Trilinear", changeType = PropertyChangeType.Modified } }; } private List GenerateSceneProperties() { return new List { new PropertyDifference { propertyName = "Camera Position", localValue = "(0, 10, 0)", remoteValue = "(5, 10, -5)", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Lighting Mode", localValue = "Realtime GI", remoteValue = "Baked GI", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Skybox", localValue = "Default", remoteValue = "Procedural", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Fog", localValue = "", remoteValue = "Enabled", changeType = PropertyChangeType.Added } }; } private List GenerateScriptProperties() { return new List { new PropertyDifference { propertyName = "speed variable", localValue = "5.0f", remoteValue = "7.0f", changeType = PropertyChangeType.Modified }, new PropertyDifference { propertyName = "Move() method", localValue = "transform.Translate()", remoteValue = "rb.velocity =", changeType = PropertyChangeType.Modified } }; } private List GenerateGenericProperties() { return new List { new PropertyDifference { propertyName = "Generic Property", localValue = "Local Value", remoteValue = "Remote Value", changeType = PropertyChangeType.Modified } }; } private void HandleEvents() { if (Event.current.type == EventType.Repaint) { // Handle any repaint-related operations if (selectedViewMode == 1 && previewCameraLeft != null && previewCameraRight != null) { // Render 3D previews if needed previewCameraLeft.Render(); previewCameraRight.Render(); } } } private void ApplyResolutions() { try { Debug.Log("Applying conflict resolutions:"); foreach (var conflict in mockConflicts) { Debug.Log($" {conflict.fileName}: {conflict.resolution}"); } EditorUtility.DisplayDialog("Resolutions Applied", "All conflicts have been resolved. Pull operation will now continue.", "OK"); // Call the callback to notify the main window that conflicts are resolved var callback = onConflictsResolved; onConflictsResolved = null; // Clear the callback first to prevent issues callback?.Invoke(); Close(); } catch (Exception ex) { Debug.LogError($"Error applying resolutions: {ex.Message}"); EditorUtility.DisplayDialog("Error", $"Failed to apply resolutions: {ex.Message}", "OK"); } } private void DrawFallbackUI() { EditorGUILayout.LabelField("Visual Conflict Resolver - Error State", EditorStyles.boldLabel); if (GUILayout.Button("Restart")) { Close(); ShowWindow(); } } #endregion #region Data Models [Serializable] public class MockConflictInfo { public string fileName; public ConflictAssetType assetType; public ConflictType conflictType; public string description; public string localVersion; public string remoteVersion; public ConflictResolution resolution; } [Serializable] public class PropertyDifference { public string propertyName; public string localValue; public string remoteValue; public PropertyChangeType changeType; public ConflictResolution resolution = ConflictResolution.Unresolved; } public enum PropertyChangeType { Added, Removed, Modified } public enum ConflictAssetType { Script, Prefab, Material, Scene, Texture, ScriptableObject, Animation, Audio, Model, Unknown } public enum ConflictType { PropertyValueDifference, PropertyAdded, PropertyRemoved, ComponentAdded, ComponentRemoved, ComponentPropertyChanged, ChildObjectAdded, ChildObjectRemoved, ChildObjectReordered, GameObjectMoved, GameObjectRenamed, SceneSettingsChanged, AssetReplaced, AssetMetadataChanged } public enum ConflictResolution { Unresolved, UseLocal, UseRemote, Manual, Merged } public abstract class ConflictViewer { public abstract bool CanHandle(ConflictAssetType assetType); public abstract void DrawConflictView(Rect area, MockConflictInfo conflict); public abstract ConflictResolution GetUserChoice(); } #endregion } }