Add example Unity Project

This commit is contained in:
Michał Gdula 2023-04-26 01:55:33 +01:00
parent fda7ff28dd
commit e3acdb9d6b
7122 changed files with 505543 additions and 2 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2dccfb4f31ae9ba44b41452beaa34658
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo("Unity.CollabProxy.EditorTests")]

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4ef26aa386b44923b61c9c4b505a67c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c18cb9388313e4287ad5895ee735c47d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
using UnityEditor;
using UnityEditor.Collaboration;
using UnityEngine;
namespace CollabProxy.UI
{
/// <summary>
/// Bootstrap Collab windows.
/// </summary>
[InitializeOnLoad]
public class Bootstrap
{
private const float kCollabToolbarButtonWidth = 78.0f;
static Bootstrap()
{
Collab.ShowHistoryWindow = CollabHistoryWindow.ShowHistoryWindow;
Collab.ShowToolbarAtPosition = CollabToolbarWindow.ShowCenteredAtPosition;
Collab.IsToolbarVisible = CollabToolbarWindow.IsVisible;
Collab.CloseToolbar = CollabToolbarWindow.CloseToolbar;
Toolbar.AddSubToolbar(new CollabToolbarButton
{
Width = kCollabToolbarButtonWidth
});
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8aa8171e088f94069bbd1978a053f7dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
using System;
namespace UnityEditor.Collaboration
{
internal static class CollabAnalytics
{
[Serializable]
private struct CollabUserActionAnalyticsEvent
{
public string category;
public string action;
}
public static void SendUserAction(string category, string action)
{
EditorAnalytics.SendCollabUserAction(new CollabUserActionAnalyticsEvent() { category = category, action = action });
}
public static readonly string historyCategoryString = "History";
};
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f944311c8fff2479fa3ba741f6039fc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,330 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEditor.Collaboration;
#if UNITY_2019_1_OR_NEWER
using UnityEditor.UIElements;
using UnityEngine.UIElements;
#else
using UnityEditor.Experimental.UIElements;
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;
#endif
using UnityEngine;
using UnityEditor.Connect;
namespace UnityEditor
{
internal class CollabHistoryWindow : EditorWindow, ICollabHistoryWindow
{
#if UNITY_2019_1_OR_NEWER
private const string ResourcesPath = "Packages/com.unity.collab-proxy/Editor/Collaborate/Resources/Styles/";
#else
private const string ResourcesPath = "StyleSheets/";
#endif
const string kWindowTitle = "Collab History";
const string kServiceUrl = "developer.cloud.unity3d.com";
[MenuItem("Window/Asset Management/Collab History", false, 1)]
public static void ShowHistoryWindow()
{
EditorWindow.GetWindow<CollabHistoryWindow>(kWindowTitle);
}
[MenuItem("Window/Asset Management/Collab History", true)]
public static bool ValidateShowHistoryWindow()
{
return Collab.instance.IsCollabEnabledForCurrentProject();
}
CollabHistoryPresenter m_Presenter;
Dictionary<HistoryState, VisualElement> m_Views;
List<CollabHistoryItem> m_HistoryItems = new List<CollabHistoryItem>();
HistoryState m_State;
VisualElement m_Container;
PagedListView m_Pager;
ScrollView m_HistoryView;
int m_ItemsPerPage = 5;
string m_InProgressRev;
bool m_RevisionActionsEnabled;
public CollabHistoryWindow()
{
minSize = new Vector2(275, 50);
}
public void OnEnable()
{
SetupGUI();
name = "CollabHistory";
if (m_Presenter == null)
{
m_Presenter = new CollabHistoryPresenter(this, new CollabHistoryItemFactory(), new RevisionsService(Collab.instance, UnityConnect.instance));
}
m_Presenter.OnWindowEnabled();
}
public void OnDisable()
{
m_Presenter.OnWindowDisabled();
}
public bool revisionActionsEnabled
{
get { return m_RevisionActionsEnabled; }
set
{
if (m_RevisionActionsEnabled == value)
return;
m_RevisionActionsEnabled = value;
foreach (var historyItem in m_HistoryItems)
{
historyItem.RevisionActionsEnabled = value;
}
}
}
private void AddStyleSheetPath(VisualElement root, string path)
{
#if UNITY_2019_1_OR_NEWER
root.styleSheets.Add(EditorGUIUtility.Load(path) as StyleSheet);
#else
root.AddStyleSheetPath(path);
#endif
}
public void SetupGUI()
{
#if UNITY_2019_1_OR_NEWER
var root = this.rootVisualElement;
#else
var root = this.GetRootVisualContainer();
#endif
AddStyleSheetPath(root, ResourcesPath + "CollabHistoryCommon.uss");
if (EditorGUIUtility.isProSkin)
{
AddStyleSheetPath(root, ResourcesPath + "CollabHistoryDark.uss");
}
else
{
AddStyleSheetPath(root, ResourcesPath + "CollabHistoryLight.uss");
}
m_Container = new VisualElement();
m_Container.StretchToParentSize();
root.Add(m_Container);
m_Pager = new PagedListView()
{
name = "PagedElement",
pageSize = m_ItemsPerPage
};
var errorView = new StatusView()
{
message = "An Error Occurred",
icon = EditorGUIUtility.LoadIconRequired("Collab.Warning") as Texture,
};
var noInternetView = new StatusView()
{
message = "No Internet Connection",
icon = EditorGUIUtility.LoadIconRequired("Collab.NoInternet") as Texture,
};
var maintenanceView = new StatusView()
{
message = "Maintenance",
};
var loginView = new StatusView()
{
message = "Sign in to access Collaborate",
buttonText = "Sign in...",
callback = SignInClick,
};
var noSeatView = new StatusView()
{
message = "Ask your project owner for access to Unity Teams",
buttonText = "Learn More",
callback = NoSeatClick,
};
var waitingView = new StatusView()
{
message = "Updating...",
};
m_HistoryView = new ScrollView() { name = "HistoryContainer", showHorizontal = false};
m_HistoryView.contentContainer.StretchToParentWidth();
m_HistoryView.Add(m_Pager);
m_Views = new Dictionary<HistoryState, VisualElement>()
{
{HistoryState.Error, errorView},
{HistoryState.Offline, noInternetView},
{HistoryState.Maintenance, maintenanceView},
{HistoryState.LoggedOut, loginView},
{HistoryState.NoSeat, noSeatView},
{HistoryState.Waiting, waitingView},
{HistoryState.Ready, m_HistoryView}
};
}
public void UpdateState(HistoryState state, bool force)
{
if (state == m_State && !force)
return;
m_State = state;
switch (state)
{
case HistoryState.Ready:
UpdateHistoryView(m_Pager);
break;
case HistoryState.Disabled:
Close();
return;
}
m_Container.Clear();
m_Container.Add(m_Views[m_State]);
}
public void UpdateRevisions(IEnumerable<RevisionData> datas, string tip, int totalRevisions, int currentPage)
{
var elements = new List<VisualElement>();
var isFullDateObtained = false; // Has everything from this date been obtained?
m_HistoryItems.Clear();
if (datas != null)
{
DateTime currentDate = DateTime.MinValue;
foreach (var data in datas)
{
if (data.timeStamp.Date != currentDate.Date)
{
elements.Add(new CollabHistoryRevisionLine(data.timeStamp, isFullDateObtained));
currentDate = data.timeStamp;
}
var item = new CollabHistoryItem(data);
m_HistoryItems.Add(item);
var container = new VisualElement();
container.style.flexDirection = FlexDirection.Row;
if (data.current)
{
isFullDateObtained = true;
container.AddToClassList("currentRevision");
container.AddToClassList("obtainedRevision");
}
else if (data.obtained)
{
container.AddToClassList("obtainedRevision");
}
else
{
container.AddToClassList("absentRevision");
}
// If we use the index as-is, the latest commit will become #1, but we want it to be last
container.Add(new CollabHistoryRevisionLine(data.index));
container.Add(item);
elements.Add(container);
}
}
m_HistoryView.scrollOffset = new Vector2(0, 0);
m_Pager.totalItems = totalRevisions;
m_Pager.curPage = currentPage;
m_Pager.items = elements;
}
public string inProgressRevision
{
get { return m_InProgressRev; }
set
{
m_InProgressRev = value;
foreach (var historyItem in m_HistoryItems)
{
historyItem.SetInProgressStatus(value);
}
}
}
public int itemsPerPage
{
set
{
if (m_ItemsPerPage == value)
return;
m_Pager.pageSize = m_ItemsPerPage;
}
}
public PageChangeAction OnPageChangeAction
{
set { m_Pager.OnPageChanged = value; }
}
public RevisionAction OnGoBackAction
{
set { CollabHistoryItem.s_OnGoBack = value; }
}
public RevisionAction OnUpdateAction
{
set { CollabHistoryItem.s_OnUpdate = value; }
}
public RevisionAction OnRestoreAction
{
set { CollabHistoryItem.s_OnRestore = value; }
}
public ShowBuildAction OnShowBuildAction
{
set { CollabHistoryItem.s_OnShowBuild = value; }
}
public Action OnShowServicesAction
{
set { CollabHistoryItem.s_OnShowServices = value; }
}
void UpdateHistoryView(VisualElement history)
{
}
void NoSeatClick()
{
var connection = UnityConnect.instance;
var env = connection.GetEnvironment();
// Map environment to url - prod is special
if (env == "production")
env = "";
else
env += "-";
var url = "https://" + env + kServiceUrl
+ "/orgs/" + connection.GetOrganizationId()
+ "/projects/" + connection.GetProjectName()
+ "/unity-teams/";
Application.OpenURL(url);
}
void SignInClick()
{
UnityConnect.instance.ShowLogin();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fed9dda667cab45d398d06402bba03f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,342 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Collaboration;
using UnityEditor.Connect;
using UnityEditor.Web;
using UnityEngine;
using Unity.PlasticSCM.Editor;
namespace UnityEditor
{
internal class CollabToolbarButton : SubToolbar, IDisposable
{
// Must match s_CollabIcon array
enum CollabToolbarState
{
NeedToEnableCollab,
UpToDate,
Conflict,
OperationError,
ServerHasChanges,
FilesToPush,
InProgress,
Disabled,
Offline,
Plastic
}
private class CollabToolbarContent
{
readonly string m_iconName;
readonly string m_toolTip;
readonly CollabToolbarState m_state;
static Dictionary<CollabToolbarContent, GUIContent> m_CollabIcons;
public CollabToolbarState RegisteredForState
{
get { return m_state; }
}
public GUIContent GuiContent
{
get
{
if (m_CollabIcons == null)
{
m_CollabIcons = new Dictionary<CollabToolbarContent, GUIContent>();
}
if (!m_CollabIcons.ContainsKey(this))
{
if (m_state == CollabToolbarState.Plastic)
{
m_CollabIcons.Add(this, EditorGUIUtility.TrTextContentWithIcon(
"Plastic SCM",
m_toolTip,
PlasticPlugin.GetPluginIcon()));
}
else
{
m_CollabIcons.Add(this, EditorGUIUtility.TrTextContentWithIcon("Collab", m_toolTip, m_iconName));
}
}
return m_CollabIcons[this];
}
}
public CollabToolbarContent(CollabToolbarState state, string iconName, string toolTip)
{
m_state = state;
m_iconName = iconName;
m_toolTip = toolTip;
}
}
CollabToolbarContent[] m_toolbarContents;
CollabToolbarState m_CollabToolbarState = CollabToolbarState.UpToDate;
const float kCollabButtonWidth = 78.0f;
const float kPlasticButtonWidth = 100.0f;
ButtonWithAnimatedIconRotation m_CollabButton;
string m_DynamicTooltip;
static bool m_ShowCollabTooltip = false;
private GUIContent currentCollabContent
{
get
{
CollabToolbarContent toolbarContent =
m_toolbarContents.FirstOrDefault(c => c.RegisteredForState.Equals(m_CollabToolbarState));
GUIContent content = new GUIContent(toolbarContent == null? m_toolbarContents.First().GuiContent : toolbarContent.GuiContent);
if (!m_ShowCollabTooltip)
{
content.tooltip = null;
}
else if (m_DynamicTooltip != "")
{
content.tooltip = m_DynamicTooltip;
}
if (Collab.instance.AreTestsRunning())
{
content.text = "CTF";
}
return content;
}
}
public CollabToolbarButton()
{
m_toolbarContents = new[]
{
new CollabToolbarContent(CollabToolbarState.NeedToEnableCollab, "CollabNew", " You need to enable collab."),
new CollabToolbarContent(CollabToolbarState.UpToDate, "Collab", " You are up to date."),
new CollabToolbarContent(CollabToolbarState.Conflict, "CollabConflict", " Please fix your conflicts prior to publishing."),
new CollabToolbarContent(CollabToolbarState.OperationError, "CollabError", " Last operation failed. Please retry later."),
new CollabToolbarContent(CollabToolbarState.ServerHasChanges, "CollabPull", " Please update, there are server changes."),
new CollabToolbarContent(CollabToolbarState.FilesToPush, "CollabPush", " You have files to publish."),
new CollabToolbarContent(CollabToolbarState.InProgress, "CollabProgress", " Operation in progress."),
new CollabToolbarContent(CollabToolbarState.Disabled, "CollabNew", " Collab is disabled."),
new CollabToolbarContent(CollabToolbarState.Offline, "CollabNew", " Please check your network connection."),
new CollabToolbarContent(CollabToolbarState.Plastic, "plastic", "Plastic SCM"),
};
Collab.instance.StateChanged += OnCollabStateChanged;
UnityConnect.instance.StateChanged += OnUnityConnectStateChanged;
UnityConnect.instance.UserStateChanged += OnUnityConnectUserStateChanged;
PlasticPlugin.OnNotificationUpdated += OnPlasticNotificationUpdated;
}
void OnUnityConnectUserStateChanged(UserInfo state)
{
UpdateCollabToolbarState();
}
void OnUnityConnectStateChanged(ConnectInfo state)
{
UpdateCollabToolbarState();
}
void OnPlasticNotificationUpdated()
{
Toolbar.RepaintToolbar();
}
public override void OnGUI(Rect rect)
{
DoCollabDropDown(rect);
}
Rect GUIToScreenRect(Rect guiRect)
{
Vector2 screenPoint = GUIUtility.GUIToScreenPoint(new Vector2(guiRect.x, guiRect.y));
guiRect.x = screenPoint.x;
guiRect.y = screenPoint.y;
return guiRect;
}
void ShowPopup(Rect rect)
{
// window should be centered on the button
ReserveRight(kCollabButtonWidth / 2, ref rect);
ReserveBottom(5, ref rect);
// calculate screen rect before saving assets since it might open the AssetSaveDialog window
var screenRect = GUIToScreenRect(rect);
// save all the assets
AssetDatabase.SaveAssets();
if (Collab.ShowToolbarAtPosition != null && Collab.ShowToolbarAtPosition(screenRect))
{
GUIUtility.ExitGUI();
}
}
void DoCollabDropDown(Rect rect)
{
UpdateCollabToolbarState();
GUIStyle plasticButtonStyle = "ToolbarButton";
GUIStyle collabButtonStyle = "OffsetDropDown";
bool openPlastic = false;
bool showPopup = Toolbar.requestShowCollabToolbar;
Toolbar.requestShowCollabToolbar = false;
bool enable = !EditorApplication.isPlaying;
using (new EditorGUI.DisabledScope(!enable))
{
bool animate = m_CollabToolbarState == CollabToolbarState.InProgress;
EditorGUIUtility.SetIconSize(new Vector2(12, 12));
if (m_CollabToolbarState == CollabToolbarState.Plastic)
{
GUIContent content = currentCollabContent;
content.image = PlasticPlugin.GetPluginIcon();
Width = kPlasticButtonWidth;
if (GUI.Button(rect, content, plasticButtonStyle))
{
openPlastic = true;
}
}
else
{
Width = kCollabButtonWidth;
if (GetCollabButton().OnGUI(rect, currentCollabContent, animate, collabButtonStyle))
{
showPopup = true;
}
}
EditorGUIUtility.SetIconSize(Vector2.zero);
}
if (m_CollabToolbarState == CollabToolbarState.Disabled)
return;
if (openPlastic)
{
PlasticWindow.Open();
}
if (showPopup)
{
ShowPopup(rect);
}
}
public void OnCollabStateChanged(CollabInfo info)
{
UpdateCollabToolbarState();
}
public void UpdateCollabToolbarState()
{
var currentCollabState = CollabToolbarState.UpToDate;
bool networkAvailable = UnityConnect.instance.connectInfo.online && UnityConnect.instance.connectInfo.loggedIn;
m_DynamicTooltip = "";
if (UnityConnect.instance.isDisableCollabWindow)
{
currentCollabState = CollabToolbarState.Plastic;
}
else if (networkAvailable)
{
Collab collab = Collab.instance;
CollabInfo currentInfo = collab.collabInfo;
UnityErrorInfo errInfo;
bool error = false;
if (collab.GetError((UnityConnect.UnityErrorFilter.ByContext | UnityConnect.UnityErrorFilter.ByChild), out errInfo))
{
error = (errInfo.priority <= (int)UnityConnect.UnityErrorPriority.Error);
m_DynamicTooltip = errInfo.shortMsg;
}
if (!currentInfo.ready)
{
currentCollabState = CollabToolbarState.InProgress;
}
else if (error)
{
currentCollabState = CollabToolbarState.OperationError;
}
else if (currentInfo.inProgress)
{
currentCollabState = CollabToolbarState.InProgress;
}
else
{
bool collabEnable = Collab.instance.IsCollabEnabledForCurrentProject();
if (UnityConnect.instance.projectInfo.projectBound == false || !collabEnable)
{
currentCollabState = CollabToolbarState.Plastic;
}
else if (currentInfo.update)
{
currentCollabState = CollabToolbarState.ServerHasChanges;
}
else if (currentInfo.conflict)
{
currentCollabState = CollabToolbarState.Conflict;
}
else if (currentInfo.publish)
{
currentCollabState = CollabToolbarState.FilesToPush;
}
}
}
else
{
currentCollabState = CollabToolbarState.Offline;
}
if (Collab.IsToolbarVisible != null)
{
if (currentCollabState != m_CollabToolbarState ||
Collab.IsToolbarVisible() == m_ShowCollabTooltip)
{
m_CollabToolbarState = currentCollabState;
m_ShowCollabTooltip = !Collab.IsToolbarVisible();
Toolbar.RepaintToolbar();
}
}
}
void ReserveRight(float width, ref Rect pos)
{
pos.x += width;
}
void ReserveBottom(float height, ref Rect pos)
{
pos.y += height;
}
ButtonWithAnimatedIconRotation GetCollabButton()
{
if (m_CollabButton == null)
{
const int repaintsPerSecond = 20;
const float animSpeed = 500f;
const bool mouseDownButton = true;
m_CollabButton = new ButtonWithAnimatedIconRotation(() => (float)EditorApplication.timeSinceStartup * animSpeed, Toolbar.RepaintToolbar, repaintsPerSecond, mouseDownButton);
}
return m_CollabButton;
}
public void Dispose()
{
Collab.instance.StateChanged -= OnCollabStateChanged;
UnityConnect.instance.StateChanged -= OnUnityConnectStateChanged;
UnityConnect.instance.UserStateChanged -= OnUnityConnectUserStateChanged;
PlasticPlugin.OnNotificationUpdated -= OnPlasticNotificationUpdated;
if (m_CollabButton != null)
m_CollabButton.Clear();
}
}
} // namespace

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 882f1a4147a284f028899b9c018e63eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,137 @@
using UnityEngine;
using UnityEditor.Collaboration;
using UnityEditor.Web;
using UnityEditor.Connect;
namespace UnityEditor
{
[InitializeOnLoad]
internal class WebViewStatic : ScriptableSingleton<WebViewStatic>
{
[SerializeField]
WebView m_WebView;
static public WebView GetWebView()
{
return instance.m_WebView;
}
static public void SetWebView(WebView webView)
{
instance.m_WebView = webView;
}
}
[InitializeOnLoad]
internal class CollabToolbarWindow : WebViewEditorStaticWindow, IHasCustomMenu
{
internal override WebView webView
{
get {return WebViewStatic.GetWebView(); }
set {WebViewStatic.SetWebView(value); }
}
private const string kWindowName = "Unity Collab Toolbar";
private static long s_LastClosedTime;
private static CollabToolbarWindow s_CollabToolbarWindow;
public static bool s_ToolbarIsVisible = false;
const int kWindowWidth = 320;
const int kWindowHeight = 350;
public static void CloseToolbar()
{
foreach (CollabToolbarWindow window in Resources.FindObjectsOfTypeAll<CollabToolbarWindow>())
window.Close();
}
[MenuItem("Window/Asset Management/Collab Toolbar", false /*IsValidateFunction*/, 2, true /* IsInternalMenu */)]
public static CollabToolbarWindow ShowToolbarWindow()
{
//Create a new window if it does not exist
if (s_CollabToolbarWindow == null)
{
s_CollabToolbarWindow = GetWindow<CollabToolbarWindow>(false, kWindowName) as CollabToolbarWindow;
}
return s_CollabToolbarWindow;
}
[MenuItem("Window/Asset Management/Collab Toolbar", true /*IsValidateFunction*/)]
public static bool ValidateShowToolbarWindow()
{
return true;
}
public static bool IsVisible()
{
return s_ToolbarIsVisible;
}
public static bool ShowCenteredAtPosition(Rect buttonRect)
{
buttonRect.x -= kWindowWidth / 2;
// We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting playmode, we assume an increasing time when comparing time.
long nowMilliSeconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond;
bool justClosed = nowMilliSeconds < s_LastClosedTime + 50;
if (!justClosed)
{
// Method may have been triggered programmatically, without a user event to consume.
if (Event.current.type != EventType.Layout)
{
Event.current.Use();
}
if (s_CollabToolbarWindow == null)
s_CollabToolbarWindow = CreateInstance<CollabToolbarWindow>() as CollabToolbarWindow;
var windowSize = new Vector2(kWindowWidth, kWindowHeight);
s_CollabToolbarWindow.initialOpenUrl = "file:///" + EditorApplication.userJavascriptPackagesPath + "unityeditor-collab-toolbar/dist/index.html";
s_CollabToolbarWindow.Init();
s_CollabToolbarWindow.ShowAsDropDown(buttonRect, windowSize);
s_CollabToolbarWindow.OnFocus();
return true;
}
return false;
}
// Receives HTML title
public void OnReceiveTitle(string title)
{
titleContent.text = title;
}
public new void OnInitScripting()
{
base.OnInitScripting();
}
public override void OnEnable()
{
minSize = new Vector2(kWindowWidth, kWindowHeight);
maxSize = new Vector2(kWindowWidth, kWindowHeight);
initialOpenUrl = "file:///" + EditorApplication.userJavascriptPackagesPath + "unityeditor-collab-toolbar/dist/index.html";
base.OnEnable();
s_ToolbarIsVisible = true;
}
internal new void OnDisable()
{
s_LastClosedTime = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond;
if (s_CollabToolbarWindow)
{
s_ToolbarIsVisible = false;
NotifyVisibility(s_ToolbarIsVisible);
}
s_CollabToolbarWindow = null;
base.OnDisable();
}
public new void OnDestroy()
{
OnLostFocus();
base.OnDestroy();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6f516f1ec21a54a59a92bf99db2d9535
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d437fe60bb34f45728664a5d930c1635
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,228 @@
using System.Collections.Generic;
using UnityEditor.Connect;
using UnityEditor.Web;
namespace UnityEditor.Collaboration
{
internal class CollabHistoryPresenter
{
public const int ItemsPerPage = 5;
ICollabHistoryWindow m_Window;
ICollabHistoryItemFactory m_Factory;
IRevisionsService m_Service;
ConnectInfo m_ConnectState;
CollabInfo m_CollabState;
bool m_IsCollabError;
int m_TotalRevisions;
int m_CurrentPage;
int m_RequestedPage;
bool m_FetchInProgress;
BuildAccess m_BuildAccess;
string m_ProgressRevision;
public bool BuildServiceEnabled {get; set; }
public CollabHistoryPresenter(ICollabHistoryWindow window, ICollabHistoryItemFactory factory, IRevisionsService service)
{
m_Window = window;
m_Factory = factory;
m_Service = service;
m_CurrentPage = 0;
m_BuildAccess = new BuildAccess();
m_Service.FetchRevisionsCallback += OnFetchRevisions;
}
public void OnWindowEnabled()
{
UnityConnect.instance.StateChanged += OnConnectStateChanged;
Collab.instance.StateChanged += OnCollabStateChanged;
Collab.instance.RevisionUpdated += OnCollabRevisionUpdated;
Collab.instance.JobsCompleted += OnCollabJobsCompleted;
Collab.instance.ErrorOccurred += OnCollabError;
Collab.instance.ErrorCleared += OnCollabErrorCleared;
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
m_ConnectState = UnityConnect.instance.GetConnectInfo();
m_CollabState = Collab.instance.GetCollabInfo();
m_Window.revisionActionsEnabled = !EditorApplication.isPlayingOrWillChangePlaymode;
// Setup window callbacks
m_Window.OnPageChangeAction = OnUpdatePage;
m_Window.OnUpdateAction = OnUpdate;
m_Window.OnRestoreAction = OnRestore;
m_Window.OnGoBackAction = OnGoBack;
m_Window.OnShowBuildAction = ShowBuildForCommit;
m_Window.OnShowServicesAction = ShowServicePage;
m_Window.itemsPerPage = ItemsPerPage;
// Initialize data
UpdateBuildServiceStatus();
var state = RecalculateState();
// Only try to load the page if we're ready
if (state == HistoryState.Ready)
OnUpdatePage(m_CurrentPage);
m_Window.UpdateState(state, true);
}
public void OnWindowDisabled()
{
UnityConnect.instance.StateChanged -= OnConnectStateChanged;
Collab.instance.StateChanged -= OnCollabStateChanged;
Collab.instance.RevisionUpdated -= OnCollabRevisionUpdated;
Collab.instance.JobsCompleted -= OnCollabJobsCompleted;
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
}
private void OnConnectStateChanged(ConnectInfo state)
{
m_ConnectState = state;
m_Window.UpdateState(RecalculateState(), false);
}
private void OnCollabStateChanged(CollabInfo state)
{
// Sometimes a collab state change will trigger even though everything is the same
if (m_CollabState.Equals(state))
return;
if (m_CollabState.tip != state.tip)
OnUpdatePage(m_CurrentPage);
m_CollabState = state;
m_Window.UpdateState(RecalculateState(), false);
if (state.inProgress)
{
m_Window.inProgressRevision = m_ProgressRevision;
}
else
{
m_Window.inProgressRevision = null;
}
}
private void OnCollabRevisionUpdated(CollabInfo state)
{
OnUpdatePage(m_CurrentPage);
}
private void OnCollabJobsCompleted(CollabInfo state)
{
m_ProgressRevision = null;
}
private void OnCollabError()
{
m_IsCollabError = true;
m_Window.UpdateState(RecalculateState(), false);
}
private void OnCollabErrorCleared()
{
m_IsCollabError = false;
m_FetchInProgress = true;
m_Service.GetRevisions(m_CurrentPage * ItemsPerPage, ItemsPerPage);
m_Window.UpdateState(RecalculateState(), false);
}
private void OnPlayModeStateChanged(PlayModeStateChange stateChange)
{
// If entering play mode, disable
if (stateChange == PlayModeStateChange.ExitingEditMode ||
stateChange == PlayModeStateChange.EnteredPlayMode)
{
m_Window.revisionActionsEnabled = false;
}
// If exiting play mode, enable!
else if (stateChange == PlayModeStateChange.EnteredEditMode ||
stateChange == PlayModeStateChange.ExitingPlayMode)
{
m_Window.revisionActionsEnabled = true;
}
}
private HistoryState RecalculateState()
{
if (!m_ConnectState.online)
return HistoryState.Offline;
if (m_ConnectState.maintenance || m_CollabState.maintenance)
return HistoryState.Maintenance;
if (!m_ConnectState.loggedIn)
return HistoryState.LoggedOut;
if (!m_CollabState.seat)
return HistoryState.NoSeat;
if (!Collab.instance.IsCollabEnabledForCurrentProject())
return HistoryState.Disabled;
if (!Collab.instance.IsConnected() || !m_CollabState.ready || m_FetchInProgress)
return HistoryState.Waiting;
if (m_ConnectState.error || m_IsCollabError)
return HistoryState.Error;
return HistoryState.Ready;
}
// TODO: Eventually this can be a listener on the build service status
public void UpdateBuildServiceStatus()
{
foreach (var service in UnityConnectServiceCollection.instance.GetAllServiceInfos())
{
if (service.name.Equals("Build"))
{
BuildServiceEnabled = service.enabled;
}
}
}
public void ShowBuildForCommit(string revisionID)
{
m_BuildAccess.ShowBuildForCommit(revisionID);
}
public void ShowServicePage()
{
m_BuildAccess.ShowServicePage();
}
public void OnUpdatePage(int page)
{
m_FetchInProgress = true;
m_Service.GetRevisions(page * ItemsPerPage, ItemsPerPage);
m_Window.UpdateState(RecalculateState(), false);
m_RequestedPage = page;
}
private void OnFetchRevisions(RevisionsResult data)
{
m_FetchInProgress = false;
IEnumerable<RevisionData> items = null;
if (data != null)
{
m_CurrentPage = m_RequestedPage;
m_TotalRevisions = data.RevisionsInRepo;
items = m_Factory.GenerateElements(data.Revisions, m_TotalRevisions, m_CurrentPage * ItemsPerPage, m_Service.tipRevision, m_Window.inProgressRevision, m_Window.revisionActionsEnabled, BuildServiceEnabled, m_Service.currentUser);
}
// State must be recalculated prior to inserting items
m_Window.UpdateState(RecalculateState(), false);
m_Window.UpdateRevisions(items, m_Service.tipRevision, m_TotalRevisions, m_CurrentPage);
}
private void OnRestore(string revisionId, bool updatetorevision)
{
m_ProgressRevision = revisionId;
Collab.instance.ResyncToRevision(revisionId);
}
private void OnGoBack(string revisionId, bool updatetorevision)
{
m_ProgressRevision = revisionId;
Collab.instance.GoBackToRevision(revisionId, false);
}
private void OnUpdate(string revisionId, bool updatetorevision)
{
m_ProgressRevision = revisionId;
Collab.instance.Update(revisionId, updatetorevision);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7c91a123806d41a0873fcdcb629b1c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fd0a39b4d296d4d509b4f1dbd08d0630
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,53 @@
using System;
using UnityEditor;
using UnityEditor.Collaboration;
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
namespace UnityEditor.Collaboration
{
internal class BuildStatusButton : Button
{
private readonly string iconPrefix = "Icons/Collab.Build";
private readonly string iconSuffix = ".png";
Label labelElement = new Label();
Image iconElement = new Image() {name = "BuildIcon"};
public BuildStatusButton(Action clickEvent) : base(clickEvent)
{
iconElement.image = EditorGUIUtility.Load(iconPrefix + iconSuffix) as Texture;
labelElement.text = "Build Now";
Add(iconElement);
Add(labelElement);
}
public BuildStatusButton(Action clickEvent, BuildState state, int failures) : base(clickEvent)
{
switch (state)
{
case BuildState.InProgress:
iconElement.image = EditorGUIUtility.Load(iconPrefix + iconSuffix) as Texture;
labelElement.text = "In progress";
break;
case BuildState.Failed:
iconElement.image = EditorGUIUtility.Load(iconPrefix + "Failed" + iconSuffix) as Texture;
labelElement.text = failures + ((failures == 1) ? " failure" : " failures");
break;
case BuildState.Success:
iconElement.image = EditorGUIUtility.Load(iconPrefix + "Succeeded" + iconSuffix) as Texture;
labelElement.text = "success";
break;
}
Add(iconElement);
Add(labelElement);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0217a80286f79419daa202f69409f19b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,78 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEditor.Connect;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
namespace UnityEditor.Collaboration
{
internal class CollabHistoryDropDown : VisualElement
{
private readonly VisualElement m_FilesContainer;
private readonly Label m_ToggleLabel;
private int m_ChangesTotal;
private string m_RevisionId;
public CollabHistoryDropDown(ICollection<ChangeData> changes, int changesTotal, bool changesTruncated, string revisionId)
{
m_FilesContainer = new VisualElement();
m_ChangesTotal = changesTotal;
m_RevisionId = revisionId;
m_ToggleLabel = new Label(ToggleText(false));
m_ToggleLabel.AddManipulator(new Clickable(ToggleDropdown));
Add(m_ToggleLabel);
foreach (ChangeData change in changes)
{
m_FilesContainer.Add(new CollabHistoryDropDownItem(change.path, change.action));
}
if (changesTruncated)
{
m_FilesContainer.Add(new Button(ShowAllClick)
{
text = "Show all on dashboard"
});
}
}
private void ToggleDropdown()
{
if (Contains(m_FilesContainer))
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "CollapseAssets");
Remove(m_FilesContainer);
m_ToggleLabel.text = ToggleText(false);
}
else
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "ExpandAssets");
Add(m_FilesContainer);
m_ToggleLabel.text = ToggleText(true);
}
}
private string ToggleText(bool open)
{
var icon = open ? "\u25bc" : "\u25b6";
var change = m_ChangesTotal == 1 ? "Change" : "Changes";
return string.Format("{0} {1} Asset {2}", icon, m_ChangesTotal, change);
}
private void ShowAllClick()
{
var host = UnityConnect.instance.GetConfigurationURL(CloudConfigUrl.CloudServicesDashboard);
var org = UnityConnect.instance.GetOrganizationId();
var proj = UnityConnect.instance.GetProjectGUID();
var url = string.Format("{0}/collab/orgs/{1}/projects/{2}/commits?commit={3}", host, org, proj, m_RevisionId);
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "ShowAllOnDashboard");
Application.OpenURL(url);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a483595b0257945278dc75c5ff7d82ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,53 @@
using System;
using System.IO;
using System.Linq;
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
namespace UnityEditor.Collaboration
{
internal class CollabHistoryDropDownItem : VisualElement
{
public CollabHistoryDropDownItem(string path, string action)
{
var fileName = Path.GetFileName(path);
var isFolder = Path.GetFileNameWithoutExtension(path).Equals(fileName);
var fileIcon = GetIconElement(action, fileName, isFolder);
var metaContainer = new VisualElement();
var fileNameLabel = new Label
{
name = "FileName",
text = fileName
};
var filePathLabel = new Label
{
name = "FilePath",
text = path
};
metaContainer.Add(fileNameLabel);
metaContainer.Add(filePathLabel);
Add(fileIcon);
Add(metaContainer);
}
private Image GetIconElement(string action, string fileName, bool isFolder)
{
var prefix = isFolder ? "Folder" : "File";
var actionName = action.First().ToString().ToUpper() + action.Substring(1);
// Use the same icon for renamed and moved files
actionName = actionName.Equals("Renamed") ? "Moved" : actionName;
var iconElement = new Image
{
name = "FileIcon",
image = EditorGUIUtility.LoadIcon("Icons/Collab." + prefix + actionName + ".png")
};
return iconElement;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d912d4873af534bd4a9d44bf1b52f14e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,229 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using UnityEditor.Connect;
using UnityEditor.Web;
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;
#endif
namespace UnityEditor.Collaboration
{
internal class CollabHistoryItem : VisualElement
{
public static RevisionAction s_OnRestore;
public static RevisionAction s_OnGoBack;
public static RevisionAction s_OnUpdate;
public static ShowBuildAction s_OnShowBuild;
public static Action s_OnShowServices;
private readonly string m_RevisionId;
private readonly string m_FullDescription;
private readonly DateTime m_TimeStamp;
private readonly Button m_Button;
private readonly HistoryProgressSpinner m_ProgressSpinner;
private VisualElement m_ActionsTray;
private VisualElement m_Details;
private Label m_Description;
private Label m_TimeAgo;
private readonly Button m_ExpandCollapseButton;
private bool m_Expanded;
private const int kMaxDescriptionChars = 500;
public bool RevisionActionsEnabled
{
set
{
m_Button.SetEnabled(value);
}
}
public DateTime timeStamp
{
get { return m_TimeStamp; }
}
public CollabHistoryItem(RevisionData data)
{
m_RevisionId = data.id;
m_TimeStamp = data.timeStamp;
name = "HistoryItem";
m_ActionsTray = new VisualElement {name = "HistoryItemActionsTray"};
m_ProgressSpinner = new HistoryProgressSpinner();
m_Details = new VisualElement {name = "HistoryDetail"};
var author = new Label(data.authorName) {name = "Author"};
m_TimeAgo = new Label(TimeAgo.GetString(m_TimeStamp));
m_FullDescription = data.comment;
var shouldTruncate = ShouldTruncateDescription(m_FullDescription);
if (shouldTruncate)
{
m_Description = new Label(GetTruncatedDescription(m_FullDescription));
}
else
{
m_Description = new Label(m_FullDescription);
}
m_Description.name = "RevisionDescription";
var dropdown = new CollabHistoryDropDown(data.changes, data.changesTotal, data.changesTruncated, data.id);
if (data.current)
{
m_Button = new Button(Restore) {name = "ActionButton", text = "Restore"};
}
else if (data.obtained)
{
m_Button = new Button(GoBackTo) {name = "ActionButton", text = "Go back to..."};
}
else
{
m_Button = new Button(UpdateTo) {name = "ActionButton", text = "Update"};
}
m_Button.SetEnabled(data.enabled);
m_ProgressSpinner.ProgressEnabled = data.inProgress;
m_ActionsTray.Add(m_ProgressSpinner);
m_ActionsTray.Add(m_Button);
m_Details.Add(author);
m_Details.Add(m_TimeAgo);
m_Details.Add(m_Description);
if (shouldTruncate)
{
m_ExpandCollapseButton = new Button(ToggleDescription) { name = "ToggleDescription", text = "Show More" };
m_Details.Add(m_ExpandCollapseButton);
}
if (data.buildState != BuildState.None)
{
BuildStatusButton buildButton;
if (data.buildState == BuildState.Configure)
buildButton = new BuildStatusButton(ShowServicePage);
else
buildButton = new BuildStatusButton(ShowBuildForCommit, data.buildState, data.buildFailures);
m_Details.Add(buildButton);
}
m_Details.Add(m_ActionsTray);
m_Details.Add(dropdown);
Add(m_Details);
this.schedule.Execute(UpdateTimeAgo).Every(1000 * 20);
}
public static void SetUpCallbacks(RevisionAction Restore, RevisionAction GoBack, RevisionAction Update)
{
s_OnRestore = Restore;
s_OnGoBack = GoBack;
s_OnUpdate = Update;
}
public void SetInProgressStatus(string revisionIdInProgress)
{
if (String.IsNullOrEmpty(revisionIdInProgress))
{
m_Button.SetEnabled(true);
m_ProgressSpinner.ProgressEnabled = false;
}
else
{
m_Button.SetEnabled(false);
if (m_RevisionId.Equals(revisionIdInProgress))
{
m_ProgressSpinner.ProgressEnabled = true;
}
}
}
void ShowBuildForCommit()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "ShowBuild");
if (s_OnShowBuild != null)
{
s_OnShowBuild(m_RevisionId);
}
}
void ShowServicePage()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "ShowServices");
if (s_OnShowServices != null)
{
s_OnShowServices();
}
}
void Restore()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "Restore");
if (s_OnRestore != null)
{
s_OnRestore(m_RevisionId, false);
}
}
void GoBackTo()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "GoBackTo");
if (s_OnGoBack != null)
{
s_OnGoBack(m_RevisionId, false);
}
}
void UpdateTo()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "Update");
if (s_OnUpdate != null)
{
s_OnUpdate(m_RevisionId, true);
}
}
void UpdateTimeAgo()
{
m_TimeAgo.text = TimeAgo.GetString(m_TimeStamp);
}
bool ShouldTruncateDescription(string description)
{
return description.Contains(Environment.NewLine) || description.Length > kMaxDescriptionChars;
}
string GetTruncatedDescription(string description)
{
string result = description.Contains(Environment.NewLine) ?
description.Substring(0, description.IndexOf(Environment.NewLine)) : description;
if (result.Length > kMaxDescriptionChars)
{
result = result.Substring(0, kMaxDescriptionChars) + "...";
}
return result;
}
void ToggleDescription()
{
if (m_Expanded)
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "CollapseDescription");
m_Expanded = false;
m_ExpandCollapseButton.text = "Show More";
m_Description.text = GetTruncatedDescription(m_FullDescription);
}
else
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "ExpandDescription");
m_Expanded = true;
m_ExpandCollapseButton.text = "Show Less";
m_Description.text = m_FullDescription;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4c1445ee948a4124bfa9fb818a17e36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Collaboration;
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;
#endif
namespace UnityEditor.Collaboration
{
internal class CollabHistoryItemFactory : ICollabHistoryItemFactory
{
const int k_MaxChangesPerRevision = 10;
public IEnumerable<RevisionData> GenerateElements(IEnumerable<Revision> revisions, int totalRevisions, int startIndex, string tipRev, string inProgressRevision, bool revisionActionsEnabled, bool buildServiceEnabled, string currentUser)
{
int index = startIndex;
foreach (var rev in revisions)
{
index++;
var current = rev.revisionID == tipRev;
// Calculate build status
BuildState buildState = BuildState.None;
int buildFailures = 0;
if (rev.buildStatuses != null && rev.buildStatuses.Length > 0)
{
bool inProgress = false;
foreach (CloudBuildStatus buildStatus in rev.buildStatuses)
{
if (buildStatus.complete)
{
if (!buildStatus.success)
{
buildFailures++;
}
}
else
{
inProgress = true;
break;
}
}
if (inProgress)
{
buildState = BuildState.InProgress;
}
else if (buildFailures > 0)
{
buildState = BuildState.Failed;
}
else
{
buildState = BuildState.Success;
}
}
else if (current && !buildServiceEnabled)
{
buildState = BuildState.Configure;
}
// Calculate the number of changes performed on files and folders (not meta files)
var paths = new Dictionary<string, ChangeData>();
foreach (ChangeAction change in rev.entries)
{
if (change.path.EndsWith(".meta"))
{
var path = change.path.Substring(0, change.path.Length - 5);
// Actions taken on meta files are secondary to any actions taken on the main file
if (!paths.ContainsKey(path))
paths[path] = new ChangeData() {path = path, action = change.action};
}
else
{
paths[change.path] = new ChangeData() {path = change.path, action = change.action};
}
}
var displayName = (rev.author != currentUser) ? rev.authorName : "You";
var item = new RevisionData
{
id = rev.revisionID,
index = totalRevisions - index + 1,
timeStamp = TimeStampToDateTime(rev.timeStamp),
authorName = displayName,
comment = rev.comment,
obtained = rev.isObtained,
current = current,
inProgress = (rev.revisionID == inProgressRevision),
enabled = revisionActionsEnabled,
buildState = buildState,
buildFailures = buildFailures,
changes = paths.Values.Take(k_MaxChangesPerRevision).ToList(),
changesTotal = paths.Values.Count,
changesTruncated = paths.Values.Count > k_MaxChangesPerRevision,
};
yield return item;
}
}
private static DateTime TimeStampToDateTime(double timeStamp)
{
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(timeStamp).ToLocalTime();
return dateTime;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc46f91ea1e8e4ca2ab693fef9156dbe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,94 @@
using System;
using UnityEditor;
using UnityEditor.Collaboration;
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
namespace UnityEditor.Collaboration
{
internal class CollabHistoryRevisionLine : VisualElement
{
public CollabHistoryRevisionLine(int number)
{
AddNumber(number);
AddLine("topLine");
AddLine("bottomLine");
AddIndicator();
}
public CollabHistoryRevisionLine(DateTime date, bool isFullDateObtained)
{
AddLine(isFullDateObtained ? "obtainedDateLine" : "absentDateLine");
AddHeader(GetFormattedHeader(date));
AddToClassList("revisionLineHeader");
}
private void AddHeader(string content)
{
Add(new Label
{
text = content
});
}
private void AddIndicator()
{
Add(new VisualElement
{
name = "RevisionIndicator"
});
}
private void AddLine(string className = null)
{
var line = new VisualElement
{
name = "RevisionLine"
};
if (!String.IsNullOrEmpty(className))
{
line.AddToClassList(className);
}
Add(line);
}
private void AddNumber(int number)
{
Add(new Label
{
text = number.ToString(),
name = "RevisionIndex"
});
}
private string GetFormattedHeader(DateTime date)
{
string result = "Commits on " + date.ToString("MMM d");
switch (date.Day)
{
case 1:
case 21:
case 31:
result += "st";
break;
case 2:
case 22:
result += "nd";
break;
case 3:
case 23:
result += "rd";
break;
default:
result += "th";
break;
}
return result;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c737f7a9d78541d1ab25f28f045dd32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,69 @@
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
namespace UnityEditor.Collaboration
{
internal class HistoryProgressSpinner : Image
{
private readonly Texture2D[] m_StatusWheelTextures;
private bool m_ProgressEnabled;
private IVisualElementScheduledItem m_Animation;
public bool ProgressEnabled
{
set
{
if (m_ProgressEnabled == value)
return;
m_ProgressEnabled = value;
visible = value;
if (value)
{
if (m_Animation == null)
{
m_Animation = this.schedule.Execute(AnimateProgress).Every(33);
}
else
{
m_Animation.Resume();
}
}
else
{
if (m_Animation != null)
{
m_Animation.Pause();
}
}
}
}
public HistoryProgressSpinner()
{
m_StatusWheelTextures = new Texture2D[12];
for (int i = 0; i < 12; i++)
{
m_StatusWheelTextures[i] = EditorGUIUtility.LoadIcon("WaitSpin" + i.ToString("00"));
}
image = m_StatusWheelTextures[0];
style.width = m_StatusWheelTextures[0].width;
style.height = m_StatusWheelTextures[0].height;
visible = false;
}
private void AnimateProgress(TimerState obj)
{
int frame = (int)Mathf.Repeat(Time.realtimeSinceStartup * 10, 11.99f);
image = m_StatusWheelTextures[frame];
MarkDirtyRepaint();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf6aca931950a4a6a886e214e9e649c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using UnityEditor.Collaboration;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
namespace UnityEditor.Collaboration
{
internal interface ICollabHistoryItemFactory
{
IEnumerable<RevisionData> GenerateElements(IEnumerable<Revision> revsRevisions, int mTotalRevisions, int startIndex, string tipRev, string inProgressRevision, bool revisionActionsEnabled, bool buildServiceEnabled, string currentUser);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 821f5482c5a3f4389885f4432433f56f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;
#endif
namespace UnityEditor.Collaboration
{
internal interface IPagerData
{
int curPage { get; }
int totalPages { get; }
PageChangeAction OnPageChanged { get; }
}
internal class PagerElement : VisualElement
{
IPagerData m_Data;
readonly Label m_PageText;
readonly Button m_DownButton;
readonly Button m_UpButton;
public PagerElement(IPagerData dataSource)
{
m_Data = dataSource;
this.style.flexDirection = FlexDirection.Row;
this.style.alignSelf = Align.Center;
Add(m_DownButton = new Button(OnPageDownClicked) {text = "\u25c5 Newer"});
m_DownButton.AddToClassList("PagerDown");
m_PageText = new Label();
m_PageText.AddToClassList("PagerLabel");
Add(m_PageText);
Add(m_UpButton = new Button(OnPageUpClicked) {text = "Older \u25bb"});
m_UpButton.AddToClassList("PagerUp");
UpdateControls();
}
void OnPageDownClicked()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "NewerPage");
m_Data.OnPageChanged(m_Data.curPage - 1);
}
void OnPageUpClicked()
{
CollabAnalytics.SendUserAction(CollabAnalytics.historyCategoryString, "OlderPage");
m_Data.OnPageChanged(m_Data.curPage + 1);
}
public void Refresh()
{
UpdateControls();
}
void UpdateControls()
{
var curPage = m_Data.curPage;
var totalPages = m_Data.totalPages;
m_PageText.text = (curPage + 1) + " / " + totalPages;
m_DownButton.SetEnabled(curPage > 0);
m_UpButton.SetEnabled(curPage < totalPages - 1);
}
}
internal enum PagerLocation
{
Top,
Bottom,
}
internal class PagedListView : VisualElement, IPagerData
{
public const int DefaultItemsPerPage = 10;
readonly VisualElement m_ItemContainer;
readonly PagerElement m_PagerTop, m_PagerBottom;
int m_PageSize = DefaultItemsPerPage;
IEnumerable<VisualElement> m_Items;
int m_TotalItems;
int m_CurPage;
public int pageSize
{
set { m_PageSize = value; }
}
public IEnumerable<VisualElement> items
{
set
{
m_Items = value;
LayoutItems();
}
}
public int totalItems
{
set
{
if (m_TotalItems == value)
return;
m_TotalItems = value;
UpdatePager();
}
}
public PageChangeAction OnPageChanged { get; set; }
public PagedListView()
{
m_PagerTop = new PagerElement(this);
m_ItemContainer = new VisualElement()
{
name = "PagerItems",
};
Add(m_ItemContainer);
m_Items = new List<VisualElement>();
m_PagerBottom = new PagerElement(this);
}
void LayoutItems()
{
m_ItemContainer.Clear();
foreach (var item in m_Items)
{
m_ItemContainer.Add(item);
}
}
void UpdatePager()
{
if (m_PagerTop.parent != this && totalPages > 1 && curPage > 0)
Insert(0, m_PagerTop);
if (m_PagerTop.parent == this && (totalPages <= 1 || curPage == 0))
Remove(m_PagerTop);
if (m_PagerBottom.parent != this && totalPages > 1)
Add(m_PagerBottom);
if (m_PagerBottom.parent == this && totalPages <= 1)
Remove(m_PagerBottom);
m_PagerTop.Refresh();
m_PagerBottom.Refresh();
}
int pageCount
{
get
{
var pages = m_TotalItems / m_PageSize;
if (m_TotalItems % m_PageSize > 0)
pages++;
return pages;
}
}
public int curPage
{
get { return m_CurPage; }
set
{
m_CurPage = value;
UpdatePager();
}
}
public int totalPages
{
get
{
var extraPage = 0;
if (m_TotalItems % m_PageSize > 0)
extraPage = 1;
return m_TotalItems / m_PageSize + extraPage;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50de529b6a28f4a7093045e08810a5df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,88 @@
using System;
using UnityEditor;
using UnityEngine;
#if UNITY_2019_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;
#endif
namespace UnityEditor.Collaboration
{
internal class StatusView : VisualElement
{
Image m_Image;
Label m_Message;
Button m_Button;
Action m_Callback;
public Texture icon
{
get { return m_Image.image; }
set
{
m_Image.image = value;
m_Image.visible = value != null;
// Until "display: hidden" is added, this is the only way to hide an element
m_Image.style.height = value != null ? 150 : 0;
}
}
public string message
{
get { return m_Message.text; }
set
{
m_Message.text = value;
m_Message.visible = value != null;
}
}
public string buttonText
{
get { return m_Button.text; }
set
{
m_Button.text = value;
UpdateButton();
}
}
public Action callback
{
get { return m_Callback; }
set
{
m_Callback = value;
UpdateButton();
}
}
public StatusView()
{
name = "StatusView";
this.StretchToParentSize();
m_Image = new Image() { name = "StatusIcon", visible = false, style = { height = 0f }};
m_Message = new Label() { name = "StatusMessage", visible = false};
m_Button = new Button(InternalCallaback) { name = "StatusButton", visible = false};
Add(m_Image);
Add(m_Message);
Add(m_Button);
}
private void UpdateButton()
{
m_Button.visible = m_Button.text != null && m_Callback != null;
}
private void InternalCallaback()
{
m_Callback();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08e9894bdf0834710b22d3c0aa245ac0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a6ab6fd2b91214e8a9c8ec2224a528de
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6b1ae1e78552c459d9ce27048ff51c7f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,259 @@
.unity-button {
min-height:0;
-unity-text-align:middle-center;
margin-left:4px;
margin-top:3px;
margin-right:4px;
margin-bottom:3px;
border-left-width:6px;
border-top-width:4px;
border-right-width:6px;
border-bottom-width:4px;
padding-left:6px;
padding-top:2px;
padding-right:6px;
padding-bottom:3px;
}
.unity-label {
overflow: hidden;
margin-left:4px;
margin-top:2px;
margin-right:4px;
margin-bottom:2px;
padding-left:2px;
padding-top:1px;
min-height: 0;
}
#HistoryContainer {
flex: 1 0 0;
}
#HistoryItem {
flex: 1 0 0;
flex-direction: row;
}
#HistoryDetail {
margin-top: 10px;
margin-left: 10px;
margin-bottom: 10px;
margin-right: 10px;
padding-top: 4px;
flex: 1 0 0;
}
#Author {
-unity-font-style: bold;
font-size: 12px;
}
#HistoryDetail > Button {
align-self: flex-end;
}
CollabHistoryRevisionLine {
width: 40px;
}
#RevisionLine {
flex: 1 0 0;
margin-left: 35px;
width: 1.5px;
}
#RevisionLine.topLine {
height: 20px;
flex: 0 0 auto;
}
#RevisionLine.absentDateLine {
background-color: #797676;
}
.absentRevision #RevisionLine {
background-color: #797676;
}
.currentRevision #RevisionLine.topLine {
background-color: #797676;
}
#RevisionIndex {
position: absolute;
min-width: 23px;
-unity-text-align: middle-right;
top: 15.8px;
font-size: 9px;
}
#RevisionIndicator {
position: absolute;
background-color: #000;
border-radius: 3px;
width: 8px;
height: 8px;
border-bottom-width: 2px;
border-left-width: 2px;
border-right-width: 2px;
border-top-width: 2px;
top: 20px;
left: 32px;
}
.revisionLineHeader {
width: 200px;
height: 20px;
}
.revisionLineHeader > .unity-label {
position: absolute;
margin-left: 47px;
margin-top: 3px;
}
#PagerItems {
flex-direction: column;
}
PagerElement > .unity-label {
margin-top: 8px;
}
.absentRevision #RevisionIndicator {
border-color: #797676;
}
.absentRevision #RevisionIndex {
color: #797676;
}
.currentRevision #HistoryDetail {
border-top-width: 2px;
}
#HistoryItem #RevisionDescription {
white-space: normal;
}
#HistoryItem #ToggleDescription {
align-self: flex-start;
padding-top: 0;
padding-left: 0;
padding-right: 0;
padding-bottom: 2px;
}
#HistoryItem #ActionButton {
position: absolute;
right: 0;
}
#HistoryItem #BuildIcon {
width: 16px;
height: 13px;
}
#HistoryItemActionsTray {
flex: 1 0 0;
flex-direction: row;
align-items: center;
height: 38px;
margin-left: 10px;
margin-right: 10px;
}
CollabHistoryDropDown {
border-top-width: 1px;
}
CollabHistoryDropDown > .unity-label {
padding-top: 10px;
padding-bottom: 10px;
}
CollabHistoryDropDownItem {
flex-direction: row;
border-top-width: 1px;
overflow: hidden;
}
#FileIcon {
align-self: center;
width: 26px;
height: 26px;
}
#FileName {
-unity-font-style: bold;
padding-bottom: 0;
margin-bottom: 0;
}
#FileIcon {
padding-top: 0;
margin-top: 0;
}
#ErrorBar {
height: 24px;
background-color: #ff0000;
color: #000;
font-size: 12px;
}
#ErrorBar > #CloseButton {
position: absolute;
right: 0;
top: 0;
width: 24px;
height: 24px;
color: #000;
font-size: 18px;
-unity-font-style: bold;
}
#StatusView {
flex-direction: column;
justify-content: center;
align-self: center;
align-items: center;
flex: 1 0 0;
}
#StatusView > #StatusIcon {
width: 115px;
height: 150px;
}
#StatusView > #StatusMessage {
font-size: 22px;
width: 230px;
white-space: normal;
-unity-text-align: middle-center;
}
#StatusView > #StatusButton {
font-size: 12px;
margin-top: 20px;
background-image: none;
width: 108px;
height: 29px;
}
BuildStatusButton.unity-button {
flex-direction: row;
align-self: flex-end;
align-items: center;
margin-right: 10px;
padding-left:0;
padding-top:0;
padding-right:0;
padding-bottom:0;
}
BuildStatusButton.unity-button .unity-label {
padding-left: 2px;
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3a2d94c8977984b67984caeff9fa666e
ScriptedImporter:
fileIDToRecycleName:
11400000: stylesheet
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}

View file

@ -0,0 +1,86 @@
#HistoryContainer {
background-color: #292929;
}
.obtainedRevision #HistoryDetail {
background-color: #333;
}
.absentRevision #HistoryDetail {
background-color: #595959;
}
#StatusView {
background-color: #292929;
}
#StatusView > #StatusMessage {
color: #959995;
}
BuildStatusButton.unity-button {
color: #B4B4B4;
background-image: resource("Builtin Skins/DarkSkin/Images/btn.png");
}
BuildStatusButton.unity-button:hover {
color: #FFF;
}
BuildStatusButton.unity-button:hover:active {
background-image: resource("Builtin Skins/DarkSkin/Images/btn act.png");
}
BuildStatusButton.unity-button:checked {
color: #F0F0F0;
background-image: resource("Builtin Skins/DarkSkin/Images/btn on.png");
}
BuildStatusButton.unity-button:hover:checked {
color: #FFF;
}
BuildStatusButton.unity-button:hover:active:checked {
background-image: resource("Builtin Skins/DarkSkin/Images/btn onact.png");
}
BuildStatusButton.unity-button:focus:checked {
background-image: resource("Builtin Skins/DarkSkin/Images/btn on focus.png");
}
CollabHistoryDropDown {
border-color: #292929;
}
CollabHistoryDropDownItem {
border-color: #292929;
}
#RevisionLine.obtainedDateLine {
background-color: #0cb4cc;
}
.obtainedRevision #RevisionLine {
background-color: #0cb4cc;
}
#RevisionIndex {
color: #0cb4cc;
}
#RevisionIndicator {
border-color: #0cb4cc;
}
.currentRevision #RevisionIndicator {
background-color: #0cb4cc;
}
.currentRevision #HistoryDetail {
border-color: #0cb4cc;
}
#StatusView > #StatusButton {
background-color: #0cb4cc;
border-color: #0cb4cc;
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 70d4d75a2877243758b0750cbc75b6eb
ScriptedImporter:
fileIDToRecycleName:
11400000: stylesheet
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}

