Skip to content

Make it easier to inherit from Button with custom properties for the editor #15

@KulaGGin

Description

@KulaGGin

I wanted to have a button that would behave like a sprite button for the background: one sprite by default, a different sprite when it's highlighted, and another for when it's pressed, but also have the ability to set the text dynamically.

To get this behavior I need to set text position dynamically for when button is in different states. Since there are no public events for when button is highlighted, selected, returns to default state, I need to either inherit from the Button class or copy paste the code and make my own class. I decided to inherit from the Button class:

    [AddComponentMenu("UI/My Button", 30)]
    public class MyButton : Button {
        HorizontalLayoutGroup HorizontalLayoutGroup;

        int NormalTextTopPadding;
        int NormalTextBottomPadding;
        [SerializeField] int HighlightedTextBottomPadding;
        [SerializeField] int PressedTextBottomPadding;

        void Awake() {
            HorizontalLayoutGroup = GetComponent<HorizontalLayoutGroup>();
            NormalTextTopPadding = HorizontalLayoutGroup.padding.top;
            NormalTextBottomPadding = HorizontalLayoutGroup.padding.bottom;
        }

        protected override void DoStateTransition(SelectionState state, bool instant) {
            base.DoStateTransition(state, instant);

            if(state == SelectionState.Normal || state == SelectionState.Selected) {
                HorizontalLayoutGroup.padding.top = NormalTextTopPadding;
                HorizontalLayoutGroup.padding.bottom = NormalTextBottomPadding;
            }
            if(state == SelectionState.Highlighted) {
                HorizontalLayoutGroup.padding.top = NormalTextTopPadding + HighlightedTextBottomPadding;
                HorizontalLayoutGroup.padding.bottom = NormalTextBottomPadding - HighlightedTextBottomPadding;
            }
            if(state == SelectionState.Pressed) {
                HorizontalLayoutGroup.padding.top = NormalTextTopPadding + HighlightedTextBottomPadding + PressedTextBottomPadding;
                HorizontalLayoutGroup.padding.bottom = NormalTextBottomPadding - HighlightedTextBottomPadding - PressedTextBottomPadding;
            }
        }
    }

This is the class. The first problem that I get is that the editor doesn't draw the properties HighlightedTextBottomPadding and PressedTextBottomPadding . I don't understand why, since in the SelectableEditor class it should draw all the child properties:

        private void ChildClassPropertiesGUI()
        {
            if (IsDerivedSelectableEditor())
                return;

            DrawPropertiesExcluding(serializedObject, m_PropertyPathToExcludeForChildClasses);
        }

So what do I do? I write my own editor to draw my properties:

    [CustomEditor(typeof(MyButton), true)]
    public class MyButtonEditor : SelectableEditor {
        SerializedProperty HighlightedTextBottomPadding;
        SerializedProperty PressedTextBottomPadding;

        protected virtual void OnEnable() {
            base.OnEnable();
            HighlightedTextBottomPadding = serializedObject.FindProperty("HighlightedTextBottomPadding");
            PressedTextBottomPadding = serializedObject.FindProperty("PressedTextBottomPadding");
        }

        public override void OnInspectorGUI() {
            base.OnInspectorGUI();

            EditorGUILayout.PropertyField(HighlightedTextBottomPadding);
            EditorGUILayout.PropertyField(PressedTextBottomPadding);

            serializedObject.ApplyModifiedProperties();
        }
    }

This starts to draw the MyButton properties but it stops drawing the original button OnClick property. That's because I now have a custom editor and IsDerivedSelectableEditor() returns false and ChildClassPropertiesGUI() doesn't draw child class properties. A naive thing to try is to add SerializedProperty OnClick; field to the editor and then find it on Enable: OnClick = serializedObject.FindProperty("m_onClick"); and then draw it: EditorGUILayout.PropertyField(OnClick);. Again, for some reason it doesn't work(FindProperty returns null), even considering the MyButton object does have m_onClick property: it inherits it from Button class.

So, how do I fix that problem? I have to write my own ButtonEditor class for Button and then inherit from that instead in my own MyButtonEditor:

    [CustomEditor(typeof(Button), true)]
    public class ButtonEditor : SelectableEditor {
        SerializedProperty OnClick;

        protected virtual void OnEnable() {
            base.OnEnable();
            OnClick = serializedObject.FindProperty("m_OnClick");
        }

        public override void OnInspectorGUI() {
            base.OnInspectorGUI();

            EditorGUILayout.PropertyField(OnClick);

            serializedObject.ApplyModifiedProperties();
        }
    }

Now with all those changes I(finally) have my own button with the behavior I want. But it's too much code for something simple like that. It would be nice to just write MyButton class and be done with that. Not sure why SelectableEditor only iterates over Button properties but not properties of my child class.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions