initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

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

View file

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

View file

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

View file

@ -0,0 +1,60 @@
using Codice.CM.Common;
namespace Unity.PlasticSCM.Editor.AssetsOverlays.Cache
{
internal interface IAssetStatusCache
{
AssetStatus GetStatus(string fullPath);
LockStatusData GetLockStatusData(string fullPath);
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.GetStatus(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;
}
LockStatusData IAssetStatusCache.GetLockStatusData(string fullPath)
{
return mLockStatusCache.GetLockStatusData(fullPath);
}
void IAssetStatusCache.Clear()
{
mLocalStatusCache.Clear();
mRemoteStatusCache.Clear();
mLockStatusCache.Clear();
}
readonly LocalStatusCache mLocalStatusCache;
readonly RemoteStatusCache mRemoteStatusCache;
readonly LockStatusCache mLockStatusCache;
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,75 @@
using System.Collections.Generic;
using Codice;
using Codice.Client.BaseCommands;
using Codice.Client.Commands.WkTree;
using Codice.CM.Common;
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()
{
mStatusByPathCache.Clear();
}
static AssetStatus CalculateStatus(
string fullPath,
string wkPath,
IgnoredFilesFilter ignoredFilter,
HiddenChangesFilesFilter hiddenChangesFilter)
{
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;
}
Dictionary<string, AssetStatus> mStatusByPathCache =
BuildPathDictionary.ForPlatform<AssetStatus>();
readonly WorkspaceInfo mWkInfo;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,250 @@
using System;
using UnityEditor;
using UnityEngine;
using PlasticGui;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.AssetsOverlays
{
internal static class DrawAssetOverlay
{
internal static void Enable(
string wkPath,
IAssetStatusCache assetStatusCache)
{
if (mIsEnabled)
return;
mWkPath = wkPath;
mAssetStatusCache = assetStatusCache;
mIsEnabled = true;
mRepaintProjectWindow = ProjectWindow.Repaint;
EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI;
mRepaintProjectWindow();
}
internal static void Disable()
{
mIsEnabled = false;
EditorApplication.projectWindowItemOnGUI -= OnProjectWindowItemGUI;
mRepaintProjectWindow();
mWkPath = null;
mAssetStatusCache = null;
}
internal static void ClearCache()
{
mAssetStatusCache.Clear();
mRepaintProjectWindow();
}
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;
string fullPath = AssetsPath.GetFullPathUnderWorkspace.
ForGuid(mWkPath, guid);
if (fullPath == null)
return;
AssetStatus assetStatus = mAssetStatusCache.GetStatus(fullPath);
LockStatusData lockStatusData =
ClassifyAssetStatus.IsLockedRemote(assetStatus) ?
mAssetStatusCache.GetLockStatusData(fullPath) :
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.GetOverlayRect(
selectionRect,
OVERLAY_ICON_OFFSET);
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 mRepaintProjectWindow;
static bool mIsEnabled;
static IAssetStatusCache mAssetStatusCache;
static string mWkPath;
const float OVERLAY_ICON_OFFSET = 20f;
}
}

View file

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