Disable Studio Add for record types the model cannot create
The window lost its disabled state when record creation moved into the model: Add and Create were enabled for abstract types and threw on click, and CreateElement(type, type) on an abstract [Union] type threw "X is not a choice for X". CultInspectorModel.CanCreate is now the one rule CreateDefault and the window both read, a slot's own type is always a choice, and an abstract one returns the "abstract or no parameterless constructor" notice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
bddec0fe4d
commit
baa8de0baf
3 changed files with 20 additions and 10 deletions
|
|
@ -345,14 +345,19 @@ namespace GameCult.Caching
|
|||
return new CultInspectorEdit(record, Clone(record.Document, record.Descriptor.DocumentType));
|
||||
}
|
||||
|
||||
// Whether CreateDefault makes a value: a string, struct, list or dictionary, or a concrete class with a parameterless constructor.
|
||||
public bool CanCreate(Type type) =>
|
||||
type == typeof(string) || type.IsValueType ||
|
||||
ShapeOf(type).Kind is CultInspectorValueKind.List or CultInspectorValueKind.Dictionary ||
|
||||
!type.IsAbstract && !type.IsInterface && type.GetConstructor(Type.EmptyTypes) != null;
|
||||
|
||||
public object? CreateDefault(Type type)
|
||||
{
|
||||
if (!CanCreate(type)) return null;
|
||||
if (type == typeof(string)) return string.Empty;
|
||||
if (type.IsValueType) return Activator.CreateInstance(type);
|
||||
var shape = ShapeOf(type);
|
||||
if (shape.Kind == CultInspectorValueKind.List && type.IsArray) return Array.CreateInstance(shape.ElementType!, 0);
|
||||
if (shape.Kind == CultInspectorValueKind.Dictionary) return Activator.CreateInstance(shape.BuildType!);
|
||||
return type.IsAbstract || type.IsInterface || type.GetConstructor(Type.EmptyTypes) == null ? null : Activator.CreateInstance(type);
|
||||
if (type.IsArray) return Array.CreateInstance(shape.ElementType!, 0);
|
||||
return Activator.CreateInstance(shape.Kind == CultInspectorValueKind.Dictionary ? shape.BuildType! : type);
|
||||
}
|
||||
|
||||
// What a new value for a slot of elementType (a list element, a dictionary value, a union pick) may be: a union's
|
||||
|
|
@ -366,11 +371,10 @@ namespace GameCult.Caching
|
|||
// A new value of choice for a slot of elementType, or null with a notice when choice cannot be made.
|
||||
public object? CreateElement(Type elementType, Type choice, out string? notice)
|
||||
{
|
||||
if (!ElementChoices(elementType).Contains(choice))
|
||||
// A slot's own type is always offered; an abstract one is refused with the notice below.
|
||||
if (choice != elementType && !ElementChoices(elementType).Contains(choice))
|
||||
throw new ArgumentException($"{choice.Name} is not a choice for {elementType.Name}.", nameof(choice));
|
||||
var created = choice == elementType ? CreateDefault(choice)
|
||||
: choice.GetConstructor(Type.EmptyTypes) == null ? null
|
||||
: Activator.CreateInstance(choice);
|
||||
var created = CreateDefault(choice);
|
||||
// Null is a value only for a Nullable<T> slot; anywhere else it is nothing made.
|
||||
notice = created == null && !choice.IsValueType
|
||||
? $"{choice.Name} is abstract or has no parameterless constructor; nothing was created."
|
||||
|
|
|
|||
|
|
@ -138,9 +138,10 @@ namespace GameCult.Unity.Caching.Editor
|
|||
{
|
||||
var descriptor = _selectedType == null ? null : _cache.Registry.GetRequired(_selectedType);
|
||||
var selected = Selected();
|
||||
var cannotAdd = descriptor == null || ReadOnly || !_model.CanCreate(_selectedType);
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(descriptor == null || descriptor.IsGlobal || ReadOnly))
|
||||
using (new EditorGUI.DisabledScope(cannotAdd || descriptor.IsGlobal))
|
||||
{
|
||||
if (GUILayout.Button("Add", EditorStyles.miniButtonLeft)) Add();
|
||||
}
|
||||
|
|
@ -169,7 +170,7 @@ namespace GameCult.Unity.Caching.Editor
|
|||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.LabelField("Global is absent.");
|
||||
using (new EditorGUI.DisabledScope(ReadOnly))
|
||||
using (new EditorGUI.DisabledScope(cannotAdd))
|
||||
{
|
||||
if (GUILayout.Button("Create", GUILayout.Width(56))) Add();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ namespace GameCult.Caching.Tests
|
|||
Assert.That(bare, Does.Contain("parameterless"), "a plain element type that cannot be made says so; nothing is added");
|
||||
Assert.That(model.CreateElement(typeof(int?), typeof(int?), out var nullable), Is.Null);
|
||||
Assert.That(nullable, Is.Null, "null is a Nullable<T> element, not a failure");
|
||||
|
||||
Assert.That(model.CreateElement(typeof(InspectShape), typeof(InspectShape), out var @abstract), Is.Null);
|
||||
Assert.That(@abstract, Does.Contain("abstract"), "a union's own type cannot be made; it says so instead of throwing");
|
||||
Assert.That(new[] { typeof(InspectShape), typeof(InspectNeedsArgs), typeof(IList<int>) }.Any(model.CanCreate), Is.False);
|
||||
Assert.That(new[] { typeof(InspectItem), typeof(string), typeof(int[]), typeof(IDictionary<string, int>), typeof(InspectOpenStruct) }.All(model.CanCreate), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue