com.wallstop-studios.dxmessaging 3.1.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +326 -0
  2. package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +3 -3
  3. package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +3 -3
  4. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +8 -8
  5. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +3 -3
  6. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +171 -13
  7. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +3 -3
  8. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +391 -96
  9. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +3 -3
  10. package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs +279 -0
  11. package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs.meta +11 -0
  12. package/Editor/DxMessagingEditorPalette.cs +66 -0
  13. package/Editor/DxMessagingEditorPalette.cs.meta +11 -0
  14. package/Editor/DxMessagingEditorTheme.cs +200 -0
  15. package/Editor/DxMessagingEditorTheme.cs.meta +11 -0
  16. package/Editor/DxMessagingMenu.cs.meta +3 -3
  17. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +3 -3
  18. package/Editor/Icons/dxmessaging-icon-256.png +0 -0
  19. package/Editor/Icons/dxmessaging-icon-256.png.meta +156 -0
  20. package/Editor/Icons/dxmessaging-icon-32.png +0 -0
  21. package/Editor/Icons/dxmessaging-icon-32.png.meta +156 -0
  22. package/Editor/Icons/dxmessaging-icon-48.png +0 -0
  23. package/Editor/Icons/dxmessaging-icon-48.png.meta +156 -0
  24. package/Editor/Icons.meta +8 -0
  25. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +2 -3
  26. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +3 -3
  27. package/Editor/Settings/DxMessagingSettingsProvider.cs +368 -33
  28. package/Editor/SetupCscRsp.cs +232 -163
  29. package/Editor/Testing/MessagingComponentEditorHarness.cs +11 -4
  30. package/Editor/Theme/DxMessagingTheme.uss +255 -0
  31. package/Editor/Theme/DxMessagingTheme.uss.meta +11 -0
  32. package/Editor/Theme/DxTokens.uss +90 -0
  33. package/Editor/Theme/DxTokens.uss.meta +11 -0
  34. package/Editor/Theme.meta +8 -0
  35. package/Editor/Windows/DxMessagingFlowGraphWindow.cs +5513 -0
  36. package/Editor/Windows/DxMessagingFlowGraphWindow.cs.meta +11 -0
  37. package/Editor/Windows/DxMessagingMessageMonitorWindow.cs +2288 -0
  38. package/Editor/Windows/DxMessagingMessageMonitorWindow.cs.meta +11 -0
  39. package/Editor/Windows.meta +8 -0
  40. package/README.md +84 -35
  41. package/Runtime/Analyzers.meta +8 -0
  42. package/Runtime/AssemblyInfo.cs +1 -0
  43. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +3 -3
  44. package/Runtime/Core/Diagnostics/MessageEmissionData.cs +29 -1
  45. package/Runtime/Core/DxMessagingStaticState.cs.meta +3 -3
  46. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +3 -3
  47. package/Runtime/Core/Extensions/MessageExtensions.cs +4 -5
  48. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +3 -3
  49. package/Runtime/Core/Helper/MessageCache.cs +56 -23
  50. package/Runtime/Core/IMessage.cs.meta +3 -3
  51. package/Runtime/Core/InstanceId.cs.meta +3 -3
  52. package/Runtime/Core/Internal/FlatDispatch.cs +32 -0
  53. package/Runtime/Core/Internal/TypedSlots.cs +45 -7
  54. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +3 -3
  55. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +3 -3
  56. package/Runtime/Core/MessageBus/IMessageBus.cs +63 -28
  57. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +3 -3
  58. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +3 -3
  59. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +3 -3
  60. package/Runtime/Core/MessageBus/MessageBus.cs +1135 -626
  61. package/Runtime/Core/MessageBus/MessageBus.cs.meta +3 -3
  62. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +3 -3
  63. package/Runtime/Core/MessageBus/MessageBusRegistration.cs +207 -0
  64. package/Runtime/Core/MessageBus/MessageBusRegistration.cs.meta +11 -0
  65. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +3 -3
  66. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +3 -3
  67. package/Runtime/Core/MessageBus/RegistrationLog.cs +25 -12
  68. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +3 -3
  69. package/Runtime/Core/MessageHandler.cs +1886 -1082
  70. package/Runtime/Core/MessageHandler.cs.meta +3 -3
  71. package/Runtime/Core/MessageRegistrationHandle.cs +19 -5
  72. package/Runtime/Core/MessageRegistrationHandle.cs.meta +3 -3
  73. package/Runtime/Core/MessageRegistrationToken.cs +1932 -895
  74. package/Runtime/Core/MessageRegistrationToken.cs.meta +3 -3
  75. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +3 -3
  76. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +3 -3
  77. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +3 -3
  78. package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +3 -3
  79. package/Runtime/Core/MessagingDebug.cs.meta +3 -3
  80. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +3 -3
  81. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +3 -3
  82. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +3 -3
  83. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +3 -3
  84. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +3 -3
  85. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +3 -3
  86. package/Runtime/Unity/MessageAwareComponent.cs.meta +3 -3
  87. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +3 -3
  88. package/Runtime/Unity/MessagingComponent.cs.meta +3 -3
  89. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +3 -3
  90. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +3 -3
  91. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +3 -3
  92. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +3 -3
  93. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +3 -3
  94. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs +173 -0
  95. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs.meta +11 -0
  96. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity +424 -0
  97. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity.meta +7 -0
  98. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs +185 -0
  99. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs.meta +11 -0
  100. package/Samples~/Diagnostics Tooling Exerciser/Messages.cs +46 -0
  101. package/Samples~/Diagnostics Tooling Exerciser/Messages.cs.meta +11 -0
  102. package/Samples~/Diagnostics Tooling Exerciser/README.md +53 -0
  103. package/Samples~/Diagnostics Tooling Exerciser/README.md.meta +7 -0
  104. package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef +13 -0
  105. package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef.meta +7 -0
  106. package/Samples~/Diagnostics Tooling Exerciser.meta +8 -0
  107. package/Samples~/Mini Combat/Boot.cs.meta +3 -3
  108. package/Samples~/Mini Combat/Enemy.cs.meta +3 -3
  109. package/Samples~/Mini Combat/Messages.cs.meta +3 -3
  110. package/Samples~/Mini Combat/Player.cs.meta +3 -3
  111. package/Samples~/Mini Combat/README.md +1 -1
  112. package/Samples~/Mini Combat/UIOverlay.cs.meta +3 -3
  113. package/Samples~/Mini Combat/Walkthrough.md +3 -3
  114. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +3 -3
  115. package/Samples~/UI Buttons + Inspector/Messages.cs.meta +3 -3
  116. package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +3 -3
  117. package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +3 -3
  118. package/SourceGenerators/Directory.Build.props +5 -5
  119. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +3 -3
  120. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +12 -11
  121. package/docs/images/DxMessaging-banner.svg +79 -0
  122. package/docs/images/DxMessaging-banner.svg.meta +53 -0
  123. package/docs/images.meta +8 -0
  124. package/docs.meta +8 -0
  125. package/package.json +17 -5
  126. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  127. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +0 -0
  128. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
  129. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll.meta +0 -0
  130. /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll +0 -0
  131. /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll.meta +0 -0
  132. /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll +0 -0
  133. /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll.meta +0 -0
  134. /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
  135. /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +0 -0
  136. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  137. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +0 -0
  138. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  139. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +0 -0
@@ -9,17 +9,18 @@ namespace DxMessaging.Editor.CustomEditors
9
9
  using UnityEditor;
10
10
  using UnityEditorInternal;
11
11
  using UnityEngine;
12
+ using UnityEngine.UIElements;
12
13
 
13
14
  /// <summary>
14
15
  /// Header-injection overlay for every Inspector showing a <see cref="MessageAwareComponent"/> subclass.
15
16
  /// </summary>
16
17
  /// <remarks>
17
- /// We hook <see cref="Editor.finishedDefaultHeaderGUI"/> rather than registering a
18
- /// <c>[CustomEditor(typeof(MessageAwareComponent), editorForChildClasses: true)]</c> so we never
19
- /// clobber a user's own custom editor. The overlay reads its data from
20
- /// <see cref="DxMessagingConsoleHarvester"/> (which reflects directly into Unity's
21
- /// <c>UnityEditor.LogEntries</c> console store) and from <see cref="DxMessagingSettings"/>
22
- /// (project-wide ignore list and master toggle).
18
+ /// The package-owned fallback editor hosts the UI Toolkit warning view for subclasses without
19
+ /// a more specific user editor. This header hook remains for user-defined custom editors, so
20
+ /// they can keep their own Inspector body while still seeing DxMessaging base-call warnings.
21
+ /// The overlay reads its data from <see cref="DxMessagingConsoleHarvester"/> (which reflects
22
+ /// directly into Unity's <c>UnityEditor.LogEntries</c> console store) and from
23
+ /// <see cref="DxMessagingSettings"/> (project-wide ignore list and master toggle).
23
24
  ///
24
25
  /// <para>
25
26
  /// <b>Layout/Repaint control-count invariant.</b> When the overlay renders from inside an
@@ -58,6 +59,27 @@ namespace DxMessaging.Editor.CustomEditors
58
59
  // and Repaint passes; that asymmetry would corrupt the inspector's layout cache.
59
60
  private static readonly HashSet<int> _renderedThisRepaint = new();
60
61
 
62
+ internal static System.Action<System.Action> AssetDatabaseMutationScheduler { get; set; } =
63
+ DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
64
+
65
+ internal static System.Action<System.Action> TransientRefreshScheduler { get; set; } =
66
+ DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
67
+
68
+ internal static System.Action InspectorRepaintAction { get; set; } = RepaintAllInspectors;
69
+
70
+ internal static System.Func<bool> InspectorResolutionTransientBlocker { get; set; } =
71
+ () => EditorApplication.isCompiling || EditorApplication.isUpdating;
72
+
73
+ internal static System.Func<MessageAwareComponent, bool> ImGuiRenderAction { get; set; } =
74
+ BuildAndRenderOverlay;
75
+
76
+ internal static System.Action<
77
+ MessageAwareComponent,
78
+ BaseCallReportEntry
79
+ > OpenScriptAction { get; set; } = OpenScriptForComponentCore;
80
+
81
+ internal static event System.Action InspectorStateInvalidated;
82
+
61
83
  static MessageAwareComponentInspectorOverlay()
62
84
  {
63
85
  Editor.finishedDefaultHeaderGUI += DrawHeader;
@@ -84,60 +106,72 @@ namespace DxMessaging.Editor.CustomEditors
84
106
  }
85
107
 
86
108
  private static void DrawHeader(Editor editor)
109
+ {
110
+ TryRenderHeaderHook(editor, Event.current != null ? Event.current.type : null);
111
+ }
112
+
113
+ internal static bool TryRenderHeaderHook(Editor editor, EventType? eventType)
114
+ {
115
+ if (!ShouldRenderHeaderHook(editor))
116
+ {
117
+ return false;
118
+ }
119
+
120
+ return TryRenderForHeaderHook(editor.target, eventType);
121
+ }
122
+
123
+ internal static bool ShouldRenderHeaderHook(Editor editor)
87
124
  {
88
125
  if (editor == null)
89
126
  {
90
- return;
127
+ return false;
91
128
  }
129
+
92
130
  // If our own fallback CustomEditor is the editor instance, skip the header path
93
- // entirely; the editor's OnInspectorGUI will call RenderInsideOnInspectorGUI and we
94
- // would otherwise render twice. Unconditional skip (not gated on EventType) keeps
131
+ // entirely; CreateInspectorGUI or OnInspectorGUI owns package fallback rendering and
132
+ // we would otherwise render twice. Unconditional skip (not gated on EventType) keeps
95
133
  // control counts balanced on both Layout and Repaint passes.
96
- if (editor is MessageAwareComponentFallbackEditor)
97
- {
98
- return;
99
- }
100
- RenderForHeaderHook(editor.target);
134
+ return editor is not MessageAwareComponentFallbackEditor;
101
135
  }
102
136
 
103
137
  /// <summary>
104
- /// Header-hook entry point. Fires after Unity's default header has been drawn, so the
138
+ /// Header-hook render body. Fires after Unity's default header has been drawn, so the
105
139
  /// inspector's layout pass for this editor has already completed. Safe to gate on
106
140
  /// <see cref="EventType.Repaint"/> here -- we are not inside an OnInspectorGUI body.
107
141
  /// </summary>
108
- private static void RenderForHeaderHook(Object target)
142
+ private static bool TryRenderForHeaderHook(Object target, EventType? eventType)
109
143
  {
110
144
  if (target == null)
111
145
  {
112
- return;
146
+ return false;
113
147
  }
114
148
  if (target is not MessageAwareComponent messageAwareComponent)
115
149
  {
116
- return;
150
+ return false;
117
151
  }
118
-
119
- Event currentEvent = Event.current;
120
- if (currentEvent == null)
152
+ if (!eventType.HasValue)
121
153
  {
122
- return;
154
+ return false;
123
155
  }
124
- if (currentEvent.type == EventType.Layout)
156
+ if (eventType.Value == EventType.Layout)
125
157
  {
126
158
  // Start of a fresh GUI cycle; wipe the per-Repaint latch.
127
159
  _renderedThisRepaint.Clear();
128
- return;
160
+ return false;
129
161
  }
130
- if (currentEvent.type != EventType.Repaint)
162
+ if (eventType.Value != EventType.Repaint)
131
163
  {
132
- return;
164
+ return false;
133
165
  }
134
166
  int instanceId = DxMessaging.Core.InstanceId.StableId(messageAwareComponent);
135
167
  if (!_renderedThisRepaint.Add(instanceId))
136
168
  {
137
- return;
169
+ return false;
138
170
  }
139
171
 
140
- BuildAndRenderOverlay(messageAwareComponent);
172
+ System.Func<MessageAwareComponent, bool> render =
173
+ ImGuiRenderAction ?? BuildAndRenderOverlay;
174
+ return render.Invoke(messageAwareComponent);
141
175
  }
142
176
 
143
177
  /// <summary>
@@ -166,15 +200,100 @@ namespace DxMessaging.Editor.CustomEditors
166
200
  /// <returns>True if the HelpBox + buttons were drawn; false if we drew nothing.</returns>
167
201
  private static bool BuildAndRenderOverlay(MessageAwareComponent messageAwareComponent)
168
202
  {
169
- // ---- Gating phase: every "should we draw?" decision happens here, before any
170
- // EditorGUILayout call. The result is a single bool: shouldRender. ----
203
+ MessageAwareComponentInspectorState state = ResolveInspectorState(
204
+ messageAwareComponent
205
+ );
206
+
207
+ if (state.Kind == MessageAwareComponentInspectorStateKind.None)
208
+ {
209
+ return false;
210
+ }
211
+
212
+ // ---- Render phase: straight-line EditorGUILayout calls, identical sequence on
213
+ // every pass. Wrapped in a vertical group so any internal mismatch we missed cannot
214
+ // propagate to sibling inspectors. ----
215
+ EditorGUILayout.BeginVertical();
216
+ try
217
+ {
218
+ switch (state.Kind)
219
+ {
220
+ case MessageAwareComponentInspectorStateKind.HarvesterUnavailable:
221
+ EditorGUILayout.HelpBox(
222
+ "DxMessaging inspector overlay is disabled on this Unity version. "
223
+ + "Check the console for DXMSG006/007/009 warnings instead.",
224
+ MessageType.Info
225
+ );
226
+ break;
227
+ case MessageAwareComponentInspectorStateKind.IgnoredType:
228
+ DrawIgnoredBox(state.Component, state.Settings, state.FullName);
229
+ break;
230
+ case MessageAwareComponentInspectorStateKind.MissingBaseCallWarning:
231
+ DrawWarningBox(
232
+ state.Component,
233
+ state.Settings,
234
+ state.FullName,
235
+ state.Entry,
236
+ state.IsFreshThisSession
237
+ );
238
+ break;
239
+ }
240
+ }
241
+ finally
242
+ {
243
+ EditorGUILayout.EndVertical();
244
+ }
245
+
246
+ return true;
247
+ }
248
+
249
+ internal static VisualElement CreateInspectorView(Object target)
250
+ {
251
+ return CreateInspectorView(ResolveInspectorState(target));
252
+ }
253
+
254
+ internal static VisualElement CreateInspectorView(MessageAwareComponentInspectorState state)
255
+ {
256
+ return MessageAwareComponentInspectorView.Create(state, CreateInspectorViewActions());
257
+ }
258
+
259
+ internal static void ResetTestSeams()
260
+ {
261
+ _renderedThisRepaint.Clear();
262
+ AssetDatabaseMutationScheduler = DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
263
+ TransientRefreshScheduler = DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
264
+ InspectorRepaintAction = RepaintAllInspectors;
265
+ InspectorResolutionTransientBlocker = () =>
266
+ EditorApplication.isCompiling || EditorApplication.isUpdating;
267
+ ImGuiRenderAction = BuildAndRenderOverlay;
268
+ OpenScriptAction = OpenScriptForComponentCore;
269
+ }
270
+
271
+ internal static bool IsInspectorResolutionTransientlyBlocked()
272
+ {
273
+ System.Func<bool> isBlocked =
274
+ InspectorResolutionTransientBlocker
275
+ ?? (() => EditorApplication.isCompiling || EditorApplication.isUpdating);
276
+ return isBlocked.Invoke();
277
+ }
278
+
279
+ internal static MessageAwareComponentInspectorState ResolveInspectorState(Object target)
280
+ {
281
+ if (target == null)
282
+ {
283
+ return MessageAwareComponentInspectorState.None;
284
+ }
171
285
 
172
286
  // Mid-compile / mid-import is the worst time to dereference the settings asset:
173
287
  // AssetDatabase may be in a transitional state. Bail and let the next OnGUI redraw
174
288
  // pick up where we left off.
175
- if (EditorApplication.isCompiling || EditorApplication.isUpdating)
289
+ if (IsInspectorResolutionTransientlyBlocked())
176
290
  {
177
- return false;
291
+ return MessageAwareComponentInspectorState.None;
292
+ }
293
+
294
+ if (target is not MessageAwareComponent messageAwareComponent)
295
+ {
296
+ return MessageAwareComponentInspectorState.None;
178
297
  }
179
298
 
180
299
  DxMessagingSettings settings;
@@ -185,101 +304,157 @@ namespace DxMessaging.Editor.CustomEditors
185
304
  catch (System.Exception ex)
186
305
  {
187
306
  DxMessagingEditorLog.LogWarning("Inspector overlay could not load settings.", ex);
188
- return false;
307
+ return MessageAwareComponentInspectorState.None;
308
+ }
309
+
310
+ string fullName = GetOverlayTypeName(messageAwareComponent);
311
+ BaseCallReportEntry entry = null;
312
+ if (!string.IsNullOrEmpty(fullName) && DxMessagingConsoleHarvester.IsAvailable)
313
+ {
314
+ DxMessagingConsoleHarvester.TryGetEntry(fullName, out entry);
315
+ }
316
+
317
+ return ResolveInspectorState(
318
+ messageAwareComponent,
319
+ settings,
320
+ DxMessagingConsoleHarvester.IsAvailable,
321
+ DxMessagingConsoleHarvester.IsFreshThisSession,
322
+ entry
323
+ );
324
+ }
325
+
326
+ internal static MessageAwareComponentInspectorState ResolveInspectorState(
327
+ Object target,
328
+ DxMessagingSettings settings,
329
+ bool harvesterAvailable,
330
+ bool isFreshThisSession,
331
+ BaseCallReportEntry entry
332
+ )
333
+ {
334
+ if (target == null)
335
+ {
336
+ return MessageAwareComponentInspectorState.None;
189
337
  }
190
338
 
339
+ if (target is not MessageAwareComponent messageAwareComponent)
340
+ {
341
+ return MessageAwareComponentInspectorState.None;
342
+ }
191
343
  if (settings == null || !settings._baseCallCheckEnabled)
192
344
  {
193
- return false;
345
+ return MessageAwareComponentInspectorState.None;
194
346
  }
195
347
 
196
348
  // S6: System.Type.FullName renders nested types as `Outer+Nested`, but the analyzer's
197
349
  // `containingType.ToDisplayString()` (which produces the FQN we key the snapshot by)
198
350
  // renders them as `Outer.Nested`. Without this normalization the lookup misses for
199
351
  // every nested MessageAwareComponent subclass and the HelpBox never shows.
200
- System.Type targetType = messageAwareComponent.GetType();
201
- string fullName = (targetType.FullName ?? string.Empty).Replace('+', '.');
352
+ string fullName = GetOverlayTypeName(messageAwareComponent);
202
353
  if (string.IsNullOrEmpty(fullName))
203
354
  {
204
- return false;
355
+ return MessageAwareComponentInspectorState.None;
205
356
  }
206
357
 
207
- // Decide which of the three render shapes (if any) to draw.
208
- // 0 = render nothing; 1 = harvester-unavailable info; 2 = ignored-type info; 3 = warning.
209
- int shape = 0;
210
- BaseCallReportEntry entry = null;
211
- bool isIgnored = false;
212
-
213
- if (!DxMessagingConsoleHarvester.IsAvailable)
358
+ if (!harvesterAvailable)
214
359
  {
215
- shape = 1;
360
+ return MessageAwareComponentInspectorState.ForHarvesterUnavailable(
361
+ messageAwareComponent,
362
+ settings,
363
+ fullName,
364
+ isFreshThisSession
365
+ );
216
366
  }
217
- else
367
+
368
+ bool isIgnored =
369
+ settings._baseCallIgnoredTypes != null
370
+ && settings._baseCallIgnoredTypes.Any(e =>
371
+ string.Equals(e, fullName, System.StringComparison.Ordinal)
372
+ );
373
+ if (isIgnored)
218
374
  {
219
- isIgnored =
220
- settings._baseCallIgnoredTypes != null
221
- && settings._baseCallIgnoredTypes.Any(e =>
222
- string.Equals(e, fullName, System.StringComparison.Ordinal)
223
- );
224
- if (isIgnored)
225
- {
226
- shape = 2;
227
- }
228
- else if (
229
- DxMessagingConsoleHarvester.TryGetEntry(fullName, out entry)
230
- && entry != null
231
- && entry.missingBaseFor != null
232
- && entry.missingBaseFor.Count > 0
233
- )
234
- {
235
- shape = 3;
236
- }
375
+ return MessageAwareComponentInspectorState.ForIgnoredType(
376
+ messageAwareComponent,
377
+ settings,
378
+ fullName,
379
+ isFreshThisSession
380
+ );
237
381
  }
238
382
 
239
- if (shape == 0)
383
+ if (entry != null && entry.missingBaseFor != null && entry.missingBaseFor.Count > 0)
240
384
  {
241
- // "Render nothing" branch: emit ZERO EditorGUILayout calls. This must hold on
242
- // both Layout and Repaint passes when called from OnInspectorGUI, so Unity's
243
- // layout cache stays consistent.
244
- return false;
385
+ return MessageAwareComponentInspectorState.ForMissingBaseCallWarning(
386
+ messageAwareComponent,
387
+ settings,
388
+ fullName,
389
+ entry,
390
+ isFreshThisSession
391
+ );
245
392
  }
246
393
 
247
- // ---- Render phase: straight-line EditorGUILayout calls, identical sequence on
248
- // every pass. Wrapped in a vertical group so any internal mismatch we missed cannot
249
- // propagate to sibling inspectors. ----
250
- EditorGUILayout.BeginVertical();
394
+ // "Render nothing" branch: emit ZERO EditorGUILayout calls. This must hold on
395
+ // both Layout and Repaint passes when called from OnInspectorGUI, so Unity's
396
+ // layout cache stays consistent.
397
+ return MessageAwareComponentInspectorState.None;
398
+ }
399
+
400
+ private static MessageAwareComponentInspectorViewActions CreateInspectorViewActions()
401
+ {
402
+ return new MessageAwareComponentInspectorViewActions(
403
+ onOpenScript: state => OpenScriptForComponent(state.Component, state.Entry),
404
+ onIgnoreType: state => TryAddIgnoredType(state.Settings, state.FullName),
405
+ onStopIgnoring: state => TryRemoveIgnoredType(state.Settings, state.FullName)
406
+ );
407
+ }
408
+
409
+ private static void NotifyInspectorStateInvalidated()
410
+ {
411
+ System.Action repaint = InspectorRepaintAction ?? RepaintAllInspectors;
251
412
  try
252
413
  {
253
- switch (shape)
254
- {
255
- case 1:
256
- EditorGUILayout.HelpBox(
257
- "DxMessaging inspector overlay is disabled on this Unity version. "
258
- + "Check the console for DXMSG006/007/009 warnings instead.",
259
- MessageType.Info
260
- );
261
- break;
262
- case 2:
263
- DrawIgnoredBox(messageAwareComponent, settings, fullName);
264
- break;
265
- case 3:
266
- DrawWarningBox(messageAwareComponent, settings, fullName, entry);
267
- break;
268
- }
414
+ repaint.Invoke();
269
415
  }
270
- finally
416
+ catch (System.Exception ex)
271
417
  {
272
- EditorGUILayout.EndVertical();
418
+ DxMessagingEditorLog.LogWarning(
419
+ "Failed to repaint inspectors after state change.",
420
+ ex
421
+ );
273
422
  }
274
423
 
275
- return true;
424
+ System.Action invalidated = InspectorStateInvalidated;
425
+ if (invalidated == null)
426
+ {
427
+ return;
428
+ }
429
+
430
+ foreach (System.Delegate callback in invalidated.GetInvocationList())
431
+ {
432
+ try
433
+ {
434
+ ((System.Action)callback).Invoke();
435
+ }
436
+ catch (System.Exception ex)
437
+ {
438
+ DxMessagingEditorLog.LogWarning(
439
+ "Failed to refresh MessageAwareComponent inspectors after state change.",
440
+ ex
441
+ );
442
+ }
443
+ }
444
+ }
445
+
446
+ private static string GetOverlayTypeName(MessageAwareComponent messageAwareComponent)
447
+ {
448
+ System.Type targetType = messageAwareComponent.GetType();
449
+ return (targetType.FullName ?? string.Empty).Replace('+', '.');
276
450
  }
