mirror of
https://github.com/Project-Redacted/Highscores-Server.git
synced 2025-05-18 01:14:56 +00:00
Add example Unity Project
This commit is contained in:
parent
fda7ff28dd
commit
e3acdb9d6b
7122 changed files with 505543 additions and 2 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2dccfb4f31ae9ba44b41452beaa34658
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,4 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.CollabProxy.EditorTests")]
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d4ef26aa386b44923b61c9c4b505a67c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c18cb9388313e4287ad5895ee735c47d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8aa8171e088f94069bbd1978a053f7dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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";
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f944311c8fff2479fa3ba741f6039fc8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fed9dda667cab45d398d06402bba03f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 882f1a4147a284f028899b9c018e63eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f516f1ec21a54a59a92bf99db2d9535
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d437fe60bb34f45728664a5d930c1635
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7c91a123806d41a0873fcdcb629b1c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fd0a39b4d296d4d509b4f1dbd08d0630
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0217a80286f79419daa202f69409f19b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a483595b0257945278dc75c5ff7d82ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d912d4873af534bd4a9d44bf1b52f14e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c4c1445ee948a4124bfa9fb818a17e36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fc46f91ea1e8e4ca2ab693fef9156dbe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3c737f7a9d78541d1ab25f28f045dd32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cf6aca931950a4a6a886e214e9e649c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 821f5482c5a3f4389885f4432433f56f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50de529b6a28f4a7093045e08810a5df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 08e9894bdf0834710b22d3c0aa245ac0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a6ab6fd2b91214e8a9c8ec2224a528de
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b1ae1e78552c459d9ce27048ff51c7f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a2d94c8977984b67984caeff9fa666e
|
||||
ScriptedImporter:
|
||||
fileIDToRecycleName:
|
||||
11400000: stylesheet
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 70d4d75a2877243758b0750cbc75b6eb
|
||||
ScriptedImporter:
|
||||
fileIDToRecycleName:
|
||||
11400000: stylesheet
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b52bde26a83564960bcb90217f72b910
|
||||
ScriptedImporter:
|
||||
fileIDToRecycleName:
|
||||
11400000: stylesheet
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 645165c8169474bfbbeb8fb0bcfd26f5
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d5ae383aa416ba14e800dff2526122ed
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,5 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.PlasticSCM.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.PlasticSCM.DevTools")]
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d50ac3212a7ff5a4795bf609a2a20350
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d3f8e034331c39c4f823ac31228dc4d9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b4eb3fe08d1dc86419dee5ddcc40baae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c8a8e3e4456f9149905cf2c80aa41a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c456fa791a741a045a8a99ee73af2ae6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b0621730f63c0c74cafa17f3ddc53eb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c8b452bcd72d8248a3297ff656f0a7a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1686ac2e1d109ed43bf2dec74fed784f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f5d6c6c129fff0140a040d43aedb9547
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 678db227e4ffec949980d309c0532b08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c44a2ac668da0a04d9e567739215b2eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b8a49294ba135a2408fd1b26bcda6f97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 491c192b16f732b4983b4a539908ad32
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 062535eac3e5dd1409b6a50b0d043e2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6a94a55dca335c547ac65bd4b85d2a55
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f227b28cf424364489edd67fce697bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c963b5d17c74314eb7105e71377cdb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 226459a134855504d841db6b61519d2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 953c29d2e0dece647a64940343c91547
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a9acb575a60d7e045ad7fadd3e3e137d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cef27b0c65987be4384e16c988465aca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d30dfeb72257204458e1e8e1576b84ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b01e5a3d2517b904698dbc9fa0df727f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8697b23ed1b3db0448e2580433ae07d7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
After Width: | Height: | Size: 442 B |
|
@ -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:
|
Binary file not shown.
After Width: | Height: | Size: 770 B |
|
@ -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:
|
Binary file not shown.
After Width: | Height: | Size: 694 B |
|
@ -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:
|
Binary file not shown.
After Width: | Height: | Size: 742 B |
|
@ -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:
|
Binary file not shown.
After Width: | Height: | Size: 573 B |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue