public GUIAnimFieldEntry(GUIAnimFieldLayouts layouts, string path, bool child, int indentAmount)
        {
            this.path = path;

            GUILayoutX toggleLayout = layouts.main.AddLayoutX();

            toggleLayout.AddSpace(indentAmount);

            selectionBtn          = new GUIButton(GetDisplayName(path, child), EditorStyles.Label, GUIOption.FlexibleWidth());
            selectionBtn.OnClick += () =>
            {
                OnEntrySelected?.Invoke(path);
            };

            toggleLayout.AddElement(selectionBtn);

            entryHeight = selectionBtn.Bounds.height;

            backgroundTexture = new GUITexture(Builtin.WhiteTexture, GUITextureScaleMode.StretchToFit,
                                               GUIOption.FlexibleWidth());
            backgroundTexture.SetTint(Color.Transparent);
            backgroundTexture.SetHeight(entryHeight);

            layouts.background.AddElement(backgroundTexture);
        }
        public GUIAnimMissingEntry(GUIAnimFieldLayouts layouts, string path)
            : base(layouts, path, false, 15)
        {
            missingLabel   = new GUILabel("Missing property!", GUIOption.FixedHeight(GetEntryHeight()));
            underlayLayout = layouts.underlay.AddLayoutX();
            underlayLayout.AddFlexibleSpace();
            underlayLayout.AddElement(missingLabel);
            underlayLayout.AddSpace(50);

            overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            layouts.overlay.AddElement(overlaySpacing);
        }
        public GUIAnimSimpleEntry(GUIAnimFieldLayouts layouts, string path, Color color, bool child = false)
            : base(layouts, path, child, child ? 45 : 30)
        {
            valueDisplay   = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            underlayLayout = layouts.underlay.AddLayoutX();
            underlayLayout.AddSpace(child ? 30 : 15);

            GUITexture colorSquare = new GUITexture(Builtin.WhiteTexture,
                                                    GUIOption.FixedWidth(10), GUIOption.FixedHeight(10));

            colorSquare.SetTint(color);

            underlayLayout.AddElement(colorSquare);
            underlayLayout.AddFlexibleSpace();
            underlayLayout.AddElement(valueDisplay);
            underlayLayout.AddSpace(50);

            overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            layouts.overlay.AddElement(overlaySpacing);
        }
        public GUIAnimComplexEntry(GUIAnimFieldLayouts layouts, string path, string[] childEntries, Color[] colors)
            : base(layouts, path, false, 20)
        {
            foldout            = new GUIToggle("", EditorStyles.Expand);
            foldout.OnToggled += Toggle;

            GUILabel spacer = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));

            foldoutLayout = layouts.overlay.AddLayoutX();

            foldoutLayout.AddSpace(5);
            foldoutLayout.AddElement(foldout);
            foldoutLayout.AddElement(spacer);
            foldoutLayout.AddFlexibleSpace();

            underlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            layouts.underlay.AddElement(underlaySpacing);

            children = new GUIAnimSimpleEntry[childEntries.Length];
            for (int i = 0; i < childEntries.Length; i++)
            {
                Color color;
                if (i < colors.Length)
                {
                    color = colors[i];
                }
                else
                {
                    color = Color.White;
                }

                children[i] = new GUIAnimSimpleEntry(layouts, path + childEntries[i], color, true);
                children[i].OnEntrySelected += x => { OnEntrySelected?.Invoke(x); };
            }

            Toggle(false);
        }
 public GUIAnimColorEntry(GUIAnimFieldLayouts layouts, string path, Color[] colors)
     : base(layouts, path, new[] { ".r", ".g", ".b", ".a" }, colors)
 {
 }
 public GUIAnimVec4Entry(GUIAnimFieldLayouts layouts, string path, Color[] colors)
     : base(layouts, path, new[] { ".x", ".y", ".z", ".w" }, colors)
 {
 }
        private void Rebuild()
        {
            scrollArea.Layout.Clear();
            fields = null;

            if (fieldInfos == null || root == null)
            {
                return;
            }

            GUILabel header = new GUILabel(new LocEdString("Properties"), EditorStyles.Header);

            scrollArea.Layout.AddElement(header);

            layouts = new GUIAnimFieldLayouts();
            GUIPanel rootPanel       = scrollArea.Layout.AddPanel();
            GUIPanel mainPanel       = rootPanel.AddPanel();
            GUIPanel underlayPanel   = rootPanel.AddPanel(1);
            GUIPanel overlayPanel    = rootPanel.AddPanel(-1);
            GUIPanel backgroundPanel = rootPanel.AddPanel(2);

            layouts.main       = mainPanel.AddLayoutY();
            layouts.underlay   = underlayPanel.AddLayoutY();
            layouts.overlay    = overlayPanel.AddLayoutY();
            layouts.background = backgroundPanel.AddLayoutY();

            GUIButton catchAll = new GUIButton("", EditorStyles.Blank);

            catchAll.Bounds   = new Rect2I(0, 0, width, height - header.Bounds.height);
            catchAll.OnClick += () => OnEntrySelected(null);

            underlayPanel.AddElement(catchAll);

            layouts.main.AddSpace(5);
            layouts.underlay.AddSpace(5);
            layouts.overlay.AddSpace(5);
            layouts.background.AddSpace(3); // Minor hack: Background starts heigher to get it to center better

            fields = new GUIAnimFieldEntry[fieldInfos.Count];
            for (int i = 0; i < fieldInfos.Count; i++)
            {
                if (string.IsNullOrEmpty(fieldInfos[i].path))
                {
                    continue;
                }

                bool entryIsMissing;
                if (fieldInfos[i].isUserCurve)
                {
                    string pathSuffix;
                    SerializableProperty property = Animation.FindProperty(root, fieldInfos[i].path, out pathSuffix);
                    entryIsMissing = property == null;
                }
                else
                {
                    entryIsMissing = false;
                }

                if (!entryIsMissing)
                {
                    Color[] colors = new Color[fieldInfos[i].curveGroup.curveInfos.Length];
                    for (int j = 0; j < fieldInfos[i].curveGroup.curveInfos.Length; j++)
                    {
                        colors[j] = fieldInfos[i].curveGroup.curveInfos[j].color;
                    }

                    switch (fieldInfos[i].curveGroup.type)
                    {
                    case SerializableProperty.FieldType.Vector2:
                        fields[i] = new GUIAnimVec2Entry(layouts, fieldInfos[i].path, colors);
                        break;

                    case SerializableProperty.FieldType.Vector3:
                        fields[i] = new GUIAnimVec3Entry(layouts, fieldInfos[i].path, colors);
                        break;

                    case SerializableProperty.FieldType.Vector4:
                        fields[i] = new GUIAnimVec4Entry(layouts, fieldInfos[i].path, colors);
                        break;

                    case SerializableProperty.FieldType.Color:
                        fields[i] = new GUIAnimColorEntry(layouts, fieldInfos[i].path, colors);
                        break;

                    case SerializableProperty.FieldType.Bool:
                    case SerializableProperty.FieldType.Int:
                    case SerializableProperty.FieldType.Float:
                    {
                        Color color;
                        if (colors.Length > 0)
                        {
                            color = colors[0];
                        }
                        else
                        {
                            color = Color.White;
                        }

                        fields[i] = new GUIAnimSimpleEntry(layouts, fieldInfos[i].path, color);
                        break;
                    }
                    }
                }
                else
                {
                    fields[i] = new GUIAnimMissingEntry(layouts, fieldInfos[i].path);
                }

                if (fields[i] != null)
                {
                    fields[i].OnEntrySelected += OnEntrySelected;
                }
            }

            if (fieldInfos.Count == 0)
            {
                GUILabel warningLbl = new GUILabel(new LocEdString("No properties. Add a new property to begin animating."));

                GUILayoutY vertLayout = layouts.main.AddLayoutY();
                vertLayout.AddFlexibleSpace();
                GUILayoutX horzLayout = vertLayout.AddLayoutX();
                vertLayout.AddFlexibleSpace();

                horzLayout.AddFlexibleSpace();
                horzLayout.AddElement(warningLbl);
                horzLayout.AddFlexibleSpace();
            }

            layouts.main.AddSpace(5);
            layouts.underlay.AddSpace(5);
            layouts.overlay.AddSpace(5);
            layouts.background.AddSpace(5);

            layouts.main.AddFlexibleSpace();
            layouts.underlay.AddFlexibleSpace();
            layouts.overlay.AddFlexibleSpace();
            layouts.background.AddFlexibleSpace();
        }
        /// <summary>
        /// Constructs a new animation field entry and appends the necessary GUI elements to the provided layouts.
        /// </summary>
        /// <param name="layouts">Layouts to append the GUI elements to.</param>
        /// <param name="path">Path of the curve field.</param>
        /// <param name="color">Color of the path field curve, to display next to the element name.</param>
        /// <param name="child">Determines if the element is a root path, or a child (sub) element.</param>
        public GUIAnimSimpleEntry(GUIAnimFieldLayouts layouts, string path, Color color, bool child = false)
            : base(layouts, path, child, child ? 45 : 30)
        {
            valueDisplay = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            underlayLayout = layouts.underlay.AddLayoutX();
            underlayLayout.AddSpace(child ? 30 : 15);

            GUITexture colorSquare = new GUITexture(Builtin.WhiteTexture,
                GUIOption.FixedWidth(10), GUIOption.FixedHeight(10));
            colorSquare.SetTint(color);

            underlayLayout.AddElement(colorSquare);
            underlayLayout.AddFlexibleSpace();
            underlayLayout.AddElement(valueDisplay);
            underlayLayout.AddSpace(10);

            overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            layouts.overlay.AddElement(overlaySpacing);
        }
 /// <summary>
 /// Constructs a new animation field entry and appends the necessary GUI elements to the provided layouts.
 /// </summary>
 /// <param name="layouts">Layouts to append the GUI elements to.</param>
 /// <param name="path">Path of the curve field.</param>
 /// <param name="colors">Colors of the curves to display, for each child entry.</param>
 public GUIAnimVec4Entry(GUIAnimFieldLayouts layouts, string path, Color[] colors)
     : base(layouts, path,  new[] { ".x", ".y", ".z", ".w" }, colors)
 {
 }
        public GUIAnimMissingEntry(GUIAnimFieldLayouts layouts, string path)
            : base(layouts, path, false, 15)
        {
            missingLabel = new GUILabel("Missing!", GUIOption.FixedHeight(GetEntryHeight()));
            underlayLayout = layouts.underlay.AddLayoutX();
            underlayLayout.AddFlexibleSpace();
            underlayLayout.AddElement(missingLabel);
            underlayLayout.AddSpace(15);

            overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            layouts.overlay.AddElement(overlaySpacing);
        }
 /// <summary>
 /// Constructs a new animation field entry and appends the necessary GUI elements to the provided layouts.
 /// </summary>
 /// <param name="layouts">Layouts to append the GUI elements to.</param>
 /// <param name="path">Path of the curve field.</param>
 /// <param name="colors">Colors of the curves to display, for each child entry.</param>
 public GUIAnimColorEntry(GUIAnimFieldLayouts layouts, string path, Color[] colors)
     : base(layouts, path, new[] { ".r", ".g", ".b", ".a" }, colors)
 {
 }
        /// <summary>
        /// Constructs a new animation field entry and appends the necessary GUI elements to the provided layouts.
        /// </summary>
        /// <param name="layouts">Layouts to append the GUI elements to.</param>
        /// <param name="path">Path of the curve field.</param>
        /// <param name="child">Determines if the element is a root path, or a child (sub) element.</param>
        /// <param name="indentAmount">Determines how much to horizontally indent the element, in pixels.</param>
        public GUIAnimFieldEntry(GUIAnimFieldLayouts layouts, string path, bool child, int indentAmount)
        {
            this.path = path;

            GUILayoutX toggleLayout = layouts.main.AddLayoutX();
            toggleLayout.AddSpace(indentAmount);

            selectionBtn = new GUIButton(GetDisplayName(path, child), EditorStyles.Label, GUIOption.FlexibleWidth());
            selectionBtn.OnClick += () =>
            {
                OnEntrySelected?.Invoke(path);
            };

            toggleLayout.AddElement(selectionBtn);

            entryHeight = EditorBuiltin.GUISkin.GetStyle(EditorStyles.Label).Height;

            backgroundTexture = new GUITexture(Builtin.WhiteTexture, GUITextureScaleMode.StretchToFit,
                GUIOption.FlexibleWidth());
            backgroundTexture.SetTint(Color.Transparent);
            backgroundTexture.SetHeight(entryHeight);

            layouts.background.AddElement(backgroundTexture);
        }
        /// <summary>
        /// Rebuilds the entire GUI based on the current field list and their properties.
        /// </summary>
        private void Rebuild()
        {
            scrollArea.Layout.Clear();
            fields = null;

            if (fieldInfos == null || root == null)
                return;

            GUILabel header = new GUILabel(new LocEdString("Properties"), EditorStyles.Header);
            scrollArea.Layout.AddElement(header);

            layouts = new GUIAnimFieldLayouts();
            GUIPanel rootPanel = scrollArea.Layout.AddPanel();
            GUIPanel mainPanel = rootPanel.AddPanel();
            GUIPanel underlayPanel = rootPanel.AddPanel(1);
            GUIPanel overlayPanel = rootPanel.AddPanel(-1);
            GUIPanel backgroundPanel = rootPanel.AddPanel(2);

            layouts.main = mainPanel.AddLayoutY();
            layouts.underlay = underlayPanel.AddLayoutY();
            layouts.overlay = overlayPanel.AddLayoutY();
            layouts.background = backgroundPanel.AddLayoutY();

            GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
            catchAll.Bounds = new Rect2I(0, 0, width, height - header.Bounds.height);
            catchAll.OnClick += () => OnEntrySelected(null);

            underlayPanel.AddElement(catchAll);

            layouts.main.AddSpace(5);
            layouts.underlay.AddSpace(5);
            layouts.overlay.AddSpace(5);
            layouts.background.AddSpace(3); // Minor hack: Background starts heigher to get it to center better

            fields = new GUIAnimFieldEntry[fieldInfos.Count];
            for (int i = 0; i < fieldInfos.Count; i++)
            {
                if (string.IsNullOrEmpty(fieldInfos[i].path))
                    continue;

                bool entryIsMissing;
                if (fieldInfos[i].curveGroup.isPropertyCurve)
                {
                    string pathSuffix;
                    SerializableProperty property = Animation.FindProperty(root, fieldInfos[i].path, out pathSuffix);
                    entryIsMissing = property == null;
                }
                else
                    entryIsMissing = false;

                if (!entryIsMissing)
                {
                    Color[] colors = new Color[fieldInfos[i].curveGroup.curveInfos.Length];
                    for (int j = 0; j < fieldInfos[i].curveGroup.curveInfos.Length; j++)
                        colors[j] = fieldInfos[i].curveGroup.curveInfos[j].color;

                    switch (fieldInfos[i].curveGroup.type)
                    {
                        case SerializableProperty.FieldType.Vector2:
                            fields[i] = new GUIAnimVec2Entry(layouts, fieldInfos[i].path, colors);
                            break;
                        case SerializableProperty.FieldType.Vector3:
                            fields[i] = new GUIAnimVec3Entry(layouts, fieldInfos[i].path, colors);
                            break;
                        case SerializableProperty.FieldType.Vector4:
                            fields[i] = new GUIAnimVec4Entry(layouts, fieldInfos[i].path, colors);
                            break;
                        case SerializableProperty.FieldType.Color:
                            fields[i] = new GUIAnimColorEntry(layouts, fieldInfos[i].path, colors);
                            break;
                        case SerializableProperty.FieldType.Bool:
                        case SerializableProperty.FieldType.Int:
                        case SerializableProperty.FieldType.Float:
                        {
                            Color color;
                            if (colors.Length > 0)
                                color = colors[0];
                            else
                                color = Color.White;

                            fields[i] = new GUIAnimSimpleEntry(layouts, fieldInfos[i].path, color);
                            break;
                        }
                    }
                }
                else
                {
                    fields[i] = new GUIAnimMissingEntry(layouts, fieldInfos[i].path);
                }

                if (fields[i] != null)
                    fields[i].OnEntrySelected += OnEntrySelected;
            }

            if (fieldInfos.Count == 0)
            {
                GUILabel warningLbl = new GUILabel(new LocEdString("No properties. Add a new property to begin animating."));

                GUILayoutY vertLayout = layouts.main.AddLayoutY();
                vertLayout.AddFlexibleSpace();
                GUILayoutX horzLayout = vertLayout.AddLayoutX();
                vertLayout.AddFlexibleSpace();

                horzLayout.AddFlexibleSpace();
                horzLayout.AddElement(warningLbl);
                horzLayout.AddFlexibleSpace();
            }

            layouts.main.AddSpace(5);
            layouts.underlay.AddSpace(5);
            layouts.overlay.AddSpace(5);
            layouts.background.AddSpace(5);

            layouts.main.AddFlexibleSpace();
            layouts.underlay.AddFlexibleSpace();
            layouts.overlay.AddFlexibleSpace();
            layouts.background.AddFlexibleSpace();
        }
        /// <summary>
        /// Constructs a new animation field entry and appends the necessary GUI elements to the provided layouts.
        /// </summary>
        /// <param name="layouts">Layouts to append the GUI elements to.</param>
        /// <param name="path">Path of the curve field.</param>
        /// <param name="childEntries">Sub-path names of the child entries to display.</param>
        /// <param name="colors">Colors of the curves to display, for each child entry.</param>
        public GUIAnimComplexEntry(GUIAnimFieldLayouts layouts, string path, string[] childEntries, Color[] colors)
            : base(layouts, path, false, 20)
        {
            foldout = new GUIToggle("", EditorStyles.Expand);
            foldout.OnToggled += Toggle;

            GUILabel spacer = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));

            foldoutLayout = layouts.overlay.AddLayoutX();

            foldoutLayout.AddSpace(5);
            foldoutLayout.AddElement(foldout);
            foldoutLayout.AddElement(spacer);
            foldoutLayout.AddFlexibleSpace();

            underlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
            layouts.underlay.AddElement(underlaySpacing);

            children = new GUIAnimSimpleEntry[childEntries.Length];
            for (int i = 0; i < childEntries.Length; i++)
            {
                Color color;
                if (i < colors.Length)
                    color = colors[i];
                else
                    color = Color.White;

                children[i] = new GUIAnimSimpleEntry(layouts, path + childEntries[i], color, true);
                children[i].OnEntrySelected += x => { OnEntrySelected?.Invoke(x); };
            }

            Toggle(false);
        }