Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,310 @@
using System.Linq;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
interface IClipCurveEditorOwner
{
ClipCurveEditor clipCurveEditor { get; }
bool inlineCurvesSelected { get; }
bool showLoops { get; }
TrackAsset owner { get; }
void SelectCurves();
void ValidateCurvesSelection();
}
class InlineCurveResizeHandle : IBounds
{
public Rect boundingRect { get; private set; }
public TimelineTrackGUI trackGUI { get; }
public InlineCurveResizeHandle(TimelineTrackGUI trackGUI)
{
this.trackGUI = trackGUI;
}
public void Draw(Rect headerRect, WindowState state)
{
const float resizeHandleHeight = WindowConstants.trackResizeHandleHeight;
var rect = new Rect(headerRect.xMin, headerRect.yMax - resizeHandleHeight + 1f, headerRect.width, resizeHandleHeight);
EditorGUIUtility.AddCursorRect(rect, MouseCursor.SplitResizeUpDown);
boundingRect = trackGUI.ToWindowSpace(rect);
if (Event.current.type == EventType.Repaint)
{
state.headerSpacePartitioner.AddBounds(this);
EditorGUI.DrawRect(rect, DirectorStyles.Instance.customSkin.colorAnimEditorBinding);
var dragStyle = DirectorStyles.Instance.inlineCurveHandle;
dragStyle.Draw(rect, GUIContent.none, false, false, false, false);
}
}
}
class InlineCurveEditor : IBounds
{
Rect m_TrackRect;
Rect m_HeaderRect;
readonly TimelineTrackGUI m_TrackGUI;
readonly InlineCurveResizeHandle m_ResizeHandle;
bool m_LastSelectionWasClip;
TimelineClipGUI m_LastSelectedClipGUI;
Rect IBounds.boundingRect { get { return m_TrackGUI.ToWindowSpace(m_TrackRect); } }
[UsedImplicitly] // Used in tests
public TimelineClipGUI currentClipGui
{
get { return m_LastSelectedClipGUI; }
}
public IClipCurveEditorOwner currentCurveEditor
{
get { return m_LastSelectionWasClip ? (IClipCurveEditorOwner)m_LastSelectedClipGUI : (IClipCurveEditorOwner)m_TrackGUI; }
}
public InlineCurveEditor(TimelineTrackGUI trackGUI)
{
m_TrackGUI = trackGUI;
m_ResizeHandle = new InlineCurveResizeHandle(trackGUI);
}
static bool MouseOverTrackArea(Rect curveRect, Rect trackRect)
{
curveRect.y = trackRect.y;
curveRect.height = trackRect.height;
// clamp the curve editor to the track. this allows the menu to scroll properly
curveRect.xMin = Mathf.Max(curveRect.xMin, trackRect.xMin);
curveRect.xMax = trackRect.xMax;
return curveRect.Contains(Event.current.mousePosition);
}
static bool MouseOverHeaderArea(Rect headerRect, Rect trackRect)
{
headerRect.y = trackRect.y;
headerRect.height = trackRect.height;
return headerRect.Contains(Event.current.mousePosition);
}
static void DrawCurveEditor(IClipCurveEditorOwner clipCurveEditorOwner, WindowState state, Rect headerRect, Rect trackRect, Vector2 activeRange, bool locked)
{
ClipCurveEditor clipCurveEditor = clipCurveEditorOwner.clipCurveEditor;
CurveDataSource dataSource = clipCurveEditor.dataSource;
Rect curveRect = dataSource.GetBackgroundRect(state);
var newlySelected = false;
var currentEvent = Event.current;
if (currentEvent.type == EventType.MouseDown || currentEvent.type == EventType.ContextClick)
newlySelected = MouseOverTrackArea(curveRect, trackRect) || MouseOverHeaderArea(headerRect, trackRect);
// make sure to not use any event before drawing the curve.
bool prevEnabledState = GUI.enabled;
GUI.enabled = true;
clipCurveEditorOwner.clipCurveEditor.DrawHeader(headerRect);
GUI.enabled = prevEnabledState;
bool displayAsSelected = !locked && (clipCurveEditorOwner.inlineCurvesSelected || newlySelected);
using (new EditorGUI.DisabledScope(locked))
clipCurveEditor.DrawCurveEditor(trackRect, state, activeRange, clipCurveEditorOwner.showLoops, displayAsSelected);
if (newlySelected && !locked)
OnMouseClick(clipCurveEditorOwner, currentEvent);
}
static void OnMouseClick(IClipCurveEditorOwner clipCurveEditorOwner, Event currentEvent)
{
if (currentEvent.modifiers == ManipulatorsUtils.actionModifier)
{
if (clipCurveEditorOwner.inlineCurvesSelected)
SelectionManager.Clear();
else
clipCurveEditorOwner.SelectCurves();
}
else
{
clipCurveEditorOwner.SelectCurves();
}
HandleCurrentEvent();
}
public void Draw(Rect headerRect, Rect trackRect, WindowState state)
{
const float inlineCurveBottomPadding = WindowConstants.inlineCurveContentPadding;
m_TrackRect = trackRect;
m_TrackRect.height -= inlineCurveBottomPadding;
if (Event.current.type == EventType.Repaint)
state.spacePartitioner.AddBounds(this);
// Remove the indentation of this track to render it properly, otherwise every GUI elements will be offsetted.
headerRect.x -= DirectorStyles.kBaseIndent;
headerRect.width += DirectorStyles.kBaseIndent;
// Remove the width of the color swatch.
headerRect.x += 4.0f;
headerRect.width -= 4.0f;
m_HeaderRect = headerRect;
EditorGUI.DrawRect(m_HeaderRect, DirectorStyles.Instance.customSkin.colorAnimEditorBinding);
if (ShouldShowClipCurves(state))
{
DrawCurveEditorsForClipsOnTrack(m_HeaderRect, m_TrackRect, state);
}
else if (ShouldShowTrackCurves())
{
DrawCurveEditorForTrack(m_HeaderRect, m_TrackRect, state);
}
else
{
DrawCurvesEditorForNothingSelected(m_HeaderRect, m_TrackRect, state);
}
m_ResizeHandle.Draw(headerRect, state);
var bottomPadding = new Rect(trackRect.xMin, trackRect.yMax - inlineCurveBottomPadding, trackRect.width, inlineCurveBottomPadding);
EditorGUI.DrawRect(bottomPadding, DirectorStyles.Instance.customSkin.colorTrackBackground);
// If MouseDown or ContextClick are not consumed by the curves, use the event to prevent it from going deeper into the treeview.
if (Event.current.type == EventType.ContextClick)
{
var r = Rect.MinMaxRect(m_HeaderRect.xMin, m_HeaderRect.yMin, m_TrackRect.xMax, m_TrackRect.yMax);
if (r.Contains(Event.current.mousePosition))
Event.current.Use();
}
UpdateViewModel();
}
void DrawCurveEditorForTrack(Rect headerRect, Rect trackRect, WindowState state)
{
if (m_TrackGUI.clipCurveEditor == null)
return;
var activeRange = new Vector2(state.TimeToPixel(0.0d), state.TimeToPixel(state.editSequence.duration));
DrawCurveEditor(m_TrackGUI, state, headerRect, trackRect, activeRange, m_TrackGUI.locked);
m_LastSelectionWasClip = false;
}
void DrawCurveEditorsForClipsOnTrack(Rect headerRect, Rect trackRect, WindowState state)
{
if (m_TrackGUI.clips.Count == 0)
return;
if (Event.current.type == EventType.Layout)
{
var selectedClip = SelectionManager.SelectedClipGUI().FirstOrDefault(x => x.parent == m_TrackGUI);
if (selectedClip != null)
{
m_LastSelectedClipGUI = selectedClip;
SelectFromCurveOwner(m_LastSelectedClipGUI);
}
else if (state.recording && state.IsArmedForRecord(m_TrackGUI.track))
{
if (m_LastSelectedClipGUI == null || !m_TrackGUI.track.IsRecordingToClip(m_LastSelectedClipGUI.clip))
{
var clip = m_TrackGUI.clips.FirstOrDefault(x => m_TrackGUI.track.IsRecordingToClip(x.clip));
if (clip != null)
m_LastSelectedClipGUI = clip;
}
}
if (m_LastSelectedClipGUI == null)
m_LastSelectedClipGUI = m_TrackGUI.clips[0];
}
if (m_LastSelectedClipGUI == null || m_LastSelectedClipGUI.clipCurveEditor == null || m_LastSelectedClipGUI.isInvalid)
return;
var activeRange = new Vector2(state.TimeToPixel(m_LastSelectedClipGUI.clip.start), state.TimeToPixel(m_LastSelectedClipGUI.clip.end));
DrawCurveEditor(m_LastSelectedClipGUI, state, headerRect, trackRect, activeRange, m_TrackGUI.locked);
m_LastSelectionWasClip = true;
}
void DrawCurvesEditorForNothingSelected(Rect headerRect, Rect trackRect, WindowState state)
{
if (m_LastSelectionWasClip || !TrackHasCurvesToShow() && m_TrackGUI.clips.Count > 0)
{
DrawCurveEditorsForClipsOnTrack(headerRect, trackRect, state);
}
else
{
DrawCurveEditorForTrack(headerRect, trackRect, state);
}
}
bool ShouldShowClipCurves(WindowState state)
{
if (m_TrackGUI.clips.Count == 0)
return false;
// Is a clip selected or being recorded to?
return SelectionManager.SelectedClipGUI().FirstOrDefault(x => x.parent == m_TrackGUI) != null ||
state.recording && state.IsArmedForRecord(m_TrackGUI.track) && m_TrackGUI.clips.FirstOrDefault(x => m_TrackGUI.track.IsRecordingToClip(x.clip)) != null;
}
bool ShouldShowTrackCurves()
{
if (m_TrackGUI == null)
return false;
var isTrackSelected = SelectionManager.SelectedTrackGUI().FirstOrDefault(x => x == m_TrackGUI) != null;
if (!isTrackSelected)
return false;
return TrackHasCurvesToShow();
}
bool TrackHasCurvesToShow()
{
var animTrack = m_TrackGUI.track as AnimationTrack;
if (animTrack != null && !animTrack.inClipMode)
return true;
return m_TrackGUI.track.HasAnyAnimatableParameters();
}
void UpdateViewModel()
{
var curveEditor = currentCurveEditor.clipCurveEditor;
if (curveEditor == null || curveEditor.bindingHierarchy.treeViewController == null)
return;
var vm = TimelineWindowViewPrefs.GetTrackViewModelData(m_TrackGUI.track);
vm.inlineCurvesState = curveEditor.bindingHierarchy.treeViewController.state;
vm.inlineCurvesShownAreaInsideMargins = curveEditor.shownAreaInsideMargins;
vm.lastInlineCurveDataID = curveEditor.dataSource.id;
}
static void HandleCurrentEvent()
{
#if UNITY_EDITOR_OSX
Event.current.type = EventType.Ignore;
#else
Event.current.Use();
#endif
}
static void SelectFromCurveOwner(IClipCurveEditorOwner curveOwner)
{
if (curveOwner.clipCurveEditor == null)
{
SelectionManager.SelectInlineCurveEditor(null);
}
else if (!curveOwner.inlineCurvesSelected && SelectionManager.Count() == 1)
{
SelectionManager.SelectInlineCurveEditor(curveOwner);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e96b886242ab404db69df796f7cd9b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,323 @@
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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7072e9e47288bac4d8c6bdfb2cf785a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,214 @@
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
abstract class TimelineTrackBaseGUI : TreeViewItem, IBounds
{
static class Styles
{
public static readonly GUIContent s_LockedAndMuted = L10n.TextContent("Locked / Muted");
public static readonly GUIContent s_LockedAndPartiallyMuted = L10n.TextContent("Locked / Partially Muted");
public static readonly GUIContent s_Locked = L10n.TextContent("Locked");
public static readonly GUIContent s_Muted = L10n.TextContent("Muted");
public static readonly GUIContent s_PartiallyMuted = L10n.TextContent("Partially Muted");
public static readonly GUIContent trackMuteBtnOnTooltip = L10n.TextContent(string.Empty, "Umute");
public static readonly GUIContent trackMuteBtnOffTooltip = L10n.TextContent(string.Empty, "Mute");
public static readonly GUIContent trackLockBtnOnTooltip = L10n.TextContent(string.Empty, "Unlock");
public static readonly GUIContent trackLockBtnOffTooltip = L10n.TextContent(string.Empty, "Lock");
public static readonly Texture2D lockBg = DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.trackLockOverlay);
}
protected bool m_IsRoot = false;
readonly TimelineTreeViewGUI m_TreeViewGUI;
readonly TrackDrawer m_Drawer;
public Vector2 treeViewToWindowTransformation { get; set; }
public bool isExpanded { get; set; }
public bool isDropTarget { protected get; set; }
public TrackAsset track { get; }
TreeViewController treeView { get; }
public TimelineWindow TimelineWindow
{
get
{
if (m_TreeViewGUI == null)
return null;
return m_TreeViewGUI.TimelineWindow;
}
}
public TrackDrawer drawer
{
get { return m_Drawer; }
}
public virtual float GetVerticalSpacingBetweenTracks()
{
return 3.0f;
}
public bool visibleRow { get; set; } // is the header row visible
public bool visibleExpanded { get; set; } // is the expanded area (group) visible
public bool drawInsertionMarkerBefore { get; set; }
public bool drawInsertionMarkerAfter { get; set; }
public abstract Rect boundingRect { get; }
public abstract bool expandable { get; }
public abstract void Draw(Rect headerRect, Rect contentRect, WindowState state);
public abstract void OnGraphRebuilt(); // callback when the corresponding graph is rebuilt. This can happen, but not have the GUI rebuilt.
protected TimelineTrackBaseGUI(int id, int depth, TreeViewItem parent, string displayName, TrackAsset trackAsset, TreeViewController tv, TimelineTreeViewGUI tvgui)
: base(id, depth, parent, displayName)
{
m_Drawer = TrackDrawer.CreateInstance(trackAsset);
m_Drawer.sequencerState = tvgui.TimelineWindow.state;
isExpanded = false;
isDropTarget = false;
track = trackAsset;
treeView = tv;
m_TreeViewGUI = tvgui;
}
public static TimelineTrackBaseGUI FindGUITrack(TrackAsset track)
{
var allTracks = TimelineWindow.instance.allTracks;
return allTracks.Find(x => x.track == track);
}
protected void DrawTrackState(Rect trackRect, Rect expandedRect, TrackAsset track)
{
if (Event.current.type == EventType.Layout)
{
bool needStateBox = false;
//Mute
if (track.muted && !TimelineUtility.IsParentMuted(track))
{
Rect bgRect = expandedRect;
TimelineWindow.instance.OverlayDrawData.Add(OverlayDrawer.CreateColorOverlay(
GUIClip.Unclip(bgRect),
DirectorStyles.Instance.customSkin.colorTrackDarken));
needStateBox = true;
}
//Lock
if (!needStateBox && track.locked && !TimelineUtility.IsLockedFromGroup(track))
{
Rect bgRect = expandedRect;
TimelineWindow.instance.OverlayDrawData.Add(OverlayDrawer.CreateTextureOverlay(
GUIClip.Unclip(bgRect),
Styles.lockBg));
needStateBox = true;
}
if (needStateBox)
{
DrawTrackStateBox(trackRect, track);
}
}
}
static void DrawTrackStateBox(Rect trackRect, TrackAsset track)
{
var styles = DirectorStyles.Instance;
bool locked = track.locked && !TimelineUtility.IsLockedFromGroup(track);
bool muted = track.muted && !TimelineUtility.IsParentMuted(track);
bool allSubTrackMuted = TimelineUtility.IsAllSubTrackMuted(track);
GUIContent content = null;
if (locked && muted)
{
content = Styles.s_LockedAndMuted;
if (!allSubTrackMuted)
content = Styles.s_LockedAndPartiallyMuted;
}
else if (locked) content = Styles.s_Locked;
else if (muted)
{
content = Styles.s_Muted;
if (!allSubTrackMuted)
content = Styles.s_PartiallyMuted;
}
// the track could be locked, but we only show the 'locked portion' on the upper most track
// that is causing the lock
if (content == null)
return;
Rect textRect = Graphics.CalculateTextBoxSize(trackRect, styles.fontClip, content, WindowConstants.overlayTextPadding);
TimelineWindow.instance.OverlayDrawData.Add(
OverlayDrawer.CreateTextBoxOverlay(
GUIClip.Unclip(textRect),
content.text, styles.fontClip,
Color.white,
styles.customSkin.colorLockTextBG,
styles.displayBackground));
}
protected void DrawMuteButton(Rect rect, WindowState state)
{
using (new EditorGUI.DisabledScope(TimelineUtility.IsParentMuted(track)))
{
EditorGUI.BeginChangeCheck();
var isMuted = track.mutedInHierarchy;
var tooltip = isMuted ? Styles.trackMuteBtnOnTooltip : Styles.trackMuteBtnOffTooltip;
var muted = GUI.Toggle(rect, isMuted, tooltip, TimelineWindow.styles.trackMuteButton);
if (EditorGUI.EndChangeCheck())
MuteTrack.Mute(new[] { track }, muted);
}
}
protected void DrawLockButton(Rect rect, WindowState state)
{
using (new EditorGUI.DisabledScope(TimelineUtility.IsLockedFromGroup(track)))
{
EditorGUI.BeginChangeCheck();
var isLocked = track.lockedInHierarchy;
var tooltip = isLocked ? Styles.trackLockBtnOnTooltip : Styles.trackLockBtnOffTooltip;
var locked = GUI.Toggle(rect, track.lockedInHierarchy, tooltip, TimelineWindow.styles.trackLockButton);
if (EditorGUI.EndChangeCheck())
LockTrack.SetLockState(new[] { track }, locked);
}
}
public void DrawInsertionMarkers(Rect rowRectWithIndent)
{
const float insertionHeight = WindowConstants.trackInsertionMarkerHeight;
if (Event.current.type == EventType.Repaint && (drawInsertionMarkerAfter || drawInsertionMarkerBefore))
{
if (drawInsertionMarkerBefore)
{
var rect = new Rect(rowRectWithIndent.x, rowRectWithIndent.y - insertionHeight * 0.5f - 2.0f, rowRectWithIndent.width, insertionHeight);
EditorGUI.DrawRect(rect, Color.white);
}
if (drawInsertionMarkerAfter)
{
var rect = new Rect(rowRectWithIndent.x, rowRectWithIndent.y + rowRectWithIndent.height - insertionHeight * 0.5f + 1.0f, rowRectWithIndent.width, insertionHeight);
EditorGUI.DrawRect(rect, Color.white);
}
}
}
public void ClearDrawFlags()
{
if (Event.current.type == EventType.Repaint)
{
isDropTarget = false;
drawInsertionMarkerAfter = false;
drawInsertionMarkerBefore = false;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6b75424ba2b73c4eb3182b08dbd71ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,185 @@
using System;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
class TimelineTrackErrorGUI : TimelineTrackBaseGUI
{
static class Styles
{
public static readonly GUIContent ErrorText = L10n.TextContent("Track cannot be loaded.", "Please fix any compile errors in the script for this track");
public static readonly Texture2D IconWarn = EditorGUIUtility.LoadIconRequired("console.warnicon.inactive.sml");
public static readonly GUIContent RemoveTrack = L10n.TextContent("Delete");
public static readonly Color WarningBoxBackgroundColor = new Color(115.0f / 255.0f, 115.0f / 255.0f, 115.0f / 255.0f); // approved for both skins
public static readonly Color WarningBoxHighlightColor = new Color(229 / 255.0f, 208 / 255.0f, 54 / 255.0f); // brigher than standard warning color for contrast
}
Rect m_TrackRect;
ScriptableObject m_ScriptableObject;
PlayableAsset m_Owner;
static GUIContent s_GUIContent = new GUIContent();
public TimelineTrackErrorGUI(TreeViewController treeview, TimelineTreeViewGUI treeviewGUI, int id, int depth, TreeViewItem parent, string displayName, ScriptableObject track, PlayableAsset owner)
: base(id, depth, parent, displayName, null, treeview, treeviewGUI)
{
m_ScriptableObject = track;
m_Owner = owner;
}
public override Rect boundingRect
{
get { return m_TrackRect; }
}
public override bool expandable
{
get { return false; }
}
public override void Draw(Rect headerRect, Rect contentRect, WindowState state)
{
m_TrackRect = contentRect;
DrawMissingTrackHeader(headerRect, state);
DrawMissingTrackBody(contentRect);
}
void DrawMissingTrackHeader(Rect headerRect, WindowState state)
{
var styles = DirectorStyles.Instance;
// Draw a header
Color backgroundColor = styles.customSkin.colorTrackHeaderBackground;
var bgRect = headerRect;
bgRect.x += styles.trackSwatchStyle.fixedWidth;
bgRect.width -= styles.trackSwatchStyle.fixedWidth;
EditorGUI.DrawRect(bgRect, backgroundColor);
// draw the warning icon
var errorIcon = Styles.IconWarn;
Rect iconRect = new Rect(headerRect.xMin + styles.trackSwatchStyle.fixedWidth, headerRect.yMin + 0.5f * (headerRect.height - errorIcon.height), errorIcon.width, errorIcon.height);
if (iconRect.width > 0 && iconRect.height > 0)
{
GUI.DrawTexture(iconRect, errorIcon, ScaleMode.ScaleAndCrop, true, 0, DirectorStyles.kClipErrorColor, 0, 0);
}
// Draw the name
// m_ScriptableObject == null will return true because the script can't be loaded. so this checks
// to make sure it is actually not null so we can grab the name
object o = m_ScriptableObject;
if (o != null)
{
s_GUIContent.text = m_ScriptableObject.name;
var textStyle = styles.trackHeaderFont;
textStyle.normal.textColor = styles.customSkin.colorTrackFont; // TODO -- we shouldn't modify the style like this. track header does it though :(
Rect textRect = headerRect;
textRect.xMin = iconRect.xMax + 1;
textRect.xMax = Math.Min(textRect.xMin + styles.trackHeaderFont.CalcSize(s_GUIContent).x, headerRect.xMax - 1);
EditorGUI.LabelField(textRect, s_GUIContent, textStyle);
}
// Draw the color swatch to the left of the track, darkened by the mute
var color = Color.Lerp(DirectorStyles.kClipErrorColor, styles.customSkin.colorTrackDarken, styles.customSkin.colorTrackDarken.a);
color.a = 1;
using (new GUIColorOverride(color))
{
var colorSwatchRect = headerRect;
colorSwatchRect.width = styles.trackSwatchStyle.fixedWidth;
GUI.Label(colorSwatchRect, GUIContent.none, styles.trackSwatchStyle);
}
// draw darken overlay
EditorGUI.DrawRect(bgRect, styles.customSkin.colorTrackDarken);
DrawRemoveMenu(headerRect, state);
}
void DrawRemoveMenu(Rect headerRect, WindowState state)
{
const float pad = 3;
const float buttonSize = 16;
var buttonRect = new Rect(headerRect.xMax - buttonSize - pad, headerRect.y + ((headerRect.height - buttonSize) / 2f) + 2, buttonSize, buttonSize);
if (GUI.Button(buttonRect, GUIContent.none, DirectorStyles.Instance.trackOptions))
{
GenericMenu menu = new GenericMenu();
var owner = m_Owner;
var scriptableObject = m_ScriptableObject;
menu.AddItem(Styles.RemoveTrack, false, () =>
{
if (TrackExtensions.RemoveBrokenTrack(owner, scriptableObject))
state.Refresh();
}
);
menu.ShowAsContext();
}
}
static void DrawMissingTrackBody(Rect contentRect)
{
if (contentRect.width < 0)
return;
var styles = DirectorStyles.Instance;
// draw a track rectangle
EditorGUI.DrawRect(contentRect, styles.customSkin.colorTrackDarken);
// draw the warning box
DrawScriptWarningBox(contentRect, Styles.ErrorText);
}
static void DrawScriptWarningBox(Rect trackRect, GUIContent content)
{
var styles = DirectorStyles.Instance;
const float kTextPadding = 52f;
var errorIcon = Styles.IconWarn;
float textWidth = styles.fontClip.CalcSize(content).x;
var outerRect = trackRect;
outerRect.width = textWidth + kTextPadding + errorIcon.width;
outerRect.x += (trackRect.width - outerRect.width) / 2f;
outerRect.height -= 4f;
outerRect.y += 1f;
bool drawText = true;
if (outerRect.width > trackRect.width)
{
outerRect.x = trackRect.x;
outerRect.width = trackRect.width;
drawText = false;
}
var innerRect = new Rect(outerRect.x + 2, outerRect.y + 2, outerRect.width - 4, outerRect.height - 4);
using (new GUIColorOverride(Styles.WarningBoxHighlightColor))
GUI.Box(outerRect, GUIContent.none, styles.displayBackground);
using (new GUIColorOverride(Styles.WarningBoxBackgroundColor))
GUI.Box(innerRect, GUIContent.none, styles.displayBackground);
if (drawText)
{
var iconRect = new Rect(outerRect.x + kTextPadding / 2.0f - 4.0f, outerRect.y + (outerRect.height - errorIcon.height) / 2.0f, errorIcon.width, errorIcon.height);
var textRect = new Rect(iconRect.xMax + 4.0f, outerRect.y, textWidth, outerRect.height);
GUI.DrawTexture(iconRect, errorIcon, ScaleMode.ScaleAndCrop, true, 0, Styles.WarningBoxHighlightColor, 0, 0);
Graphics.ShadowLabel(textRect, content, styles.fontClip, Color.white, Color.black);
}
else if (errorIcon.width > innerRect.width)
{
var iconRect = new Rect(outerRect.x + (outerRect.width - errorIcon.width) / 2.0f, outerRect.y + (outerRect.height - errorIcon.height) / 2.0f, errorIcon.width, errorIcon.height);
GUI.DrawTexture(iconRect, errorIcon, ScaleMode.ScaleAndCrop, true, 0, Styles.WarningBoxHighlightColor, 0, 0);
}
}
public override void OnGraphRebuilt() {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e00dc5a0cda466b41b09a50bd18b4916
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,826 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class TimelineTrackGUI : TimelineGroupGUI, IClipCurveEditorOwner, IRowGUI
{
struct TrackDrawData
{
public bool m_AllowsRecording;
public bool m_ShowTrackBindings;
public bool m_HasBinding;
public bool m_IsSubTrack;
public PlayableBinding m_Binding;
public Object m_TrackBinding;
public Texture m_TrackIcon;
public bool m_HasMarkers;
}
static class Styles
{
public static readonly GUIContent trackCurvesBtnOnTooltip = DirectorStyles.TrTextContent(string.Empty, "Hide curves view");
public static readonly GUIContent trackCurvesBtnOffTooltip = DirectorStyles.TrTextContent(string.Empty, "Show curves view");
public static readonly GUIContent trackMarkerBtnOnTooltip = DirectorStyles.TrTextContent(string.Empty, "Collapse Track Markers");
public static readonly GUIContent trackMarkerBtnOffTooltip = DirectorStyles.TrTextContent(string.Empty, "Expand Track Markers");
public static readonly GUIContent kActiveRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty, "End recording");
public static readonly GUIContent kInactiveRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty, "Start recording");
public static readonly GUIContent kIgnorePreviewRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty, "Recording is disabled: scene preview is ignored for this TimelineAsset");
public static readonly GUIContent kDisabledRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty,
"Recording is not permitted when Track Offsets are set to Auto. Track Offset settings can be changed in the track menu of the base track.");
public static Texture2D kProblemIcon = DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.warning);
}
static GUIContent s_ArmForRecordContentOn;
static GUIContent s_ArmForRecordContentOff;
static GUIContent s_ArmForRecordDisabled;
readonly InfiniteTrackDrawer m_InfiniteTrackDrawer;
readonly TrackEditor m_TrackEditor;
readonly GUIContent m_DefaultTrackIcon;
readonly TrackResizeHandle m_ResizeHandle;
TrackItemsDrawer m_ItemsDrawer;
TrackDrawData m_TrackDrawData;
TrackDrawOptions m_TrackDrawOptions;
bool m_InlineCurvesSkipped;
int m_TrackHash = -1;
int m_BlendHash = -1;
int m_LastDirtyIndex = -1;
bool? m_TrackHasAnimatableParameters;
int m_HeightExtension;
public override bool expandable
{
get { return hasChildren; }
}
internal InlineCurveEditor inlineCurveEditor { get; set; }
public ClipCurveEditor clipCurveEditor { get; private set; }
public bool inlineCurvesSelected => SelectionManager.IsCurveEditorFocused(this);
bool IClipCurveEditorOwner.showLoops
{
get { return false; }
}
TrackAsset IClipCurveEditorOwner.owner
{
get { return track; }
}
static bool DoesTrackAllowsRecording(TrackAsset track)
{
// if the root animation track is in auto mode, recording is not allowed
var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
if (animTrack != null)
return animTrack.trackOffset != TrackOffset.Auto;
return false;
}
bool trackHasAnimatableParameters
{
get
{
// cache this value to avoid the recomputation
if (!m_TrackHasAnimatableParameters.HasValue)
m_TrackHasAnimatableParameters = track.HasAnyAnimatableParameters() ||
track.clips.Any(c => c.HasAnyAnimatableParameters());
return m_TrackHasAnimatableParameters.Value;
}
}
public bool locked
{
get { return track.lockedInHierarchy; }
}
public bool showMarkers
{
get { return track.GetShowMarkers(); }
}
public bool muted
{
get { return track.muted; }
}
public List<TimelineClipGUI> clips
{
get
{
return m_ItemsDrawer.clips == null ? new List<TimelineClipGUI>(0) : m_ItemsDrawer.clips;
}
}
TrackAsset IRowGUI.asset { get { return track; } }
bool showTrackRecordingDisabled
{
get
{
// if the root animation track is in auto mode, recording is not allowed
var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
return animTrack != null && animTrack.trackOffset == TrackOffset.Auto;
}
}
public int heightExtension
{
get => m_HeightExtension;
set => m_HeightExtension = Math.Max(0, value);
}
float minimumHeight => m_TrackDrawOptions.minimumHeight <= 0.0f ? TrackEditor.DefaultTrackHeight : m_TrackDrawOptions.minimumHeight;
public TimelineTrackGUI(TreeViewController tv, TimelineTreeViewGUI w, int id, int depth, TreeViewItem parent, string displayName, TrackAsset sequenceActor)
: base(tv, w, id, depth, parent, displayName, sequenceActor, false)
{
var animationTrack = sequenceActor as AnimationTrack;
if (animationTrack != null)
m_InfiniteTrackDrawer = new InfiniteTrackDrawer(new AnimationTrackKeyDataSource(animationTrack));
else if (sequenceActor.HasAnyAnimatableParameters() && !sequenceActor.clips.Any())
m_InfiniteTrackDrawer = new InfiniteTrackDrawer(new TrackPropertyCurvesDataSource(sequenceActor));
UpdateInfiniteClipEditor(w.TimelineWindow);
var bindings = track.outputs.ToArray();
m_TrackDrawData.m_HasBinding = bindings.Length > 0;
if (m_TrackDrawData.m_HasBinding)
m_TrackDrawData.m_Binding = bindings[0];
m_TrackDrawData.m_IsSubTrack = IsSubTrack();
m_TrackDrawData.m_AllowsRecording = DoesTrackAllowsRecording(sequenceActor);
m_TrackDrawData.m_HasMarkers = track.GetMarkerCount() > 0;
m_DefaultTrackIcon = TrackResourceCache.GetTrackIcon(track);
m_TrackEditor = CustomTimelineEditorCache.GetTrackEditor(sequenceActor);
m_TrackDrawOptions = m_TrackEditor.GetTrackOptions_Safe(track, null);
m_TrackDrawOptions.errorText = null; // explicitly setting to null for an uninitialized state
m_ResizeHandle = new TrackResizeHandle(this);
heightExtension = TimelineWindowViewPrefs.GetTrackHeightExtension(track);
RebuildGUICacheIfNecessary();
}
public override float GetVerticalSpacingBetweenTracks()
{
if (track != null && track.isSubTrack)
return 1.0f; // subtracks have less of a gap than tracks
return base.GetVerticalSpacingBetweenTracks();
}
void UpdateInfiniteClipEditor(TimelineWindow window)
{
if (clipCurveEditor != null || track == null || !ShouldShowInfiniteClipEditor())
return;
var dataSource = CurveDataSource.Create(this);
clipCurveEditor = new ClipCurveEditor(dataSource, window, track);
}
void DetectTrackChanged()
{
if (Event.current.type == EventType.Layout)
{
// incremented when a track or it's clips changed
if (m_LastDirtyIndex != track.DirtyIndex)
{
m_TrackEditor.OnTrackChanged_Safe(track);
m_LastDirtyIndex = track.DirtyIndex;
}
OnTrackChanged();
}
}
// Called when the source track data, including it's clips have changed has changed.
void OnTrackChanged()
{
// recompute blends if necessary
int newBlendHash = BlendHash();
if (m_BlendHash != newBlendHash)
{
UpdateClipOverlaps();
m_BlendHash = newBlendHash;
}
RebuildGUICacheIfNecessary();
}
void UpdateDrawData(WindowState state)
{
if (Event.current.type == EventType.Layout)
{
m_TrackDrawData.m_ShowTrackBindings = false;
m_TrackDrawData.m_TrackBinding = null;
if (state.editSequence.director != null && showSceneReference)
{
m_TrackDrawData.m_ShowTrackBindings = state.GetWindow().currentMode.ShouldShowTrackBindings(state);
m_TrackDrawData.m_TrackBinding = state.editSequence.director.GetGenericBinding(track);
}
var lastHeight = m_TrackDrawOptions.minimumHeight;
m_TrackDrawOptions = m_TrackEditor.GetTrackOptions_Safe(track, m_TrackDrawData.m_TrackBinding);
m_TrackDrawData.m_HasMarkers = track.GetMarkerCount() > 0;
m_TrackDrawData.m_AllowsRecording = DoesTrackAllowsRecording(track);
m_TrackDrawData.m_TrackIcon = m_TrackDrawOptions.icon;
if (m_TrackDrawData.m_TrackIcon == null)
m_TrackDrawData.m_TrackIcon = m_DefaultTrackIcon.image;
// track height has changed. need to update gui
if (!Mathf.Approximately(lastHeight, m_TrackDrawOptions.minimumHeight))
state.Refresh();
}
}
public override void Draw(Rect headerRect, Rect contentRect, WindowState state)
{
DetectTrackChanged();
UpdateDrawData(state);
UpdateInfiniteClipEditor(state.GetWindow());
var trackHeaderRect = headerRect;
var trackContentRect = contentRect;
float inlineCurveHeight = contentRect.height - GetTrackContentHeight(state);
bool hasInlineCurve = inlineCurveHeight > 0.0f;
if (hasInlineCurve)
{
trackHeaderRect.height -= inlineCurveHeight;
trackContentRect.height -= inlineCurveHeight;
}
if (Event.current.type == EventType.Repaint)
{
m_TreeViewRect = trackContentRect;
}
track.SetCollapsed(!isExpanded);
RebuildGUICacheIfNecessary();
// Prevents from drawing outside of bounds, but does not effect layout or markers
bool isOwnerDrawSucceed = false;
Vector2 visibleTime = state.timeAreaShownRange;
if (drawer != null)
isOwnerDrawSucceed = drawer.DrawTrack(trackContentRect, track, visibleTime, state);
if (!isOwnerDrawSucceed)
{
using (new GUIViewportScope(trackContentRect))
DrawBackground(trackContentRect, track, visibleTime, state);
if (m_InfiniteTrackDrawer != null)
m_InfiniteTrackDrawer.DrawTrack(trackContentRect, track, visibleTime, state);
// draw after user customization so overlay text shows up
using (new GUIViewportScope(trackContentRect))
m_ItemsDrawer.Draw(trackContentRect, state);
}
DrawTrackHeader(trackHeaderRect, state);
if (hasInlineCurve)
{
var curvesHeaderRect = headerRect;
curvesHeaderRect.yMin = trackHeaderRect.yMax;
var curvesContentRect = contentRect;
curvesContentRect.yMin = trackContentRect.yMax;
DrawInlineCurves(curvesHeaderRect, curvesContentRect, state);
}
DrawTrackColorKind(headerRect);
DrawTrackState(contentRect, contentRect, track);
}
void DrawInlineCurves(Rect curvesHeaderRect, Rect curvesContentRect, WindowState state)
{
if (!track.GetShowInlineCurves())
return;
// Inline curves are not within the editor window -- case 952571
if (!IsInlineCurvesEditorInBounds(ToWindowSpace(curvesHeaderRect), curvesContentRect.height, state))
{
m_InlineCurvesSkipped = true;
return;
}
// If inline curves were skipped during the last event; we want to avoid rendering them until
// the next Layout event. Otherwise, we still get the RTE prevented above when the user resizes
// the timeline window very fast. -- case 952571
if (m_InlineCurvesSkipped && Event.current.type != EventType.Layout)
return;
m_InlineCurvesSkipped = false;
if (inlineCurveEditor == null)
inlineCurveEditor = new InlineCurveEditor(this);
curvesHeaderRect.x += DirectorStyles.kBaseIndent;
curvesHeaderRect.width -= DirectorStyles.kBaseIndent;
inlineCurveEditor.Draw(curvesHeaderRect, curvesContentRect, state);
}
static bool IsInlineCurvesEditorInBounds(Rect windowSpaceTrackRect, float inlineCurveHeight, WindowState state)
{
var legalHeight = state.windowHeight;
var trackTop = windowSpaceTrackRect.y;
var inlineCurveOffset = windowSpaceTrackRect.height - inlineCurveHeight;
return legalHeight - trackTop - inlineCurveOffset > 0;
}
void DrawErrorIcon(Rect position, WindowState state)
{
Rect bindingLabel = position;
bindingLabel.x = position.xMax + 3;
bindingLabel.width = state.bindingAreaWidth;
EditorGUI.LabelField(position, m_ProblemIcon);
}
void DrawBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state)
{
bool canDrawRecordBackground = IsRecording(state);
if (canDrawRecordBackground)
{
DrawRecordingTrackBackground(trackRect, trackAsset, visibleTime, state);
}
else
{
Color trackBackgroundColor;
if (SelectionManager.Contains(track))
{
trackBackgroundColor = state.IsEditingASubTimeline() ?
DirectorStyles.Instance.customSkin.colorTrackSubSequenceBackgroundSelected :
DirectorStyles.Instance.customSkin.colorTrackBackgroundSelected;
}
else
{
trackBackgroundColor = state.IsEditingASubTimeline() ?
DirectorStyles.Instance.customSkin.colorTrackSubSequenceBackground :
DirectorStyles.Instance.customSkin.colorTrackBackground;
}
EditorGUI.DrawRect(trackRect, trackBackgroundColor);
}
}
float InlineCurveHeight()
{
return track.GetShowInlineCurves() && CanDrawInlineCurve()
? TimelineWindowViewPrefs.GetInlineCurveHeight(track)
: 0.0f;
}
public override float GetHeight(WindowState state)
{
var height = GetTrackContentHeight(state);
if (CanDrawInlineCurve())
height += InlineCurveHeight();
return height;
}
float GetTrackContentHeight(WindowState state)
{
var defaultHeight = Mathf.Min(minimumHeight, TrackEditor.MaximumTrackHeight);
return (defaultHeight + heightExtension) * state.trackScale;
}
static bool CanDrawIcon(GUIContent icon)
{
return icon != null && icon != GUIContent.none && icon.image != null;
}
bool showSceneReference
{
get
{
return track != null &&
m_TrackDrawData.m_HasBinding &&
!m_TrackDrawData.m_IsSubTrack &&
m_TrackDrawData.m_Binding.sourceObject != null &&
m_TrackDrawData.m_Binding.outputTargetType != null &&
typeof(Object).IsAssignableFrom(m_TrackDrawData.m_Binding.outputTargetType);
}
}
void DrawTrackHeader(Rect trackHeaderRect, WindowState state)
{
using (new GUIViewportScope(trackHeaderRect))
{
var rect = trackHeaderRect;
DrawHeaderBackground(trackHeaderRect);
rect.x += m_Styles.trackSwatchStyle.fixedWidth;
const float buttonSize = WindowConstants.trackHeaderButtonSize;
const float padding = WindowConstants.trackHeaderButtonPadding;
var buttonRect = new Rect(trackHeaderRect.xMax - buttonSize - padding, rect.y + (rect.height - buttonSize) / 2f, buttonSize, buttonSize);
rect.x += DrawTrackIconKind(rect, state);
if (track is GroupTrack)
return;
buttonRect.x -= DrawTrackDropDownMenu(buttonRect);
var suiteRect = DrawGeneralSuite(state, buttonRect);
suiteRect = DrawCustomSuite(state, suiteRect);
var bindingRect = new Rect(rect.x, rect.y, suiteRect.xMax - rect.x, rect.height);
DrawTrackBinding(bindingRect, trackHeaderRect);
}
m_ResizeHandle.Draw(trackHeaderRect, state);
}
Rect DrawGeneralSuite(WindowState state, Rect rect)
{
const float buttonWidth = WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
var padding = DrawButtonSuite(3, ref rect);
DrawMuteButton(rect, state);
rect.x -= buttonWidth;
DrawLockButton(rect, state);
rect.x -= buttonWidth;
DrawLockMarkersButton(rect, state);
rect.x -= buttonWidth;
rect.x -= padding;
return rect;
}
Rect DrawCustomSuite(WindowState state, Rect rect)
{
var numberOfButtons = 0;
if (m_TrackDrawData.m_AllowsRecording || showTrackRecordingDisabled)
numberOfButtons++;
if (CanDrawInlineCurve())
numberOfButtons++;
if (drawer.HasCustomTrackHeaderButton())
numberOfButtons++;
if (numberOfButtons == 0)
return rect;
var padding = DrawButtonSuite(numberOfButtons, ref rect);
rect.x -= DrawRecordButton(rect, state);
rect.x -= DrawInlineCurveButton(rect, state);
rect.x -= DrawCustomTrackButton(rect, state);
rect.x -= padding;
return rect;
}
void DrawHeaderBackground(Rect headerRect)
{
Color backgroundColor = SelectionManager.Contains(track)
? DirectorStyles.Instance.customSkin.colorSelection
: DirectorStyles.Instance.customSkin.colorTrackHeaderBackground;
var bgRect = headerRect;
bgRect.x += m_Styles.trackSwatchStyle.fixedWidth;
bgRect.width -= m_Styles.trackSwatchStyle.fixedWidth;
EditorGUI.DrawRect(bgRect, backgroundColor);
}
void DrawTrackColorKind(Rect rect)
{
// subtracks don't draw the color, the parent does that.
if (track != null && track.isSubTrack)
return;
if (rect.width <= 0) return;
using (new GUIColorOverride(m_TrackDrawOptions.trackColor))
{
rect.width = m_Styles.trackSwatchStyle.fixedWidth;
GUI.Label(rect, GUIContent.none, m_Styles.trackSwatchStyle);
}
}
float DrawTrackIconKind(Rect rect, WindowState state)
{
// no icons on subtracks
if (track != null && track.isSubTrack)
return 0.0f;
rect.yMin += (rect.height - 16f) / 2f;
rect.width = 16.0f;
rect.height = 16.0f;
if (!string.IsNullOrEmpty(m_TrackDrawOptions.errorText))
{
m_ProblemIcon.image = Styles.kProblemIcon;
m_ProblemIcon.tooltip = m_TrackDrawOptions.errorText;
if (CanDrawIcon(m_ProblemIcon))
DrawErrorIcon(rect, state);
}
else
{
var content = GUIContent.Temp(m_TrackDrawData.m_TrackIcon, m_DefaultTrackIcon.tooltip);
if (CanDrawIcon(content))
GUI.Box(rect, content, GUIStyle.none);
}
return rect.width;
}
void DrawTrackBinding(Rect rect, Rect headerRect)
{
if (m_TrackDrawData.m_ShowTrackBindings)
{
DoTrackBindingGUI(rect);
return;
}
var textStyle = m_Styles.trackHeaderFont;
textStyle.normal.textColor = SelectionManager.Contains(track) ? Color.white : m_Styles.customSkin.colorTrackFont;
string trackName = track.name;
EditorGUI.BeginChangeCheck();
// by default the size is just the width of the string (for selection purposes)
rect.width = m_Styles.trackHeaderFont.CalcSize(new GUIContent(trackName)).x;
// if we are editing, supply the entire width of the header
if (GUIUtility.keyboardControl == track.GetInstanceID())
rect.width = (headerRect.xMax - rect.xMin) - (5 * WindowConstants.trackHeaderButtonSize);
trackName = EditorGUI.DelayedTextField(rect, GUIContent.none, track.GetInstanceID(), track.name, textStyle);
if (EditorGUI.EndChangeCheck())
{
UndoExtensions.RegisterTrack(track, L10n.Tr("Rename Track"));
track.name = trackName;
}
}
float DrawTrackDropDownMenu(Rect rect)
{
if (GUI.Button(rect, GUIContent.none, m_Styles.trackOptions))
{
// the drop down will apply to all selected tracks
if (!SelectionManager.Contains(track))
{
SelectionManager.Clear();
SelectionManager.Add(track);
}
SequencerContextMenu.ShowTrackContextMenu(null);
}
return WindowConstants.trackHeaderButtonSize;
}
bool CanDrawInlineCurve()
{
// Note: A track with animatable parameters always has inline curves.
return trackHasAnimatableParameters || TimelineUtility.TrackHasAnimationCurves(track);
}
float DrawInlineCurveButton(Rect rect, WindowState state)
{
if (!CanDrawInlineCurve())
{
//Force to close Inline Curve UI if the inline cannot be drawn.
if (track.GetShowInlineCurves())
track.SetShowInlineCurves(false);
return 0.0f;
}
// Override enable state to display "Show Inline Curves" button in disabled state.
bool prevEnabledState = GUI.enabled;
GUI.enabled = true;
var showInlineCurves = track.GetShowInlineCurves();
var tooltip = showInlineCurves ? Styles.trackCurvesBtnOnTooltip : Styles.trackCurvesBtnOffTooltip;
var newValue = GUI.Toggle(rect, track.GetShowInlineCurves(), tooltip, DirectorStyles.Instance.trackCurvesButton);
GUI.enabled = prevEnabledState;
if (newValue != track.GetShowInlineCurves())
{
track.SetShowInlineCurves(newValue);
state.GetWindow().treeView.CalculateRowRects();
}
return WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
}
float DrawRecordButton(Rect rect, WindowState state)
{
var style = DirectorStyles.Instance.trackRecordButton;
const float buttonWidth = WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
if (m_TrackDrawData.m_AllowsRecording)
{
bool isPlayerDisabled = state.editSequence.director != null && !state.editSequence.director.isActiveAndEnabled;
GameObject goBinding = m_TrackDrawData.m_TrackBinding as GameObject;
if (goBinding == null)
{
Component c = m_TrackDrawData.m_TrackBinding as Component;
if (c != null)
goBinding = c.gameObject;
}
if (goBinding == null && m_TrackDrawData.m_IsSubTrack)
goBinding = ParentTrack().GetGameObjectBinding(state.editSequence.director);
var isTrackBindingValid = goBinding != null;
var trackErrorDisableButton = !string.IsNullOrEmpty(m_TrackDrawOptions.errorText) && isTrackBindingValid && goBinding.activeInHierarchy;
var disableButton = track.lockedInHierarchy || isPlayerDisabled || trackErrorDisableButton || !isTrackBindingValid || state.ignorePreview;
using (new EditorGUI.DisabledScope(disableButton))
{
if (IsRecording(state))
{
state.editorWindow.Repaint();
var remainder = Time.realtimeSinceStartup % 1;
if (remainder < 0.22f)
style = GUIStyle.none;
if (GUI.Button(rect, Styles.kActiveRecordButtonTooltip, style) || isPlayerDisabled || !isTrackBindingValid)
state.UnarmForRecord(track);
}
else if (!track.timelineAsset.editorSettings.scenePreview)
GUI.Button(rect, Styles.kIgnorePreviewRecordButtonTooltip, style);
else
{
if (GUI.Button(rect, Styles.kInactiveRecordButtonTooltip, style))
state.ArmForRecord(track);
}
return buttonWidth;
}
}
if (showTrackRecordingDisabled)
{
using (new EditorGUI.DisabledScope(true))
GUI.Button(rect, Styles.kDisabledRecordButtonTooltip, style);
return buttonWidth;
}
return 0.0f;
}
float DrawCustomTrackButton(Rect rect, WindowState state)
{
if (!drawer.HasCustomTrackHeaderButton())
return 0.0f;
drawer.DrawTrackHeaderButton(rect, state);
return WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
}
void DrawLockMarkersButton(Rect rect, WindowState state)
{
var hasMarkers = track.GetMarkerCount() != 0;
var markersShown = showMarkers && hasMarkers;
var style = TimelineWindow.styles.trackMarkerButton;
EditorGUI.BeginChangeCheck();
var tooltip = markersShown ? Styles.trackMarkerBtnOnTooltip : Styles.trackMarkerBtnOffTooltip;
var toggleMarkers = GUI.Toggle(rect, markersShown, tooltip, style);
if (EditorGUI.EndChangeCheck() && hasMarkers)
track.SetShowTrackMarkers(toggleMarkers);
}
static void ObjectBindingField(Rect position, Object obj, PlayableBinding binding, int controlId)
{
var allowScene =
typeof(GameObject).IsAssignableFrom(binding.outputTargetType) ||
typeof(Component).IsAssignableFrom(binding.outputTargetType);
var bindingFieldRect = EditorGUI.IndentedRect(position);
using (new GUIViewportScope(bindingFieldRect))
{
EditorGUI.BeginChangeCheck();
var newObject = UnityEditorInternals.DoObjectField(EditorGUI.IndentedRect(position), obj, binding.outputTargetType, controlId, allowScene, true);
if (EditorGUI.EndChangeCheck())
BindingUtility.BindWithInteractiveEditorValidation(TimelineEditor.inspectedDirector, binding.sourceObject as TrackAsset, newObject);
}
}
void DoTrackBindingGUI(Rect rect)
{
var bindingRect = new Rect(
rect.xMin,
rect.y + (rect.height - WindowConstants.trackHeaderBindingHeight) / 2f,
Mathf.Min(rect.width, WindowConstants.trackBindingMaxSize) - WindowConstants.trackBindingPadding,
WindowConstants.trackHeaderBindingHeight);
if (m_TrackDrawData.m_Binding.outputTargetType != null && typeof(Object).IsAssignableFrom(m_TrackDrawData.m_Binding.outputTargetType))
{
var controlId = GUIUtility.GetControlID("s_ObjectFieldHash".GetHashCode(), FocusType.Passive, rect);
var previousActiveControlId = DragAndDrop.activeControlID;
ObjectBindingField(bindingRect, m_TrackDrawData.m_TrackBinding, m_TrackDrawData.m_Binding, controlId);
if (previousActiveControlId != controlId && DragAndDrop.activeControlID == controlId)
TimelineDragging.OnTrackBindingDragUpdate(track);
}
}
bool IsRecording(WindowState state)
{
return state.recording && state.IsArmedForRecord(track);
}
// background to draw during recording
void DrawRecordingTrackBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state)
{
if (drawer != null)
drawer.DrawRecordingBackground(trackRect, trackAsset, visibleTime, state);
}
void UpdateClipOverlaps()
{
TrackExtensions.ComputeBlendsFromOverlaps(track.clips);
}
internal void RebuildGUICacheIfNecessary()
{
if (m_TrackHash == track.Hash())
return;
m_ItemsDrawer = new TrackItemsDrawer(this);
m_TrackHash = track.Hash();
}
int BlendHash()
{
var hash = 0;
foreach (var clip in track.clips)
{
hash = HashUtility.CombineHash(hash,
(clip.duration - clip.start).GetHashCode(),
((int)clip.blendInCurveMode).GetHashCode(),
((int)clip.blendOutCurveMode).GetHashCode());
}
return hash;
}
// callback when the corresponding graph is rebuilt. This can happen, but not have the GUI rebuilt.
public override void OnGraphRebuilt()
{
RefreshCurveEditor();
}
void RefreshCurveEditor()
{
var window = TimelineWindow.instance;
if (track != null && window != null && window.state != null)
{
bool hasEditor = clipCurveEditor != null;
bool shouldHaveEditor = ShouldShowInfiniteClipEditor();
if (hasEditor != shouldHaveEditor)
window.state.AddEndFrameDelegate((x, currentEvent) =>
{
x.Refresh();
return true;
});
}
}
bool ShouldShowInfiniteClipEditor()
{
var animationTrack = track as AnimationTrack;
if (animationTrack != null)
return animationTrack.ShouldShowInfiniteClipEditor();
return trackHasAnimatableParameters;
}
public void SelectCurves()
{
SelectionManager.RemoveTimelineSelection();
SelectionManager.SelectInlineCurveEditor(this);
}
public void ValidateCurvesSelection() {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3b27007577486c4d8dc088f989b288c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
class TrackResizeHandle : IBounds
{
public Rect boundingRect { get; private set; }
public TimelineTrackGUI trackGUI { get; }
public TrackResizeHandle(TimelineTrackGUI trackGUI)
{
this.trackGUI = trackGUI;
}
public void Draw(Rect headerRect, WindowState state)
{
const float resizeHandleHeight = WindowConstants.trackResizeHandleHeight;
var rect = new Rect(headerRect.xMin, headerRect.yMax - (0.5f * resizeHandleHeight), headerRect.width, resizeHandleHeight);
boundingRect = trackGUI.ToWindowSpace(rect);
Rect cursorRect = rect;
cursorRect.height--;
if (GUIUtility.hotControl == 0)
{
EditorGUIUtility.AddCursorRect(cursorRect, MouseCursor.SplitResizeUpDown);
state.headerSpacePartitioner.AddBounds(this);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 042c802f94d43dc45a049bf3b5be79ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: