Show an unkeyed struct's fields or its properties, never both
Unity's Quaternion showed eulerAngles beside x/y/z/w, Rect about eleven rows over four values, and Bounds center/extents/size/min/max: settable properties alias the fields they wrap. A struct with public fields now shows only those; a struct without shows its settable properties, so record structs and CultMath matrices (properties over private rows) still get clean rows and vectors show fields only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
baa8de0baf
commit
ccf9bd0c0c
3 changed files with 27 additions and 15 deletions
|
|
@ -552,16 +552,19 @@ namespace GameCult.Caching
|
|||
var keyed = KeyedMembers(type);
|
||||
if (keyed.Length > 0)
|
||||
return new CultInspectorShape(type, CultInspectorValueKind.Nested) { Members = keyed };
|
||||
// An unkeyed struct is edited in place: its public fields, readonly ones read-only, then its public properties with
|
||||
// a setter, init-only ones read-only. Get-only properties are computed (a vector's swizzles) or kept by the
|
||||
// in-place edit, so they are not rows.
|
||||
// An unkeyed struct is edited in place. With public fields it shows only them, readonly ones read-only: its
|
||||
// properties alias those values (a Quaternion's eulerAngles, a Rect's min and max). Without, it shows its public
|
||||
// properties with a setter, init-only ones read-only (record structs, matrices over private rows). Get-only
|
||||
// properties are computed or kept by the in-place edit, so they are never rows.
|
||||
if (type.IsValueType)
|
||||
{
|
||||
var members = type.GetFields(Public).OrderBy(field => field.MetadataToken)
|
||||
.Select(field => (Member: (MemberInfo)field, Type: field.FieldType, Assignable: !field.IsInitOnly))
|
||||
.Concat(type.GetProperties(Public).OrderBy(property => property.MetadataToken)
|
||||
.Where(property => property.GetIndexParameters().Length == 0 && property.GetGetMethod() != null && property.GetSetMethod() != null)
|
||||
.Select(property => (Member: (MemberInfo)property, Type: property.PropertyType, Assignable: !IsInitOnly(property.GetSetMethod()!))))
|
||||
var fields = type.GetFields(Public);
|
||||
var members = (fields.Length > 0
|
||||
? fields.OrderBy(field => field.MetadataToken)
|
||||
.Select(field => (Member: (MemberInfo)field, Type: field.FieldType, Assignable: !field.IsInitOnly))
|
||||
: type.GetProperties(Public).OrderBy(property => property.MetadataToken)
|
||||
.Where(property => property.GetIndexParameters().Length == 0 && property.GetGetMethod() != null && property.GetSetMethod() != null)
|
||||
.Select(property => (Member: (MemberInfo)property, Type: property.PropertyType, Assignable: !IsInitOnly(property.GetSetMethod()!))))
|
||||
.Select((member, slot) => new CultInspectorMember(member.Member, member.Type, slot, member.Assignable, MetadataOf(member.Member)))
|
||||
.ToArray();
|
||||
if (members.Length > 0)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ catalog slots, `CultInspector*` metadata, whether a member is assignable), the
|
|||
shape of any value (string, integer, float, bool, enum, `CultRecordRef<T>` with
|
||||
its candidate records, rank-1 list or array, dictionary, `[Union]` with only
|
||||
its declared subtypes, nested keyed object, struct edited in place through its
|
||||
public fields and settable properties, or unsupported with a reason), drawer claim resolution
|
||||
public fields, or its settable properties when it has none, or unsupported with a reason), drawer claim resolution
|
||||
(`CultInspectorDrawerClaims`), and the edit decisions: the key a dictionary
|
||||
entry keeps and the notice when a key is refused (null, an empty record
|
||||
reference, a duplicate: serialized the same as another key under the owning
|
||||
|
|
@ -59,10 +59,12 @@ renderer and drawers to show and edit the same documents under the same rules.
|
|||
|
||||
## Drawers
|
||||
|
||||
Built in: every model shape above. A struct without `[Key]` members folds out
|
||||
to its public fields and its public properties with a setter, and is edited in
|
||||
place; readonly fields and `init` properties show read-only, and get-only
|
||||
properties are not shown (record structs work as written). Unity object
|
||||
Built in: every model shape above. A struct without `[Key]` members is edited
|
||||
in place. With public fields it folds out to those fields only (readonly ones
|
||||
read-only), so aliasing properties such as `Quaternion.eulerAngles` or
|
||||
`Rect.min` add no rows; without public fields it folds out to its public
|
||||
properties with a setter, `init` ones read-only (record structs and CultMath
|
||||
matrices work as written). Get-only properties are never shown. Unity object
|
||||
references draw as object fields. A value no drawer claims and no shape covers
|
||||
shows a red error row with the model's reason, as does a multi-dimensional
|
||||
array.
|
||||
|
|
|
|||
|
|
@ -255,8 +255,10 @@ namespace GameCult.Caching.Tests
|
|||
Assert.That(open.Members.Select(member => member.Name), Is.EqualTo(new[] { "x", "y" }));
|
||||
Assert.That(open.Members.All(member => member.IsAssignable), Is.True);
|
||||
(string, bool)[] Rows(Type type) => model.ShapeOf(type).Members.Select(member => (member.Name, member.IsReadOnly)).ToArray();
|
||||
Assert.That(Rows(typeof(InspectMixedStruct)), Is.EqualTo(new[] { ("x", false), ("y", true), ("Settable", false), ("Initable", true) }),
|
||||
"fields, then settable properties; readonly and init are read-only; get-only is not a row");
|
||||
Assert.That(Rows(typeof(InspectMixedStruct)), Is.EqualTo(new[] { ("x", false), ("y", true) }),
|
||||
"a struct with public fields shows only them; its settable properties are aliases, not rows");
|
||||
Assert.That(Rows(typeof(InspectPropertyStruct)), Is.EqualTo(new[] { ("Settable", false), ("Initable", true) }),
|
||||
"without public fields, settable properties are rows; init is read-only; get-only is not a row");
|
||||
Assert.That(Rows(typeof(InspectRecordStruct)), Is.EqualTo(new[] { ("A", false), ("B", false) }));
|
||||
Assert.That(Rows(typeof(InspectReadonlyRecordStruct)), Is.EqualTo(new[] { ("A", true), ("B", true) }));
|
||||
object boxed = new InspectRecordStruct(1, 2f);
|
||||
|
|
@ -372,6 +374,11 @@ namespace GameCult.Caching.Tests
|
|||
{
|
||||
public float x;
|
||||
public readonly float y;
|
||||
public float Alias { get => x; set => x = value; }
|
||||
}
|
||||
|
||||
public struct InspectPropertyStruct
|
||||
{
|
||||
public int Settable { get; set; }
|
||||
public int Initable { get; init; }
|
||||
public int Computed => Settable + 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue