com.artromskiy.deltaecs.unity 0.0.37

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 (61) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/Editor/BoundEntityRootEditor.cs +288 -0
  3. package/Editor/BoundEntityRootEditor.cs.meta +2 -0
  4. package/Editor/ComponentDropdown.cs +73 -0
  5. package/Editor/ComponentDropdown.cs.meta +2 -0
  6. package/Editor/ComponentListController.cs +26 -0
  7. package/Editor/ComponentListController.cs.meta +2 -0
  8. package/Editor/DeltaECS.Unity.Editor.asmdef +18 -0
  9. package/Editor/DeltaECS.Unity.Editor.asmdef.meta +7 -0
  10. package/Editor/EditorLifecycle.cs +143 -0
  11. package/Editor/EditorLifecycle.cs.meta +2 -0
  12. package/Editor/EntityInspectorTarget.cs +26 -0
  13. package/Editor/EntityInspectorTarget.cs.meta +3 -0
  14. package/Editor/HierarchyMenu.cs +85 -0
  15. package/Editor/HierarchyMenu.cs.meta +2 -0
  16. package/Editor/HierarchyNodeHandler.cs +950 -0
  17. package/Editor/HierarchyNodeHandler.cs.meta +2 -0
  18. package/Editor/HierarchySelection.cs +171 -0
  19. package/Editor/HierarchySelection.cs.meta +3 -0
  20. package/Editor/InspectorState.cs +220 -0
  21. package/Editor/InspectorState.cs.meta +2 -0
  22. package/Editor/Resources/Inspector/BoundEntityRootEditor.uss +28 -0
  23. package/Editor/Resources/Inspector/BoundEntityRootEditor.uss.meta +2 -0
  24. package/Editor/Resources/Inspector/SceneAuthoringEditor.uss +282 -0
  25. package/Editor/Resources/Inspector/SceneAuthoringEditor.uss.meta +12 -0
  26. package/Editor/Resources/Inspector/SceneAuthoringEditor.uxml +8 -0
  27. package/Editor/Resources/Inspector/SceneAuthoringEditor.uxml.meta +10 -0
  28. package/Editor/Resources/Inspector.meta +8 -0
  29. package/Editor/Resources.meta +8 -0
  30. package/Editor/SceneEntityInspectorView.cs +1103 -0
  31. package/Editor/SceneEntityInspectorView.cs.meta +2 -0
  32. package/Editor/SelectionProxy.cs +19 -0
  33. package/Editor/SelectionProxy.cs.meta +2 -0
  34. package/Editor/SelectionProxyBridge.cs +64 -0
  35. package/Editor/SelectionProxyBridge.cs.meta +2 -0
  36. package/Editor/SelectionProxyEditor.cs +33 -0
  37. package/Editor/SelectionProxyEditor.cs.meta +2 -0
  38. package/LICENSE.md +21 -0
  39. package/LICENSE.md.meta +7 -0
  40. package/README.md +66 -0
  41. package/README.md.meta +2 -0
  42. package/Runtime/AssemblyInfo.cs +3 -0
  43. package/Runtime/AssemblyInfo.cs.meta +2 -0
  44. package/Runtime/ComponentData.cs +120 -0
  45. package/Runtime/ComponentData.cs.meta +2 -0
  46. package/Runtime/DeltaECS.Unity.asmdef +14 -0
  47. package/Runtime/DeltaECS.Unity.asmdef.meta +7 -0
  48. package/Runtime/Editor.meta +8 -0
  49. package/Runtime/EntityAuthoring.cs +47 -0
  50. package/Runtime/EntityAuthoring.cs.meta +2 -0
  51. package/Runtime/EntityReference.cs +21 -0
  52. package/Runtime/EntityReference.cs.meta +2 -0
  53. package/Runtime/SceneAuthoring.cs +202 -0
  54. package/Runtime/SceneAuthoring.cs.meta +2 -0
  55. package/Runtime/SceneData.cs +120 -0
  56. package/Runtime/SceneData.cs.meta +2 -0
  57. package/Runtime/SceneWorld.cs +618 -0
  58. package/Runtime/SceneWorld.cs.meta +2 -0
  59. package/Runtime/ThrowHelpers.cs +44 -0
  60. package/Runtime/ThrowHelpers.cs.meta +2 -0
  61. package/package.json +31 -0
