private List <AnimationWindowCurve> GetCurvesAffectedByNodes(List <AnimationWindowHierarchyNode> nodes, bool includeLinkedCurves) { List <AnimationWindowCurve> curves = new List <AnimationWindowCurve>(); foreach (var node in nodes) { AnimationWindowHierarchyNode hierarchyNode = node; if (hierarchyNode.parent is AnimationWindowHierarchyPropertyGroupNode && includeLinkedCurves) { hierarchyNode = (AnimationWindowHierarchyNode)hierarchyNode.parent; } if (hierarchyNode.curves == null) { continue; } if (hierarchyNode.curves.Length > 0) { // Property or propertygroup if (hierarchyNode is AnimationWindowHierarchyPropertyGroupNode || hierarchyNode is AnimationWindowHierarchyPropertyNode) { curves.AddRange(AnimationWindowUtility.FilterCurves(hierarchyNode.curves, hierarchyNode.path, hierarchyNode.animatableObjectType, hierarchyNode.propertyName)); } else { curves.AddRange(AnimationWindowUtility.FilterCurves(hierarchyNode.curves, hierarchyNode.path, hierarchyNode.animatableObjectType)); } } } return(curves.Distinct().ToList()); }
override protected void RenameEnded() { string newName = GetRenameOverlay().name; string oldName = GetRenameOverlay().originalName; if (newName != oldName) { Undo.RecordObject(state.activeAnimationClip, "Rename Curve"); foreach (AnimationWindowCurve curve in m_RenamedNode.curves) { EditorCurveBinding newBinding = AnimationWindowUtility.GetRenamedBinding(curve.binding, newName); if (AnimationWindowUtility.CurveExists(newBinding, state.allCurves.ToArray())) { Debug.LogWarning("Curve already exists, renaming cancelled."); continue; } AnimationWindowUtility.RenameCurvePath(curve, newBinding, curve.clip); } } m_RenamedNode = null; }
public float GetNodeHeight(AnimationWindowHierarchyNode node) { if (node is AnimationWindowHierarchyAddButtonNode) { return(k_AddCurveButtonNodeHeight); } AnimationWindowHierarchyState hierarchyState = m_TreeView.state as AnimationWindowHierarchyState; return(hierarchyState.GetTallMode(node) ? k_DopeSheetRowHeightTall : k_DopeSheetRowHeight); }
public void SetTallMode(AnimationWindowHierarchyNode node, bool tallMode) { if (tallMode) { m_TallInstanceIDs.Add(node.id); } else { m_TallInstanceIDs.Remove(node.id); } }
float GetTopPixelOfRow(int row, IList <TreeViewItem> rows) { float top = 0f; for (int i = 0; i < row && i < rows.Count; i++) { AnimationWindowHierarchyNode node = rows[i] as AnimationWindowHierarchyNode; top += GetNodeHeight(node); } return(top); }
public override Vector2 GetTotalSize() { var rows = m_TreeView.data.GetRows(); float height = 0f; for (int i = 0; i < rows.Count; i++) { AnimationWindowHierarchyNode node = rows[i] as AnimationWindowHierarchyNode; height += GetNodeHeight(node); } return(new Vector2(1, height)); }
public override Rect GetRowRect(int row, float rowWidth) { var rows = m_TreeView.data.GetRows(); AnimationWindowHierarchyNode hierarchyNode = rows[row] as AnimationWindowHierarchyNode; if (hierarchyNode.topPixel == null) { hierarchyNode.topPixel = GetTopPixelOfRow(row, rows); } float rowHeight = GetNodeHeight(hierarchyNode); return(new Rect(0, (float)hierarchyNode.topPixel, rowWidth, rowHeight)); }
protected void DoNodeGUI(Rect rect, AnimationWindowHierarchyNode node, bool selected, bool focused, int row) { InitStyles(); if (node is AnimationWindowHierarchyMasterNode) { return; } float indent = k_BaseIndent + (node.depth + node.indent) * k_IndentWidth; if (node is AnimationWindowHierarchyAddButtonNode) { if (Event.current.type == EventType.MouseMove && s_WasInsideValueRectFrame >= 0) { if (s_WasInsideValueRectFrame >= Time.frameCount - 1) { Event.current.Use(); } else { s_WasInsideValueRectFrame = -1; } } using (new EditorGUI.DisabledScope(!state.selection.canAddCurves)) { DoAddCurveButton(rect, node, row); } } else { DoRowBackground(rect, row); DoIconAndName(rect, node, selected, focused, indent); DoFoldout(node, rect, indent, row); bool enabled = false; if (node.curves != null) { enabled = !Array.Exists(node.curves, curve => curve.animationIsEditable == false); } using (new EditorGUI.DisabledScope(!enabled)) { DoValueField(rect, node, row); } DoCurveDropdown(rect, node, row, enabled); HandleContextMenu(rect, node, enabled); DoCurveColorIndicator(rect, node); } EditorGUIUtility.SetIconSize(Vector2.zero); }
private void HandleContextMenu(Rect rect, AnimationWindowHierarchyNode node, bool enabled) { if (Event.current.type != EventType.ContextClick) { return; } if (rect.Contains(Event.current.mousePosition)) { state.SelectHierarchyItem(node.id, true, true); //state.animationWindow.RefreshShownCurves (true); GenerateMenu(state.selectedHierarchyNodes, enabled).ShowAsContext(); Event.current.Use(); } }
// Changing rotation interpolation will change the propertynames of the curves // Propertynames are used in treeview node IDs, so we need to anticipate the new IDs by injecting them into treeview state // This way treeview state (selection and expanding) will be preserved once the curve data is eventually reloaded private void MaintainTreeviewStateAfterRotationInterpolation(RotationCurveInterpolation.Mode newMode) { List <int> selectedInstaceIDs = state.hierarchyState.selectedIDs; List <int> expandedInstaceIDs = state.hierarchyState.expandedIDs; List <int> oldIDs = new List <int>(); List <int> newIds = new List <int>(); for (int i = 0; i < selectedInstaceIDs.Count; i++) { AnimationWindowHierarchyNode node = state.hierarchyData.FindItem(selectedInstaceIDs[i]) as AnimationWindowHierarchyNode; if (node != null && !node.propertyName.Equals(RotationCurveInterpolation.GetPrefixForInterpolation(newMode))) { string oldPrefix = node.propertyName.Split('.')[0]; string newPropertyName = node.propertyName.Replace(oldPrefix, RotationCurveInterpolation.GetPrefixForInterpolation(newMode)); // old treeview node id oldIDs.Add(selectedInstaceIDs[i]); // and its new replacement newIds.Add((node.path + node.animatableObjectType.Name + newPropertyName).GetHashCode()); } } // Replace old IDs with new ones for (int i = 0; i < oldIDs.Count; i++) { if (selectedInstaceIDs.Contains(oldIDs[i])) { int index = selectedInstaceIDs.IndexOf(oldIDs[i]); selectedInstaceIDs[index] = newIds[i]; } if (expandedInstaceIDs.Contains(oldIDs[i])) { int index = expandedInstaceIDs.IndexOf(oldIDs[i]); expandedInstaceIDs[index] = newIds[i]; } if (state.hierarchyState.lastClickedID == oldIDs[i]) { state.hierarchyState.lastClickedID = newIds[i]; } } state.hierarchyState.selectedIDs = new List <int>(selectedInstaceIDs); state.hierarchyState.expandedIDs = new List <int>(expandedInstaceIDs); }
override public bool BeginRename(TreeViewItem item, float delay) { m_RenamedNode = item as AnimationWindowHierarchyNode; GameObject rootGameObject = null; if (m_RenamedNode.curves.Length > 0) { AnimationWindowSelectionItem selectionBinding = m_RenamedNode.curves[0].selectionBinding; if (selectionBinding != null) { rootGameObject = selectionBinding.rootGameObject; } } return(GetRenameOverlay().BeginRename(m_RenamedNode.path, item.id, delay)); }
private void DoCurveDropdown(Rect rect, AnimationWindowHierarchyNode node, int row, bool enabled) { rect = new Rect( rect.xMax - k_RowRightOffset - 12, rect.yMin + 2, 22, 12); // case 767863. // This control id is unique to the hierarchy node it refers to. // The tree view only renders the elements that are visible, and will cause // the control id counter to shift when scrolling through the view. if (DoTreeViewButton(m_HierarchyItemButtonControlIDs[row], rect, GUIContent.none, m_AnimationCurveDropdown)) { state.SelectHierarchyItem(node.id, false, false); GenericMenu menu = GenerateMenu(new AnimationWindowHierarchyNode[] { node }.ToList(), enabled); menu.DropDown(rect); Event.current.Use(); } }
public List <AnimationWindowHierarchyNode> CreateTreeFromCurves() { List <AnimationWindowHierarchyNode> nodes = new List <AnimationWindowHierarchyNode>(); List <AnimationWindowCurve> singlePropertyCurves = new List <AnimationWindowCurve>(); AnimationWindowCurve[] curves = state.allCurves.ToArray(); AnimationWindowHierarchyNode parentNode = (AnimationWindowHierarchyNode)m_RootItem; for (int i = 0; i < curves.Length; i++) { AnimationWindowCurve curve = curves[i]; if (!state.ShouldShowCurve(curve)) { continue; } AnimationWindowCurve nextCurve = i < curves.Length - 1 ? curves[i + 1] : null; singlePropertyCurves.Add(curve); bool areSameGroup = nextCurve != null && AnimationWindowUtility.GetPropertyGroupName(nextCurve.propertyName) == AnimationWindowUtility.GetPropertyGroupName(curve.propertyName); bool areSamePathAndType = nextCurve != null && curve.path.Equals(nextCurve.path) && curve.type == nextCurve.type; // We expect curveBindings to come sorted by propertyname // So we compare curve vs nextCurve. If its different path or different group (think "scale.xyz" as group), then we know this is the last element of such group. if (i == curves.Length - 1 || !areSameGroup || !areSamePathAndType) { if (singlePropertyCurves.Count > 1) { nodes.Add(AddPropertyGroupToHierarchy(singlePropertyCurves.ToArray(), parentNode)); } else { nodes.Add(AddPropertyToHierarchy(singlePropertyCurves[0], parentNode)); } singlePropertyCurves.Clear(); } } return(nodes); }
// Draw foldout (after text content above to ensure drop down icon is rendered above selection highlight) private void DoFoldout(AnimationWindowHierarchyNode node, Rect rect, float indent, int row) { if (m_TreeView.data.IsExpandable(node)) { Rect toggleRect = rect; toggleRect.x = indent; toggleRect.width = foldoutStyleWidth; EditorGUI.BeginChangeCheck(); bool newExpandedValue = GUI.Toggle(toggleRect, m_HierarchyItemFoldControlIDs[row], m_TreeView.data.IsExpanded(node), GUIContent.none, foldoutStyle); if (EditorGUI.EndChangeCheck()) { if (Event.current.alt) { m_TreeView.data.SetExpandedWithChildren(node, newExpandedValue); } else { m_TreeView.data.SetExpanded(node, newExpandedValue); } } } else { AnimationWindowHierarchyPropertyNode hierarchyPropertyNode = node as AnimationWindowHierarchyPropertyNode; AnimationWindowHierarchyState hierarchyState = m_TreeView.state as AnimationWindowHierarchyState; if (hierarchyPropertyNode != null && hierarchyPropertyNode.isPptrNode) { Rect toggleRect = rect; toggleRect.x = indent; toggleRect.width = foldoutStyleWidth; EditorGUI.BeginChangeCheck(); bool tallMode = hierarchyState.GetTallMode(hierarchyPropertyNode); tallMode = GUI.Toggle(toggleRect, m_HierarchyItemFoldControlIDs[row], tallMode, GUIContent.none, foldoutStyle); if (EditorGUI.EndChangeCheck()) { hierarchyState.SetTallMode(hierarchyPropertyNode, tallMode); } } } }
private void RemoveCurvesFromNodes(List <AnimationWindowHierarchyNode> nodes) { string undoLabel = "Remove Curve"; state.SaveKeySelection(undoLabel); foreach (var node in nodes) { AnimationWindowHierarchyNode hierarchyNode = (AnimationWindowHierarchyNode)node; if (hierarchyNode.parent is AnimationWindowHierarchyPropertyGroupNode && hierarchyNode.binding != null && AnimationWindowUtility.ForceGrouping((EditorCurveBinding)hierarchyNode.binding)) { hierarchyNode = (AnimationWindowHierarchyNode)hierarchyNode.parent; } if (hierarchyNode.curves == null) { continue; } List <AnimationWindowCurve> curves = null; // Property or propertygroup if (hierarchyNode is AnimationWindowHierarchyPropertyGroupNode || hierarchyNode is AnimationWindowHierarchyPropertyNode) { curves = AnimationWindowUtility.FilterCurves(hierarchyNode.curves.ToArray(), hierarchyNode.path, hierarchyNode.animatableObjectType, hierarchyNode.propertyName); } else { curves = AnimationWindowUtility.FilterCurves(hierarchyNode.curves.ToArray(), hierarchyNode.path, hierarchyNode.animatableObjectType); } foreach (AnimationWindowCurve animationWindowCurve in curves) { state.RemoveCurve(animationWindowCurve, undoLabel); } } m_TreeView.ReloadData(); state.controlInterface.ResampleAnimation(); }
private void DoCurveColorIndicator(Rect rect, AnimationWindowHierarchyNode node) { if (Event.current.type != EventType.Repaint) { return; } Color originalColor = GUI.color; if (!state.showCurveEditor) { GUI.color = k_KeyColorInDopesheetMode; } else if (node.curves.Length == 1 && !node.curves[0].isPPtrCurve) { GUI.color = CurveUtility.GetPropertyColor(node.curves[0].binding.propertyName); } else { GUI.color = k_KeyColorForNonCurves; } bool hasKey = false; if (state.previewing) { foreach (var curve in node.curves) { if (curve.m_Keyframes.Any(key => state.time.ContainsTime(key.time))) { hasKey = true; } } } Texture icon = hasKey ? CurveUtility.GetIconKey() : CurveUtility.GetIconCurve(); rect = new Rect(rect.xMax - k_RowRightOffset - (icon.width / 2) - 5, rect.yMin + k_ColorIndicatorTopMargin, icon.width, icon.height); GUI.DrawTexture(rect, icon, ScaleMode.ScaleToFit, true, 1); GUI.color = originalColor; }
private void DoAddCurveButton(Rect rect, AnimationWindowHierarchyNode node, int row) { const int k_ButtonWidth = 230; float xMargin = (rect.width - k_ButtonWidth) / 2f; float yMargin = 10f; Rect rectWithMargin = new Rect(rect.xMin + xMargin, rect.yMin + yMargin, rect.width - xMargin * 2f, rect.height - yMargin * 2f); // case 767863. // This control id is unique to the hierarchy node it refers to. // The tree view only renders the elements that are visible, and will cause // the control id counter to shift when scrolling through the view. if (DoTreeViewButton(m_HierarchyItemButtonControlIDs[row], rectWithMargin, k_AnimatePropertyLabel, GUI.skin.button)) { AddCurvesPopupHierarchyDataSource.showEntireHierarchy = true; if (AddCurvesPopup.ShowAtPosition(rectWithMargin, state, OnNewCurveAdded)) { GUIUtility.ExitGUI(); } } }
private AnimationWindowHierarchyPropertyGroupNode AddPropertyGroupToHierarchy(AnimationWindowCurve[] curves, AnimationWindowHierarchyNode parentNode) { List <AnimationWindowHierarchyNode> childNodes = new List <AnimationWindowHierarchyNode>(); System.Type animatableObjectType = curves[0].type; AnimationWindowHierarchyPropertyGroupNode node = new AnimationWindowHierarchyPropertyGroupNode(animatableObjectType, 0, AnimationWindowUtility.GetPropertyGroupName(curves[0].propertyName), curves[0].path, parentNode); node.icon = GetIcon(curves[0].binding); node.indent = curves[0].depth; node.curves = curves; foreach (AnimationWindowCurve curve in curves) { AnimationWindowHierarchyPropertyNode childNode = AddPropertyToHierarchy(curve, node); // For child nodes we do not want to display the type in front (It is already shown by the group node) childNode.displayName = AnimationWindowUtility.GetPropertyDisplayName(childNode.propertyName); childNodes.Add(childNode); } TreeViewUtility.SetChildParentReferences(new List <TreeViewItem>(childNodes.ToArray()), node); return(node); }
public override void OnRowGUI(Rect rowRect, TreeViewItem node, int row, bool selected, bool focused) { AnimationWindowHierarchyNode hierarchyNode = node as AnimationWindowHierarchyNode; DoNodeGUI(rowRect, hierarchyNode, selected, focused, row); }
public bool GetTallMode(AnimationWindowHierarchyNode node) { return(m_TallInstanceIDs.Contains(node.id)); }
private void DoIconAndName(Rect rect, AnimationWindowHierarchyNode node, bool selected, bool focused, float indent) { EditorGUIUtility.SetIconSize(new Vector2(13, 13)); // If not set we see icons scaling down if text is being cropped // TODO: All this is horrible. SHAME FIX! if (Event.current.type == EventType.Repaint) { if (selected) { selectionStyle.Draw(rect, false, false, true, focused); } // Leave some space for the value field that comes after. if (node is AnimationWindowHierarchyPropertyNode) { rect.width -= k_ValueFieldOffsetFromRightSide + 2; } bool isLeftOverCurve = AnimationWindowUtility.IsNodeLeftOverCurve(node); bool isAmbiguous = AnimationWindowUtility.IsNodeAmbiguous(node); bool isPhantom = AnimationWindowUtility.IsNodePhantom(node); string warningText = ""; string tooltipText = ""; if (isPhantom) { warningText = " (Default Value)"; tooltipText = "Transform position, rotation and scale can't be partially animated. This value will be animated to the default value"; } if (isLeftOverCurve) { warningText = " (Missing!)"; tooltipText = "The GameObject or Component is missing (" + node.path + ")"; } if (isAmbiguous) { warningText = " (Duplicate GameObject name!)"; tooltipText = "Target for curve is ambiguous since there are multiple GameObjects with same name (" + node.path + ")"; } Color oldColor = lineStyle.normal.textColor; Color textColor = oldColor; if (node.depth == 0) { string nodePrefix = ""; if (node.curves.Length > 0) { AnimationWindowSelectionItem selectionBinding = node.curves[0].selectionBinding; string gameObjectName = GetGameObjectName(selectionBinding != null ? selectionBinding.rootGameObject : null, node.path); nodePrefix = string.IsNullOrEmpty(gameObjectName) ? "" : gameObjectName + " : "; } Styles.content = new GUIContent(nodePrefix + node.displayName + warningText, GetIconForItem(node), tooltipText); textColor = EditorGUIUtility.isProSkin ? Color.gray * 1.35f : Color.black; } else { Styles.content = new GUIContent(node.displayName + warningText, GetIconForItem(node), tooltipText); textColor = EditorGUIUtility.isProSkin ? Color.gray : m_LightSkinPropertyTextColor; var phantomColor = selected ? m_PhantomCurveColor * k_SelectedPhantomCurveColorMultiplier : m_PhantomCurveColor; textColor = isPhantom ? phantomColor : textColor; } textColor = isLeftOverCurve || isAmbiguous ? k_LeftoverCurveColor : textColor; SetStyleTextColor(lineStyle, textColor); rect.xMin += (int)(indent + foldoutStyleWidth + lineStyle.margin.left); GUI.Label(rect, Styles.content, lineStyle); SetStyleTextColor(lineStyle, oldColor); } if (IsRenaming(node.id) && Event.current.type != EventType.Layout) { GetRenameOverlay().editFieldRect = new Rect(rect.x + k_IndentWidth, rect.y, rect.width - k_IndentWidth - 1, rect.height); } }
private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) { bool curvesChanged = false; if (node is AnimationWindowHierarchyPropertyNode) { AnimationWindowCurve[] curves = node.curves; if (curves == null || curves.Length == 0) { return; } // We do valuefields for dopelines that only have single curve AnimationWindowCurve curve = curves[0]; object objectValue = CurveBindingUtility.GetCurrentValue(state, curve); if (objectValue is float) { float value = (float)objectValue; Rect valueFieldDragRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide - k_ValueFieldDragWidth, rect.y, k_ValueFieldDragWidth, rect.height); Rect valueFieldRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide, rect.y, k_ValueFieldWidth, rect.height); if (Event.current.type == EventType.MouseMove && valueFieldRect.Contains(Event.current.mousePosition)) { s_WasInsideValueRectFrame = Time.frameCount; } EditorGUI.BeginChangeCheck(); if (curve.valueType == typeof(bool)) { value = GUI.Toggle(valueFieldRect, m_HierarchyItemValueControlIDs[row], value != 0, GUIContent.none, EditorStyles.toggle) ? 1 : 0; } else { int id = m_HierarchyItemValueControlIDs[row]; bool enterInTextField = (EditorGUIUtility.keyboardControl == id && EditorGUIUtility.editingTextField && Event.current.type == EventType.KeyDown && (Event.current.character == '\n' || (int)Event.current.character == 3)); // Force back keyboard focus to float field editor when editing it. // TreeView forces keyboard focus on itself at mouse down and we lose focus here. if (EditorGUI.s_RecycledEditor.controlID == id && Event.current.type == EventType.MouseDown && valueFieldRect.Contains(Event.current.mousePosition)) { GUIUtility.keyboardControl = id; } value = EditorGUI.DoFloatField(EditorGUI.s_RecycledEditor, valueFieldRect, valueFieldDragRect, id, value, "g5", m_AnimationSelectionTextField, true); if (enterInTextField) { GUI.changed = true; Event.current.Use(); } } if (float.IsInfinity(value) || float.IsNaN(value)) { value = 0; } if (EditorGUI.EndChangeCheck()) { string undoLabel = "Edit Key"; AnimationKeyTime newAnimationKeyTime = AnimationKeyTime.Time(state.currentTime, curve.clip.frameRate); AnimationWindowKeyframe existingKeyframe = null; foreach (AnimationWindowKeyframe keyframe in curve.m_Keyframes) { if (Mathf.Approximately(keyframe.time, state.currentTime)) { existingKeyframe = keyframe; } } if (existingKeyframe == null) { AnimationWindowUtility.AddKeyframeToCurve(curve, value, curve.valueType, newAnimationKeyTime); } else { existingKeyframe.value = value; } state.SaveCurve(curve.clip, curve, undoLabel); curvesChanged = true; } } } if (curvesChanged) { state.ResampleAnimation(); } }
private AnimationWindowHierarchyPropertyNode AddPropertyToHierarchy(AnimationWindowCurve curve, AnimationWindowHierarchyNode parentNode) { AnimationWindowHierarchyPropertyNode node = new AnimationWindowHierarchyPropertyNode(curve.type, 0, curve.propertyName, curve.path, parentNode, curve.binding, curve.isPPtrCurve); if (parentNode.icon != null) { node.icon = parentNode.icon; } else { node.icon = GetIcon(curve.binding); } node.indent = curve.depth; node.curves = new[] { curve }; return(node); }