using System; using System.Collections.Generic; using Codice.CM.Common; using PlasticGui; using PlasticGui.WorkspaceWindow.Topbar.WorkingObjectInfo.BranchesList; using Unity.PlasticSCM.Editor.UI; using UnityEditor.IMGUI.Controls; using UnityEngine; #if UNITY_6000_2_OR_NEWER using TreeView = UnityEditor.IMGUI.Controls.TreeView; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewState = UnityEditor.IMGUI.Controls.TreeViewState; #endif namespace Unity.PlasticSCM.Editor.Toolbar.PopupWindow.BranchesList { internal class BranchesTreeView : TreeView { internal interface IClickListener { void OnItemClicked(BranchTreeViewItem item); } internal BranchesTreeView(IClickListener listener) : base(new TreeViewState()) { mClickListener = listener; } internal void SetWorkingBranch(Func fetchWorkingBranch) { mFetchWorkingBranch = fetchWorkingBranch; } internal void SetBranches(ClassifiedBranchesList branches) { mBranches = branches; if (!string.IsNullOrEmpty(mSearchString)) { SetSearchString(mSearchString); return; } Reload(); } internal void SetSearchString(string searchString) { mSearchString = searchString; } protected override bool CanMultiSelect(TreeViewItem item) { return false; } protected override float GetCustomRowHeight(int row, TreeViewItem item) { if (IsCategory(item)) return 26; else return 38f; } public override void OnGUI(Rect rect) { if (Event.current.type == EventType.MouseMove) { Vector2 localMousePos = Event.current.mousePosition - rect.position + state.scrollPos; int newHoveredId = GetHoveredItemId(localMousePos); if (newHoveredId != mLastHoveredItemId) { mLastHoveredItemId = newHoveredId; Repaint(); } } base.OnGUI(rect); } protected override void RowGUI(RowGUIArgs args) { Rect rowRect = args.rowRect; Event evt = Event.current; switch (evt.type) { case EventType.MouseDown: if (rowRect.Contains(evt.mousePosition) && evt.button == 0) { mClickedItemId = args.item.id; evt.Use(); } break; case EventType.MouseUp: if (rowRect.Contains(evt.mousePosition) && evt.button == 0 && mClickedItemId == args.item.id && !IsCategory(args.item)) { mClickedItemId = -1; evt.Use(); mClickListener.OnItemClicked((BranchTreeViewItem)args.item); } break; case EventType.Repaint: DrawRowGUI(args); break; } } bool IsCategory(TreeViewItem item) { return item is CategoryTreeViewItem; } void DrawRowGUI(RowGUIArgs args) { if (IsCategory(args.item)) { DrawCategory(args); return; } DrawBranch(args); } void DrawBranch(RowGUIArgs args) { bool isHover = args.rowRect.Contains(Event.current.mousePosition); GUIStyle titleStyle = isHover ? UnityStyles.EditorToolbar.Popup.BranchesList.TitleHover : UnityStyles.EditorToolbar.Popup.BranchesList.Title; GUIStyle descriptionStyle = isHover ? UnityStyles.EditorToolbar.Popup.BranchesList.DescriptionHover : UnityStyles.EditorToolbar.Popup.BranchesList.Description; GUIStyle timeAgoStyle = isHover ? UnityStyles.EditorToolbar.Popup.BranchesList.TimeAgoHover : UnityStyles.EditorToolbar.Popup.BranchesList.TimeAgo; if (isHover) { GUI.Label(args.rowRect, GUIContent.none, UnityStyles.EditorToolbar.Popup.Hover); } BranchTreeViewItem branchItem = (BranchTreeViewItem)args.item; bool isWorkingBranch = branchItem.BranchInfo.Equals(mFetchWorkingBranch()); Texture icon = isWorkingBranch ? Images.GetCurrentBranchIcon() : Images.GetBranchIcon(); GUIContent titleContent = new GUIContent(args.item.displayName, icon); GUIContent descriptionContent = new GUIContent(branchItem.FormattedComment); string tooltip = string.Format("{0}, {1}", branchItem.BranchInfo.LocalTimeStamp.ToLongDateString(), branchItem.BranchInfo.LocalTimeStamp.ToLongTimeString()); GUIContent timeAgoContent = new GUIContent(Codice.Client.Common.Time.GetTimeAgoString( branchItem.BranchInfo.LocalTimeStamp), tooltip); Vector2 titleContentSize = titleStyle.CalcSize(titleContent); Vector2 descriptionContentSize = descriptionStyle.CalcSize(descriptionContent); Vector2 timeAgoSize = timeAgoStyle.CalcSize(timeAgoContent); Rect timeAgoRect = new Rect( args.rowRect.x + args.rowRect.width - timeAgoSize.x, args.rowRect.y + (args.rowRect.height - timeAgoSize.y) / 2, timeAgoSize.x, timeAgoSize.y); float titleAndDescriptionHeight = titleContentSize.y + descriptionContentSize.y; Rect titleRect = new Rect( args.rowRect.x, args.rowRect.y + (args.rowRect.height - titleAndDescriptionHeight) / 2, args.rowRect.width - timeAgoSize.x, titleContentSize.y); Rect desciptionRect = new Rect( args.rowRect.x, args.rowRect.y + (args.rowRect.height - titleAndDescriptionHeight) / 2 + titleContentSize.y, args.rowRect.width - timeAgoSize.x, descriptionContentSize.y); // add a tooltip if title or description is not completely displayed if (titleContentSize.x > args.rowRect.width - timeAgoSize.x) titleContent.tooltip = titleContent.text; if (descriptionContentSize.x > args.rowRect.width - timeAgoSize.x) descriptionContent.tooltip = descriptionContent.text; DrawContentWithSearchString( titleRect, titleContent, mSearchString, titleStyle); DrawContentWithSearchString( desciptionRect, descriptionContent, mSearchString, descriptionStyle); GUI.Label(timeAgoRect, timeAgoContent, timeAgoStyle); } static void DrawCategory(RowGUIArgs args) { GUI.Label( args.rowRect, args.item.displayName, UnityStyles.EditorToolbar.Popup.BranchesList.Category); } static void DrawContentWithSearchString( Rect rect, GUIContent content, string searchString, GUIStyle style) { int titleSearchStringIndex = string.IsNullOrEmpty(searchString) ? -1 : content.text.IndexOf(searchString, StringComparison.Ordinal); float iconWidth = content.image != null ? 16 : 0f; Rect iconRect = new Rect( rect.x + style.padding.left, rect.y, iconWidth, rect.height); Rect textRect = new Rect( rect.x + iconWidth + style.margin.left, rect.y, rect.width - iconWidth, rect.height); if (content.image != null) { GUI.DrawTexture(iconRect, content.image); } if (titleSearchStringIndex == -1) { GUI.Label(textRect, new GUIContent(content.text, content.tooltip), style); } else { style.DrawWithTextSelection( textRect, new GUIContent(content.text, content.tooltip), -1, titleSearchStringIndex, titleSearchStringIndex + searchString.Length ); } } int GetHoveredItemId(Vector2 mousePos) { int firstVisibleRow, lastVisibleRow; this.GetFirstAndLastVisibleRows(out firstVisibleRow, out lastVisibleRow); for (int i = firstVisibleRow; i <= lastVisibleRow; i++) { Rect rowRect = GetRowRect(i); if (rowRect.Contains(mousePos)) { return i; } } return -1; // No hover } protected override TreeViewItem BuildRoot() { var root = new TreeViewItem { id = -1, depth = -1 }; root.children = new List(); if (mBranches == null) return root; int currentId = -1; if (mBranches.IsMainBranchVisible) { root.AddChild(new CategoryTreeViewItem( currentId++, PlasticLocalization.Name.MainBranchSection.GetString())); root.AddChild(new BranchTreeViewItem(currentId++, mBranches.MainBranch)); } if (mBranches.AreRecentBranchesVisible) { root.AddChild(new CategoryTreeViewItem( currentId++, PlasticLocalization.Name.RecentBranchesSection.GetString())); foreach (BranchInfo branch in mBranches.RecentBranches) { root.AddChild(new BranchTreeViewItem(currentId++, branch)); } } if (mBranches.AreOtherBranchesVisible) { root.AddChild(new CategoryTreeViewItem( currentId++, PlasticLocalization.Name.OtherBranchesSection.GetString())); foreach (BranchInfo branch in mBranches.OtherBranches) { root.AddChild(new BranchTreeViewItem(currentId++, branch)); } } return root; } readonly IClickListener mClickListener; int mClickedItemId; string mSearchString; ClassifiedBranchesList mBranches; Func mFetchWorkingBranch; int mLastHoveredItemId = -1; } class BranchTreeViewItem : TreeViewItem { string mFormattedComment; internal BranchInfo BranchInfo { get; private set; } public string FormattedComment { get { if (string.IsNullOrEmpty(mFormattedComment)) mFormattedComment = GetFormattedComment(BranchInfo.Comment); return mFormattedComment; } } internal BranchTreeViewItem(int id, BranchInfo branchInfo) { this.id = id; this.displayName = branchInfo.Name; BranchInfo = branchInfo; } static string GetFormattedComment(string comment) { if (string.IsNullOrEmpty(comment)) return PlasticLocalization.Name.NoCommentSet.GetString(); return CommentFormatter.GetFormattedComment(comment); } } class CategoryTreeViewItem : TreeViewItem { internal CategoryTreeViewItem(int id, string displayName) { this.id = id; this.displayName = displayName; } } }