311 lines
10 KiB
C#
311 lines
10 KiB
C#
|
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;
|
||
|
}
|
||
|
|
||
|
internal static float Spaced(float width)
|
||
|
{
|
||
|
return width > 0 ? width + WindowConstants.trackHeaderButtonSpacing : 0;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
buttonRect.x -= Spaced(DrawMuteButton(buttonRect, state));
|
||
|
buttonRect.x -= Spaced(DrawLockButton(buttonRect, state));
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|