Implement menu source generation#76
Conversation
|
Suppose I have a menu generated with this procedure, and I want to modify the values of the underlying data object in code. What is the recommended way to do this? In HK.MC I believe the answer was to modify the menu elements directly - is the expectation here that they have to hold a reference to the generated menu and check if it exists before deciding whether to modify the data on the generated menu or on the data directly? it may or may not be the case that the answer to this question is simply an article |
I wrote an article. The answer depends on what sort of syncing behaviour your actually want between the menu and the underlying data; if the answer is "always in-sync" then you want to respond to every value change with |
07d94a1 to
cc44d03
Compare
BadMagic100
left a comment
There was a problem hiding this comment.
I would really strongly recommend creating a roslyn test project to verify the generated sources as well.
For locally testing the nuget packaging, you can set the package version as a prerelease and build, then set up a local package source with dotnet nuget add source and then take the prerelease version as a dependency. If you don't need the extra assurance you can also just change the extension of the package to .zip and inspect the content (nuget has an online package browser you can use to compare against other packages)
|
|
||
| Each declared element on your custom menu class can be changed directly, via setting the property to a new element. If you don't want a property to be managed by the custom menu, annotate it with @"Silksong.ModMenu.Models.ModMenuIgnoreAttribute" in the data class. | ||
|
|
||
| You can also succinctly specify minimums and maximums for numeric properties, using @"Silksong.ModMenu.Generator.ModMenuRangeAttribute". If you have a special type or you want to use a custom element for a specific property, use the @"Silksong.ModMenu.Generator.ElementFactoryAttribute`1" with an @"Silksong.ModMenu.Generator.IElementFactory`2" implementing class. |
There was a problem hiding this comment.
Is there a reason that these are a different set of attributes than the ones used for bepinex configs (particularly, for element customization)
There was a problem hiding this comment.
They're not compatible. MenuElementGenerator is a delegate, not an Attribute, it requires a ConfigEntryBase which is not available here, and even if it were Attributes cannot contain function-refs, only Types, so it would need to be packaged into a new interface anyway. AcceptableValueRange is also not an Attribute.
| /// Attribute to mark a field or property of a data class as requiring its own custom sub-menu, of the parameterized type. | ||
| /// </summary> | ||
| [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] | ||
| public class SubMenuAttribute<T> : Attribute |
There was a problem hiding this comment.
The doc article could probably do with an explanation of how to generate submenus
There was a problem hiding this comment.
It does, it's the last section of the doc. Sub-menus are generated the same way normal menus are generated, so it's not a very long section.
|
|
||
| /// Custom menu class generated for {className}. | ||
| [System.CodeDom.Compiler.GeneratedCode(""ModMenuGenerator"", {VERSION.MakeLiteral()})] | ||
| public class {menuClassName} : Silksong.ModMenu.Generator.ICustomMenu<{className}> |
There was a problem hiding this comment.
is there a particular reason to not have this implement INotifyPropertyChange in addition/instead/have ICustomMenu derive from that?
There was a problem hiding this comment.
I don't really understand the suggestion here, at least the 'instead' part. All of the elements have different types and sub menus get different code, so making this work in a generic fashion seems like it would require some awful reflection at minimum.
Summary of Changes
This enables quick and easy generation of menus for classes intended to be serialized with Json, as opposed to data specified through BepInEx configuration files. It does not yet have feature parity with MenuElementFactory but it is close and I think the generated API is much nicer to use than runtime reflection. Attributes exist to plug in custom elements, sub menus, and to clamp numeric values to bounded ranges.
I don't know if the packaging set up will work with nuget; I copied stuff from https://github.com/BadMagic100/DataDrivenConstants/blob/main/DataDrivenConstants.Package/DataDrivenConstants.Package.csproj. I've also never written a Roslyn analyzer before so I probably have some stuff wrong, but it at least all works in the test project and the diagnostics seem to be working too.
Checklist