View file

@ -0,0 +1,86 @@
#HistoryContainer {
background-color: #a2a2a2;
}
.obtainedRevision #HistoryDetail {
background-color: #c2c2c2;
}
.absentRevision #HistoryDetail {
background-color: #dedede;
}
#StatusView {
background-color: #a2a2a3;
}
#StatusView > #StatusMessage {
color: #000;
}
BuildStatusButton.unity-button {
color: #111;
background-image: resource("Builtin Skins/LightSkin/Images/btn.png");
}
BuildStatusButton.unity-button:hover {
color: #000;
}
BuildStatusButton.unity-button:hover:active {
background-image: resource("Builtin Skins/LightSkin/Images/btn act.png");
}
BuildStatusButton.unity-button:checked {
color: #F0F0F0;
background-image: resource("Builtin Skins/LightSkin/Images/btn on.png");
}
BuildStatusButton.unity-button:hover:checked {
color: #000;
}
BuildStatusButton.unity-button:hover:active:checked {
background-image: resource("Builtin Skins/LightSkin/Images/btn onact.png");
}
BuildStatusButton.unity-button:focus:checked {
background-image: resource("Builtin Skins/LightSkin/Images/btn on focus.png");
}
CollabHistoryDropDown {
border-color: #a2a2a2;
}
CollabHistoryDropDownItem {
border-color: #a2a2a2;
}
#RevisionLine.obtainedDateLine {
background-color: #018d98;
}
.obtainedRevision #RevisionLine {
background-color: #018d98;
}
#RevisionIndex {
color: #018d98;
}
#RevisionIndicator {
border-color: #018d98;
}
.currentRevision #RevisionIndicator {
background-color: #018d98;
}
.currentRevision #HistoryDetail {
border-color: #018d98;
}
#StatusView > #StatusButton {
background-color: #018d98;
border-color: #018d98;
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b52bde26a83564960bcb90217f72b910
ScriptedImporter:
fileIDToRecycleName:
11400000: stylesheet
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}

