// Copyright (c) 2025 TerraByte Inc. // // A modal editor window that displays pull conflicts and allows the user // to select files to reset before proceeding. using UnityEditor; using UnityEngine; using System.Linq; using UnityEngine.Scripting; using Terra.Arbitrator.Services; using System.Collections.Generic; namespace Terra.Arbitrator.GUI { [Preserve] public class ConflictResolutionWindow : EditorWindow { private Vector2 _scrollPosition; private GUIStyle _wordWrapStyle; private IConflictSource _conflictSource; private ArbitratorController _controller; private List _conflictingFiles; /// /// Shows a modal window to display pull conflicts and get user action. /// /// Reference to the arbitrator controller /// The list of files that have conflicts. /// The source of the conflict (e.g., pull, stash). public static void ShowWindow(ArbitratorController controller, IReadOnlyList conflictingFiles, IConflictSource source) { var window = GetWindow(true, "Pull Conflicts Detected", true); window.minSize = new Vector2(600, 400); window._controller = controller; window._conflictingFiles = new List(conflictingFiles); window._conflictSource = source; window.ShowModalUtility(); } private void OnGUI() { _wordWrapStyle ??= new GUIStyle(EditorStyles.label) { wordWrap = true }; EditorGUILayout.HelpBox("A pull would result in conflicts with your local changes. To proceed with a clean pull, you must choose a resolution for the conflicting files listed below.", MessageType.Warning); EditorGUILayout.LabelField($"Mine: {_conflictSource?.MineLabel ?? "Your Changes"}", EditorStyles.boldLabel); EditorGUILayout.LabelField($"Theirs: {_conflictSource?.TheirsLabel ?? "Incoming Changes"}", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); GUILayout.Label("Filename"); GUILayout.FlexibleSpace(); GUILayout.Label(""); //Spacer GUILayout.FlexibleSpace(); GUILayout.Label("Choice", GUILayout.Width(70)); EditorGUILayout.EndHorizontal(); _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, EditorStyles.helpBox); if (_conflictingFiles != null) { foreach (var change in _conflictingFiles) { DrawFileRow(change); } } EditorGUILayout.EndScrollView(); EditorGUILayout.Space(); DrawActionButtons(); } private void DrawFileRow(GitChange change) { EditorGUILayout.BeginHorizontal(EditorStyles.helpBox); EditorGUILayout.LabelField(new GUIContent(change.FilePath, change.FilePath), _wordWrapStyle, GUILayout.ExpandWidth(true)); change.Resolution = (GitChange.ConflictResolution)EditorGUILayout.EnumPopup(change.Resolution, GUILayout.Width(70)); EditorGUILayout.EndHorizontal(); } private void DrawActionButtons() { EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); var allResolved = _conflictingFiles != null && _conflictingFiles.All(c => c.Resolution != GitChange.ConflictResolution.None); var pullButtonText = allResolved ? "Resolve Safely" : "Resolve with Unchosen (Risky)"; if (GUILayout.Button(pullButtonText, GUILayout.Height(30), GUILayout.Width(180))) { if (_controller != null && _conflictSource != null) { _controller.ResolveConflicts(_conflictingFiles, _conflictSource); } Close(); } if (GUILayout.Button("Cancel", GUILayout.Height(30))) { Close(); } EditorGUILayout.EndHorizontal(); } } }