// 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 System; using System.Linq; using UnityEditor; using UnityEngine; using Terra.Arbitrator.Services; using System.Collections.Generic; namespace Terra.Arbitrator.GUI { public class ConflictResolutionWindow : EditorWindow { private List _conflictingFiles; private List _filesToReset; private Vector2 _scrollPosition; private Action> _onCloseCallback; private bool _callbackInvoked; /// /// Shows a modal window to display pull conflicts and get user action. /// /// The list of files that have conflicts. /// A callback that provides the list of files the user selected to reset. If the user cancels, the list will be null. public static void ShowWindow(List conflictingFiles, Action> onCloseCallback) { var window = GetWindow(true, "Pull Conflicts Detected", true); window.minSize = new Vector2(600, 400); window._conflictingFiles = conflictingFiles; window._filesToReset = new List(new bool[conflictingFiles.Count]); // Initialize all to false window._onCloseCallback = onCloseCallback; window._callbackInvoked = false; window.ShowModalUtility(); } private void OnGUI() { EditorGUILayout.HelpBox("A pull would result in conflicts with your local changes. To proceed with a clean pull, you must reset your local changes for the conflicting files listed below.", MessageType.Warning); EditorGUILayout.LabelField("Conflicting Files:", EditorStyles.boldLabel); _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, EditorStyles.helpBox); for (var i = 0; i < _conflictingFiles.Count; i++) { EditorGUILayout.BeginHorizontal(); _filesToReset[i] = EditorGUILayout.Toggle(_filesToReset[i], GUILayout.Width(20)); EditorGUILayout.LabelField(_conflictingFiles[i]); if (GUILayout.Button("Local Diff", GUILayout.Width(100))) { // Find the GitChange object to pass to the diff service. var change = GitService.GetChangeForFile(_conflictingFiles[i]); if (change != null) { GitService.LaunchExternalDiff(change); } else { Debug.LogWarning($"Could not get status for {_conflictingFiles[i]} to launch diff."); } } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndScrollView(); EditorGUILayout.Space(); DrawActionButtons(); } private void DrawActionButtons() { var selectedCount = _filesToReset.Count(selected => selected); var canReset = selectedCount > 0; EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); EditorGUI.BeginDisabledGroup(!canReset); if (GUILayout.Button($"Reset {selectedCount} Selected File(s)", GUILayout.Height(30), GUILayout.Width(200))) { var filesToResetPaths = new List(); for (var i = 0; i < _conflictingFiles.Count; i++) { if (_filesToReset[i]) { filesToResetPaths.Add(_conflictingFiles[i]); } } _callbackInvoked = true; _onCloseCallback?.Invoke(filesToResetPaths); Close(); } EditorGUI.EndDisabledGroup(); if (GUILayout.Button(new GUIContent("Pull Anyway", "This will perform the pull, leaving merge conflicts in your local files for you to resolve."), GUILayout.Height(30))) { _callbackInvoked = true; _onCloseCallback?.Invoke(new List()); Close(); } if (GUILayout.Button("Cancel", GUILayout.Height(30))) { Close(); } EditorGUILayout.EndHorizontal(); } private void OnDestroy() { if (!_callbackInvoked) { _onCloseCallback?.Invoke(null); } } } }