using System;
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
using System.Linq;
using System.Collections.Generic;
using GitMerge.Utilities;
using Object = UnityEngine.Object;
namespace GitMerge
{
///
/// The window that lets you perform merges on scenes and prefabs.
///
public class GitMergeWindow : EditorWindow
{
private VCS vcs = new VCSGit();
private const string EDITOR_PREFS_AUTOMERGE = "GitMerge_automerge";
private const string EDITOR_PREFS_AUTOFOCUS = "GitMerge_autofocus";
public static bool automerge { private set; get; }
public static bool autofocus { private set; get; }
private MergeManagerBase mergeManager;
private MergeFilter filter = new MergeFilter();
private MergeFilterBar filterBar = new MergeFilterBar();
private ConflictWatcher conflictWatcher = new ConflictWatcher();
public bool mergeInProgress => mergeManager != null;
private PageView pageView = new PageView();
private Vector2 scrollPosition = Vector2.zero;
private int tab = 0;
private List mergeActionsFiltered;
private Texture2D brokenLogo;
private Texture2D fixedLogo;
[MenuItem("Window/GitMerge")]
static void OpenEditor()
{
var window = EditorWindow.GetWindow(typeof(GitMergeWindow), false, "GitMerge");
// In case we're merging and the scene becomes edited,
// the shown SerializedProperties should be repainted
window.autoRepaintOnSceneChange = true;
window.minSize = new Vector2(500, 100);
}
private void OnEnable()
{
brokenLogo = UnityEngine.Resources.Load("chain-broken");
fixedLogo = UnityEngine.Resources.Load("check");
pageView.NumElementsPerPage = 200;
filterBar.filter = filter;
filter.OnChanged += CacheMergeActions;
conflictWatcher.OnConflict += InitializeMerge;
Selection.selectionChanged += Repaint;
EditorApplication.hierarchyWindowItemOnGUI += HighlightItems;
LoadSettings();
}
private void OnDisable()
{
conflictWatcher.OnConflict -= InitializeMerge;
}
private static void LoadSettings()
{
automerge = EditorPrefs.GetBool(EDITOR_PREFS_AUTOMERGE, true);
autofocus = EditorPrefs.GetBool(EDITOR_PREFS_AUTOFOCUS, true);
}
void OnHierarchyChange()
{
// Repaint if we changed the scene
this.Repaint();
}
// Always check for editor state changes, and abort the active merge process if needed
private void Update()
{
if (MergeAction.inMergePhase &&
(EditorApplication.isCompiling ||
EditorApplication.isPlayingOrWillChangePlaymode))
{
ShowNotification(new GUIContent("Aborting merge due to editor state change."));
AbortMerge(false);
}
}
private void AbortMerge(bool showNotification = true)
{
mergeManager.AbortMerge(showNotification);
mergeManager = null;
}
private void OnGUI()
{
Resources.DrawLogo();
DrawTabButtons();
EditorGUILayout.Space();
DrawHorizontalDivider();
EditorGUILayout.Space();
switch (tab)
{
case 0:
OnGUIStartMergeTab();
break;
default:
OnGUISettingsTab();
break;
}
}
///
/// Tab that offers scene merging.
///
private void OnGUIStartMergeTab()
{
if (!mergeInProgress)
{
DisplayPrefabMergeField();
GUILayout.Space(20);
DisplaySceneMergeButton();
}
else
{
DisplayMergeProcess();
}
}
private void DisplaySceneMergeButton()
{
var activeScene = SceneManager.GetActiveScene();
GUILayout.Label("Open Scene: " + activeScene.path);
if (activeScene.path != "" &&
!mergeInProgress &&
GUILayout.Button("Start merging the open scene", GUILayout.Height(30)))
{
var manager = new MergeManagerScene(this, vcs);
if (manager.TryInitializeMerge())
{
this.mergeManager = manager;
CacheMergeActions();
}
}
}
private void DisplayPrefabMergeField()
{
if (!mergeInProgress)
{
var path = PathDetectingDragAndDropField("Drag a scene or prefab here to start merging", 80);
if (path != null)
{
InitializeMerge(path);
}
}
}
private void HighlightItems(int instanceID, Rect selectionRect)
{
var target = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
Texture2D drawableLogo;
if (target == null)
{
return;
}
bool isResolved = false;
bool found = false;
if (mergeManager is { allMergeActions: not null })
{
GlobalObjectId targetId = GlobalObjectId.GetGlobalObjectIdSlow(target);
foreach (var mergeAction in mergeManager.allMergeActions)
{
GlobalObjectId actionId = GlobalObjectId.GetGlobalObjectIdSlow(mergeAction.ours);
if (targetId.targetObjectId == actionId.targetObjectId)
{
found = true;
isResolved = mergeAction.merged;
break;
}
}
}
if (!found) return;
if (!isResolved)
{
drawableLogo = brokenLogo;
}
else
{
drawableLogo = fixedLogo;
}
var iconSize = 16;
var iconRect = new Rect(
selectionRect.xMax - iconSize - 2,
selectionRect.y + (selectionRect.height - iconSize) / 2f,
iconSize,
iconSize
);
GUI.DrawTexture(iconRect, drawableLogo, ScaleMode.ScaleToFit);
}
private void InitializeMerge(string path)
{
if (path == null) return;
var asset = AssetDatabase.LoadAssetAtPath