View file

@ -0,0 +1,17 @@
{
"name": "Unity.CollabProxy.Editor",
"references": [
"Unity.PlasticSCM.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 645165c8169474bfbbeb8fb0bcfd26f5
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d5ae383aa416ba14e800dff2526122ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo("Unity.PlasticSCM.EditorTests")]
[assembly: InternalsVisibleTo("Unity.PlasticSCM.DevTools")]

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d50ac3212a7ff5a4795bf609a2a20350
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d3f8e034331c39c4f823ac31228dc4d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,235 @@
using Codice.Client.BaseCommands;
using PlasticGui;
using PlasticGui.WorkspaceWindow.Items;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal interface IAssetFilesFilterPatternsMenuOperations
{
void AddFilesFilterPatterns(
FilterTypes type, FilterActions action, FilterOperationType operation);
}
internal class AssetFilesFilterPatternsMenuBuilder
{
internal AssetFilesFilterPatternsMenuBuilder(
IAssetFilesFilterPatternsMenuOperations operations,
int ignoredMenuItemsPriority,
int hiddenChangesMenuItemsPriority)
{
mOperations = operations;
mIgnoredMenuItemsPriority = ignoredMenuItemsPriority;
mHiddenChangesMenuItemsPriority = hiddenChangesMenuItemsPriority;
mIgnoredSubmenuItem = string.Format(
"{0}/{1}",
PlasticLocalization.GetString(PlasticLocalization.Name.PrefixPlasticMenu),
PlasticLocalization.GetString(PlasticLocalization.Name.MenuAddToIgnoreList));
mHiddenChangesSubmenuItem = string.Format(
"{0}/{1}",
PlasticLocalization.GetString(PlasticLocalization.Name.PrefixPlasticMenu),
PlasticLocalization.GetString(PlasticLocalization.Name.MenuAddToHiddenChangesList));
}
internal void UpdateMenuItems(FilterMenuActions actions)
{
UpdateIgnoredMenuItems(actions);
UpdateHiddenChangesMenuItems(actions);
HandleMenuItem.UpdateAllMenus();
}
internal void RemoveMenuItems()
{
RemoveIgnoredMenuItems();
RemoveHiddenChangesMenuItems();
}
void IgnoredByName_Click()
{
mOperations.AddFilesFilterPatterns(
FilterTypes.Ignored, FilterActions.ByName,
GetIgnoredFilterOperationType());
}
void IgnoredByExtension_Click()
{
mOperations.AddFilesFilterPatterns(
FilterTypes.Ignored, FilterActions.ByExtension,
GetIgnoredFilterOperationType());
}
void IgnoredByFullPath_Click()
{
mOperations.AddFilesFilterPatterns(
FilterTypes.Ignored, FilterActions.ByFullPath,
GetIgnoredFilterOperationType());
}
void HiddenChangesByName_Click()
{
mOperations.AddFilesFilterPatterns(
FilterTypes.HiddenChanges, FilterActions.ByName,
GetHiddenChangesFilterOperationType());
}
void HiddenChangesByExtension_Click()
{
mOperations.AddFilesFilterPatterns(
FilterTypes.HiddenChanges, FilterActions.ByExtension,
GetHiddenChangesFilterOperationType());
}
void HiddenChangesByFullPath_Click()
{
mOperations.AddFilesFilterPatterns(
FilterTypes.HiddenChanges, FilterActions.ByFullPath,
GetHiddenChangesFilterOperationType());
}
void UpdateIgnoredMenuItems(FilterMenuActions actions)
{
RemoveIgnoredMenuItems();
if (!actions.Operations.HasFlag(FilterMenuOperations.Ignore))
{
HandleMenuItem.AddMenuItem(
mIgnoredSubmenuItem,
mIgnoredMenuItemsPriority,
DisabledMenuItem_Click, ValidateDisabledMenuItem);
return;
}
mIgnoredByNameMenuItem = GetIgnoredMenuItemName(actions.FilterByName);
mIgnoredByExtensionMenuItem = GetIgnoredMenuItemName(actions.FilterByExtension);
mIgnoredByFullPathMenuItem = GetIgnoredMenuItemName(actions.FilterByFullPath);
HandleMenuItem.AddMenuItem(
mIgnoredByNameMenuItem,
mIgnoredMenuItemsPriority,
IgnoredByName_Click, ValidateEnabledMenuItem);
if (!actions.Operations.HasFlag(FilterMenuOperations.IgnoreByExtension))
HandleMenuItem.AddMenuItem(
mIgnoredByExtensionMenuItem,
mIgnoredMenuItemsPriority,
IgnoredByExtension_Click, ValidateEnabledMenuItem);
HandleMenuItem.AddMenuItem(
mIgnoredByFullPathMenuItem,
mIgnoredMenuItemsPriority,
IgnoredByFullPath_Click, ValidateEnabledMenuItem);
}
void UpdateHiddenChangesMenuItems(FilterMenuActions actions)
{
RemoveHiddenChangesMenuItems();
if (!actions.Operations.HasFlag(FilterMenuOperations.HideChanged))
{
HandleMenuItem.AddMenuItem(
mHiddenChangesSubmenuItem,
mHiddenChangesMenuItemsPriority,
DisabledMenuItem_Click, ValidateDisabledMenuItem);
return;
}
mHiddenChangesByNameMenuItem = GetHiddenChangesMenuItemName(actions.FilterByName);
mHiddenChangesByExtensionMenuItem = GetHiddenChangesMenuItemName(actions.FilterByExtension);
mHiddenChangesByFullPathMenuItem = GetHiddenChangesMenuItemName(actions.FilterByFullPath);
HandleMenuItem.AddMenuItem(
mHiddenChangesByNameMenuItem,
mIgnoredMenuItemsPriority,
HiddenChangesByName_Click, ValidateEnabledMenuItem);
if (actions.Operations.HasFlag(FilterMenuOperations.HideChangedByExtension))
HandleMenuItem.AddMenuItem(
mHiddenChangesByExtensionMenuItem,
mIgnoredMenuItemsPriority,
HiddenChangesByExtension_Click, ValidateEnabledMenuItem);
HandleMenuItem.AddMenuItem(
mHiddenChangesByFullPathMenuItem,
mIgnoredMenuItemsPriority,
HiddenChangesByFullPath_Click, ValidateEnabledMenuItem);
}
void RemoveIgnoredMenuItems()
{
HandleMenuItem.RemoveMenuItem(mIgnoredSubmenuItem);
HandleMenuItem.RemoveMenuItem(mIgnoredByNameMenuItem);
HandleMenuItem.RemoveMenuItem(mIgnoredByExtensionMenuItem);
HandleMenuItem.RemoveMenuItem(mIgnoredByFullPathMenuItem);
}
void RemoveHiddenChangesMenuItems()
{
HandleMenuItem.RemoveMenuItem(mHiddenChangesSubmenuItem);
HandleMenuItem.RemoveMenuItem(mHiddenChangesByNameMenuItem);
HandleMenuItem.RemoveMenuItem(mHiddenChangesByExtensionMenuItem);
HandleMenuItem.RemoveMenuItem(mHiddenChangesByFullPathMenuItem);
}
FilterOperationType GetIgnoredFilterOperationType()
{
if (mIgnoredByNameMenuItem.StartsWith(PlasticLocalization.GetString(
PlasticLocalization.Name.MenuAddToIgnoreList)))
{
return FilterOperationType.Add;
}
return FilterOperationType.Remove;
}
FilterOperationType GetHiddenChangesFilterOperationType()
{
if (mHiddenChangesByNameMenuItem.StartsWith(PlasticLocalization.GetString(
PlasticLocalization.Name.MenuAddToHiddenChangesList)))
{
return FilterOperationType.Add;
}
return FilterOperationType.Remove;
}
void DisabledMenuItem_Click() { }
bool ValidateEnabledMenuItem() { return true; }
bool ValidateDisabledMenuItem() { return false; }
string GetIgnoredMenuItemName(string filterPattern)
{
return UnityMenuItem.GetText(
mIgnoredSubmenuItem,
UnityMenuItem.EscapedText(filterPattern));
}
string GetHiddenChangesMenuItemName(string filterPattern)
{
return UnityMenuItem.GetText(
mHiddenChangesSubmenuItem,
UnityMenuItem.EscapedText(filterPattern));
}
readonly int mIgnoredMenuItemsPriority;
readonly int mHiddenChangesMenuItemsPriority;
string mIgnoredSubmenuItem;
string mHiddenChangesSubmenuItem;
string mIgnoredByNameMenuItem;
string mHiddenChangesByNameMenuItem;
string mIgnoredByExtensionMenuItem;
string mHiddenChangesByExtensionMenuItem;
string mIgnoredByFullPathMenuItem;
string mHiddenChangesByFullPathMenuItem;
readonly IAssetFilesFilterPatternsMenuOperations mOperations;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4eb3fe08d1dc86419dee5ddcc40baae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,232 @@
using UnityEngine;
using UnityEditor;
using Codice.CM.Common;
using Codice.Client.BaseCommands.EventTracking;
using PlasticGui;
using PlasticGui.WorkspaceWindow.Items;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class AssetMenuItems
{
internal static void Enable()
{
if (sIsEnabled)
return;
sIsEnabled = true;
sOperations = new AssetMenuRoutingOperations();
sAssetSelection = new ProjectViewAssetSelection(UpdateFilterMenuItems);
sFilterMenuBuilder = new AssetFilesFilterPatternsMenuBuilder(
sOperations,
IGNORE_MENU_ITEMS_PRIORITY,
HIDDEN_MENU_ITEMS_PRIORITY);
AddMenuItems();
}
internal static void Disable()
{
sIsEnabled = false;
RemoveMenuItems();
if (sAssetSelection != null)
sAssetSelection.Dispose();
}
static void AddMenuItems()
{
// TODO: Try removing this
// Somehow first item always disappears. So this is a filler item
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.PendingChangesPlasticMenu),
PENDING_CHANGES_MENU_ITEM_PRIORITY,
PendingChanges, ValidatePendingChanges);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.PendingChangesPlasticMenu),
PENDING_CHANGES_MENU_ITEM_PRIORITY,
PendingChanges, ValidatePendingChanges);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.AddPlasticMenu),
ADD_MENU_ITEM_PRIORITY,
Add, ValidateAdd);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.CheckoutPlasticMenu),
CHECKOUT_MENU_ITEM_PRIORITY,
Checkout, ValidateCheckout);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.CheckinPlasticMenu),
CHECKIN_MENU_ITEM_PRIORITY,
Checkin, ValidateCheckin);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.UndoPlasticMenu),
UNDO_MENU_ITEM_PRIORITY,
Undo, ValidateUndo);
UpdateFilterMenuItems();
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.DiffPlasticMenu),
GetPlasticShortcut.ForAssetDiff(),
DIFF_MENU_ITEM_PRIORITY,
Diff, ValidateDiff);
HandleMenuItem.AddMenuItem(
GetPlasticMenuItemName(PlasticLocalization.Name.HistoryPlasticMenu),
GetPlasticShortcut.ForHistory(),
HISTORY_MENU_ITEM_PRIORITY,
History, ValidateHistory);
HandleMenuItem.UpdateAllMenus();
}
static void UpdateFilterMenuItems()
{
SelectedPathsGroupInfo info = AssetsSelection.GetSelectedPathsGroupInfo(
((AssetOperations.IAssetSelection)sAssetSelection).GetSelectedAssets(),
PlasticPlugin.AssetStatusCache);
sFilterMenuBuilder.UpdateMenuItems(FilterMenuUpdater.GetMenuActions(info));
}
static string GetPlasticMenuItemName(PlasticLocalization.Name name)
{
return string.Format("{0}/{1}",
PlasticLocalization.GetString(PlasticLocalization.Name.PrefixPlasticMenu),
PlasticLocalization.GetString(name));
}
static void PendingChanges()
{
ShowWindow.Plastic();
((IAssetMenuOperations)sOperations).ShowPendingChanges();
}
static bool ValidatePendingChanges()
{
return true;
}
static void Add()
{
((IAssetMenuOperations)sOperations).Add();
}
static bool ValidateAdd()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Add);
}
static void Checkout()
{
((IAssetMenuOperations)sOperations).Checkout();
}
static bool ValidateCheckout()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Checkout);
}
static void Checkin()
{
WorkspaceInfo wkInfo = FindWorkspace.InfoForApplicationPath(
Application.dataPath,
PlasticApp.PlasticAPI);
if (wkInfo != null)
{
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
TrackFeatureUseEvent.Features.ContextMenuCheckinOption);
}
((IAssetMenuOperations)sOperations).Checkin();
}
static bool ValidateCheckin()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Checkin);
}
static void Undo()
{
((IAssetMenuOperations)sOperations).Undo();
}
static bool ValidateUndo()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Undo);
}
static void Diff()
{
((IAssetMenuOperations)sOperations).ShowDiff();
}
static bool ValidateDiff()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.Diff);
}
static void History()
{
ShowWindow.Plastic();
((IAssetMenuOperations)sOperations).ShowHistory();
}
static bool ValidateHistory()
{
return ShouldMenuItemBeEnabled(AssetMenuOperations.History);
}
static bool ShouldMenuItemBeEnabled(AssetMenuOperations operation)
{
if (sOperations == null)
return false;
SelectedAssetGroupInfo selectedGroupInfo = SelectedAssetGroupInfo.
BuildFromAssetList(
((AssetOperations.IAssetSelection)sAssetSelection).GetSelectedAssets(),
PlasticPlugin.AssetStatusCache);
AssetMenuOperations operations = AssetMenuUpdater.
GetAvailableMenuOperations(selectedGroupInfo);
return operations.HasFlag(operation);
}
static void RemoveMenuItems()
{
sFilterMenuBuilder.RemoveMenuItems();
HandleMenuItem.RemoveMenuItem(
PlasticLocalization.GetString(PlasticLocalization.Name.PrefixPlasticMenu));
HandleMenuItem.UpdateAllMenus();
}
static AssetMenuRoutingOperations sOperations;
static ProjectViewAssetSelection sAssetSelection;
static AssetFilesFilterPatternsMenuBuilder sFilterMenuBuilder;
static bool sIsEnabled;
const int BASE_MENU_ITEM_PRIORITY = 19; // Puts Plastic SCM right below Create menu
// incrementing the "order" param by 11 causes the menu system to add a separator
const int PENDING_CHANGES_MENU_ITEM_PRIORITY = BASE_MENU_ITEM_PRIORITY;
const int ADD_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 11;
const int CHECKOUT_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 12;
const int CHECKIN_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 13;
const int UNDO_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 14;
const int IGNORE_MENU_ITEMS_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 25;
const int HIDDEN_MENU_ITEMS_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 26;
const int DIFF_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 37;
const int HISTORY_MENU_ITEM_PRIORITY = PENDING_CHANGES_MENU_ITEM_PRIORITY + 38;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c8a8e3e4456f9149905cf2c80aa41a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,227 @@
using System;
using System.IO;
using UnityEditor.VersionControl;
using Codice;
using Codice.Client.Commands.WkTree;
using PlasticGui;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetsOverlays;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
[Flags]
internal enum AssetMenuOperations : byte
{
None = 0,
Checkout = 1 << 0,
Diff = 1 << 1,
History = 1 << 2,
Add = 1 << 3,
Checkin = 1 << 4,
Undo = 1 << 5,
}
internal class SelectedAssetGroupInfo
{
internal int SelectedCount;
internal bool IsControlledSelection;
internal bool IsCheckedInSelection;
internal bool IsCheckedOutSelection;
internal bool IsPrivateSelection;
internal bool IsAddedSelection;
internal bool IsFileSelection;
internal bool HasAnyAddedInSelection;
internal bool HasAnyRemoteLockedInSelection;
internal static SelectedAssetGroupInfo BuildFromAssetList(
AssetList assetList,
IAssetStatusCache statusCache)
{
bool isCheckedInSelection = true;
bool isControlledSelection = true;
bool isCheckedOutSelection = true;
bool isPrivateSelection = true;
bool isAddedSelection = true;
bool isFileSelection = true;
bool hasAnyAddedInSelection = false;
bool hasAnyRemoteLockedInSelection = false;
int selectedCount = 0;
foreach (Asset asset in assetList)
{
if (string.IsNullOrEmpty(asset.path))
continue;
SelectedAssetGroupInfo singleFileGroupInfo = BuildFromSingleFile(
asset.path,
asset.isFolder,
statusCache);
if (!singleFileGroupInfo.IsCheckedInSelection)
isCheckedInSelection = false;
if (!singleFileGroupInfo.IsControlledSelection)
isControlledSelection = false;
if (!singleFileGroupInfo.IsCheckedOutSelection)
isCheckedOutSelection = false;
if (!singleFileGroupInfo.IsPrivateSelection)
isPrivateSelection = false;
if (!singleFileGroupInfo.IsAddedSelection)
isAddedSelection = false;
if (!singleFileGroupInfo.IsFileSelection)
isFileSelection = false;
if (singleFileGroupInfo.HasAnyAddedInSelection)
hasAnyAddedInSelection = true;
if (singleFileGroupInfo.HasAnyRemoteLockedInSelection)
hasAnyRemoteLockedInSelection = true;
selectedCount++;
}
return new SelectedAssetGroupInfo()
{
IsCheckedInSelection = isCheckedInSelection,
IsCheckedOutSelection = isCheckedOutSelection,
IsControlledSelection = isControlledSelection,
IsPrivateSelection = isPrivateSelection,
IsAddedSelection = isAddedSelection,
IsFileSelection = isFileSelection,
HasAnyAddedInSelection = hasAnyAddedInSelection,
HasAnyRemoteLockedInSelection = hasAnyRemoteLockedInSelection,
SelectedCount = selectedCount,
};
}
internal static SelectedAssetGroupInfo BuildFromSingleFile(
string path,
bool isDirectory,
IAssetStatusCache statusCache)
{
bool isCheckedInSelection = true;
bool isControlledSelection = true;
bool isCheckedOutSelection = true;
bool isPrivateSelection = true;
bool isAddedSelection = true;
bool isFileSelection = true;
bool hasAnyAddedInSelection = false;
bool hasAnyRemoteLockedInSelection = false;
string assetPath = Path.GetFullPath(path);
WorkspaceTreeNode wkTreeNode =
PlasticGui.Plastic.API.GetWorkspaceTreeNode(assetPath);
if (isDirectory)
isFileSelection = false;
if (CheckWorkspaceTreeNodeStatus.IsPrivate(wkTreeNode))
isControlledSelection = false;
else
isPrivateSelection = false;
if (CheckWorkspaceTreeNodeStatus.IsCheckedOut(wkTreeNode))
isCheckedInSelection = false;
else
isCheckedOutSelection = false;
if (CheckWorkspaceTreeNodeStatus.IsAdded(wkTreeNode))
hasAnyAddedInSelection = true;
else
isAddedSelection = false;
AssetsOverlays.AssetStatus assetStatus = statusCache.GetStatusForPath(assetPath);
if (ClassifyAssetStatus.IsLockedRemote(assetStatus))
hasAnyRemoteLockedInSelection = true;
return new SelectedAssetGroupInfo()
{
IsCheckedInSelection = isCheckedInSelection,
IsCheckedOutSelection = isCheckedOutSelection,
IsControlledSelection = isControlledSelection,
IsPrivateSelection = isPrivateSelection,
IsAddedSelection = isAddedSelection,
IsFileSelection = isFileSelection,
HasAnyAddedInSelection = hasAnyAddedInSelection,
HasAnyRemoteLockedInSelection = hasAnyRemoteLockedInSelection,
SelectedCount = 1,
};
}
}
internal interface IAssetMenuOperations
{
void ShowPendingChanges();
void Add();
void Checkout();
void Checkin();
void Undo();
void ShowDiff();
void ShowHistory();
}
internal static class AssetMenuUpdater
{
internal static AssetMenuOperations GetAvailableMenuOperations(
SelectedAssetGroupInfo info)
{
AssetMenuOperations result = AssetMenuOperations.None;
if (info.SelectedCount == 0)
{
return result;
}
if (info.IsControlledSelection &&
info.IsCheckedInSelection &&
info.IsFileSelection &&
!info.HasAnyRemoteLockedInSelection)
{
result |= AssetMenuOperations.Checkout;
}
if (info.IsFileSelection &&
info.IsPrivateSelection)
{
result |= AssetMenuOperations.Add;
}
if (info.IsFileSelection &&
info.IsControlledSelection &&
info.IsCheckedOutSelection)
{
result |= AssetMenuOperations.Checkin;
result |= AssetMenuOperations.Undo;
}
if (info.SelectedCount == 1 &&
info.IsControlledSelection &&
!info.HasAnyAddedInSelection &&
info.IsFileSelection)
{
result |= AssetMenuOperations.Diff;
}
if (info.SelectedCount == 1 &&
info.IsControlledSelection &&
!info.HasAnyAddedInSelection)
{
result |= AssetMenuOperations.History;
}
return result;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c456fa791a741a045a8a99ee73af2ae6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,62 @@
using UnityEditor;
using Codice.Client.BaseCommands;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class AssetMenuRoutingOperations :
IAssetMenuOperations,
IAssetFilesFilterPatternsMenuOperations
{
void IAssetMenuOperations.ShowPendingChanges()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.ShowPendingChanges();
}
void IAssetMenuOperations.Add()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.Add();
}
void IAssetMenuOperations.Checkout()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.Checkout();
}
void IAssetMenuOperations.Checkin()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.Checkin();
}
void IAssetMenuOperations.Undo()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.Undo();
}
void IAssetMenuOperations.ShowDiff()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.ShowDiff();
}
void IAssetMenuOperations.ShowHistory()
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.ShowHistory();
}
void IAssetFilesFilterPatternsMenuOperations.AddFilesFilterPatterns(
FilterTypes type,
FilterActions action,
FilterOperationType operation)
{
PlasticWindow plasticWindow = EditorWindow.GetWindow<PlasticWindow>();
plasticWindow.AddFilesFilterPatterns(type, action, operation);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0621730f63c0c74cafa17f3ddc53eb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,316 @@
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.VersionControl;
using Codice.Client.BaseCommands;
using Codice.Client.BaseCommands.EventTracking;
using Codice.Client.Commands;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.Client.Common.Threading;
using Codice.CM.Common;
using GluonGui;
using PlasticGui;
using PlasticGui.Gluon;
using PlasticGui.WorkspaceWindow;
using PlasticGui.WorkspaceWindow.Diff;
using PlasticGui.WorkspaceWindow.Items;
using Unity.PlasticSCM.Editor.AssetMenu.Dialogs;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.Tool;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.Views.PendingChanges.Dialogs;
using GluonCheckoutOperation = GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Explorer.Operations.CheckoutOperation;
using GluonUndoCheckoutOperation = GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Explorer.Operations.UndoCheckoutOperation;
using GluonAddoperation = GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Explorer.Operations.AddOperation;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class AssetOperations :
IAssetMenuOperations,
IAssetFilesFilterPatternsMenuOperations
{
internal interface IAssetSelection
{
AssetList GetSelectedAssets();
}
internal AssetOperations(
WorkspaceInfo wkInfo,
IWorkspaceWindow workspaceWindow,
IViewSwitcher viewSwitcher,
IHistoryViewLauncher historyViewLauncher,
ViewHost viewHost,
NewIncomingChangesUpdater newIncomingChangesUpdater,
IAssetStatusCache assetStatusCache,
IMergeViewLauncher mergeViewLauncher,
IGluonViewSwitcher gluonViewSwitcher,
EditorWindow parentWindow,
IAssetSelection assetSelection,
bool isGluonMode)
{
mWkInfo = wkInfo;
mWorkspaceWindow = workspaceWindow;
mViewSwitcher = viewSwitcher;
mHistoryViewLauncher = historyViewLauncher;
mViewHost = viewHost;
mNewIncomingChangesUpdater = newIncomingChangesUpdater;
mAssetStatusCache = assetStatusCache;
mMergeViewLauncher = mergeViewLauncher;
mGluonViewSwitcher = gluonViewSwitcher;
mAssetSelection = assetSelection;
mIsGluonMode = isGluonMode;
mParentWindow = parentWindow;
mGuiMessage = new UnityPlasticGuiMessage();
mProgressControls = new EditorProgressControls(mGuiMessage);
}
void IAssetMenuOperations.ShowPendingChanges()
{
mViewSwitcher.ShowPendingChanges();
}
void IAssetMenuOperations.Add()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection.GetSelectedAssets(),
mAssetStatusCache,
AssetMenuOperations.Add);
if (mIsGluonMode)
{
GluonAddoperation.Add(
mViewHost,
mProgressControls,
mGuiMessage,
selectedPaths.ToArray(),
false,
RefreshAsset.VersionControlCache);
return;
}
AddOperation.Run(
mWorkspaceWindow,
mProgressControls,
null,
null,
selectedPaths,
false,
mNewIncomingChangesUpdater,
RefreshAsset.VersionControlCache);
}
void IAssetMenuOperations.Checkout()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection.GetSelectedAssets(),
mAssetStatusCache,
AssetMenuOperations.Checkout);
if (mIsGluonMode)
{
GluonCheckoutOperation.Checkout(
mViewHost,
mProgressControls,
mGuiMessage,
selectedPaths.ToArray(),
false,
RefreshAsset.VersionControlCache);
return;
}
CheckoutOperation.Checkout(
mWorkspaceWindow,
null,
mProgressControls,
selectedPaths,
mNewIncomingChangesUpdater,
RefreshAsset.VersionControlCache);
}
void IAssetMenuOperations.Checkin()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection.GetSelectedAssets(),
mAssetStatusCache,
AssetMenuOperations.Checkin);
if (!CheckinDialog.CheckinPaths(
mWkInfo,
selectedPaths,
mAssetStatusCache,
mIsGluonMode,
mParentWindow,
mWorkspaceWindow,
mViewHost,
mGuiMessage,
mMergeViewLauncher,
mGluonViewSwitcher))
return;
RefreshAsset.UnityAssetDatabase();
}
void IAssetMenuOperations.Undo()
{
List<string> selectedPaths = GetSelectedPaths.ForOperation(
mAssetSelection.GetSelectedAssets(),
mAssetStatusCache,
AssetMenuOperations.Undo);
SaveAssets.ForPathsWithoutConfirmation(selectedPaths);
if (mIsGluonMode)
{
GluonUndoCheckoutOperation.UndoCheckout(
mWkInfo,
mViewHost,
mProgressControls,
mGuiMessage,
selectedPaths.ToArray(),
false,
RefreshAsset.UnityAssetDatabase);
return;
}
UndoCheckoutOperation.Run(
mWorkspaceWindow,
null,
mProgressControls,
selectedPaths,
mNewIncomingChangesUpdater,
RefreshAsset.UnityAssetDatabase);
}
void IAssetMenuOperations.ShowDiff()
{
if (LaunchTool.ShowDownloadPlasticExeWindow(
mWkInfo,
mIsGluonMode,
TrackFeatureUseEvent.Features.InstallPlasticCloudFromShowDiff,
TrackFeatureUseEvent.Features.InstallPlasticEnterpriseFromFromShowDiff,
TrackFeatureUseEvent.Features.CancelPlasticInstallationFromFromShowDiff))
return;
string selectedPath = AssetsSelection.GetSelectedPath(
mAssetSelection.GetSelectedAssets());
DiffInfo diffInfo = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
string symbolicName = GetSymbolicName(selectedPath);
string extension = Path.GetExtension(selectedPath);
diffInfo = PlasticGui.Plastic.API.BuildDiffInfoForDiffWithPrevious(
selectedPath, symbolicName, selectedPath, extension, mWkInfo);
},
/*afterOperationDelegate*/ delegate
{
if (waiter.Exception != null)
{
ExceptionsHandler.DisplayException(waiter.Exception);
return;
}
DiffOperation.DiffWithPrevious(
diffInfo,
null,
null);
});
}
void IAssetMenuOperations.ShowHistory()
{
if (LaunchTool.ShowDownloadPlasticExeWindow(
mWkInfo,
mIsGluonMode,
TrackFeatureUseEvent.Features.InstallPlasticCloudFromShowHistory,
TrackFeatureUseEvent.Features.InstallPlasticEnterpriseFromShowHistory,
TrackFeatureUseEvent.Features.CancelPlasticInstallationFromShowHistory))
return;
AssetList assetList = mAssetSelection.GetSelectedAssets();
Asset selectedAsset = AssetsSelection.GetSelectedAsset(
assetList);
string selectedPath = AssetsSelection.GetSelectedPath(
assetList);
WorkspaceTreeNode node = PlasticGui.Plastic.API.
GetWorkspaceTreeNode(selectedPath);
mHistoryViewLauncher.ShowHistoryView(
node.RepSpec,
node.RevInfo.ItemId,
selectedPath,
selectedAsset.isFolder);
}
void IAssetFilesFilterPatternsMenuOperations.AddFilesFilterPatterns(
FilterTypes type,
FilterActions action,
FilterOperationType operation)
{
List<string> selectedPaths = AssetsSelection.GetSelectedPaths(
mAssetSelection.GetSelectedAssets());
string[] rules = FilterRulesGenerator.GenerateRules(
selectedPaths, mWkInfo.ClientPath, action, operation);
bool isApplicableToAllWorkspaces = !mIsGluonMode;
bool isAddOperation = operation == FilterOperationType.Add;
FilterRulesConfirmationData filterRulesConfirmationData =
FilterRulesConfirmationDialog.AskForConfirmation(
rules, isAddOperation, isApplicableToAllWorkspaces, mParentWindow);
AddFilesFilterPatternsOperation.Run(
mWkInfo, mWorkspaceWindow, type, operation, filterRulesConfirmationData);
}
static string GetSymbolicName(string selectedPath)
{
WorkspaceTreeNode node = PlasticGui.Plastic.API.
GetWorkspaceTreeNode(selectedPath);
string branchName = string.Empty;
BranchInfoCache.TryGetBranchName(
node.RepSpec, node.RevInfo.BranchId, out branchName);
string userName = PlasticGui.Plastic.API.GetUserName(
node.RepSpec.Server, node.RevInfo.Owner);
string symbolicName = string.Format(
"cs:{0}@{1} {2} {3}",
node.RevInfo.Changeset,
string.Format("br:{0}", branchName),
userName,
"Workspace Revision");
return symbolicName;
}
readonly WorkspaceInfo mWkInfo;
readonly IViewSwitcher mViewSwitcher;
readonly IHistoryViewLauncher mHistoryViewLauncher;
readonly IWorkspaceWindow mWorkspaceWindow;
readonly ViewHost mViewHost;
readonly NewIncomingChangesUpdater mNewIncomingChangesUpdater;
readonly IAssetStatusCache mAssetStatusCache;
readonly IMergeViewLauncher mMergeViewLauncher;
readonly IGluonViewSwitcher mGluonViewSwitcher;
readonly bool mIsGluonMode;
readonly GuiMessage.IGuiMessage mGuiMessage;
readonly EditorProgressControls mProgressControls;
readonly EditorWindow mParentWindow;
readonly IAssetSelection mAssetSelection;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c8b452bcd72d8248a3297ff656f0a7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,125 @@
using System.Collections.Generic;
using System.IO;
using UnityEditor.VersionControl;
using PlasticGui.WorkspaceWindow.Items;
using Unity.PlasticSCM.Editor.AssetsOverlays;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal static class AssetsSelection
{
internal static Asset GetSelectedAsset(AssetList assetList)
{
if (assetList.Count == 0)
return null;
return assetList[0];
}
internal static string GetSelectedPath(AssetList assetList)
{
if (assetList.Count == 0)
return null;
return Path.GetFullPath(assetList[0].path);
}
internal static List<string> GetSelectedPaths(AssetList selectedAssets)
{
List<string> result = new List<string>();
foreach (Asset asset in selectedAssets)
{
string fullPath = Path.GetFullPath(asset.path);
result.Add(fullPath);
}
return result;
}
internal static SelectedPathsGroupInfo GetSelectedPathsGroupInfo(
AssetList selectedAssets,
IAssetStatusCache assetStatusCache)
{
SelectedPathsGroupInfo result = new SelectedPathsGroupInfo();
if (selectedAssets.Count == 0)
return result;
result.SelectedCount = selectedAssets.Count;
result.IsRootSelected = false;
result.IsCheckedoutEverySelected = true;
result.IsDirectoryEverySelected = true;
result.IsCheckedinEverySelected = true;
result.IsChangedEverySelected = true;
Asset firstAsset = selectedAssets[0];
string firstAssetName = GetAssetName(firstAsset);
AssetStatus firstStatus = GetAssetStatus(
firstAsset,
assetStatusCache);
result.FirstIsControlled = ClassifyAssetStatus.IsControlled(firstStatus);
result.FirstIsDirectory = firstAsset.isFolder;
result.FilterInfo.CommonName = firstAssetName;
result.FilterInfo.CommonExtension = Path.GetExtension(firstAssetName);
result.FilterInfo.CommonFullPath = firstAsset.assetPath;
foreach (Asset asset in selectedAssets)
{
string assetName = GetAssetName(asset);
AssetStatus status = GetAssetStatus(
asset,
assetStatusCache);
result.IsCheckedoutEverySelected &= ClassifyAssetStatus.IsCheckedOut(status);
result.IsDirectoryEverySelected &= asset.isFolder;
result.IsCheckedinEverySelected &= false; // TODO: not implemented yet
result.IsChangedEverySelected &= false; // TODO: not implemented yet
result.IsAnyDirectorySelected |= asset.isFolder;
result.IsAnyPrivateSelected |= ClassifyAssetStatus.IsPrivate(status);
result.FilterInfo.IsAnyIgnoredSelected |= ClassifyAssetStatus.IsIgnored(status);
result.FilterInfo.IsAnyHiddenChangedSelected |= ClassifyAssetStatus.IsHiddenChanged(status);
if (result.SelectedCount == 1)
continue;
if (result.FilterInfo.CommonName != assetName)
result.FilterInfo.CommonName = null;
if (result.FilterInfo.CommonExtension != Path.GetExtension(assetName))
result.FilterInfo.CommonExtension = null;
if (result.FilterInfo.CommonFullPath != asset.assetPath)
result.FilterInfo.CommonFullPath = null;
}
return result;
}
static AssetStatus GetAssetStatus(Asset asset, IAssetStatusCache assetStatusCache)
{
string assetPath = Path.GetFullPath(asset.assetPath);
if (MetaPath.IsMetaPath(assetPath))
assetPath = MetaPath.GetPathFromMetaPath(assetPath);
return assetStatusCache.GetStatusForPath(assetPath);
}
static string GetAssetName(Asset asset)
{
if (asset.isFolder)
return Path.GetFileName(Path.GetDirectoryName(asset.path));
return asset.fullName;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1686ac2e1d109ed43bf2dec74fed784f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f5d6c6c129fff0140a040d43aedb9547
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,384 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using Codice.Client.BaseCommands.EventTracking;
using Codice.Client.Common;
using Codice.CM.Common;
using GluonGui;
using PlasticGui;
using PlasticGui.Gluon;
using Unity.PlasticSCM.Editor.AssetsOverlays;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Unity.PlasticSCM.Editor.UI.Tree;
namespace Unity.PlasticSCM.Editor.AssetMenu.Dialogs
{
internal class CheckinDialog : PlasticDialog
{
protected override Rect DefaultRect
{
get
{
var baseRect = base.DefaultRect;
return new Rect(baseRect.x, baseRect.y, 700, 450);
}
}
protected override string GetTitle()
{
return PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinChanges);
}
internal static bool CheckinPaths(
WorkspaceInfo wkInfo,
List<string> paths,
IAssetStatusCache assetStatusCache,
bool isGluonMode,
EditorWindow parentWindow,
IWorkspaceWindow workspaceWindow,
ViewHost viewHost,
GuiMessage.IGuiMessage guiMessage,
IMergeViewLauncher mergeViewLauncher,
IGluonViewSwitcher gluonViewSwitcher)
{
MetaCache metaCache = new MetaCache();
metaCache.Build(paths);
CheckinDialog dialog = Create(
wkInfo,
paths,
assetStatusCache,
metaCache,
isGluonMode,
new ProgressControlsForDialogs(),
workspaceWindow,
viewHost,
guiMessage,
mergeViewLauncher,
gluonViewSwitcher);
return dialog.RunModal(parentWindow) == ResponseType.Ok;
}
protected override void OnModalGUI()
{
Title(PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinComment));
GUI.SetNextControlName(CHECKIN_TEXTAREA_NAME);
mComment = GUILayout.TextArea(
mComment,
EditorStyles.textArea,
GUILayout.MinHeight(120));
if (!mTextAreaFocused)
{
EditorGUI.FocusTextInControl(CHECKIN_TEXTAREA_NAME);
mTextAreaFocused = true;
}
Title(PlasticLocalization.GetString(PlasticLocalization.Name.Files));
DoFileList(
mWkInfo,
mPaths,
mAssetStatusCache,
mMetaCache);
DrawProgressForDialogs.For(
mProgressControls.ProgressData);
DoButtonsArea();
mProgressControls.ForcedUpdateProgress(this);
}
void DoFileList(
WorkspaceInfo wkInfo,
List<string> paths,
IAssetStatusCache assetStatusCache,
MetaCache metaCache)
{
mFileListScrollPosition = GUILayout.BeginScrollView(
mFileListScrollPosition,
EditorStyles.helpBox,
GUILayout.ExpandHeight(true));
foreach (string path in paths)
{
if (MetaPath.IsMetaPath(path))
continue;
Texture fileIcon = Directory.Exists(path) ?
Images.GetDirectoryIcon() :
Images.GetFileIcon(path);
string label = WorkspacePath.GetWorkspaceRelativePath(
wkInfo.ClientPath, path);
if (metaCache.HasMeta(path))
label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL);
AssetsOverlays.AssetStatus assetStatus =
assetStatusCache.GetStatusForPath(path);
Rect selectionRect = EditorGUILayout.GetControlRect();
DoListViewItem(selectionRect, fileIcon, label, assetStatus);
}
GUILayout.EndScrollView();
}
void DoListViewItem(
Rect itemRect,
Texture fileIcon,
string label,
AssetsOverlays.AssetStatus statusToDraw)
{
Texture overlayIcon = DrawAssetOverlay.DrawOverlayIcon.
GetOverlayIcon(statusToDraw);
itemRect = DrawTreeViewItem.DrawIconLeft(
itemRect,
UnityConstants.TREEVIEW_ROW_HEIGHT,
fileIcon,
overlayIcon);
GUI.Label(itemRect, label);
}
void DoButtonsArea()
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (Application.platform == RuntimePlatform.WindowsEditor)
{
DoCheckinButton();
DoCancelButton();
return;
}
DoCancelButton();
DoCheckinButton();
}
}
void DoCheckinButton()
{
GUI.enabled = !string.IsNullOrEmpty(mComment) && !mIsRunningCheckin;
try
{
if (!AcceptButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinButton)))
return;
}
finally
{
if (!mSentCheckinTrackEvent)
{
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
TrackFeatureUseEvent.Features.ContextMenuCheckinDialogCheckin);
mSentCheckinTrackEvent = true;
}
GUI.enabled = true;
}
OkButtonWithCheckinAction();
}
void DoCancelButton()
{
if (!NormalButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CancelButton)))
return;
if (!mSentCancelTrackEvent)
{
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
TrackFeatureUseEvent.Features.ContextMenuCheckinDialogCancel);
mSentCancelTrackEvent = true;
}
CancelButtonAction();
}
void OkButtonWithCheckinAction()
{
bool isCancelled;
SaveAssets.ForPathsWithConfirmation(mPaths, out isCancelled);
if (isCancelled)
return;
mIsRunningCheckin = true;
mPaths.AddRange(mMetaCache.GetExistingMeta(mPaths));
if (mIsGluonMode)
{
CheckinDialogOperations.CheckinPathsPartial(
mWkInfo,
mPaths,
mComment,
mViewHost,
this,
mGuiMessage,
mProgressControls,
mGluonViewSwitcher);
return;
}
CheckinDialogOperations.CheckinPaths(
mWkInfo,
mPaths,
mComment,
mWorkspaceWindow,
this,
mGuiMessage,
mProgressControls,
mMergeViewLauncher);
}
static CheckinDialog Create(
WorkspaceInfo wkInfo,
List<string> paths,
IAssetStatusCache assetStatusCache,
MetaCache metaCache,
bool isGluonMode,
ProgressControlsForDialogs progressControls,
IWorkspaceWindow workspaceWindow,
ViewHost viewHost,
GuiMessage.IGuiMessage guiMessage,
IMergeViewLauncher mergeViewLauncher,
IGluonViewSwitcher gluonViewSwitcher)
{
var instance = CreateInstance<CheckinDialog>();
instance.IsResizable = true;
instance.minSize = new Vector2(520, 370);
instance.mWkInfo = wkInfo;
instance.mPaths = paths;
instance.mAssetStatusCache = assetStatusCache;
instance.mMetaCache = metaCache;
instance.mIsGluonMode = isGluonMode;
instance.mProgressControls = progressControls;
instance.mWorkspaceWindow = workspaceWindow;
instance.mViewHost = viewHost;
instance.mGuiMessage = guiMessage;
instance.mMergeViewLauncher = mergeViewLauncher;
instance.mGluonViewSwitcher = gluonViewSwitcher;
instance.mEnterKeyAction = instance.OkButtonAction;
instance.mEscapeKeyAction = instance.CancelButtonAction;
return instance;
}
WorkspaceInfo mWkInfo;
List<string> mPaths;
IAssetStatusCache mAssetStatusCache;
MetaCache mMetaCache;
bool mIsGluonMode;
bool mTextAreaFocused;
string mComment;
bool mIsRunningCheckin;
Vector2 mFileListScrollPosition;
// IMGUI evaluates every frame, need to make sure feature tracks get sent only once
bool mSentCheckinTrackEvent = false;
bool mSentCancelTrackEvent = false;
ProgressControlsForDialogs mProgressControls;
IWorkspaceWindow mWorkspaceWindow;
ViewHost mViewHost;
IMergeViewLauncher mMergeViewLauncher;
IGluonViewSwitcher mGluonViewSwitcher;
GuiMessage.IGuiMessage mGuiMessage;
const string CHECKIN_TEXTAREA_NAME = "checkin_textarea";
class MetaCache
{
internal bool HasMeta(string path)
{
return mCache.Contains(MetaPath.GetMetaPath(path));
}
internal List<string> GetExistingMeta(List<string> paths)
{
List<string> result = new List<string>();
foreach (string path in paths)
{
string metaPath = MetaPath.GetMetaPath(path);
if (!mCache.Contains(metaPath))
continue;
result.Add(metaPath);
}
return result;
}
internal void Build(List<string> paths)
{
HashSet<string> indexedKeys = BuildIndexedKeys(paths);
for (int i = paths.Count - 1; i >= 0; i--)
{
string currentPath = paths[i];
if (!MetaPath.IsMetaPath(currentPath))
continue;
string realPath = MetaPath.GetPathFromMetaPath(currentPath);
if (!indexedKeys.Contains(realPath))
continue;
// found foo.c and foo.c.meta
// with the same chage types - move .meta to cache
mCache.Add(currentPath);
paths.RemoveAt(i);
}
}
static HashSet<string> BuildIndexedKeys(List<string> paths)
{
HashSet<string> result = new HashSet<string>();
foreach (string path in paths)
{
if (MetaPath.IsMetaPath(path))
continue;
result.Add(path);
}
return result;
}
HashSet<string> mCache =
new HashSet<string>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 678db227e4ffec949980d309c0532b08
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.CheckIn;
using Codice.Client.Common;
using Codice.Client.Common.Threading;
using Codice.Client.GameUI.Checkin;
using Codice.CM.Common;
using GluonGui;
using PlasticGui;
using PlasticGui.Gluon;
using PlasticGui.WorkspaceWindow.PendingChanges;
namespace Unity.PlasticSCM.Editor.AssetMenu.Dialogs
{
internal static class CheckinDialogOperations
{
internal static void CheckinPaths(
WorkspaceInfo wkInfo,
List<string> paths,
string comment,
IWorkspaceWindow workspaceWindow,
CheckinDialog dialog,
GuiMessage.IGuiMessage guiMessage,
IProgressControls progressControls,
IMergeViewLauncher mergeViewLauncher)
{
BaseCommandsImpl baseCommands = new BaseCommandsImpl();
progressControls.ShowProgress("Checkin in files");
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
CheckinParams ciParams = new CheckinParams();
ciParams.paths = paths.ToArray();
ciParams.comment = comment;
ciParams.time = DateTime.MinValue;
ciParams.flags = CheckinFlags.Recurse | CheckinFlags.ProcessSymlinks;
baseCommands.CheckIn(ciParams);
},
/*afterOperationDelegate*/ delegate
{
progressControls.HideProgress();
((IPlasticDialogCloser)dialog).CloseDialog();
if (waiter.Exception is CmClientMergeNeededException ||
waiter.Exception is CmClientUpdateMergeNeededException)
{
// we need to explicitly call EditorWindow.Close() to ensure
// that the dialog is closed before asking the user
dialog.Close();
if (!UserWantsToShowIncomingView(guiMessage))
return;
ShowIncomingChanges.FromCheckin(
wkInfo,
mergeViewLauncher,
progressControls);
return;
}
if (waiter.Exception != null)
{
ExceptionsHandler.DisplayException(waiter.Exception);
return;
}
workspaceWindow.RefreshView(ViewType.PendingChangesView);
workspaceWindow.RefreshView(ViewType.HistoryView);
});
}
internal static void CheckinPathsPartial(
WorkspaceInfo wkInfo,
List<string> paths,
string comment,
ViewHost viewHost,
CheckinDialog dialog,
GuiMessage.IGuiMessage guiMessage,
IProgressControls progressControls,
IGluonViewSwitcher gluonViewSwitcher)
{
BaseCommandsImpl baseCommands = new BaseCommandsImpl();
progressControls.ShowProgress(PlasticLocalization.GetString(
PlasticLocalization.Name.CheckinInFilesProgress));
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
baseCommands.PartialCheckin(wkInfo, paths, comment);
},
/*afterOperationDelegate*/ delegate
{
progressControls.HideProgress();
((IPlasticDialogCloser)dialog).CloseDialog();
if (waiter.Exception is CheckinConflictsException)
{
// we need to explicitly call EditorWindow.Close() to ensure
// that the dialog is closed before asking the user
dialog.Close();
if (!UserWantsToShowIncomingView(guiMessage))
return;
gluonViewSwitcher.ShowIncomingChangesView();
return;
}
if (waiter.Exception != null)
{
ExceptionsHandler.DisplayException(waiter.Exception);
return;
}
viewHost.RefreshView(ViewType.CheckinView);
viewHost.RefreshView(ViewType.HistoryView);
});
}
static bool UserWantsToShowIncomingView(GuiMessage.IGuiMessage guiMessage)
{
GuiMessage.GuiMessageResponseButton result = guiMessage.ShowQuestion(
PlasticLocalization.GetString(PlasticLocalization.Name.CheckinConflictsTitle),
PlasticLocalization.GetString(PlasticLocalization.Name.UnityCheckinConflictsExplanation),
"",
PlasticLocalization.GetString(PlasticLocalization.Name.CheckinShowIncomingChangesView),
PlasticLocalization.GetString(PlasticLocalization.Name.CancelButton),
false);
return result == GuiMessage.GuiMessageResponseButton.Second;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c44a2ac668da0a04d9e567739215b2eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,49 @@
using System;
using UnityEditor;
using UnityEditor.VersionControl;
namespace Unity.PlasticSCM.Editor.AssetMenu
{
internal class ProjectViewAssetSelection : AssetOperations.IAssetSelection
{
internal ProjectViewAssetSelection() { }
internal ProjectViewAssetSelection(Action assetSelectionChangedAction)
{
mAssetSelectionChangedAction = assetSelectionChangedAction;
Selection.selectionChanged += SelectionChanged;
}
internal void Dispose()
{
Selection.selectionChanged -= SelectionChanged;
}
void SelectionChanged()
{
// Selection.selectionChanged gets triggered on both
// project view and scene view. We only want to trigger
// the action if user selects on project view (has assets)
if (HasSelectedAssets())
mAssetSelectionChangedAction();
}
AssetList AssetOperations.IAssetSelection.GetSelectedAssets()
{
if (Selection.assetGUIDs.Length == 0)
return new AssetList();
return Provider.GetAssetListFromSelection();
}
bool HasSelectedAssets()
{
// Objects in project view have GUIDs, objects in scene view don't
return Selection.assetGUIDs.Length > 0;
}
Action mAssetSelectionChangedAction;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8a49294ba135a2408fd1b26bcda6f97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 491c192b16f732b4983b4a539908ad32
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,102 @@
using System;
namespace Unity.PlasticSCM.Editor.AssetsOverlays
{
[Flags]
internal enum AssetStatus
{
None = 0,
Private = 1 << 0,
Ignored = 1 << 2,
Added = 1 << 3,
Checkout = 1 << 4,
Controlled = 1 << 5,
UpToDate = 1 << 6,
OutOfDate = 1 << 7,
Conflicted = 1 << 8,
DeletedOnServer = 1 << 9,
Locked = 1 << 10,
LockedRemote = 1 << 11,
HiddenChanged = 1 << 12,
}
internal class LockStatusData
{
internal readonly AssetStatus Status;
internal readonly string LockedBy;
internal readonly string WorkspaceName;
internal LockStatusData(
AssetStatus status,
string lockedBy,
string workspaceName)
{
Status = status;
LockedBy = lockedBy;
WorkspaceName = workspaceName;
}
}
internal class ClassifyAssetStatus
{
internal static bool IsPrivate(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Private);
}
internal static bool IsIgnored(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Ignored);
}
internal static bool IsControlled(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Controlled);
}
internal static bool IsLocked(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Locked);
}
internal static bool IsLockedRemote(AssetStatus status)
{
return ContainsAny(status, AssetStatus.LockedRemote);
}
internal static bool IsOutOfDate(AssetStatus status)
{
return ContainsAny(status, AssetStatus.OutOfDate);
}
internal static bool IsDeletedOnServer(AssetStatus status)
{
return ContainsAny(status, AssetStatus.DeletedOnServer);
}
internal static bool IsConflicted(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Conflicted);
}
internal static bool IsAdded(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Added);
}
internal static bool IsCheckedOut(AssetStatus status)
{
return ContainsAny(status, AssetStatus.Checkout);
}
internal static bool IsHiddenChanged(AssetStatus status)
{
return ContainsAny(status, AssetStatus.HiddenChanged);
}
static bool ContainsAny(AssetStatus status, AssetStatus matchTo)
{
return (status & matchTo) != AssetStatus.None;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 062535eac3e5dd1409b6a50b0d043e2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6a94a55dca335c547ac65bd4b85d2a55
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,97 @@
using System;
using System.IO;
using UnityEditor;
using Codice.CM.Common;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal interface IAssetStatusCache
{
AssetStatus GetStatusForPath(string fullPath);
AssetStatus GetStatusForGuid(string guid);
LockStatusData GetLockStatusData(string guid);
LockStatusData GetLockStatusDataForPath(string path);
void Clear();
}
internal class AssetStatusCache : IAssetStatusCache
{
internal AssetStatusCache(
WorkspaceInfo wkInfo,
bool isGluonMode)
{
mLocalStatusCache = new LocalStatusCache(wkInfo);
mRemoteStatusCache = new RemoteStatusCache(
wkInfo,
isGluonMode,
ProjectWindow.Repaint);
mLockStatusCache = new LockStatusCache(
wkInfo,
ProjectWindow.Repaint);
}
AssetStatus IAssetStatusCache.GetStatusForPath(string fullPath)
{
AssetStatus localStatus = mLocalStatusCache.GetStatus(fullPath);
if (!ClassifyAssetStatus.IsControlled(localStatus))
return localStatus;
AssetStatus remoteStatus = mRemoteStatusCache.GetStatus(fullPath);
AssetStatus lockStatus = mLockStatusCache.GetStatus(fullPath);
return localStatus | remoteStatus | lockStatus;
}
AssetStatus IAssetStatusCache.GetStatusForGuid(string guid)
{
string fullPath = GetAssetPath(guid);
if (string.IsNullOrEmpty(fullPath))
return AssetStatus.None;
return ((IAssetStatusCache)this).GetStatusForPath(fullPath);
}
LockStatusData IAssetStatusCache.GetLockStatusDataForPath(string path)
{
if (string.IsNullOrEmpty(path))
return null;
return mLockStatusCache.GetLockStatusData(path);
}
LockStatusData IAssetStatusCache.GetLockStatusData(string guid)
{
string fullPath = GetAssetPath(guid);
return ((IAssetStatusCache)this).GetLockStatusDataForPath(fullPath);
}
void IAssetStatusCache.Clear()
{
mLocalStatusCache.Clear();
mRemoteStatusCache.Clear();
mLockStatusCache.Clear();
}
static string GetAssetPath(string guid)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(assetPath))
return null;
return Path.GetFullPath(assetPath);
}
readonly LocalStatusCache mLocalStatusCache;
readonly RemoteStatusCache mRemoteStatusCache;
readonly LockStatusCache mLockStatusCache;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f227b28cf424364489edd67fce697bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Codice.Utils;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal static class BuildPathDictionary
{
internal static Dictionary<string, T> ForPlatform<T>()
{
if (PlatformIdentifier.IsWindows())
return new Dictionary<string, T>(
StringComparer.OrdinalIgnoreCase);
return new Dictionary<string, T>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c963b5d17c74314eb7105e71377cdb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,88 @@
using System.Collections.Generic;
using Codice;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.CM.Common;
using PlasticGui;
using PlasticGui.WorkspaceWindow;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal class LocalStatusCache
{
internal LocalStatusCache(WorkspaceInfo wkInfo)
{
mWkInfo = wkInfo;
}
internal AssetStatus GetStatus(string fullPath)
{
AssetStatus result;
if (mStatusByPathCache.TryGetValue(fullPath, out result))
return result;
result = CalculateStatus(
fullPath,
mWkInfo.ClientPath,
FilterManager.Get().GetIgnoredFilter(),
FilterManager.Get().GetHiddenChangesFilter());
mStatusByPathCache.Add(fullPath, result);
return result;
}
internal void Clear()
{
FilterManager.Get().Reload();
mStatusByPathCache.Clear();
}
static AssetStatus CalculateStatus(
string fullPath,
string wkPath,
IgnoredFilesFilter ignoredFilter,
HiddenChangesFilesFilter hiddenChangesFilter)
{
if (!IsOnWorkspace(fullPath, wkPath))
return AssetStatus.None;
WorkspaceTreeNode treeNode = PlasticGui.Plastic.API.GetWorkspaceTreeNode(fullPath);
if (CheckWorkspaceTreeNodeStatus.IsPrivate(treeNode))
{
return ignoredFilter.IsIgnored(fullPath) ?
AssetStatus.Ignored : AssetStatus.Private;
}
if (CheckWorkspaceTreeNodeStatus.IsAdded(treeNode))
return AssetStatus.Added;
AssetStatus result = AssetStatus.Controlled;
if (CheckWorkspaceTreeNodeStatus.IsCheckedOut(treeNode) &&
!CheckWorkspaceTreeNodeStatus.IsDirectory(treeNode))
result |= AssetStatus.Checkout;
if (hiddenChangesFilter.IsHiddenChanged(fullPath))
result |= AssetStatus.HiddenChanged;
return result;
}
static bool IsOnWorkspace(string fullPath, string clientPath)
{
return PathHelper.IsContainedOn(fullPath, clientPath);
}
Dictionary<string, AssetStatus> mStatusByPathCache =
BuildPathDictionary.ForPlatform<AssetStatus>();
readonly WorkspaceInfo mWkInfo;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 226459a134855504d841db6b61519d2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using Codice;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.Client.Common.Locks;
using Codice.Client.Common.Threading;
using Codice.Client.Common.WkTree;
using Codice.CM.Common;
using Codice.Utils;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal class LockStatusCache
{
internal LockStatusCache(
WorkspaceInfo wkInfo,
Action repaintProjectWindow)
{
mWkInfo = wkInfo;
mRepaintProjectWindow = repaintProjectWindow;
}
internal AssetStatus GetStatus(string fullPath)
{
LockStatusData lockStatusData = GetLockStatusData(fullPath);
if (lockStatusData == null)
return AssetStatus.None;
return lockStatusData.Status;
}
internal LockStatusData GetLockStatusData(string fullPath)
{
lock (mLock)
{
if (mStatusByPathCache == null)
{
mStatusByPathCache = BuildPathDictionary.ForPlatform<LockStatusData>();
mCurrentCancelToken.Cancel();
mCurrentCancelToken = new CancelToken();
AsyncCalculateStatus(mCurrentCancelToken);
return null;
}
LockStatusData result;
if (mStatusByPathCache.TryGetValue(fullPath, out result))
return result;
return null;
}
}
internal void Clear()
{
lock (mLock)
{
mCurrentCancelToken.Cancel();
mStatusByPathCache = null;
}
}
void AsyncCalculateStatus(CancelToken cancelToken)
{
Dictionary<string, LockStatusData> statusByPathCache = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> lockCandidates =
new Dictionary<RepositorySpec, List<WorkspaceTreeNode>>();
FillLockCandidates.ForTree(mWkInfo, lockCandidates);
if (cancelToken.IsCancelled())
return;
Dictionary<WorkspaceTreeNode, LockInfo> lockInfoByNode =
SearchLocks.GetLocksInfo(mWkInfo, lockCandidates);
if (cancelToken.IsCancelled())
return;
statusByPathCache = BuildStatusByNodeCache.
ForLocks(mWkInfo.ClientPath, lockInfoByNode);
},
/*afterOperationDelegate*/ delegate
{
if (waiter.Exception != null)
{
ExceptionsHandler.LogException(
"LockStatusCache",
waiter.Exception);
return;
}
if (cancelToken.IsCancelled())
return;
lock (mLock)
{
mStatusByPathCache = statusByPathCache;
}
mRepaintProjectWindow();
});
}
static class FillLockCandidates
{
internal static void ForTree(
WorkspaceInfo wkInfo,
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> lockCandidates)
{
WorkspaceTreeNode rootNode = CmConnection.Get().GetWorkspaceTreeHandler().
GetWorkspaceTree(wkInfo, wkInfo.ClientPath, true);
Queue<WorkspaceTreeNode> pendingDirectories = new Queue<WorkspaceTreeNode>();
pendingDirectories.Enqueue(rootNode);
while (pendingDirectories.Count > 0)
{
WorkspaceTreeNode directoryNode = pendingDirectories.Dequeue();
ForChildren(directoryNode, pendingDirectories, lockCandidates);
}
}
static void ForChildren(
WorkspaceTreeNode directoryNode,
Queue<WorkspaceTreeNode> pendingDirectories,
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> lockCandidates)
{
if (!directoryNode.HasChildren)
return;
foreach (WorkspaceTreeNode child in directoryNode.Children)
{
if (CheckWorkspaceTreeNodeStatus.IsDirectory(child))
{
pendingDirectories.Enqueue(child);
continue;
}
if (CheckWorkspaceTreeNodeStatus.IsAdded(child))
continue;
List<WorkspaceTreeNode> nodes = null;
if (!lockCandidates.TryGetValue(child.RepSpec, out nodes))
{
nodes = new List<WorkspaceTreeNode>();
lockCandidates.Add(child.RepSpec, nodes);
}
nodes.Add(child);
}
}
}
static class BuildStatusByNodeCache
{
internal static Dictionary<string, LockStatusData> ForLocks(
string wkPath,
Dictionary<WorkspaceTreeNode, LockInfo> lockInfoByNode)
{
Dictionary<string, LockStatusData> result =
BuildPathDictionary.ForPlatform<LockStatusData>();
LockOwnerNameResolver nameResolver = new LockOwnerNameResolver();
foreach (WorkspaceTreeNode node in lockInfoByNode.Keys)
{
LockStatusData lockStatusData = BuildLockStatusData(
node, lockInfoByNode[node], nameResolver);
string nodeWkPath = WorkspacePath.GetWorkspacePathFromCmPath(
wkPath,
WorkspaceNodeOperations.GetCmPath(node),
PathHelper.GetDirectorySeparatorChar(wkPath));
result.Add(nodeWkPath, lockStatusData);
}
return result;
}
static LockStatusData BuildLockStatusData(
WorkspaceTreeNode node,
LockInfo lockInfo,
LockOwnerNameResolver nameResolver)
{
AssetStatus status = CheckWorkspaceTreeNodeStatus.IsCheckedOut(node) ?
AssetStatus.Locked : AssetStatus.LockedRemote;
return new LockStatusData(
status,
nameResolver.GetSeidName(lockInfo.SEIDData),
LockWkInfo.GetWkCleanName(lockInfo));
}
}
CancelToken mCurrentCancelToken = new CancelToken();
Dictionary<string, LockStatusData> mStatusByPathCache;
readonly WorkspaceInfo mWkInfo;
readonly Action mRepaintProjectWindow;
static object mLock = new object();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 953c29d2e0dece647a64940343c91547
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using Codice.Client.BaseCommands;
using Codice.Client.Commands;
using Codice.Client.Common;
using Codice.Client.Common.Threading;
using Codice.Client.GameUI;
using Codice.Client.GameUI.Update;
using Codice.CM.Common;
using Codice.CM.Common.Merge;
using Codice.Utils;
using GluonGui.WorkspaceWindow.Views;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal class RemoteStatusCache
{
internal RemoteStatusCache(
WorkspaceInfo wkInfo,
bool isGluonMode,
Action repaintProjectWindow)
{
mWkInfo = wkInfo;
mIsGluonMode = isGluonMode;
mRepaintProjectWindow = repaintProjectWindow;
}
internal AssetStatus GetStatus(string fullPath)
{
if (!mIsGluonMode)
return AssetStatus.UpToDate;
lock(mLock)
{
if (mStatusByPathCache == null)
{
mStatusByPathCache = BuildPathDictionary.ForPlatform<AssetStatus>();
mCurrentCancelToken.Cancel();
mCurrentCancelToken = new CancelToken();
AsyncCalculateStatus(mCurrentCancelToken);
return AssetStatus.UpToDate;
}
AssetStatus result;
if (mStatusByPathCache.TryGetValue(fullPath, out result))
return result;
return AssetStatus.UpToDate;
}
}
internal void Clear()
{
lock (mLock)
{
mCurrentCancelToken.Cancel();
mStatusByPathCache = null;
}
}
void AsyncCalculateStatus(CancelToken cancelToken)
{
Dictionary<string, AssetStatus> statusByPathCache = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(50);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
OutOfDateItems outOfDateItems =
OutOfDateUpdater.CalculateOutOfDateItems(
mWkInfo, new List<ErrorMessage>(),
OutOfDateCalculator.Options.IsIncomingChanges);
if (cancelToken.IsCancelled())
return;
statusByPathCache = BuildStatusByPathCache.
ForOutOfDateItems(outOfDateItems, mWkInfo.ClientPath);
},
/*afterOperationDelegate*/ delegate
{
if (waiter.Exception != null)
{
ExceptionsHandler.LogException(
"RemoteStatusCache",
waiter.Exception);
return;
}
if (cancelToken.IsCancelled())
return;
lock (mLock)
{
mStatusByPathCache = statusByPathCache;
}
mRepaintProjectWindow();
});
}
static class BuildStatusByPathCache
{
internal static Dictionary<string, AssetStatus> ForOutOfDateItems(
OutOfDateItems outOfDateItems,
string wkPath)
{
Dictionary<string, AssetStatus> result =
BuildPathDictionary.ForPlatform<AssetStatus>();
if (outOfDateItems == null)
return result;
foreach (OutOfDateItemsByMount diffs in
outOfDateItems.GetOutOfDateItemsByMountList())
{
foreach (Difference diff in diffs.Changed)
{
if (diff is DiffXlinkChanged)
continue;
string path = GetPathForDiff(wkPath, diffs.Mount, diff.Path);
result.Add(path, AssetStatus.OutOfDate);
}
foreach (Difference diff in diffs.Deleted)
{
string path = GetPathForDiff(wkPath, diffs.Mount, diff.Path);
result.Add(path, AssetStatus.DeletedOnServer);
}
}
foreach (GluonFileConflict fileConflict in
outOfDateItems.GetFileConflicts())
{
string path = GetPathForConflict(wkPath, fileConflict.CmPath);
result.Add(path, AssetStatus.Conflicted);
}
return result;
}
static string GetPathForDiff(
string wkPath,
MountPointWithPath mountPoint,
string cmSubPath)
{
return WorkspacePath.GetWorkspacePathFromCmPath(
wkPath,
WorkspacePath.ComposeMountPath(mountPoint.MountPath, cmSubPath),
PathHelper.GetDirectorySeparatorChar(wkPath));
}
static string GetPathForConflict(
string wkPath,
string cmPath)
{
return WorkspacePath.GetWorkspacePathFromCmPath(
wkPath, cmPath,
PathHelper.GetDirectorySeparatorChar(wkPath));
}
}
CancelToken mCurrentCancelToken = new CancelToken();
Dictionary<string, AssetStatus> mStatusByPathCache;
readonly Action mRepaintProjectWindow;
readonly bool mIsGluonMode;
readonly WorkspaceInfo mWkInfo;
static object mLock = new object();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a9acb575a60d7e045ad7fadd3e3e137d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.Client.Common.Locks;
using Codice.Client.Common.WkTree;
using Codice.CM.Common;
using Codice.CM.WorkspaceServer.DataStore.Guids;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal static class SearchLocks
{
internal static Dictionary<WorkspaceTreeNode, LockInfo> GetLocksInfo(
WorkspaceInfo wkInfo,
Dictionary<RepositorySpec, List<WorkspaceTreeNode>> locksCandidates)
{
Dictionary<WorkspaceTreeNode, LockInfo> result =
new Dictionary<WorkspaceTreeNode, LockInfo>();
Dictionary<string, Dictionary<Guid, LockInfo>> locksByItemByServer =
new Dictionary<string, Dictionary<Guid, LockInfo>>(
StringComparer.InvariantCultureIgnoreCase);
foreach (KeyValuePair<RepositorySpec, List<WorkspaceTreeNode>> each in locksCandidates)
{
FillRepositoryLocks(
wkInfo, each.Key, each.Value,
locksByItemByServer, result);
}
return result;
}
static void FillRepositoryLocks(
WorkspaceInfo wkInfo,
RepositorySpec repSpec,
List<WorkspaceTreeNode> candidates,
Dictionary<string, Dictionary<Guid, LockInfo>> locksByItemByServer,
Dictionary<WorkspaceTreeNode, LockInfo> locks)
{
if (candidates.Count == 0)
return;
LockRule lockRule = ServerLocks.GetLockRule(repSpec);
if (lockRule == null)
return;
candidates = GetLockableCandidates(candidates, lockRule);
if (candidates.Count == 0)
return;
string lockServer = string.IsNullOrEmpty(lockRule.LockServer) ?
repSpec.Server : lockRule.LockServer;
Dictionary<Guid, LockInfo> serverlocksByItem =
ServerLocks.GetServerLocksByItem(
lockServer, locksByItemByServer);
if (serverlocksByItem == null || serverlocksByItem.Count == 0)
return;
IList<Guid> candidatesGuids = GetCandidatesGuids(
wkInfo, repSpec, candidates);
for (int index = 0; index < candidates.Count; index++)
{
LockInfo serverLock;
if (!serverlocksByItem.TryGetValue(
candidatesGuids[index], out serverLock))
continue;
locks[candidates[index]] = serverLock;
}
}
static List<WorkspaceTreeNode> GetLockableCandidates(
List<WorkspaceTreeNode> candidates,
LockRule lockRule)
{
List<WorkspaceTreeNode> result = new List<WorkspaceTreeNode>();
LockedFilesFilter filter = new LockedFilesFilter(lockRule.Rules);
foreach (WorkspaceTreeNode candidate in candidates)
{
string cmPath = WorkspaceNodeOperations.GetCmPath(candidate);
if (cmPath == null)
{
//The node could not be on the head tree (like copied items) so when we
//cannot calculate the path we assume that it's lockable.
result.Add(candidate);
continue;
}
if (filter.IsLockable(cmPath))
result.Add(candidate);
}
return result;
}
static IList<Guid> GetCandidatesGuids(
WorkspaceInfo wkInfo,
RepositorySpec repSpec,
List<WorkspaceTreeNode> candidates)
{
RepositoryInfo repInfo = RepositorySpecResolverProvider.
Get().GetRepInfo(repSpec);
IList<long> ids = new List<long>(candidates.Count);
foreach (WorkspaceTreeNode candidate in candidates)
ids.Add(candidate.RevInfo.ItemId);
return GuidResolver.Get().GetObjectGuids(repInfo, wkInfo, ids);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cef27b0c65987be4384e16c988465aca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,228 @@
using System;
using UnityEditor;
using UnityEngine;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.AssetsOverlays
{
internal static class DrawAssetOverlay
{
internal static void Enable()
{
if (sIsEnabled)
return;
sIsEnabled = true;
sRepaintProjectWindow = ProjectWindow.Repaint;
EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI;
sRepaintProjectWindow();
}
internal static void Disable()
{
sIsEnabled = false;
EditorApplication.projectWindowItemOnGUI -= OnProjectWindowItemGUI;
sRepaintProjectWindow();
}
internal static void ClearCache()
{
PlasticPlugin.AssetStatusCache.Clear();
sRepaintProjectWindow();
}
internal static string GetStatusString(AssetStatus assetStatus)
{
if (ClassifyAssetStatus.IsPrivate(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.Private);
if (ClassifyAssetStatus.IsIgnored(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusIgnored);
if (ClassifyAssetStatus.IsAdded(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusAdded);
if (ClassifyAssetStatus.IsConflicted(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusConflicted);
if (ClassifyAssetStatus.IsDeletedOnServer(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusDeletedOnServer);
if (ClassifyAssetStatus.IsLockedRemote(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusLockedRemote);
if (ClassifyAssetStatus.IsOutOfDate(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusOutOfDate);
if (ClassifyAssetStatus.IsLocked(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusLockedMe);
if (ClassifyAssetStatus.IsCheckedOut(assetStatus))
return PlasticLocalization.GetString(
PlasticLocalization.Name.StatusCheckout);
return string.Empty;
}
internal static string GetTooltipText(
AssetStatus statusValue,
LockStatusData lockStatusData)
{
string statusText = GetStatusString(statusValue);
if (lockStatusData == null)
return statusText;
// example:
// Changed by:
// * dani_pen@hotmail.com
// * workspace wkLocal"
char bulletCharacter = '\u25cf';
string line1 = PlasticLocalization.GetString(
PlasticLocalization.Name.AssetOverlayTooltipStatus, statusText);
string line2 = string.Format("{0} {1}",
bulletCharacter,
lockStatusData.LockedBy);
string line3 = string.Format("{0} {1}",
bulletCharacter,
PlasticLocalization.GetString(
PlasticLocalization.Name.AssetOverlayTooltipWorkspace,
lockStatusData.WorkspaceName));
return string.Format(
"{0}" + Environment.NewLine +
"{1}" + Environment.NewLine +
"{2}",
line1,
line2,
line3);
}
static void OnProjectWindowItemGUI(string guid, Rect selectionRect)
{
if (string.IsNullOrEmpty(guid))
return;
if (Event.current.type != EventType.Repaint)
return;
AssetStatus assetStatus = PlasticPlugin.AssetStatusCache.GetStatusForGuid(guid);
LockStatusData lockStatusData =
ClassifyAssetStatus.IsLockedRemote(assetStatus) ?
PlasticPlugin.AssetStatusCache.GetLockStatusData(guid) :
null;
string tooltipText = GetTooltipText(
assetStatus,
lockStatusData);
DrawOverlayIcon.ForStatus(
selectionRect,
assetStatus,
tooltipText);
}
internal static class DrawOverlayIcon
{
internal static void ForStatus(
Rect selectionRect,
AssetStatus status,
string tooltipText)
{
Texture overlayIcon = GetOverlayIcon(status);
if (overlayIcon == null)
return;
Rect overlayRect = OverlayRect.GetRightBottonRect(
selectionRect);
GUI.DrawTexture(
overlayRect, overlayIcon, ScaleMode.ScaleToFit);
Rect tooltipRect = GetTooltipRect(selectionRect, overlayRect);
GUI.Label(tooltipRect, new GUIContent(string.Empty, tooltipText));
}
internal static Texture GetOverlayIcon(AssetStatus assetStatus)
{
if (ClassifyAssetStatus.IsPrivate(assetStatus))
return Images.GetPrivatedOverlayIcon();
if (ClassifyAssetStatus.IsIgnored(assetStatus))
return Images.GetIgnoredOverlayIcon();
if (ClassifyAssetStatus.IsAdded(assetStatus))
return Images.GetAddedOverlayIcon();
if (ClassifyAssetStatus.IsConflicted(assetStatus))
return Images.GetConflictedOverlayIcon();
if (ClassifyAssetStatus.IsDeletedOnServer(assetStatus))
return Images.GetDeletedRemoteOverlayIcon();
if (ClassifyAssetStatus.IsLockedRemote(assetStatus))
return Images.GetLockedRemoteOverlayIcon();
if (ClassifyAssetStatus.IsOutOfDate(assetStatus))
return Images.GetOutOfSyncOverlayIcon();
if (ClassifyAssetStatus.IsLocked(assetStatus))
return Images.GetLockedLocalOverlayIcon();
if (ClassifyAssetStatus.IsCheckedOut(assetStatus))
return Images.GetCheckedOutOverlayIcon();
return null;
}
static Rect Inflate(Rect rect, float width, float height)
{
return new Rect(
rect.x - width,
rect.y - height,
rect.width + 2f * width,
rect.height + 2f * height);
}
static Rect GetTooltipRect(
Rect selectionRect,
Rect overlayRect)
{
if (selectionRect.width > selectionRect.height)
{
return overlayRect;
}
return Inflate(overlayRect, 3f, 3f);
}
}
static Action sRepaintProjectWindow;
static bool sIsEnabled;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d30dfeb72257204458e1e8e1576b84ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b01e5a3d2517b904698dbc9fa0df727f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8697b23ed1b3db0448e2580433ae07d7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: d24e7e0b75e1a244bb9687d6fd4315ef
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: ddf2e76e8a1f88f46ae1e42277850b48
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: f09eae8c0ea04254b90c5386a034a225
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,92 @@
fileFormatVersion: 2
guid: f9161c6c9e06dc445ada3ee16d991f90
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show more