You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
11 KiB
323 lines
11 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using UnityEditor.IMGUI.Controls; |
|
using UnityEngine; |
|
using UnityEngine.Timeline; |
|
|
|
namespace UnityEditor.Timeline |
|
{ |
|
class TimelineGroupGUI : TimelineTrackBaseGUI |
|
{ |
|
protected DirectorStyles m_Styles; |
|
protected Rect m_TreeViewRect = new Rect(0, 0, 0, 0); |
|
protected GUIContent m_ProblemIcon = new GUIContent(); |
|
|
|
bool m_MustRecomputeUnions = true; |
|
int m_GroupDepth; |
|
readonly bool m_IsReferencedTrack; |
|
readonly List<TimelineClipUnion> m_Unions = new List<TimelineClipUnion>(); |
|
|
|
public override Rect boundingRect |
|
{ |
|
get { return ToWindowSpace(m_TreeViewRect); } |
|
} |
|
|
|
public Rect ToWindowSpace(Rect localRect) |
|
{ |
|
localRect.position += treeViewToWindowTransformation; |
|
return localRect; |
|
} |
|
|
|
public override bool expandable |
|
{ |
|
get { return !m_IsRoot; } |
|
} |
|
|
|
// The expanded rectangle (contains children) as calculated by the the tree gui |
|
public Rect expandedRect { get; set; } |
|
// The row rectangle (header only) as calculated by the tree gui |
|
public Rect rowRect { get; set; } |
|
// the drop rectangle as set by the tree gui when targetted by a drag and drop |
|
public Rect dropRect { get; set; } |
|
|
|
public TimelineGroupGUI(TreeViewController treeview, TimelineTreeViewGUI treeviewGUI, int id, int depth, TreeViewItem parent, string displayName, TrackAsset trackAsset, bool isRoot) |
|
: base(id, depth, parent, displayName, trackAsset, treeview, treeviewGUI) |
|
{ |
|
m_Styles = DirectorStyles.Instance; |
|
m_IsRoot = isRoot; |
|
|
|
var trackPath = AssetDatabase.GetAssetPath(trackAsset); |
|
var sequencePath = AssetDatabase.GetAssetPath(treeviewGUI.TimelineWindow.state.editSequence.asset); |
|
if (trackPath != sequencePath) |
|
m_IsReferencedTrack = true; |
|
|
|
m_GroupDepth = CalculateGroupDepth(parent); |
|
} |
|
|
|
public virtual float GetHeight(WindowState state) |
|
{ |
|
// group tracks don't scale in height |
|
return TrackEditor.DefaultTrackHeight; |
|
} |
|
|
|
public override void OnGraphRebuilt() { } |
|
|
|
static int CalculateGroupDepth(TreeViewItem parent) |
|
{ |
|
int depth = 0; |
|
|
|
bool done = false; |
|
do |
|
{ |
|
var gui = parent as TimelineGroupGUI; |
|
if (gui == null || gui.track == null) |
|
done = true; |
|
else |
|
{ |
|
if (gui.track is GroupTrack) |
|
depth++; |
|
|
|
parent = parent.parent; |
|
} |
|
} |
|
while (!done); |
|
|
|
return depth; |
|
} |
|
|
|
void DrawTrackButtons(Rect headerRect, WindowState state) |
|
{ |
|
const float buttonSize = WindowConstants.trackHeaderButtonSize; |
|
const float padding = WindowConstants.trackHeaderButtonPadding; |
|
|
|
var buttonRect = new Rect(headerRect.xMax - buttonSize - padding, headerRect.y + ((headerRect.height - buttonSize) / 2f), buttonSize, buttonSize); |
|
|
|
if (GUI.Button(buttonRect, EditorGUIUtility.IconContent("CreateAddNew"), m_Styles.trackGroupAddButton)) |
|
{ |
|
// the drop down will apply to all selected tracks |
|
if (!SelectionManager.Contains(track)) |
|
{ |
|
SelectionManager.Clear(); |
|
SelectionManager.Add(track); |
|
} |
|
SequencerContextMenu.ShowNewTracksContextMenu(SelectionManager.SelectedTracks().ToArray(), TimelineWindow.state, buttonRect); |
|
} |
|
buttonRect.x -= buttonSize; |
|
|
|
var suitePadding = DrawButtonSuite(2, ref buttonRect); |
|
|
|
DrawMuteButton(buttonRect, state); |
|
buttonRect.x -= buttonSize + padding; |
|
DrawLockButton(buttonRect, state); |
|
buttonRect.x -= suitePadding; |
|
} |
|
|
|
public void SetExpanded(bool expanded) |
|
{ |
|
var collapseChanged = expanded != isExpanded; |
|
isExpanded = expanded; |
|
if (collapseChanged) |
|
{ |
|
track.SetCollapsed(!expanded); |
|
m_MustRecomputeUnions = true; |
|
} |
|
} |
|
|
|
public override void Draw(Rect headerRect, Rect contentRect, WindowState state) |
|
{ |
|
if (track == null || m_IsRoot) |
|
return; |
|
|
|
if (m_MustRecomputeUnions) |
|
RecomputeRectUnions(); |
|
|
|
if (depth == 1) |
|
Graphics.DrawBackgroundRect(state, headerRect); |
|
|
|
var background = headerRect; |
|
background.height = expandedRect.height; |
|
|
|
var groupColor = TrackResourceCache.GetTrackColor(track); |
|
|
|
m_TreeViewRect = contentRect; |
|
|
|
var col = groupColor; |
|
|
|
var isSelected = SelectionManager.Contains(track); |
|
|
|
if (isSelected) |
|
col = DirectorStyles.Instance.customSkin.colorSelection; |
|
else if (isDropTarget) |
|
col = DirectorStyles.Instance.customSkin.colorDropTarget; |
|
else |
|
{ |
|
if (m_GroupDepth % 2 == 1) |
|
{ |
|
float h, s, v; |
|
Color.RGBToHSV(col, out h, out s, out v); |
|
v += 0.06f; |
|
col = Color.HSVToRGB(h, s, v); |
|
} |
|
} |
|
|
|
if (background.width > 0) |
|
{ |
|
using (new GUIColorOverride(col)) |
|
GUI.Box(background, GUIContent.none, m_Styles.groupBackground); |
|
} |
|
|
|
var trackRectBackground = headerRect; |
|
trackRectBackground.xMin += background.width; |
|
trackRectBackground.width = contentRect.width; |
|
trackRectBackground.height = background.height; |
|
|
|
if (isSelected) |
|
{ |
|
col = state.IsEditingASubTimeline() |
|
? m_Styles.customSkin.colorTrackSubSequenceBackgroundSelected |
|
: m_Styles.customSkin.colorTrackBackgroundSelected; |
|
} |
|
else |
|
{ |
|
col = m_Styles.customSkin.colorGroupTrackBackground; |
|
} |
|
|
|
EditorGUI.DrawRect(trackRectBackground, col); |
|
if (!isExpanded && children != null && children.Count > 0) |
|
{ |
|
var collapsedTrackRect = contentRect; |
|
|
|
foreach (var u in m_Unions) |
|
u.Draw(collapsedTrackRect, state); |
|
} |
|
|
|
using (new GUIGroupScope(headerRect)) |
|
{ |
|
var groupRect = new Rect(0, 0, headerRect.width, headerRect.height); |
|
DrawName(groupRect, isSelected); |
|
DrawTrackButtons(groupRect, state); |
|
} |
|
|
|
if (IsTrackRecording(state)) |
|
{ |
|
using (new GUIColorOverride(DirectorStyles.Instance.customSkin.colorTrackBackgroundRecording)) |
|
GUI.Label(background, GUIContent.none, m_Styles.displayBackground); |
|
} |
|
|
|
// is this a referenced track? |
|
if (m_IsReferencedTrack) |
|
{ |
|
var refRect = contentRect; |
|
refRect.x = state.timeAreaRect.xMax - 20.0f; |
|
refRect.y += 5.0f; |
|
refRect.width = 30.0f; |
|
GUI.Label(refRect, DirectorStyles.referenceTrackLabel, EditorStyles.label); |
|
} |
|
|
|
var bgRect = contentRect; |
|
if (track as GroupTrack != null || AllChildrenMuted(this)) |
|
bgRect.height = expandedRect.height; |
|
DrawTrackState(contentRect, bgRect, track); |
|
} |
|
|
|
void DrawName(Rect rect, bool isSelected) |
|
{ |
|
var labelRect = rect; |
|
labelRect.xMin += 20; |
|
var actorName = track != null ? track.name : "missing"; |
|
labelRect.width = m_Styles.groupFont.CalcSize(new GUIContent(actorName)).x; |
|
labelRect.width = Math.Max(labelRect.width, 50.0f); |
|
|
|
// if we aren't bound to anything, we show a text field that allows to rename the actor |
|
// otherwise we show a ObjectField to allow binding to a go |
|
if (track != null && track is GroupTrack) |
|
{ |
|
var textColor = m_Styles.groupFont.normal.textColor; |
|
|
|
if (isSelected) |
|
textColor = Color.white; |
|
|
|
string newName; |
|
|
|
EditorGUI.BeginChangeCheck(); |
|
using (new StyleNormalColorOverride(m_Styles.groupFont, textColor)) |
|
{ |
|
newName = EditorGUI.DelayedTextField(labelRect, GUIContent.none, track.GetInstanceID(), track.name, m_Styles.groupFont); |
|
} |
|
|
|
if (EditorGUI.EndChangeCheck() && !string.IsNullOrEmpty(newName)) |
|
{ |
|
track.name = newName; |
|
displayName = track.name; |
|
} |
|
} |
|
} |
|
|
|
protected bool IsSubTrack() |
|
{ |
|
if (track == null) |
|
return false; |
|
|
|
var parentTrack = track.parent as TrackAsset; |
|
if (parentTrack == null) |
|
return false; |
|
|
|
return parentTrack.GetType() != typeof(GroupTrack); |
|
} |
|
|
|
protected TrackAsset ParentTrack() |
|
{ |
|
if (IsSubTrack()) |
|
return track.parent as TrackAsset; |
|
return null; |
|
} |
|
|
|
// is there currently a recording track |
|
bool IsTrackRecording(WindowState state) |
|
{ |
|
if (!state.recording) |
|
return false; |
|
if (track.GetType() != typeof(GroupTrack)) |
|
return false; |
|
|
|
return state.GetArmedTrack(track) != null; |
|
} |
|
|
|
void RecomputeRectUnions() |
|
{ |
|
m_MustRecomputeUnions = false; |
|
m_Unions.Clear(); |
|
if (children == null) |
|
return; |
|
|
|
foreach (var c in children.OfType<TimelineTrackGUI>()) |
|
{ |
|
c.RebuildGUICacheIfNecessary(); |
|
m_Unions.AddRange(TimelineClipUnion.Build(c.clips)); |
|
} |
|
} |
|
|
|
static bool AllChildrenMuted(TimelineGroupGUI groupGui) |
|
{ |
|
if (!groupGui.track.muted) |
|
return false; |
|
if (groupGui.children == null) |
|
return true; |
|
return groupGui.children.OfType<TimelineGroupGUI>().All(AllChildrenMuted); |
|
} |
|
|
|
protected static float DrawButtonSuite(int numberOfButtons, ref Rect buttonRect) |
|
{ |
|
var style = DirectorStyles.Instance.trackButtonSuite; |
|
var buttonWidth = WindowConstants.trackHeaderButtonSize * numberOfButtons + WindowConstants.trackHeaderButtonPadding * Math.Max(0, numberOfButtons - 1); |
|
var suiteWidth = buttonWidth + style.padding.right + style.padding.left; |
|
|
|
var rect = new Rect(buttonRect.xMax - style.margin.right - suiteWidth, buttonRect.y + style.margin.top, suiteWidth, buttonRect.height); |
|
if (Event.current.type == EventType.Repaint) |
|
style.Draw(rect, false, false, false, false); |
|
buttonRect.x -= style.margin.right + style.padding.right; |
|
return style.margin.left + style.padding.left; |
|
} |
|
} |
|
}
|
|
|