Skip to content

Instantly share code, notes, and snippets.

@shane-harper
Created May 15, 2025 10:09
Show Gist options
  • Save shane-harper/0bc85511193600bceb6e618212bb04cc to your computer and use it in GitHub Desktop.
Save shane-harper/0bc85511193600bceb6e618212bb04cc to your computer and use it in GitHub Desktop.
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// A component for adding annotations to GameObjects in a scene
/// </summary>
[AddComponentMenu("Annotation")]
internal sealed class Annotation : MonoBehaviour
{
[SerializeField, TextArea] private string _text;
#if UNITY_EDITOR
[CustomEditor(typeof(Annotation), true), CanEditMultipleObjects]
internal sealed class AnnotationEditor : Editor
{
private SerializedProperty _text;
private bool _isEditable;
private string _lastFocusedControl;
private void OnEnable()
{
// Get text property and begin editing if annotation has not been set
_text = serializedObject.FindProperty(nameof(_text));
if (string.IsNullOrWhiteSpace(_text.stringValue))
{
Edit();
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// Draw the non-editable annotation
if (!_isEditable)
{
var style = new GUIStyle(EditorStyles.label) { wordWrap = true, richText = true };
EditorGUILayout.LabelField(GUIContent.none, new GUIContent(_text.stringValue), style);
// Add context menu to allow editing
var e = Event.current;
if (e.type == EventType.ContextClick && GUILayoutUtility.GetLastRect().Contains(e.mousePosition))
{
ShowContextMenu();
e.Use();
}
return;
}
// Draw editable annotation
using (var check = new EditorGUI.ChangeCheckScope())
{
var style = new GUIStyle(EditorStyles.textArea) { wordWrap = true, richText = false };
GUI.SetNextControlName(nameof(_text));
_text.stringValue = EditorGUILayout.TextArea(_text.stringValue, style);
serializedObject.ApplyModifiedProperties();
if (check.changed)
{
EditorUtility.SetDirty(target);
}
// Disable editing when losing focus
var currentFocusedControl = GUI.GetNameOfFocusedControl();
if (_lastFocusedControl == nameof(_text) && currentFocusedControl != nameof(_text))
{
_isEditable = false;
}
_lastFocusedControl = currentFocusedControl;
}
}
private void ShowContextMenu()
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Edit"), false, Edit);
menu.ShowAsContext();
}
private void Edit()
{
_isEditable = true;
}
}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment