123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- using System.Collections.Generic;
- using System.Linq;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
- namespace UnityEditor.Timeline
- {
- class TimelineTreeView : ITreeViewGUI
- {
- float m_FoldoutWidth;
- Rect m_DraggingInsertionMarkerRect;
- readonly TreeViewController m_TreeView;
- List<Rect> m_RowRects = new List<Rect>();
- List<Rect> m_ExpandedRowRects = new List<Rect>();
- float m_MaxWidthOfRows;
- readonly WindowState m_State;
- static readonly float kMinTrackHeight = 25.0f;
- static readonly float kFoldOutOffset = 14.0f;
- static DirectorStyles m_Styles;
- public bool showInsertionMarker { get; set; }
- public virtual float topRowMargin { get; private set; }
- public virtual float bottomRowMargin { get; private set; }
- public TimelineTreeView(TimelineWindow sequencerWindow, TreeViewController treeView)
- {
- m_TreeView = treeView;
- m_TreeView.useExpansionAnimation = true;
- m_TreeView.selectionChangedCallback += SelectionChangedCallback;
- m_TreeView.contextClickOutsideItemsCallback += ContextClickOutsideItemsCallback;
- m_TreeView.itemDoubleClickedCallback += ItemDoubleClickedCallback;
- m_TreeView.contextClickItemCallback += ContextClickItemCallback;
- m_TreeView.SetConsumeKeyDownEvents(false);
- m_Styles = DirectorStyles.Instance;
- m_State = sequencerWindow.state;
- m_FoldoutWidth = DirectorStyles.Instance.foldout.fixedWidth;
- }
- void ItemDoubleClickedCallback(int id)
- {
- var trackGUI = m_TreeView.FindItem(id) as TimelineTrackGUI;
- if (trackGUI == null)
- return;
- if (trackGUI.track == null || trackGUI.track.lockedInHierarchy)
- return;
- var selection = SelectionManager.SelectedItems().ToList();
- var items = ItemsUtils.GetItems(trackGUI.track).ToList();
- var addToSelection = !selection.SequenceEqual(items);
- foreach (var i in items)
- {
- if (addToSelection)
- SelectionManager.Add(i);
- else
- SelectionManager.Remove(i);
- }
- }
- void ContextClickOutsideItemsCallback()
- {
- SequencerContextMenu.ShowNewTracksContextMenu(null, m_State);
- Event.current.Use();
- }
- void ContextClickItemCallback(int id)
- {
- // may not occur if another menu is active
- if (!m_TreeView.IsSelected(id))
- SelectionChangedCallback(new[] {id});
- SequencerContextMenu.ShowTrackContextMenu(SelectionManager.SelectedTracks().ToArray(), Event.current.mousePosition);
- Event.current.Use();
- }
- void SelectionChangedCallback(int[] ids)
- {
- if (Event.current.button == 1 && PickerUtils.PickedLayerableOfType<ISelectable>() != null)
- return;
- if (Event.current.command || Event.current.control || Event.current.shift)
- SelectionManager.UnSelectTracks();
- else
- SelectionManager.Clear();
- foreach (var id in ids)
- {
- var trackGUI = (TimelineTrackBaseGUI)m_TreeView.FindItem(id);
- SelectionManager.Add(trackGUI.track);
- }
- m_State.GetWindow().Repaint();
- }
- public void OnInitialize() {}
- public Rect GetRectForFraming(int row)
- {
- return GetRowRect(row, 1); // We ignore width by default when framing (only y scroll is affected)
- }
- protected virtual Vector2 GetSizeOfRow(TreeViewItem item)
- {
- if (item.displayName == "root")
- return new Vector2(m_TreeView.GetTotalRect().width, 0.0f);
- var trackGroupGui = item as TimelineGroupGUI;
- if (trackGroupGui != null)
- {
- return new Vector2(m_TreeView.GetTotalRect().width, trackGroupGui.GetHeight(m_State));
- }
- float height = TrackEditor.DefaultTrackHeight;
- if (item.hasChildren && m_TreeView.data.IsExpanded(item))
- {
- height = Mathf.Min(TrackEditor.DefaultTrackHeight, kMinTrackHeight);
- }
- return new Vector2(m_TreeView.GetTotalRect().width, height);
- }
- public virtual void BeginRowGUI()
- {
- if (m_TreeView.GetTotalRect().width != GetRowRect(0).width)
- {
- CalculateRowRects();
- }
- m_DraggingInsertionMarkerRect.x = -1;
- m_TreeView.SetSelection(SelectionManager.SelectedTrackGUI().Select(t => t.id).ToArray(), false);
- }
- public virtual void EndRowGUI()
- {
- // Draw row marker when dragging
- if (m_DraggingInsertionMarkerRect.x >= 0 && Event.current.type == EventType.Repaint)
- {
- Rect insertionRect = m_DraggingInsertionMarkerRect;
- const float insertionHeight = 1.0f;
- insertionRect.height = insertionHeight;
- if (m_TreeView.dragging.drawRowMarkerAbove)
- insertionRect.y -= insertionHeight * 0.5f + 2.0f;
- else
- insertionRect.y += m_DraggingInsertionMarkerRect.height - insertionHeight * 0.5f + 1.0f;
- EditorGUI.DrawRect(insertionRect, Color.white);
- }
- }
- public virtual void OnRowGUI(Rect rowRect, TreeViewItem item, int row, bool selected, bool focused)
- {
- using (new EditorGUI.DisabledScope(TimelineWindow.instance.currentMode.TrackState(TimelineWindow.instance.state) == TimelineModeGUIState.Disabled))
- {
- var sqvi = (TimelineTrackBaseGUI)item;
- sqvi.treeViewToWindowTransformation = m_TreeView.GetTotalRect().position - m_TreeView.state.scrollPos;
- // this may be called because an encompassing parent is visible
- if (!sqvi.visibleExpanded)
- return;
- Rect headerRect = rowRect;
- Rect contentRect = rowRect;
- headerRect.width = m_State.sequencerHeaderWidth - 2.0f;
- contentRect.xMin += m_State.sequencerHeaderWidth;
- contentRect.width = rowRect.width - m_State.sequencerHeaderWidth - 1.0f;
- Rect foldoutRect = rowRect;
- var indent = GetFoldoutIndent(item);
- var headerRectWithIndent = headerRect;
- headerRectWithIndent.xMin = indent;
- var rowRectWithIndent = new Rect(rowRect.x + indent, rowRect.y, rowRect.width - indent, rowRect.height);
- sqvi.Draw(headerRectWithIndent, contentRect, m_State);
- sqvi.DrawInsertionMarkers(rowRectWithIndent);
- if (Event.current.type == EventType.Repaint)
- {
- m_State.spacePartitioner.AddBounds(sqvi);
- // Show marker below this Item
- if (showInsertionMarker)
- {
- if (m_TreeView.dragging != null && m_TreeView.dragging.GetRowMarkerControlID() == TreeViewController.GetItemControlID(item))
- m_DraggingInsertionMarkerRect = rowRectWithIndent;
- }
- }
- // Draw foldout (after text content above to ensure drop down icon is rendered above selection highlight)
- DrawFoldout(item, foldoutRect, indent);
- sqvi.ClearDrawFlags();
- }
- }
- void DrawFoldout(TreeViewItem item, Rect foldoutRect, float indent)
- {
- var showFoldout = m_TreeView.data.IsExpandable(item);
- if (showFoldout)
- {
- foldoutRect.x = indent - kFoldOutOffset;
- foldoutRect.width = m_FoldoutWidth;
- EditorGUI.BeginChangeCheck();
- float foldoutIconHeight = DirectorStyles.Instance.foldout.fixedHeight;
- foldoutRect.y += foldoutIconHeight / 2.0f;
- foldoutRect.height = foldoutIconHeight;
- if (foldoutRect.xMax > m_State.sequencerHeaderWidth)
- return;
- //Override Disable state for TrakGroup toggle button to expand/collapse group.
- bool previousEnableState = GUI.enabled;
- GUI.enabled = true;
- bool newExpandedValue = GUI.Toggle(foldoutRect, m_TreeView.data.IsExpanded(item), GUIContent.none, m_Styles.foldout);
- GUI.enabled = previousEnableState;
- if (EditorGUI.EndChangeCheck())
- {
- if (Event.current.alt)
- m_TreeView.data.SetExpandedWithChildren(item, newExpandedValue);
- else
- m_TreeView.data.SetExpanded(item, newExpandedValue);
- }
- }
- }
- public Rect GetRenameRect(Rect rowRect, int row, TreeViewItem item)
- {
- return rowRect;
- }
- public void BeginPingItem(TreeViewItem item, float topPixelOfRow, float availableWidth) {}
- public void EndPingItem() {}
- public Rect GetRowRect(int row, float rowWidth)
- {
- return GetRowRect(row);
- }
- public Rect GetRowRect(int row)
- {
- if (m_RowRects.Count == 0)
- return new Rect();
- if (row >= m_RowRects.Count)
- return new Rect();
- return m_RowRects[row];
- }
- static float GetSpacing(TreeViewItem item)
- {
- var trackBase = item as TimelineTrackBaseGUI;
- if (trackBase != null)
- return trackBase.GetVerticalSpacingBetweenTracks();
- return 3.0f;
- }
- public void CalculateRowRects()
- {
- if (m_TreeView.isSearching)
- return;
- const float startY = 6.0f;
- IList<TreeViewItem> rows = m_TreeView.data.GetRows();
- m_RowRects = new List<Rect>(rows.Count);
- m_ExpandedRowRects = new List<Rect>(rows.Count);
- float curY = startY;
- m_MaxWidthOfRows = 1f;
- // first pass compute the row rects
- for (int i = 0; i < rows.Count; ++i)
- {
- var item = rows[i];
- if (i != 0)
- curY += GetSpacing(item);
- Vector2 rowSize = GetSizeOfRow(item);
- m_RowRects.Add(new Rect(0, curY, rowSize.x, rowSize.y));
- m_ExpandedRowRects.Add(m_RowRects[i]);
- curY += rowSize.y;
- if (rowSize.x > m_MaxWidthOfRows)
- m_MaxWidthOfRows = rowSize.x;
- // updated the expanded state
- var groupGUI = item as TimelineGroupGUI;
- if (groupGUI != null)
- groupGUI.SetExpanded(m_TreeView.data.IsExpanded(item));
- }
- float halfHeight = halfDropBetweenHeight;
- const float kGroupPad = 1.0f;
- const float kSkinPadding = 5.0f * 0.6f;
- // work bottom up and compute visible regions for groups
- for (int i = rows.Count - 1; i > 0; i--)
- {
- float height = 0;
- TimelineTrackBaseGUI item = (TimelineTrackBaseGUI)rows[i];
- if (item.isExpanded && item.children != null && item.children.Count > 0)
- {
- for (var j = 0; j < item.children.Count; j++)
- {
- var child = item.children[j];
- int index = rows.IndexOf(child);
- if (index > i)
- height += m_ExpandedRowRects[index].height + kSkinPadding;
- }
- height += kGroupPad;
- }
- m_ExpandedRowRects[i] = new Rect(m_RowRects[i].x, m_RowRects[i].y, m_RowRects[i].width, m_RowRects[i].height + height);
- var groupGUI = item as TimelineGroupGUI;
- if (groupGUI != null)
- {
- var spacing = GetSpacing(item) + 1;
- groupGUI.expandedRect = m_ExpandedRowRects[i];
- groupGUI.rowRect = m_RowRects[i];
- groupGUI.dropRect = new Rect(m_RowRects[i].x, m_RowRects[i].y - spacing, m_RowRects[i].width, m_RowRects[i].height + Mathf.Max(halfHeight, spacing));
- }
- }
- }
- public virtual bool BeginRename(TreeViewItem item, float delay)
- {
- return false;
- }
- public virtual void EndRename() {}
- protected virtual float GetFoldoutIndent(TreeViewItem item)
- {
- // Ignore depth when showing search results
- if (item.depth <= 1 || m_TreeView.isSearching)
- return DirectorStyles.kBaseIndent;
- int depth = item.depth;
- var trackGUI = item as TimelineTrackGUI;
- // first level subtracks are not indented
- if (trackGUI != null && trackGUI.track != null && trackGUI.track.isSubTrack)
- depth--;
- return depth * DirectorStyles.kBaseIndent;
- }
- public virtual float GetContentIndent(TreeViewItem item)
- {
- return GetFoldoutIndent(item);
- }
- public int GetNumRowsOnPageUpDown(TreeViewItem fromItem, bool pageUp, float heightOfTreeView)
- {
- return (int)Mathf.Floor(heightOfTreeView / 30); // return something
- }
- // Should return the row number of the first and last row thats fits in the pixel rect defined by top and height
- public void GetFirstAndLastRowVisible(out int firstRowVisible, out int lastRowVisible)
- {
- int rowCount = m_TreeView.data.rowCount;
- if (rowCount == 0)
- {
- firstRowVisible = lastRowVisible = -1;
- return;
- }
- if (rowCount != m_ExpandedRowRects.Count)
- {
- Debug.LogError("Mismatch in state: rows vs cached rects. Did you remember to hook up: dataSource.onVisibleRowsChanged += gui.CalculateRowRects ?");
- CalculateRowRects();
- }
- float topPixel = m_TreeView.state.scrollPos.y;
- float heightInPixels = m_TreeView.GetTotalRect().height;
- int firstVisible = -1;
- int lastVisible = -1;
- Rect visibleRect = new Rect(0, topPixel, m_ExpandedRowRects[0].width, heightInPixels);
- for (int i = 0; i < m_ExpandedRowRects.Count; ++i)
- {
- bool visible = visibleRect.Overlaps(m_ExpandedRowRects[i]);
- if (visible)
- {
- if (firstVisible == -1)
- firstVisible = i;
- lastVisible = i;
- }
- TimelineTrackBaseGUI gui = m_TreeView.data.GetItem(i) as TimelineTrackBaseGUI;
- if (gui != null)
- {
- gui.visibleExpanded = visible;
- gui.visibleRow = visibleRect.Overlaps(m_RowRects[i]);
- }
- }
- if (firstVisible != -1 && lastVisible != -1)
- {
- firstRowVisible = firstVisible;
- lastRowVisible = lastVisible;
- }
- else
- {
- firstRowVisible = 0;
- lastRowVisible = rowCount - 1;
- }
- }
- public Vector2 GetTotalSize()
- {
- if (m_RowRects.Count == 0)
- return new Vector2(0, 0);
- return new Vector2(m_MaxWidthOfRows, m_RowRects[m_RowRects.Count - 1].yMax);
- }
- public virtual float halfDropBetweenHeight
- {
- get { return 8f; }
- }
- }
- }
|