@@ -0,0 +1,1103 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using System.Reflection;
5
+ using Delta;
6
+ using Delta.ECS;
7
+ using Delta.ECS.Integration;
8
+ using UnityEditor;
9
+ using UnityEditor.UIElements;
10
+ using UnityEngine;
11
+ using UnityEngine.UIElements;
12
+
13
+ namespace Delta.ECS.Unity.Editor
14
+ {
15
+ internal sealed class SceneEntityInspectorView : IDisposable
16
+ {
17
+ private const string UxmlResourcePath = "Inspector/SceneAuthoringEditor";
18
+ private readonly List<ComponentEntry> _componentEntries = new();
19
+ private readonly List<ComponentEntry> _componentItems = new();
20
+ private readonly List<RuntimeValueBinding> _runtimeBindings = new();
21
+ private readonly Func<EntityInspectorTarget> _getTarget;
22
+ private readonly bool _embedded;
23
+ private EntityInspectorTarget _target;
24
+ private VisualElement _header;
25
+ private VisualElement _footer;
26
+ private VisualElement _root;
27
+ private ListView _componentList;
28
+ private SceneWorld _previewWorld;
29
+ private EntityAuthoring _authoringEntity;
30
+ private ComponentId[] _runtimeComponentBuffer;
31
+ private ComponentId[] _runtimeComponentIds;
32
+ private SceneAuthoring Authoring => _target.Authoring;
33
+
34
+ public SceneEntityInspectorView(SelectionProxy proxy)
35
+ : this(() => proxy != null ? proxy.Target : default)
36
+ {
37
+ HierarchySelection.Changed += OnEntitySelectionChanged;
38
+ }
39
+
40
+ public SceneEntityInspectorView(Func<EntityInspectorTarget> getTarget, bool embedded = false)
41
+ {
42
+ _getTarget = getTarget;
43
+ _embedded = embedded;
44
+ SyncSelection();
45
+ Undo.undoRedoPerformed += RebuildAfterUndo;
46
+ HierarchyMenu.AuthoringChanged += OnAuthoringChanged;
47
+ }
48
+
49
+ public void Dispose()
50
+ {
51
+ HierarchySelection.Changed -= OnEntitySelectionChanged;
52
+ Undo.undoRedoPerformed -= RebuildAfterUndo;
53
+ HierarchyMenu.AuthoringChanged -= OnAuthoringChanged;
54
+ EditorApplication.update -= RefreshRuntimeView;
55
+ _previewWorld?.Dispose();
56
+ _previewWorld = null;
57
+ _runtimeBindings.Clear();
58
+ _runtimeComponentBuffer = null;
59
+ _runtimeComponentIds = null;
60
+ }
61
+
62
+ public VisualElement CreateGUI()
63
+ {
64
+ _root = new VisualElement();
65
+ VisualTreeAsset tree = Resources.Load<VisualTreeAsset>(UxmlResourcePath);
66
+ if (tree == null)
67
+ {
68
+ _root.Add(new HelpBox($"Could not load Resources/{UxmlResourcePath}.uxml.", HelpBoxMessageType.Error));
69
+ return _root;
70
+ }
71
+
72
+ tree.CloneTree(_root);
73
+ if (_embedded)
74
+ {
75
+ _root.AddToClassList("ecs-bound-inspector");
76
+ }
77
+ _header = _root.Q<VisualElement>("inspectorHeader");
78
+ _header.style.fontSize = EditorStyles.label.fontSize;
79
+ _header.style.minHeight = StyleKeyword.Auto;
80
+ _footer = _root.Q<VisualElement>("inspectorFooter");
81
+ _componentList = _root.Q<ListView>("componentList");
82
+ _componentList.SetViewController(new ComponentListController(this));
83
+ _componentList.makeItem = () => new VisualElement();
84
+ _componentList.bindItem = BindComponentItem;
85
+ _componentList.unbindItem = UnbindComponentItem;
86
+ _componentList.itemIndexChanged += OnComponentIndexChanged;
87
+ _componentList.canStartDrag += args => args.id < _componentEntries.Count;
88
+ _root.RegisterCallback<AttachToPanelEvent>(_ =>
89
+ {
90
+ EditorApplication.update -= RefreshRuntimeView;
91
+ EditorApplication.update += RefreshRuntimeView;
92
+ });
93
+ _root.RegisterCallback<DetachFromPanelEvent>(_ => EditorApplication.update -= RefreshRuntimeView);
94
+ RefreshContent();
95
+ return _root;
96
+ }
97
+
98
+ private void OnEntitySelectionChanged()
99
+ {
100
+ SyncSelection();
101
+ RefreshContent();
102
+ }
103
+
104
+ internal int ComponentCount => _componentEntries.Count;
105
+
106
+ internal void ScheduleRefreshContent() => _root.schedule.Execute(RefreshContent);
107
+
108
+ private void OnAuthoringChanged(SceneAuthoring changedAuthoring)
109
+ {
110
+ if (changedAuthoring != Authoring || EditorApplication.isPlayingOrWillChangePlaymode)
111
+ {
112
+ return;
113
+ }
114
+
115
+ RebuildPreviewAndView();
116
+ }
117
+
118
+ private bool SyncSelection(bool rebuildPreview = true)
119
+ {
120
+ EntityInspectorTarget next = _getTarget();
121
+ bool authoringChanged = Authoring != next.Authoring;
122
+ bool targetChanged = authoringChanged
123
+ || !string.Equals(_target.StableId, next.StableId, StringComparison.Ordinal)
124
+ || _target.RuntimeWorld != next.RuntimeWorld
125
+ || _target.RuntimeEntity != next.RuntimeEntity;
126
+
127
+ _target = next;
128
+ if (targetChanged && !next.IsRuntime)
129
+ {
130
+ _runtimeBindings.Clear();
131
+ _runtimeComponentBuffer = null;
132
+ _runtimeComponentIds = null;
133
+ }
134
+
135
+ if (rebuildPreview && (authoringChanged || (next.Authoring != null && _previewWorld == null)))
136
+ {
137
+ RebuildPreviewWorld();
138
+ }
139
+
140
+ return targetChanged;
141
+ }
142
+
143
+ private void RefreshContent()
144
+ {
145
+ if (_header == null || _footer == null || _componentList == null)
146
+ {
147
+ return;
148
+ }
149
+
150
+ _header.Clear();
151
+ _footer.Clear();
152
+ _footer.style.display = _target.IsRuntime ? DisplayStyle.None : DisplayStyle.Flex;
153
+ _componentList.itemsSource = null;
154
+ _componentItems.Clear();
155
+ _componentEntries.Clear();
156
+ _runtimeBindings.Clear();
157
+ _authoringEntity = null;
158
+ if (_target.IsRuntime)
159
+ {
160
+ BuildRuntimeEntity();
161
+ return;
162
+ }
163
+
164
+ if (Authoring == null || string.IsNullOrEmpty(_target.StableId))
165
+ {
166
+ _header.Add(new HelpBox("Select a Delta ECS entity in the Hierarchy to inspect its data.", HelpBoxMessageType.Info));
167
+ return;
168
+ }
169
+
170
+ if (EditorApplication.isPlayingOrWillChangePlaymode)
171
+ {
172
+ _header.Add(new HelpBox("Scene-authored ECS data is read-only in Play Mode. Select a runtime entity in the Hierarchy to inspect live data.", HelpBoxMessageType.Info));
173
+ return;
174
+ }
175
+
176
+ if (_previewWorld == null)
177
+ {
178
+ _header.Add(new HelpBox("Could not create a Delta ECS preview world. See the Console for details.", HelpBoxMessageType.Error));
179
+ return;
180
+ }
181
+
182
+ if (!_previewWorld.TryGetAuthoring(_target.StableId, out EntityAuthoring authoring))
183
+ {
184
+ _header.Add(new HelpBox("The selected ECS entity no longer exists.", HelpBoxMessageType.Warning));
185
+ return;
186
+ }
187
+
188
+ BuildAuthoringEntity(authoring);
189
+ }
190
+
191
+ private void BuildAuthoringEntity(EntityAuthoring authoring)
192
+ {
193
+ _authoringEntity = authoring;
194
+ _header.Add(BuildEntityHeader(authoring));
195
+ _componentEntries.AddRange(ProjectAuthoringComponents(authoring));
196
+ RenderComponentRows();
197
+ var deleteButton = new Button(() => HierarchyMenu.DeleteEntity(Authoring, authoring.StableId))
198
+ {
199
+ text = "Delete Entity"
200
+ };
201
+ deleteButton.AddToClassList("ecs-delete-entity");
202
+ _footer.Add(deleteButton);
203
+ }
204
+
205
+ private VisualElement BuildEntityHeader(EntityAuthoring authoring)
206
+ {
207
+ var card = new VisualElement();
208
+ card.AddToClassList("ecs-entity-header");
209
+
210
+ var titleRow = new VisualElement();
211
+ titleRow.AddToClassList("ecs-entity-title-row");
212
+ titleRow.Add(CreateEntityIcon());
213
+
214
+ var nameField = new TextField { value = authoring.Name, isDelayed = true, label = string.Empty };
215
+ nameField.AddToClassList("ecs-entity-name");
216
+ nameField.RegisterValueChangedCallback(evt =>
217
+ {
218
+ if (string.Equals(evt.previousValue, evt.newValue, StringComparison.Ordinal))
219
+ {
220
+ return;
221
+ }
222
+
223
+ Undo.RecordObject(Authoring, "Rename Delta ECS Entity");
224
+ Authoring.SceneData.RenameEntity(authoring.StableId, evt.newValue);
225
+ MarkSceneDirty();
226
+ HierarchySelection.RenameSelected(Authoring, authoring.StableId);
227
+ });
228
+ titleRow.Add(nameField);
229
+ card.Add(titleRow);
230
+
231
+ _previewWorld.TryGetEntity(authoring, out Entity entity);
232
+ AddEntityInfo(card, entity, authoring.StableId);
233
+ card.Add(BuildAuthoringViewField(Authoring, authoring.StableId));
234
+ return card;
235
+ }
236
+
237
+ private VisualElement BuildAuthoringViewField(SceneAuthoring authoring, string stableId)
238
+ {
239
+ authoring.TryGetView(stableId, out GameObject currentView);
240
+ return BuildViewField(currentView, view =>
241
+ {
242
+ if (view == currentView)
243
+ {
244
+ return;
245
+ }
246
+
247
+ Undo.RecordObject(authoring, "Bind Unity View to Delta ECS Entity");
248
+ if (!authoring.SetView(stableId, view))
249
+ {
250
+ Debug.LogWarning("A Unity view must be a GameObject in the same scene as its Delta ECS entity.", authoring);
251
+ RefreshContent();
252
+ return;
253
+ }
254
+
255
+ MarkSceneDirty();
256
+ HierarchyNodeHandler.RefreshAll();
257
+ });
258
+ }
259
+
260
+ private VisualElement BuildRuntimeViewField(SceneWorld world, Entity entity)
261
+ {
262
+ world.TryGetView(entity, out GameObject currentView);
263
+ return BuildViewField(currentView, view =>
264
+ {
265
+ if (view == currentView)
266
+ {
267
+ return;
268
+ }
269
+
270
+ if (view == null)
271
+ {
272
+ world.UnbindView(entity);
273
+ HierarchyNodeHandler.RefreshAll();
274
+ }
275
+ else if (!world.BindView(entity, view))
276
+ {
277
+ RefreshContent();
278
+ }
279
+ else
280
+ {
281
+ HierarchyNodeHandler.RefreshAll();
282
+ }
283
+ });
284
+ }
285
+
286
+ private static VisualElement BuildViewField(GameObject currentView, Action<GameObject> onChanged)
287
+ {
288
+ var row = new VisualElement();
289
+ row.AddToClassList("ecs-view-binding");
290
+
291
+ var field = new ObjectField("Unity View")
292
+ {
293
+ objectType = typeof(GameObject),
294
+ allowSceneObjects = true,
295
+ value = currentView
296
+ };
297
+ AddInspectorFieldClasses(field);
298
+ row.Add(field);
299
+
300
+ var unbind = new Button(() => field.value = null)
301
+ {
302
+ text = "Unbind",
303
+ tooltip = "Remove the GameObject binding from this entity"
304
+ };
305
+ unbind.SetEnabled(currentView != null);
306
+ field.RegisterValueChangedCallback(evt =>
307
+ {
308
+ GameObject nextView = evt.newValue as GameObject;
309
+ unbind.SetEnabled(nextView != null);
310
+ onChanged(nextView);
311
+ });
312
+ row.Add(unbind);
313
+ return row;
314
+ }
315
+
316
+ private static void AddEntityInfo(VisualElement card, Entity entity, string stableId)
317
+ {
318
+ var idRow = new VisualElement { pickingMode = PickingMode.Ignore };
319
+ idRow.AddToClassList("ecs-entity-id-row");
320
+ idRow.Add(CreateEntityMetadata($"EntityId: {entity.Index}"));
321
+ idRow.Add(CreateEntityMetadata($"Generation: {entity.Generation}"));
322
+ card.Add(idRow);
323
+ card.Add(CreateEntityMetadata($"StableId: {(string.IsNullOrEmpty(stableId) ? "—" : stableId)}"));
324
+ }
325
+
326
+ private static Label CreateEntityMetadata(string text)
327
+ {
328
+ var label = CreateClassLabel(text, "ecs-entity-metadata");
329
+ label.pickingMode = PickingMode.Ignore;
330
+ label.style.minHeight = EditorGUIUtility.singleLineHeight;
331
+ return label;
332
+ }
333
+
334
+ private VisualElement BuildAuthoringComponentCard(EntityAuthoring authoring, ComponentData data, ComponentDescriptor descriptor)
335
+ {
336
+ return BuildComponentCard(descriptor, body =>
337
+ {
338
+ if (!_previewWorld.TryRead(authoring, descriptor, out ComponentSnapshot snapshot, out EcsReadError readError))
339
+ {
340
+ body.Add(new HelpBox($"Could not read component: {readError.Code}.", HelpBoxMessageType.Error));
341
+ return;
342
+ }
343
+
344
+ DrawEditableValue(body, authoring, data, descriptor, snapshot.Value);
345
+ },
346
+ () => ResetComponent(authoring, data, descriptor),
347
+ () => RemoveComponent(authoring, data, descriptor));
348
+ }
349
+
350
+ private VisualElement BuildUnknownComponentCard(EntityAuthoring authoring, ComponentData data)
351
+ {
352
+ ulong? schema = GetSchemaId(data);
353
+ bool expanded = !schema.HasValue || IsComponentExpanded(schema.Value);
354
+ return BuildComponentFoldout("Unregistered component", schema, expanded, body =>
355
+ {
356
+ body.Add(CreateClassLabel($"Schema: {data?.SchemaIdText ?? "<null>"}", "ecs-schema-label"));
357
+ }, header =>
358
+ {
359
+ var actions = CreateComponentMenu();
360
+ actions.menu.AppendAction("Remove Component", _ => RemoveComponent(authoring, data));
361
+ AddHeaderAction(header, actions);
362
+ AddHeaderAction(header, CreateRemoveComponentButton(() => RemoveComponent(authoring, data)));
363
+ });
364
+ }
365
+
366
+ private VisualElement BuildAddComponentMenu(EntityAuthoring authoring)
367
+ {
368
+ var button = new Button { text = "Add Component" };
369
+ button.AddToClassList("ecs-add-component");
370
+ HashSet<ulong> existingSchemas = authoring.Components
371
+ .Select(GetSchemaId)
372
+ .Where(schema => schema.HasValue)
373
+ .Select(schema => schema.Value)
374
+ .ToHashSet();
375
+ ComponentDescriptor[] available = Array.FindAll(
376
+ _previewWorld.Integration.Catalog.Components.ToArray(),
377
+ descriptor => (descriptor.Capabilities & ComponentCapabilities.Write) != 0
378
+ && !existingSchemas.Contains(descriptor.Schema.Value));
379
+ button.SetEnabled(available.Length != 0);
380
+ button.clicked += () => new ComponentDropdown(available, GetComponentMenuPath,
381
+ descriptor => AddComponent(authoring, descriptor))
382
+ .Show(button.worldBound);
383
+ return button;
384
+ }
385
+
386
+ private VisualElement BuildComponentCard(ComponentDescriptor descriptor, Action<VisualElement> buildBody,
387
+ Action resetComponent = null, Action removeComponent = null)
388
+ {
389
+ bool expanded = IsComponentExpanded(descriptor.Schema.Value);
390
+ return BuildComponentFoldout(GetShortTypeName(descriptor.ValueType), descriptor.Schema.Value, expanded, buildBody,
391
+ header => AddComponentActions(header, descriptor, resetComponent, removeComponent));
392
+ }
393
+
394
+ private Foldout BuildComponentFoldout(string title, ulong? schema, bool expanded,
395
+ Action<VisualElement> buildBody, Action<Toggle> buildHeader)
396
+ {
397
+ var foldout = new Foldout { name = "componentFoldout", text = title, value = expanded };
398
+ foldout.AddToClassList("ecs-component-card");
399
+
400
+ Toggle header = foldout.Q<Toggle>(className: Foldout.toggleUssClassName);
401
+ header.AddToClassList("ecs-component-card-header");
402
+ header.Q<Label>(className: Toggle.textUssClassName)?.AddToClassList("ecs-component-title");
403
+
404
+ var body = new VisualElement { name = "componentBody" };
405
+ body.AddToClassList("ecs-component-body");
406
+ foldout.Add(body);
407
+ buildHeader?.Invoke(header);
408
+
409
+ if (expanded)
410
+ {
411
+ buildBody(body);
412
+ }
413
+
414
+ foldout.RegisterValueChangedCallback(evt =>
415
+ {
416
+ if (schema.HasValue)
417
+ {
418
+ SetComponentExpanded(schema.Value, evt.newValue);
419
+ }
420
+
421
+ if (evt.newValue)
422
+ {
423
+ if (body.childCount == 0) buildBody(body);
424
+ }
425
+ else
426
+ {
427
+ body.Clear();
428
+ if (schema.HasValue)
429
+ {
430
+ _runtimeBindings.RemoveAll(binding => binding.Descriptor.Schema.Value == schema.Value);
431
+ }
432
+ }
433
+ });
434
+ return foldout;
435
+ }
436
+
437
+ private void AddComponentActions(Toggle header, ComponentDescriptor descriptor,
438
+ Action resetComponent, Action removeComponent)
439
+ {
440
+ var actions = CreateComponentMenu();
441
+ actions.menu.AppendAction("Move Up", _ => MoveComponent(descriptor.Schema.Value, -1));
442
+ actions.menu.AppendAction("Move Down", _ => MoveComponent(descriptor.Schema.Value, 1));
443
+ if (resetComponent != null || removeComponent != null)
444
+ {
445
+ actions.menu.AppendSeparator();
446
+ if (resetComponent != null) actions.menu.AppendAction("Reset to Default", _ => resetComponent());
447
+ if (removeComponent != null) actions.menu.AppendAction("Remove Component", _ => removeComponent());
448
+ }
449
+
450
+ AddHeaderAction(header, actions);
451
+ if (removeComponent != null)
452
+ {
453
+ AddHeaderAction(header, CreateRemoveComponentButton(removeComponent));
454
+ }
455
+ }
456
+
457
+ private static ToolbarMenu CreateComponentMenu()
458
+ {
459
+ var menu = new ToolbarMenu { text = "⋮" };
460
+ menu.AddToClassList("ecs-component-menu");
461
+ menu.Q<VisualElement>(className: ToolbarMenu.arrowUssClassName)?.AddToClassList("ecs-component-menu-arrow");
462
+ menu.RegisterCallback<AttachToPanelEvent>(_ =>
463
+ menu.Q<VisualElement>(className: ToolbarMenu.arrowUssClassName)?.AddToClassList("ecs-component-menu-arrow"));
464
+ menu.RegisterCallback<PointerDownEvent>(evt => evt.StopPropagation());
465
+ menu.RegisterCallback<ClickEvent>(evt => evt.StopPropagation());
466
+ return menu;
467
+ }
468
+
469
+ private static Button CreateRemoveComponentButton(Action removeComponent)
470
+ {
471
+ var button = new Button(removeComponent) { text = "×", tooltip = "Remove component" };
472
+ button.AddToClassList("ecs-component-remove");
473
+ button.RegisterCallback<PointerDownEvent>(evt => evt.StopPropagation());
474
+ button.RegisterCallback<ClickEvent>(evt => evt.StopPropagation());
475
+ return button;
476
+ }
477
+
478
+ private static void AddHeaderAction(Toggle header, VisualElement action)
479
+ {
480
+ header.Add(action);
481
+ }
482
+
483
+ private void DrawEditableValue(VisualElement body, EntityAuthoring authoring, ComponentData data,
484
+ ComponentDescriptor descriptor, object value)
485
+ {
486
+ FieldInfo[] fields = GetComponentFields(descriptor.ValueType);
487
+ if (fields.Length == 0)
488
+ {
489
+ body.Add(CreateClassLabel("Component has no public fields.", "ecs-empty-component"));
490
+ return;
491
+ }
492
+
493
+ for (int i = 0; i < fields.Length; i++)
494
+ {
495
+ FieldInfo field = fields[i];
496
+ object current = field.GetValue(value);
497
+ VisualElement control = CreateFieldControl(field, current, next =>
498
+ {
499
+ if (Equals(current, next)) return;
500
+ Undo.RecordObject(Authoring, $"Edit {descriptor.Name}");
501
+ field.SetValue(value, next);
502
+ if (_previewWorld.TryWrite(authoring, data, descriptor, value, out EcsWriteError writeError))
503
+ {
504
+ current = next;
505
+ MarkSceneDirty();
506
+ }
507
+ else
508
+ {
509
+ Debug.LogError($"Could not write {descriptor.Name}: {writeError.Code}.", Authoring);
510
+ RefreshContent();
511
+ }
512
+ });
513
+ body.Add(control);
514
+ }
515
+ }
516
+
517
+ private VisualElement CreateFieldControl(FieldInfo field, object value, Action<object> onChanged)
518
+ {
519
+ string label = ObjectNames.NicifyVariableName(field.Name);
520
+ Type type = field.FieldType;
521
+ if (type == typeof(float))
522
+ {
523
+ var fieldControl = new FloatField(label) { value = (float)value, isDelayed = true };
524
+ AddInspectorFieldClasses(fieldControl);
525
+ fieldControl.RegisterValueChangedCallback(evt => onChanged(evt.newValue));
526
+ return fieldControl;
527
+ }
528
+
529
+ if (type == typeof(byte) || type == typeof(int))
530
+ {
531
+ var fieldControl = new IntegerField(label) { value = Convert.ToInt32(value), isDelayed = true };
532
+ AddInspectorFieldClasses(fieldControl);
533
+ fieldControl.RegisterValueChangedCallback(evt => onChanged(type == typeof(byte)
534
+ ? (object)(byte)Mathf.Clamp(evt.newValue, byte.MinValue, byte.MaxValue)
535
+ : evt.newValue));
536
+ return fieldControl;
537
+ }
538
+
539
+ if (type == typeof(bool))
540
+ {
541
+ var fieldControl = new Toggle(label) { value = (bool)value };
542
+ AddInspectorFieldClasses(fieldControl);
543
+ fieldControl.RegisterValueChangedCallback(evt => onChanged(evt.newValue));
544
+ return fieldControl;
545
+ }
546
+
547
+ if (type == typeof(float3))
548
+ {
549
+ float3 vector = (float3)value;
550
+ var fieldControl = new Vector3Field(label) { value = new Vector3(vector.x, vector.y, vector.z) };
551
+ AddInspectorFieldClasses(fieldControl);
552
+ fieldControl.RegisterValueChangedCallback(evt => onChanged(new float3(evt.newValue.x, evt.newValue.y, evt.newValue.z)));
553
+ return fieldControl;
554
+ }
555
+
556
+ if (type == typeof(Entity))
557
+ {
558
+ return CreateEntityReferenceField(label, (Entity)value, onChanged);
559
+ }
560
+
561
+ if (type.IsEnum)
562
+ {
563
+ var fieldControl = new EnumField(label, (Enum)value);
564
+ AddInspectorFieldClasses(fieldControl);
565
+ fieldControl.RegisterValueChangedCallback(evt => onChanged(evt.newValue));
566
+ return fieldControl;
567
+ }
568
+
569
+ var unsupported = new Label($"{label} · Unsupported: {type.Name}");
570
+ unsupported.AddToClassList("ecs-unsupported-field");
571
+ return unsupported;
572
+ }
573
+
574
+ private VisualElement CreateEntityReferenceField(string label, Entity current, Action<object> onChanged)
575
+ {
576
+ EntityAuthoring[] entities = Authoring.SceneData.Entities.ToArray();
577
+ string[] names = Array.ConvertAll(entities, entity => entity?.Name ?? "<missing entity>");
578
+ string[] stableIds = Array.ConvertAll(entities, entity => entity?.StableId ?? string.Empty);
579
+ HashSet<string> duplicateNames = names
580
+ .GroupBy(name => name, StringComparer.Ordinal)
581
+ .Where(group => group.Count() > 1)
582
+ .Select(group => group.Key)
583
+ .ToHashSet(StringComparer.Ordinal);
584
+
585
+ _previewWorld.TryGetStableId(current, out string currentId);
586
+ string[] ids = new[] { string.Empty }.Concat(stableIds).ToArray();
587
+ List<string> options = names
588
+ .Select((name, index) => duplicateNames.Contains(name) && entities[index] != null
589
+ ? $"{name} ({stableIds[index].Substring(0, Math.Min(8, stableIds[index].Length))})"
590
+ : name)
591
+ .Prepend("None")
592
+ .ToList();
593
+ int selected = Mathf.Max(0, Array.IndexOf(ids, currentId ?? string.Empty));
594
+
595
+ var fieldControl = new DropdownField(label, options, selected);
596
+ AddInspectorFieldClasses(fieldControl);
597
+ fieldControl.RegisterValueChangedCallback(evt =>
598
+ {
599
+ int index = options.IndexOf(evt.newValue);
600
+ Entity next = index > 0 && index < ids.Length ? _previewWorld.ResolveEntity(ids[index]) : default;
601
+ onChanged(next);
602
+ });
603
+ return fieldControl;
604
+ }
605
+
606
+ private static void AddInspectorFieldClasses(VisualElement field)
607
+ {
608
+ field.AddToClassList(BaseField<float>.alignedFieldUssClassName);
609
+ field.AddToClassList("ecs-value-field");
610
+ }
611
+
612
+ private void BuildRuntimeEntity()
613
+ {
614
+ _runtimeBindings.Clear();
615
+ SceneWorld world = _target.RuntimeWorld;
616
+ Entity entity = _target.RuntimeEntity;
617
+ if (world == null || !world.IsBound || !world.Integration.IsAlive(entity))
618
+ {
619
+ _header.Add(new HelpBox("The selected runtime entity is no longer alive.", HelpBoxMessageType.Warning));
620
+ return;
621
+ }
622
+
623
+ _header.Add(BuildRuntimeHeader(world, entity));
624
+ ComponentDescriptor[] components = ProjectRuntimeComponents(world, entity);
625
+ if (components == null)
626
+ {
627
+ _header.Add(new HelpBox("Could not read the selected entity's components.", HelpBoxMessageType.Error));
628
+ return;
629
+ }
630
+
631
+ foreach (ComponentDescriptor descriptor in components)
632
+ {
633
+ _componentEntries.Add(new ComponentEntry(null, descriptor));
634
+ }
635
+
636
+ RenderComponentRows();
637
+ }
638
+
639
+ private VisualElement BuildRuntimeComponentCard(SceneWorld world, Entity entity, ComponentDescriptor descriptor)
640
+ {
641
+ return BuildComponentCard(descriptor, body =>
642
+ {
643
+ if (!world.Integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out EcsReadError error))
644
+ {
645
+ body.Add(new HelpBox($"Could not read component: {error.Code}.", HelpBoxMessageType.Error));
646
+ return;
647
+ }
648
+
649
+ DrawReadOnlyValue(body, descriptor, snapshot.Value, world);
650
+ });
651
+ }
652
+
653
+ private void RenderComponentRows()
654
+ {
655
+ _componentItems.Clear();
656
+ _componentItems.AddRange(_componentEntries);
657
+ if (_authoringEntity != null) _componentItems.Add(default);
658
+ _componentList.itemsSource = _componentItems;
659
+ _componentList.Rebuild();
660
+ }
661
+
662
+ private void BindComponentItem(VisualElement row, int index)
663
+ {
664
+ row.Clear();
665
+ bool isAddButton = _authoringEntity != null && index == _componentEntries.Count;
666
+ row.parent.parent.EnableInClassList("ecs-add-component-row", isAddButton);
667
+ row.parent.parent.EnableInClassList("ecs-component-last-bound-row",
668
+ _embedded && _target.IsRuntime && index == _componentEntries.Count - 1);
669
+ row.EnableInClassList("ecs-component-row", !isAddButton);
670
+ if (isAddButton)
671
+ {
672
+ row.userData = null;
673
+ row.Add(BuildAddComponentMenu(_authoringEntity));
674
+ return;
675
+ }
676
+
677
+ ComponentEntry entry = _componentItems[index];
678
+ row.userData = entry;
679
+ row.Add(_target.IsRuntime
680
+ ? BuildRuntimeComponentCard(_target.RuntimeWorld, _target.RuntimeEntity, entry.Descriptor.Value)
681
+ : entry.Descriptor.HasValue
682
+ ? BuildAuthoringComponentCard(_authoringEntity, entry.Data, entry.Descriptor.Value)
683
+ : BuildUnknownComponentCard(_authoringEntity, entry.Data));
684
+ }
685
+
686
+ private void UnbindComponentItem(VisualElement row, int index)
687
+ {
688
+ if (row.userData is ComponentEntry entry && entry.Descriptor.HasValue)
689
+ _runtimeBindings.RemoveAll(binding => binding.Descriptor.Schema == entry.Descriptor.Value.Schema);
690
+ row.userData = null;
691
+ row.Clear();
692
+ }
693
+
694
+ private void OnComponentIndexChanged(int from, int to)
695
+ {
696
+ int componentCount = _componentEntries.Count;
697
+ _componentEntries.Clear();
698
+ _componentEntries.AddRange(_componentItems.Take(componentCount));
699
+ PersistVisibleComponentOrder();
700
+ }
701
+
702
+ private void PersistVisibleComponentOrder()
703
+ {
704
+ InspectorState state = InspectorState.instance;
705
+ List<ulong> componentOrder = state.GetComponentOrderSnapshot();
706
+
707
+ ulong[] visibleOrder = _componentEntries
708
+ .Select(entry => entry.Descriptor.HasValue
709
+ ? (ulong?)entry.Descriptor.Value.Schema.Value
710
+ : GetSchemaId(entry.Data))
711
+ .Where(schema => schema.HasValue)
712
+ .Select(schema => schema.Value)
713
+ .Distinct()
714
+ .ToArray();
715
+ HashSet<ulong> visibleSchemas = visibleOrder.ToHashSet();
716
+
717
+ IEnumerable<ComponentDescriptor> catalog = _previewWorld != null
718
+ ? _previewWorld.Integration.Catalog.Components.ToArray()
719
+ : _target.RuntimeWorld != null && _target.RuntimeWorld.IsBound
720
+ ? _target.RuntimeWorld.Integration.Catalog.Components.ToArray()
721
+ : Array.Empty<ComponentDescriptor>();
722
+ foreach (ComponentDescriptor descriptor in catalog
723
+ .OrderBy(descriptor => GetShortTypeName(descriptor.ValueType), StringComparer.Ordinal))
724
+ {
725
+ if (!componentOrder.Contains(descriptor.Schema.Value))
726
+ {
727
+ componentOrder.Add(descriptor.Schema.Value);
728
+ }
729
+ }
730
+
731
+ foreach (ulong schema in visibleOrder)
732
+ {
733
+ if (!componentOrder.Contains(schema))
734
+ {
735
+ componentOrder.Add(schema);
736
+ }
737
+ }
738
+
739
+ int nextVisible = 0;
740
+ for (int i = 0; i < componentOrder.Count && nextVisible < visibleOrder.Length; i++)
741
+ {
742
+ if (visibleSchemas.Contains(componentOrder[i]))
743
+ {
744
+ componentOrder[i] = visibleOrder[nextVisible++];
745
+ }
746
+ }
747
+
748
+ state.SetComponentOrder(componentOrder);
749
+ }
750
+
751
+ private ComponentDescriptor[] ProjectRuntimeComponents(SceneWorld world, Entity entity)
752
+ {
753
+ ReadOnlyMemory<ComponentDescriptor> catalog = world.Integration.Catalog.Components;
754
+ _runtimeComponentBuffer = new ComponentId[catalog.Length];
755
+ if (!world.Integration.TryGetComponents(entity, _runtimeComponentBuffer, out int count))
756
+ {
757
+ _runtimeComponentIds = null;
758
+ return null;
759
+ }
760
+
761
+ _runtimeComponentIds = new ComponentId[count];
762
+ Array.Copy(_runtimeComponentBuffer, _runtimeComponentIds, count);
763
+ Dictionary<ComponentId, ComponentDescriptor> descriptorsById = catalog.ToArray()
764
+ .ToDictionary(descriptor => descriptor.Id);
765
+
766
+ return OrderComponentDescriptors(_runtimeComponentIds
767
+ .Where(descriptorsById.ContainsKey)
768
+ .Select(id => descriptorsById[id]));
769
+ }
770
+
771
+ private ComponentDescriptor[] OrderComponentDescriptors(IEnumerable<ComponentDescriptor> descriptors)
772
+ {
773
+ return descriptors
774
+ .OrderBy(descriptor => GetComponentOrder(descriptor.Schema.Value))
775
+ .ThenBy(descriptor => GetShortTypeName(descriptor.ValueType), StringComparer.Ordinal)
776
+ .ToArray();
777
+ }
778
+
779
+ private VisualElement BuildRuntimeHeader(SceneWorld world, Entity entity)
780
+ {
781
+ var card = new VisualElement();
782
+ card.AddToClassList("ecs-entity-header");
783
+ var titleRow = new VisualElement();
784
+ titleRow.AddToClassList("ecs-entity-title-row");
785
+ titleRow.Add(CreateEntityIcon());
786
+ bool hasStableId = world.TryGetStableId(entity, out string stableId);
787
+ string name = hasStableId && world.TryGetAuthoring(stableId, out EntityAuthoring authoring)
788
+ ? authoring.Name
789
+ : $"Entity {entity}";
790
+ titleRow.Add(CreateClassLabel(name, "ecs-runtime-entity-name"));
791
+ card.Add(titleRow);
792
+ AddEntityInfo(card, entity, stableId);
793
+ card.Add(BuildRuntimeViewField(world, entity));
794
+ return card;
795
+ }
796
+
797
+ private void DrawReadOnlyValue(VisualElement body, ComponentDescriptor descriptor, object value, SceneWorld world)
798
+ {
799
+ FieldInfo[] fields = GetComponentFields(descriptor.ValueType);
800
+ if (fields.Length == 0)
801
+ {
802
+ body.Add(CreateClassLabel("Component has no public fields.", "ecs-empty-component"));
803
+ return;
804
+ }
805
+
806
+ var labels = new TextField[fields.Length];
807
+ for (int i = 0; i < fields.Length; i++)
808
+ {
809
+ FieldInfo field = fields[i];
810
+ object fieldValue = field.GetValue(value);
811
+ string display = FormatValue(field.FieldType, fieldValue, world);
812
+ labels[i] = new TextField(ObjectNames.NicifyVariableName(field.Name))
813
+ {
814
+ value = display,
815
+ isReadOnly = true
816
+ };
817
+ AddInspectorFieldClasses(labels[i]);
818
+ body.Add(labels[i]);
819
+ }
820
+
821
+ _runtimeBindings.Add(new RuntimeValueBinding(descriptor, fields, labels));
822
+ }
823
+
824
+ private string FormatValue(Type type, object value, SceneWorld world)
825
+ {
826
+ if (type == typeof(Entity) && value is Entity entity)
827
+ {
828
+ if (!entity.IsValid) return "None";
829
+ if (world.TryGetStableId(entity, out string stableId) && world.TryGetAuthoring(stableId, out EntityAuthoring authoring))
830
+ {
831
+ return authoring.Name;
832
+ }
833
+
834
+ return entity.ToString();
835
+ }
836
+
837
+ if (value is float3 vector)
838
+ {
839
+ return $"({vector.x:G}, {vector.y:G}, {vector.z:G})";
840
+ }
841
+
842
+ return value?.ToString() ?? "None";
843
+ }
844
+
845
+ private static FieldInfo[] GetComponentFields(Type type) =>
846
+ type.GetFields(BindingFlags.Instance | BindingFlags.Public);
847
+
848
+ private static Label CreateClassLabel(string text, string className)
849
+ {
850
+ var label = new Label(text);
851
+ label.AddToClassList(className);
852
+ return label;
853
+ }
854
+
855
+ private static Label CreateEntityIcon() => CreateClassLabel("◆", "ecs-entity-icon");
856
+
857
+ private void RefreshRuntimeView()
858
+ {
859
+ if (SyncSelection())
860
+ {
861
+ RefreshContent();
862
+ return;
863
+ }
864
+ if (!_target.IsRuntime || !EditorApplication.isPlaying || _header == null || _componentList == null)
865
+ {
866
+ return;
867
+ }
868
+
869
+ SceneWorld world = _target.RuntimeWorld;
870
+ Entity entity = _target.RuntimeEntity;
871
+ if (world == null || !world.IsBound || !world.Integration.IsAlive(entity))
872
+ {
873
+ if (_runtimeComponentIds != null)
874
+ {
875
+ _runtimeComponentIds = null;
876
+ _runtimeComponentBuffer = null;
877
+ _runtimeBindings.Clear();
878
+ RefreshContent();
879
+ }
880
+
881
+ return;
882
+ }
883
+
884
+ if (_runtimeComponentBuffer == null || _runtimeComponentIds == null)
885
+ {
886
+ RefreshContent();
887
+ return;
888
+ }
889
+
890
+ if (!world.Integration.TryGetComponents(entity, _runtimeComponentBuffer, out int count)
891
+ || count != _runtimeComponentIds.Length)
892
+ {
893
+ RefreshContent();
894
+ return;
895
+ }
896
+
897
+ for (int i = 0; i < count; i++)
898
+ {
899
+ if (_runtimeComponentBuffer[i] != _runtimeComponentIds[i])
900
+ {
901
+ RefreshContent();
902
+ return;
903
+ }
904
+ }
905
+
906
+ for (int i = 0; i < _runtimeBindings.Count; i++)
907
+ {
908
+ RuntimeValueBinding binding = _runtimeBindings[i];
909
+ if (!world.Integration.TryRead(entity, binding.Descriptor.Id, out ComponentSnapshot snapshot, out _))
910
+ {
911
+ continue;
912
+ }
913
+
914
+ for (int fieldIndex = 0; fieldIndex < binding.Fields.Length; fieldIndex++)
915
+ {
916
+ FieldInfo field = binding.Fields[fieldIndex];
917
+ string text = FormatValue(field.FieldType, field.GetValue(snapshot.Value), world);
918
+ if (!string.Equals(binding.Labels[fieldIndex].value, text, StringComparison.Ordinal))
919
+ {
920
+ binding.Labels[fieldIndex].SetValueWithoutNotify(text);
921
+ }
922
+ }
923
+ }
924
+ }
925
+
926
+ private ComponentEntry[] ProjectAuthoringComponents(EntityAuthoring authoring)
927
+ {
928
+ Dictionary<ulong, ComponentDescriptor> descriptors = _previewWorld.Integration.Catalog.Components
929
+ .ToArray()
930
+ .ToDictionary(descriptor => descriptor.Schema.Value);
931
+
932
+ return authoring.Components
933
+ .Select(data => new ComponentEntry(data, TryGetDescriptor(data, descriptors)))
934
+ .OrderBy(entry => entry.Descriptor.HasValue ? GetComponentOrder(entry.Descriptor.Value.Schema.Value) : int.MaxValue)
935
+ .ThenBy(entry => entry.Descriptor.HasValue
936
+ ? GetShortTypeName(entry.Descriptor.Value.ValueType)
937
+ : entry.Data?.SchemaIdText ?? string.Empty, StringComparer.Ordinal)
938
+ .ToArray();
939
+ }
940
+
941
+ private static ComponentDescriptor? TryGetDescriptor(ComponentData data,
942
+ IReadOnlyDictionary<ulong, ComponentDescriptor> descriptors)
943
+ {
944
+ ulong? schema = GetSchemaId(data);
945
+ return schema.HasValue && descriptors.TryGetValue(schema.Value, out ComponentDescriptor descriptor)
946
+ ? descriptor
947
+ : null;
948
+ }
949
+
950
+ private static string GetComponentMenuPath(ComponentDescriptor descriptor)
951
+ {
952
+ string namespacePath = descriptor.ValueType.Namespace?.Replace('.', '/') ?? string.Empty;
953
+ string shortName = GetShortTypeName(descriptor.ValueType);
954
+ return string.IsNullOrEmpty(namespacePath) ? shortName : $"{namespacePath}/{shortName}";
955
+ }
956
+
957
+ private static string GetShortTypeName(Type type)
958
+ {
959
+ string name = type.Name;
960
+ int genericMarker = name.IndexOf('`');
961
+ return genericMarker < 0 ? name : name.Substring(0, genericMarker);
962
+ }
963
+
964
+ private void AddComponent(EntityAuthoring authoring, ComponentDescriptor descriptor)
965
+ {
966
+ Undo.RecordObject(Authoring, $"Add {GetShortTypeName(descriptor.ValueType)}");
967
+ ComponentData data = ComponentData.CreateDefault(descriptor);
968
+ if (!_previewWorld.AddComponent(authoring, descriptor, data, out EcsWriteError error, out string failureReason))
969
+ {
970
+ Debug.LogError($"Could not add {GetShortTypeName(descriptor.ValueType)}: {failureReason ?? error.Code.ToString()}.", Authoring);
971
+ RebuildPreviewAndView();
972
+ return;
973
+ }
974
+
975
+ authoring.Components.Add(data);
976
+ MarkSceneDirty();
977
+ RebuildPreviewAndView();
978
+ }
979
+
980
+ private void RemoveComponent(EntityAuthoring authoring, ComponentData data,
981
+ ComponentDescriptor? descriptor = null)
982
+ {
983
+ int index = authoring.Components.IndexOf(data);
984
+ if (index < 0 || index >= authoring.Components.Count) return;
985
+ if (!descriptor.HasValue && _previewWorld.TryGetDescriptor(data, out ComponentDescriptor resolved))
986
+ {
987
+ descriptor = resolved;
988
+ }
989
+
990
+ if (!descriptor.HasValue)
991
+ {
992
+ Undo.RecordObject(Authoring, "Remove Unregistered Delta ECS Component");
993
+ authoring.Components.RemoveAt(index);
994
+ MarkSceneDirty();
995
+ RebuildPreviewAndView();
996
+ return;
997
+ }
998
+
999
+ ComponentDescriptor component = descriptor.Value;
1000
+ Undo.RecordObject(Authoring, $"Remove {GetShortTypeName(component.ValueType)}");
1001
+ if (!_previewWorld.RemoveComponent(authoring, component, out EcsWriteError error))
1002
+ {
1003
+ Debug.LogError($"Could not remove {GetShortTypeName(component.ValueType)}: {error.Code}.", Authoring);
1004
+ return;
1005
+ }
1006
+
1007
+ authoring.Components.RemoveAt(index);
1008
+ MarkSceneDirty();
1009
+ RebuildPreviewAndView();
1010
+ }
1011
+
1012
+ private void ResetComponent(EntityAuthoring authoring, ComponentData data, ComponentDescriptor descriptor)
1013
+ {
1014
+ Undo.RecordObject(Authoring, $"Reset {GetShortTypeName(descriptor.ValueType)}");
1015
+ object value = Activator.CreateInstance(descriptor.ValueType);
1016
+ if (_previewWorld.TryWrite(authoring, data, descriptor, value, out EcsWriteError error)) MarkSceneDirty();
1017
+ else Debug.LogError($"Could not reset {GetShortTypeName(descriptor.ValueType)}: {error.Code}.", Authoring);
1018
+ RefreshContent();
1019
+ }
1020
+
1021
+ private static ulong? GetSchemaId(ComponentData data)
1022
+ {
1023
+ return data != null && data.TryGetSchemaId(out SchemaId schema) ? schema.Value : null;
1024
+ }
1025
+
1026
+ private static bool IsComponentExpanded(ulong schema) =>
1027
+ InspectorState.instance.IsComponentExpanded(schema);
1028
+
1029
+ private static void SetComponentExpanded(ulong schema, bool expanded) =>
1030
+ InspectorState.instance.SetComponentExpanded(schema, expanded);
1031
+
1032
+ private static int GetComponentOrder(ulong schema) =>
1033
+ InspectorState.instance.GetComponentOrder(schema);
1034
+
1035
+ private void MoveComponent(ulong schema, int direction)
1036
+ {
1037
+ int index = _componentEntries.FindIndex(entry => entry.Descriptor?.Schema.Value == schema);
1038
+ int next = index + direction;
1039
+ if (index < 0 || next < 0 || next >= _componentEntries.Count) return;
1040
+ _componentList.viewController.Move(index, next);
1041
+ }
1042
+
1043
+ private void RebuildAfterUndo()
1044
+ {
1045
+ SyncSelection(rebuildPreview: false);
1046
+ RebuildPreviewWorld();
1047
+ HierarchyNodeHandler.RefreshAll();
1048
+ RefreshContent();
1049
+ }
1050
+
1051
+ private void RebuildPreviewWorld()
1052
+ {
1053
+ _previewWorld?.Dispose();
1054
+ _previewWorld = null;
1055
+ if (Authoring == null || EditorApplication.isPlayingOrWillChangePlaymode) return;
1056
+
1057
+ try
1058
+ {
1059
+ _previewWorld = SceneWorld.CreateAuthoringPreview(Authoring);
1060
+ }
1061
+ catch (Exception exception)
1062
+ {
1063
+ Debug.LogException(exception, Authoring);
1064
+ }
1065
+ }
1066
+
1067
+ private void RebuildPreviewAndView()
1068
+ {
1069
+ RebuildPreviewWorld();
1070
+ RefreshContent();
1071
+ }
1072
+
1073
+ private void MarkSceneDirty()
1074
+ {
1075
+ HierarchyMenu.MarkSceneDirty(Authoring);
1076
+ }
1077
+
1078
+ private readonly struct ComponentEntry
1079
+ {
1080
+ public readonly ComponentData Data;
1081
+ public readonly ComponentDescriptor? Descriptor;
1082
+ public ComponentEntry(ComponentData data, ComponentDescriptor? descriptor)
1083
+ {
1084
+ Data = data;
1085
+ Descriptor = descriptor;
1086
+ }
1087
+ }
1088
+
1089
+ private readonly struct RuntimeValueBinding
1090
+ {
1091
+ public readonly ComponentDescriptor Descriptor;
1092
+ public readonly FieldInfo[] Fields;
1093
+ public readonly TextField[] Labels;
1094
+
1095
+ public RuntimeValueBinding(ComponentDescriptor descriptor, FieldInfo[] fields, TextField[] labels)
1096
+ {
1097
+ Descriptor = descriptor;
1098
+ Fields = fields;
1099
+ Labels = labels;
1100
+ }
1101
+ }
1102
+ }
1103
+ }