using System; using UnityEditor; using UnityEngine; using PlasticGui; using Unity.PlasticSCM.Editor.AssetsOverlays.Cache; using Unity.PlasticSCM.Editor.UI; namespace Unity.PlasticSCM.Editor.AssetsOverlays { internal static class DrawAssetOverlay { internal static void Initialize( IAssetStatusCache cache, Action repaintProjectWindow) { mAssetStatusCache = cache; mRepaintProjectWindow = repaintProjectWindow; EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI; mRepaintProjectWindow(); } internal static void Dispose() { EditorApplication.projectWindowItemOnGUI -= OnProjectWindowItemGUI; if (mRepaintProjectWindow != null) mRepaintProjectWindow(); } internal static void ClearCache() { mAssetStatusCache.Clear(); mRepaintProjectWindow(); } internal static AssetStatus GetStatusesToDraw(AssetStatus status) { if (status.HasFlag(AssetStatus.Checkout) && status.HasFlag(AssetStatus.Locked)) return status & ~AssetStatus.Checkout; if (status.HasFlag(AssetStatus.DeletedOnServer) && status.HasFlag(AssetStatus.LockedRemote)) return status & ~AssetStatus.LockedRemote; return status; } internal static string GetStatusString(AssetStatus statusValue) { switch (statusValue) { case AssetStatus.Private: return PlasticLocalization.GetString( PlasticLocalization.Name.Private); case AssetStatus.Ignored: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusIgnored); case AssetStatus.Added: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusAdded); case AssetStatus.Checkout: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusCheckout); case AssetStatus.OutOfDate: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusOutOfDate); case AssetStatus.Conflicted: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusConflicted); case AssetStatus.DeletedOnServer: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusDeletedOnServer); case AssetStatus.Locked: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusLockedMe); case AssetStatus.LockedRemote: return PlasticLocalization.GetString( PlasticLocalization.Name.StatusLockedRemote); } 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 statusesToDraw = GetStatusesToDraw( mAssetStatusCache.GetStatusForGuid(guid)); foreach (AssetStatus status in Enum.GetValues(typeof(AssetStatus))) { if (status == AssetStatus.None) continue; if (!statusesToDraw.HasFlag(status)) continue; LockStatusData lockStatusData = ClassifyAssetStatus.IsLockedRemote(status) ? mAssetStatusCache.GetLockStatusData(guid) : null; string tooltipText = GetTooltipText( status, lockStatusData); DrawOverlayIcon.ForStatus( selectionRect, status, 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 = GetOverlayRect( selectionRect, overlayIcon, status); GUI.DrawTexture( overlayRect, overlayIcon, ScaleMode.ScaleToFit); Rect tooltipRect = GetTooltipRect(selectionRect, overlayRect); GUI.Label(tooltipRect, new GUIContent(string.Empty, tooltipText)); } internal static Texture GetOverlayIcon(AssetStatus status) { switch (status) { case AssetStatus.Ignored: return Images.GetImage(Images.Name.Ignored); case AssetStatus.Private: return Images.GetPrivatedOverlayIcon(); case AssetStatus.Added: return Images.GetAddedOverlayIcon(); case AssetStatus.Checkout: return Images.GetCheckedOutOverlayIcon(); case AssetStatus.OutOfDate: return Images.GetOutOfSyncOverlayIcon(); case AssetStatus.Conflicted: return Images.GetConflictedOverlayIcon(); case AssetStatus.DeletedOnServer: return Images.GetDeletedRemoteOverlayIcon(); case AssetStatus.Locked: return Images.GetLockedLocalOverlayIcon(); case AssetStatus.LockedRemote: return Images.GetLockedRemoteOverlayIcon(); } return null; } static Rect Inflate(Rect rect, float width, float height) { return new Rect( rect.x - width, rect.y - height, rect.width + 2 * width, rect.height + 2 * height); } static Rect GetOverlayRect( Rect selectionRect, Texture overlayIcon, AssetStatus status) { OverlayAlignment alignment = GetIconPosition(status); if (selectionRect.width > selectionRect.height) return GetOverlayRectForSmallestSize( selectionRect, overlayIcon, alignment); return GetOverlayRectForOtherSizes( selectionRect, overlayIcon, alignment); } static Rect GetTooltipRect( Rect selectionRect, Rect overlayRect) { if (selectionRect.width > selectionRect.height) { return overlayRect; } return Inflate(overlayRect, 3, 3); } static Rect GetOverlayRectForSmallestSize( Rect selectionRect, Texture overlayIcon, OverlayAlignment alignment) { float xOffset = IsLeftAligned(alignment) ? -5 : 5; float yOffset = IsTopAligned(alignment) ? -4 : 4; return new Rect( selectionRect.x + xOffset, selectionRect.y + yOffset, OVERLAY_ICON_SIZE, OVERLAY_ICON_SIZE); } static Rect GetOverlayRectForOtherSizes( Rect selectionRect, Texture overlayIcon, OverlayAlignment alignment) { float xOffset = IsLeftAligned(alignment) ? 0 : selectionRect.width - overlayIcon.width; float yOffset = IsTopAligned(alignment) ? 0 : selectionRect.height - overlayIcon.height - 12; return new Rect( selectionRect.x + xOffset, selectionRect.y + yOffset, OVERLAY_ICON_SIZE, OVERLAY_ICON_SIZE); } static OverlayAlignment GetIconPosition(AssetStatus status) { if (status == AssetStatus.Checkout || status == AssetStatus.Locked) return OverlayAlignment.TopLeft; if (status == AssetStatus.DeletedOnServer || status == AssetStatus.LockedRemote) return OverlayAlignment.TopRight; if (status == AssetStatus.OutOfDate) return OverlayAlignment.BottomRight; return OverlayAlignment.BottomLeft; } static bool IsLeftAligned(OverlayAlignment alignment) { return alignment == OverlayAlignment.BottomLeft || alignment == OverlayAlignment.TopLeft; } static bool IsTopAligned(OverlayAlignment alignment) { return alignment == OverlayAlignment.TopLeft || alignment == OverlayAlignment.TopRight; } enum OverlayAlignment { TopLeft, BottomLeft, TopRight, BottomRight } } static IAssetStatusCache mAssetStatusCache; static Action mRepaintProjectWindow; const float OVERLAY_ICON_SIZE = 16; } }