277
451
 
278
452
  private static void DrawWarningBox(
279
453
  MessageAwareComponent component,
280
454
  DxMessagingSettings settings,
281
455
  string fullName,
282
- BaseCallReportEntry entry
456
+ BaseCallReportEntry entry,
457
+ bool isFreshThisSession
283
458
  )
284
459
  {
285
460
  string missingMethods = string.Join(", ", entry.missingBaseFor);
@@ -303,7 +478,7 @@ namespace DxMessaging.Editor.CustomEditors
303
478
  // `Library/DxMessaging/baseCallReport.json` and the first post-reload scan has not
304
479
  // yet completed; once the scan flips IsFreshThisSession to true and RepaintAllInspectors
305
480
  // fires, the overlay redraws without the suffix.
306
- string freshnessSuffix = DxMessagingConsoleHarvester.IsFreshThisSession
481
+ string freshnessSuffix = isFreshThisSession
307
482
  ? string.Empty
308
483
  : "\n(cached from previous session; refreshing...)";
309
484
  string message =
@@ -349,6 +524,16 @@ namespace DxMessaging.Editor.CustomEditors
349
524
  MessageAwareComponent component,
350
525
  BaseCallReportEntry entry
351
526
  )
527
+ {
528
+ System.Action<MessageAwareComponent, BaseCallReportEntry> openScript =
529
+ OpenScriptAction ?? OpenScriptForComponentCore;
530
+ openScript.Invoke(component, entry);
531
+ }
532
+
533
+ private static void OpenScriptForComponentCore(
534
+ MessageAwareComponent component,
535
+ BaseCallReportEntry entry
536
+ )
352
537
  {
353
538
  try
354
539
  {
@@ -380,7 +565,10 @@ namespace DxMessaging.Editor.CustomEditors
380
565
  // frame, corrupting Unity's per-window layout cache. delayCall fires AFTER the
381
566
  // current GUI cycle, so the next frame's Layout pass sees the new state and
382
567
  // both passes emit consistent control counts.
383
- DxMessagingEditorIdle.ScheduleAssetDatabaseMutation(() =>
568
+ System.Action<System.Action> schedule =
569
+ AssetDatabaseMutationScheduler
570
+ ?? DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
571
+ schedule(() =>
384
572
  {
385
573
  if (settings == null)
386
574
  {
@@ -389,6 +577,7 @@ namespace DxMessaging.Editor.CustomEditors
389
577
  try
390
578
  {
391
579
  settings.AddIgnoredType(fullName);
580
+ NotifyInspectorStateInvalidated();
392
581
  }
393
582
  catch (System.Exception ex)
394
583
  {
@@ -405,7 +594,10 @@ namespace DxMessaging.Editor.CustomEditors
405
594
  // Same reasoning as TryAddIgnoredType: defer mutation past the current GUI cycle so
406
595
  // the overlay's shape gating remains identical on Layout and Repaint passes of THIS
407
596
  // frame. The next frame's Layout pass observes the new state; both passes agree.
408
- DxMessagingEditorIdle.ScheduleAssetDatabaseMutation(() =>
597
+ System.Action<System.Action> schedule =
598
+ AssetDatabaseMutationScheduler
599
+ ?? DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
600
+ schedule(() =>
409
601
  {
410
602
  if (settings == null)
411
603
  {
@@ -414,6 +606,7 @@ namespace DxMessaging.Editor.CustomEditors
414
606
  try
415
607
  {
416
608
  settings.RemoveIgnoredType(fullName);
609
+ NotifyInspectorStateInvalidated();
417
610
  }
418
611
  catch (System.Exception ex)
419
612
  {
@@ -425,5 +618,107 @@ namespace DxMessaging.Editor.CustomEditors
425
618
  });
426
619
  }
427
620
  }
621
+
622
+ internal enum MessageAwareComponentInspectorStateKind
623
+ {
624
+ None,
625
+ HarvesterUnavailable,
626
+ IgnoredType,
627
+ MissingBaseCallWarning,
628
+ }
629
+
630
+ internal readonly struct MessageAwareComponentInspectorState
631
+ {
632
+ private MessageAwareComponentInspectorState(
633
+ MessageAwareComponentInspectorStateKind kind,
634
+ MessageAwareComponent component,
635
+ DxMessagingSettings settings,
636
+ string fullName,
637
+ BaseCallReportEntry entry,
638
+ bool isFreshThisSession
639
+ )
640
+ {
641
+ Kind = kind;
642
+ Component = component;
643
+ Settings = settings;
644
+ FullName = fullName ?? string.Empty;
645
+ Entry = entry;
646
+ IsFreshThisSession = isFreshThisSession;
647
+ }
648
+
649
+ internal static MessageAwareComponentInspectorState None { get; } =
650
+ new(
651
+ MessageAwareComponentInspectorStateKind.None,
652
+ null,
653
+ null,
654
+ string.Empty,
655
+ null,
656
+ isFreshThisSession: true
657
+ );
658
+
659
+ internal MessageAwareComponentInspectorStateKind Kind { get; }
660
+
661
+ internal MessageAwareComponent Component { get; }
662
+
663
+ internal DxMessagingSettings Settings { get; }
664
+
665
+ internal string FullName { get; }
666
+
667
+ internal BaseCallReportEntry Entry { get; }
668
+
669
+ internal bool IsFreshThisSession { get; }
670
+
671
+ internal static MessageAwareComponentInspectorState ForHarvesterUnavailable(
672
+ MessageAwareComponent component,
673
+ DxMessagingSettings settings,
674
+ string fullName,
675
+ bool isFreshThisSession
676
+ )
677
+ {
678
+ return new MessageAwareComponentInspectorState(
679
+ MessageAwareComponentInspectorStateKind.HarvesterUnavailable,
680
+ component,
681
+ settings,
682
+ fullName,
683
+ null,
684
+ isFreshThisSession
685
+ );
686
+ }
687
+
688
+ internal static MessageAwareComponentInspectorState ForIgnoredType(
689
+ MessageAwareComponent component,
690
+ DxMessagingSettings settings,
691
+ string fullName,
692
+ bool isFreshThisSession
693
+ )
694
+ {
695
+ return new MessageAwareComponentInspectorState(
696
+ MessageAwareComponentInspectorStateKind.IgnoredType,
697
+ component,
698
+ settings,
699
+ fullName,
700
+ null,
701
+ isFreshThisSession
702
+ );
703
+ }
704
+
705
+ internal static MessageAwareComponentInspectorState ForMissingBaseCallWarning(
706
+ MessageAwareComponent component,
707
+ DxMessagingSettings settings,
708
+ string fullName,
709
+ BaseCallReportEntry entry,
710
+ bool isFreshThisSession
711
+ )
712
+ {
713
+ return new MessageAwareComponentInspectorState(
714
+ MessageAwareComponentInspectorStateKind.MissingBaseCallWarning,
715
+ component,
716
+ settings,
717
+ fullName,
718
+ entry,
719
+ isFreshThisSession
720
+ );
721
+ }
722
+ }
428
723
  #endif
429
724
  }
@@ -6,6 +6,6 @@ MonoImporter:
6
6
  defaultReferences: []
7
7
  executionOrder: 0
8
8
  icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: