123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // Copyright (c) 2025 TerraByte Inc.
- //
- // This script is the View component for the Better Git tool. Its only
- // responsibility is to draw the UI based on the state provided by the
- // ArbitratorController.
- using System.Linq;
- using UnityEngine;
- using UnityEditor;
- using UnityEngine.Scripting;
- namespace Terra.Arbitrator.GUI
- {
- [Preserve]
- public class ArbitratorWindow : EditorWindow
- {
- private const string DockNextTo = "UnityEditor.GameView,UnityEditor.dll";
- internal static ArbitratorWindow Instance { get; private set; }
-
- private ArbitratorController _controller;
- private string _commitMessage = "";
- private Vector2 _scrollPosition;
- private GUIStyle _evenRowStyle;
- private bool _stylesInitialized;
- [MenuItem("Version Control/Better Git")]
- public static void ShowWindow()
- {
- var type = System.Type.GetType(DockNextTo);
- var window = GetWindow<ArbitratorWindow>(type);
- window.titleContent = new GUIContent("Better Git", EditorGUIUtility.IconContent("d_UnityEditor.VersionControl").image);
- }
-
- private void OnEnable()
- {
- Instance = this;
-
- _controller = new ArbitratorController(
- requestRepaint: Repaint,
- displayDialog: EditorUtility.DisplayDialog,
- promptForUnsavedChanges: PromptForUnsavedChanges
- );
-
- _controller.OnEnable();
- }
- private void OnDisable()
- {
- Instance = null;
- }
- private void InitializeStyles()
- {
- if (_stylesInitialized) return;
- _evenRowStyle = new GUIStyle();
-
- var texture = new Texture2D(1, 1);
- var color = EditorGUIUtility.isProSkin
- ? new Color(0.3f, 0.3f, 0.3f, 0.3f)
- : new Color(0.8f, 0.8f, 0.8f, 0.5f);
- texture.SetPixel(0, 0, color);
- texture.Apply();
-
- _evenRowStyle.normal.background = texture;
-
- _stylesInitialized = true;
- }
-
- public void TriggerAutoRefresh()
- {
- if (_controller is { IsLoading: false })
- {
- _controller.Refresh();
- }
- }
- private void OnGUI()
- {
- InitializeStyles();
- DrawToolbar();
- EditorGUILayout.Space();
- DrawMessageArea();
- DrawMainContent();
- }
-
- private void DrawToolbar()
- {
- EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
- EditorGUI.BeginDisabledGroup(_controller.IsLoading);
-
- DrawBranchSelector();
- if (GUILayout.Button(new GUIContent("Refresh", EditorGUIUtility.IconContent("Refresh").image), EditorStyles.toolbarButton, GUILayout.Width(80)))
- {
- _controller.Refresh();
- }
-
- if (!_controller.IsInConflictState)
- {
- var commitsToPull = _controller.CommitsToPull;
- EditorGUI.BeginDisabledGroup(commitsToPull == 0);
-
- var pullLabel = $"Pull ({_controller.CommitsToPull})";
- if (GUILayout.Button(new GUIContent(pullLabel), EditorStyles.toolbarButton, GUILayout.Width(80)))
- {
- _controller.Pull();
- }
-
- EditorGUI.EndDisabledGroup();
- }
-
- EditorGUI.BeginDisabledGroup(!_controller.HasStash);
- if (GUILayout.Button("See Stash Changes", EditorStyles.toolbarButton, GUILayout.Width(130)))
- {
- _controller.ShowStashedChangesWindow();
- }
- EditorGUI.EndDisabledGroup();
-
- if (_controller.Changes is { Count: > 0 })
- {
- if (GUILayout.Button("Select All", EditorStyles.toolbarButton, GUILayout.Width(80)))
- {
- _controller.SetAllSelection(true);
- }
- if (GUILayout.Button("Deselect All", EditorStyles.toolbarButton, GUILayout.Width(80)))
- {
- _controller.SetAllSelection(false);
- }
-
- GUILayout.FlexibleSpace();
-
- var stashSelections = _controller.Changes?.Count(c => c.IsSelectedForCommit) ?? 0;
- EditorGUI.BeginDisabledGroup(stashSelections == 0);
- if (GUILayout.Button(new GUIContent("Stash", EditorGUIUtility.IconContent("d_Import").image), EditorStyles.toolbarButton, GUILayout.Width(80)))
- {
- _controller.StashSelectedFiles();
- }
- EditorGUI.EndDisabledGroup();
-
- var noChanges = _controller.Changes != null && !_controller.Changes.Any();
- if (_controller.Changes != null)
- {
- var selectedCount = _controller.Changes.Count(c => c.IsSelectedForCommit);
- EditorGUI.BeginDisabledGroup(selectedCount == 0 || noChanges);
-
- var originalColor = UnityEngine.GUI.backgroundColor;
- UnityEngine.GUI.backgroundColor = new Color(1f, 0.5f, 0.5f, 0.8f);
- if (GUILayout.Button(new GUIContent($"Reset Selected ({selectedCount})", EditorGUIUtility.IconContent("d_TreeEditor.Trash").image), EditorStyles.toolbarButton, GUILayout.Width(200)))
- {
- EditorApplication.delayCall += _controller.ResetSelected;
- }
- UnityEngine.GUI.backgroundColor = originalColor;
- EditorGUI.EndDisabledGroup();
- }
- }
- EditorGUI.EndDisabledGroup();
-
- if (_controller.IsLoading)
- {
- GUILayout.FlexibleSpace();
- GUILayout.Label(_controller.LoadingMessage);
- }
- EditorGUILayout.EndHorizontal();
- }
-
- private void DrawBranchSelector()
- {
- var branches = _controller.RemoteBranchList.ToArray();
- var currentIndex = System.Array.IndexOf(branches, _controller.CurrentBranchName);
- if (currentIndex == -1) currentIndex = 0;
- var newIndex = EditorGUILayout.Popup(currentIndex, branches, EditorStyles.toolbarPopup, GUILayout.Width(150));
- if (newIndex == currentIndex) return;
- var selectedBranch = branches[newIndex];
- EditorApplication.delayCall += () => _controller.SwitchToBranch(selectedBranch);
- }
-
- private void DrawMessageArea()
- {
- if (!string.IsNullOrEmpty(_controller.ErrorMessage))
- {
- EditorGUILayout.HelpBox(_controller.ErrorMessage, MessageType.Error);
- }
- else if (!string.IsNullOrEmpty(_controller.InfoMessage) && !_controller.IsLoading)
- {
- EditorGUILayout.HelpBox(_controller.InfoMessage, MessageType.Info);
- }
- }
-
- private void DrawMainContent()
- {
- if (_controller.IsLoading)
- {
- GUILayout.FlexibleSpace();
- if (_controller.OperationProgress > 0)
- {
- EditorGUILayout.LabelField(_controller.OperationProgressMessage, EditorStyles.centeredGreyMiniLabel);
- var rect = EditorGUILayout.GetControlRect(false, 20);
- EditorGUI.ProgressBar(rect, _controller.OperationProgress, $"{_controller.OperationProgress:P0}");
- }
- else
- {
- EditorGUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
- GUILayout.Label(_controller.LoadingMessage, EditorStyles.largeLabel);
- GUILayout.FlexibleSpace();
- EditorGUILayout.EndHorizontal();
- }
- GUILayout.FlexibleSpace();
- }
- else if (_controller.Changes != null && _controller.Changes.Any())
- {
- DrawChangesList();
- DrawCommitSection();
- }
- else if (_controller.ErrorMessage == null && _controller.InfoMessage == null)
- {
- EditorGUILayout.HelpBox("You are up-to-date! No local changes detected.", MessageType.Info);
- }
- }
- private void DrawChangesList()
- {
- EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
- DrawHeaderButton("Commit", SortColumn.Commit, 45);
- GUILayout.Space(10);
- DrawHeaderButton("Status", SortColumn.Status, 50);
- DrawHeaderButton("File Path", SortColumn.FilePath, -1);
- EditorGUILayout.LabelField("Actions", GUILayout.Width(100));
- EditorGUILayout.EndHorizontal();
- _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.ExpandHeight(true));
- for (var i = 0; i < _controller.Changes.Count; i++)
- {
- var change = _controller.Changes[i];
- var rowStyle = i % 2 == 0 ? _evenRowStyle : GUIStyle.none;
-
- EditorGUILayout.BeginHorizontal(rowStyle);
- GUILayout.Space(15);
- if (change.Status == LibGit2Sharp.ChangeKind.Conflicted)
- {
- EditorGUI.BeginDisabledGroup(true);
- EditorGUILayout.Toggle(false, GUILayout.Width(45));
- EditorGUI.EndDisabledGroup();
- }
- else
- {
- change.IsSelectedForCommit = EditorGUILayout.Toggle(change.IsSelectedForCommit, GUILayout.Width(45));
- }
- string status;
- Color statusColor;
- switch (change.Status)
- {
- case LibGit2Sharp.ChangeKind.Added: status = "[+]"; statusColor = Color.green; break;
- case LibGit2Sharp.ChangeKind.Deleted: status = "[-]"; statusColor = Color.red; break;
- case LibGit2Sharp.ChangeKind.Modified: status = "[M]"; statusColor = new Color(1.0f, 0.6f, 0.0f); break;
- case LibGit2Sharp.ChangeKind.Renamed: status = "[R]"; statusColor = new Color(0.6f, 0.6f, 1.0f); break;
- case LibGit2Sharp.ChangeKind.Conflicted: status = "[C]"; statusColor = Color.magenta; break;
- default: status = "[?]"; statusColor = Color.white; break;
- }
-
- var filePathDisplay = change.Status == LibGit2Sharp.ChangeKind.Renamed
- ? $"{change.OldFilePath} -> {change.FilePath}"
- : change.FilePath;
- var originalColor = UnityEngine.GUI.color;
- UnityEngine.GUI.color = statusColor;
- EditorGUILayout.LabelField(new GUIContent(status, change.Status.ToString()), GUILayout.Width(50));
- UnityEngine.GUI.color = originalColor;
- EditorGUILayout.LabelField(new GUIContent(filePathDisplay, filePathDisplay));
- EditorGUILayout.BeginHorizontal(GUILayout.Width(120));
- EditorGUI.BeginDisabledGroup(_controller.IsLoading);
- if (change.Status == LibGit2Sharp.ChangeKind.Conflicted)
- {
- if (GUILayout.Button("Resolve", GUILayout.Width(70)))
- {
- EditorApplication.delayCall += () => _controller.ResolveConflict(change);
- }
- }
- else
- {
- if (GUILayout.Button("Diff", GUILayout.Width(45)))
- {
- EditorApplication.delayCall += () => _controller.DiffFile(change);
- }
- }
-
- if (GUILayout.Button(new GUIContent("Reset", "Revert changes for this file"), GUILayout.Width(55)))
- {
- EditorApplication.delayCall += () => _controller.ResetFile(change);
- }
-
- EditorGUI.EndDisabledGroup();
- EditorGUILayout.EndHorizontal();
-
- EditorGUILayout.EndHorizontal();
- }
- EditorGUILayout.EndScrollView();
- }
-
- private void DrawHeaderButton(string text, SortColumn column, float width)
- {
- var label = text;
- if (_controller.CurrentSortColumn == column)
- {
- label = $"[*] {text}";
- }
- var buttonStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft };
- if (width > 0)
- {
- if (GUILayout.Button(label, buttonStyle, GUILayout.Width(width))) { _controller.SetSortColumn(column); }
- }
- else
- {
- if (GUILayout.Button(label, buttonStyle)) { _controller.SetSortColumn(column); }
- }
- }
-
- private void DrawCommitSection()
- {
- EditorGUILayout.Space(10);
- if (_controller.Changes.Any(c => c.Status == LibGit2Sharp.ChangeKind.Conflicted))
- {
- EditorGUILayout.HelpBox("You must resolve all conflicts before you can commit.", MessageType.Warning);
- return;
- }
-
- EditorGUILayout.LabelField("Commit & Push", EditorStyles.boldLabel);
- _commitMessage = EditorGUILayout.TextArea(_commitMessage, GUILayout.Height(60), GUILayout.ExpandWidth(true));
- var isPushDisabled = string.IsNullOrWhiteSpace(_commitMessage) || !_controller.Changes.Any(c => c.IsSelectedForCommit);
- EditorGUI.BeginDisabledGroup(isPushDisabled);
- if (GUILayout.Button("Commit & Push Selected Files", GUILayout.Height(40)))
- {
- _controller.CommitAndPush(_commitMessage);
- _commitMessage = "";
- }
- EditorGUI.EndDisabledGroup();
-
- if (isPushDisabled)
- {
- EditorGUILayout.HelpBox("Please enter a commit message and select at least one file.", MessageType.Warning);
- }
- }
-
- private static UserAction PromptForUnsavedChanges()
- {
- var result = EditorUtility.DisplayDialogComplex(
- "Unsaved Scene Changes",
- "You have unsaved changes in the current scene. Would you like to save them before proceeding?",
- "Save and Continue",
- "Cancel",
- "Continue without Saving");
- return result switch
- {
- 0 => UserAction.SaveAndProceed,
- 1 => UserAction.Cancel,
- 2 => UserAction.Proceed,
- _ => UserAction.Cancel
- };
- }
- }
- }
|