vexe / VFW

MIT License
492 stars 67 forks source link

[AnimVar] isn't compatible with [Inline] #86

Open s0nerik opened 8 years ago

s0nerik commented 8 years ago

When I change a value of [AnimVar]-annotated field inside an [Inline]-annotated component it gets changed, but the drawer on the actual component shows the old value. I've found a way to fix it but I'm not sure if it's 100% correct. Here's how I did it:

I've replaced

        public override void OnGUI() {
            if (!Ready) {
                if (memberType.IsA<string>())
                    member.Value = gui.Text(displayText, (string) member.Value);
                else {
                    using (gui.Horizontal()) {
                        gui.Label(displayText);
                        gui.Label("N/A: Please Attach Animator with Controller");
                    }
                }
            } else {
                if (_parameters.IsNullOrEmpty())
                    FetchVariables();

                var selection = gui.Popup(displayText, _current, _names);
                {
                    if (_current != selection || Current != _parameters[selection])
                        Current = _parameters[_current = selection];
                }
            }
        }

with

        public override void OnGUI() {
            if (!Ready) {
                if (memberType.IsA<string>())
                    member.Value = gui.Text(displayText, (string) member.Value);
                else {
                    using (gui.Horizontal()) {
                        gui.Label(displayText);
                        gui.Label("N/A: Please Attach Animator with Controller");
                    }
                }
            } else {
                if (_parameters.IsNullOrEmpty())
                    FetchVariables();

                if (member.Value != _lastMemberValue)
                    _current = _names.IndexOf(member.Value);

                var selection = gui.Popup(displayText, _current, _names);
                {
                    if (_current != selection || Current != _parameters[selection])
                        Current = _parameters[_current = selection];
                }

                _lastMemberValue = member.Value;
            }
        }

where _lastMemberValue is object.

It seems to work in my case, but I don't know if this would work in every case, so I want you to either confirm that this approach is right (in which case I'll prepare a PR with a fix) or maybe make some other fix for this problem that would work better.

UPD: My fix did help me to properly update annotated field views inside the editor, but if the values were set on the inlined component inside the containing component editor, then in play mode those changes were lost and fields of the inlined component reverted to their previous values. The thing here was that an EditorMember's setter notified Unity that only a containing component is dirty now, but not those inlined components that were actually changed. So I replaced the EditorMember's setter with this:

        public void Set(object value)
        {
            bool sameValue = value.GenericEquals(Get());
            if (sameValue)
                return;

            HandleUndoAndSet(value);

            if (UnityTarget != null)
            {
                EditorUtility.SetDirty(UnityTarget);

                // For [Inline] to work properly, we need to tell Unity that referenced Components are also dirty.
                if (UnityTarget is Component)
                {
                    var target = UnityTarget as Component;
                    var fields = target.GetType().GetFields();
                    foreach (var field in fields)
                    {
                        if (Attribute.IsDefined(field, typeof(InlineAttribute)))
                        {
                            if (field.GetDataType().IsSubclassOf<UnityObject>())
                            {
                                var val = field.GetValue(target) as UnityObject;
                                if (val != null)
                                    EditorUtility.SetDirty(val);
                            }
                        }
                    }
                }
            }
        }

and everything started to work as it should.

For now I have fixed those issues that bothered me, but I want you to look at the way I did it and maybe you'll come up with some better idea of how to fix all that stuff.