From ff03866c8a9f79ee39bcc042b4d80ddc0e7e0eb0 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 14 Sep 2026 14:38:24 +0200 Subject: [PATCH] Take record creation and type matching out of the Studio window The window decided whether a record type could be added (GetConstructor(Type.EmptyTypes)), made it with Activator.CreateInstance, and matched records to a type with IsAssignableFrom twice, duplicating RecordCandidates. Add and Create now ask the model's CreateElement, and its notice is the refusal. The record list and the per-type counts come from RecordCandidates. The window only renders. Co-Authored-By: Claude Opus 5 --- .../Caching/Editor/CultCacheStudioWindow.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs b/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs index a3dc49a4..5e6474dc 100644 --- a/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs +++ b/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs @@ -110,7 +110,6 @@ namespace GameCult.Unity.Caching.Editor using (new EditorGUILayout.VerticalScope(GUILayout.Width(240))) { _search = EditorGUILayout.TextField(_search, EditorStyles.toolbarSearchField); - var counts = _records.GroupBy(r => r.Descriptor.DocumentType).ToDictionary(g => g.Key, g => g.Count()); _typeScroll = EditorGUILayout.BeginScrollView(_typeScroll); foreach (var descriptor in _cache.Registry.AllDescriptors) { @@ -120,7 +119,7 @@ namespace GameCult.Unity.Caching.Editor type.Name.IndexOf(_search, StringComparison.OrdinalIgnoreCase) < 0) continue; - var count = counts.Where(c => type.IsAssignableFrom(c.Key)).Sum(c => c.Value); + var count = _model.RecordCandidates(typeof(CultRecordRef<>).MakeGenericType(type), _records).Count; var label = descriptor.SchemaName + (descriptor.IsGlobal ? count == 0 ? " (absent)" : " (global)" : " (" + count + ")"); if (GUILayout.Toggle(_selectedType == type, label, EditorStyles.miniButton) && _selectedType != type) { @@ -139,10 +138,9 @@ namespace GameCult.Unity.Caching.Editor { var descriptor = _selectedType == null ? null : _cache.Registry.GetRequired(_selectedType); var selected = Selected(); - var constructible = _selectedType != null && _selectedType.GetConstructor(Type.EmptyTypes) != null; using (new EditorGUILayout.HorizontalScope()) { - using (new EditorGUI.DisabledScope(descriptor == null || descriptor.IsGlobal || ReadOnly || !constructible)) + using (new EditorGUI.DisabledScope(descriptor == null || descriptor.IsGlobal || ReadOnly)) { if (GUILayout.Button("Add", EditorStyles.miniButtonLeft)) Add(); } @@ -165,24 +163,22 @@ namespace GameCult.Unity.Caching.Editor } else { - var records = _records.Where(r => _selectedType.IsAssignableFrom(r.Descriptor.DocumentType)) - .Select(r => (Record: r, Label: CultInspectorModel.RecordLabel(r))) - .OrderBy(r => r.Label, StringComparer.OrdinalIgnoreCase) - .ToArray(); - if (descriptor.IsGlobal && records.Length == 0) + var records = _model.RecordCandidates(typeof(CultRecordRef<>).MakeGenericType(_selectedType), _records); + if (descriptor.IsGlobal && records.Count == 0) { using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.LabelField("Global is absent."); - using (new EditorGUI.DisabledScope(ReadOnly || !constructible)) + using (new EditorGUI.DisabledScope(ReadOnly)) { if (GUILayout.Button("Create", GUILayout.Width(56))) Add(); } } } - foreach (var (record, label) in records) + foreach (var record in records) { + var label = CultInspectorModel.RecordLabel(record); var text = record.Descriptor.DocumentType == _selectedType ? label : label + " <" + record.Descriptor.DocumentType.Name + ">"; if (GUILayout.Toggle(_selectedKey == record.Key.Value, text, EditorStyles.miniButton)) _selectedKey = record.Key.Value; } @@ -343,7 +339,9 @@ namespace GameCult.Unity.Caching.Editor private void Add() { var type = _selectedType; - Run("Added " + type.Name + ".", () => _selectedKey = _cache.UpsertAsync(type, Activator.CreateInstance(type)).GetAwaiter().GetResult().Value); + // The model decides whether the type can be made; its notice is the refusal. + Run("Added " + type.Name + ".", () => _selectedKey = _cache.UpsertAsync(type, + _model.CreateElement(type, type, out var notice) ?? throw new InvalidOperationException(notice)).GetAwaiter().GetResult().Value); GUIUtility.ExitGUI(); }