1
\$\begingroup\$

I am creating a prefab for a stylized input, which proxies properties to the underlying game objects that make up the input parts. (Using NovaUI for some parts)

Here is my script for the UnderlinedTextInput:

using Nova;
using UnityEngine;

public class UnderlinedTextInput : MonoBehaviour
{
  [SerializeField]
  public Color32 TextColor
  {
    get => _InputField.Color;
    set => _InputField.Color = value;
  }
  [SerializeField]
  public string Text
  {
    get => _InputField.Text;
    set => _InputField.Text = value;
  }

  [SerializeField]
  public Color32 PlaceholderColor
  {
    get => _Placeholder.Color;
    set => _Placeholder.Color = value;
  }

  [SerializeField]
  public string Placeholder
  {
    get => _Placeholder.Text;
    set => _Placeholder.Text = value;
  }

  [SerializeField]
  public Color32 LabelColor
  {
    get => _Label.Color;
    set => _Label.Color = value;
  }
  [SerializeField]
  public string Label
  {
    get => _Label.Text;
    set => _Label.Text = value;
  }

  [SerializeField]
  public Color32 UnderlineColor
  {
    get => _Underline.Color;
    set => _Underline.Color = value;
  }


  [Header("Prefab Editing-Only")]
  [SerializeField] private TextBlock _InputField;
  [SerializeField] private TextBlock _Placeholder;
  [SerializeField] private TextBlock _Label;
  [SerializeField] private UIBlock2D _Underline;
}

And here is the Editor script:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(UnderlinedTextInput))]
public class UnderlinedTextInputEditor : Editor
{
  public override void OnInspectorGUI()
  {
    UnderlinedTextInput inputComponent = (UnderlinedTextInput)target;
    bool isEditingPrefab = PrefabUtility.GetPrefabInstanceStatus(inputComponent) == PrefabInstanceStatus.NotAPrefab;

    CheckForInputChanges(inputComponent);
    DrawEditorFields(isEditingPrefab);
  }

  private void DrawEditorFields(bool isEditingPrefab)
  {
    EditorGUILayout.BeginVertical();
    // only show all the properties if we are editing the prefab
    if (isEditingPrefab)
    {
      // draw everything else
      DrawDefaultInspector();
    }

    EditorGUILayout.EndVertical();
  }

  private void CheckForInputChanges(UnderlinedTextInput inputComponent)
  {
    EditorGUI.BeginChangeCheck();

    string text = EditorGUILayout.TextField("Text", inputComponent.Text);
    string placeholder = EditorGUILayout.TextField("Placeholder", inputComponent.Placeholder);
    string label = EditorGUILayout.TextField("Label", inputComponent.Label);
    Color32 labelColor = EditorGUILayout.ColorField("Label Color", inputComponent.LabelColor);
    Color32 underlineColor = EditorGUILayout.ColorField("Underline Color", inputComponent.UnderlineColor);
    Color32 placeholderColor = EditorGUILayout.ColorField("Placeholder Color", inputComponent.PlaceholderColor);
    Color32 textColor = EditorGUILayout.ColorField("Text Color", inputComponent.TextColor);

    if (EditorGUI.EndChangeCheck())
    {
      inputComponent.Text = text;
      inputComponent.Placeholder = placeholder;
      inputComponent.Label = label;
      inputComponent.LabelColor = labelColor;
      inputComponent.UnderlineColor = underlineColor;
      inputComponent.PlaceholderColor = placeholderColor;
      inputComponent.TextColor = textColor;

      EditorUtility.SetDirty(inputComponent);
      Undo.RecordObject(inputComponent, "Modify UnderlinedTextInput Properties");
    }
  }

  private void DrawPrefabInstanceInspector(UnderlinedTextInput inputComponent)
  {
    EditorGUILayout.ColorField("Label Color", inputComponent.LabelColor);
    EditorGUILayout.TextField("Label", inputComponent.Label);

    EditorGUILayout.ColorField("Placeholder Color", inputComponent.PlaceholderColor);
    EditorGUILayout.TextField("Placeholder", inputComponent.Placeholder);

    EditorGUILayout.ColorField("Text Color", inputComponent.TextColor);
    EditorGUILayout.TextField("Text", inputComponent.Text);

    EditorGUILayout.ColorField("Underline Color", inputComponent.UnderlineColor);
  }
}

I'm fairly new to Unity, but my guess is that I'm not properly marking the instantiated prefab as dirty. That, or something about using getter/setters for Inspector fields doesn't allow Unity to save those values? Anyways, is there something I can change to make this work, or is this not a supported workflow?

Here is an example of updating one of the fields:

Before:

enter image description here

After editing "Label" field (no indication that value was changed):

enter image description here

A different component (shows that values have been changed):

enter image description here


UnderlinedTextInput prefab:

enter image description here

Inspector:

enter image description here

Thank you in advance!

\$\endgroup\$
2
  • \$\begingroup\$ Have you tried EditorSceneManager.MarkSceneDirty()? \$\endgroup\$
    – Kevin
    Commented Apr 25 at 17:56
  • \$\begingroup\$ Thanks - I gave that a shot but didn't seem to make a difference \$\endgroup\$
    – e-e
    Commented Apr 25 at 18:34

1 Answer 1

0
\$\begingroup\$

Your code references TextBlock and UITextBlock2D. These appear to be components in the Nova UI framework.

You are setting the UnderlinedTextInput component dirty. However, the UnderlinedTextInput itself has no state and hasn't changed. It's the various components that UnderlinedTextInput references which need to be marked as dirty.

If all of the components are attached to the same GameObject as UnderlinedTextInput, or to children of that GameObject, then all you need to do is change the SetDirty line to

EditorUtility.SetDirty(inputComponent.gameObject);

Otherwise, you'll need to set each of components dirty individually. One way you could do this is to add a new function to UnderlinedTextInput:

#if UNITY_EDITOR
public void SetDirty() {
    UnityEditor.EditorUtility.SetDirty(_InputField);
    UnityEditor.EditorUtility.SetDirty(_Placeholder);
    UnityEditor.EditorUtility.SetDirty(_Label);
    UnityEditor.EditorUtility.SetDirty(_Underline);
}
#endif

You would then call this function from your Editor code.

You'll also need to update your Undo.RecordObject() for the same reason - it's recording the UnderlinedTextInput, which hasn't changed.

Lastly, you need to call Undo.RecordObject() before making changes, not after. RecordObject records the current state of the object, so that the next Undo operation will return to that state.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you for the detailed response. I've tried both suggestions, but unfortunately am still seeing the same thing. I've updated the original question with two additional screenshots for more context: 1) the prefab node tree, and 2) the inspector with the components set \$\endgroup\$
    – e-e
    Commented Apr 25 at 20:32
  • \$\begingroup\$ @e-e I'm not sure what else could be wrong. Please update your answer with the code you tried. Another correction to your original code - [SerializeField] does not belong in front of properties, because they are not fields. Unity does not serialize properties. \$\endgroup\$
    – Kevin
    Commented Apr 26 at 0:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .