focustense / StardewUI

UI/widget library for Stardew modding
MIT License
5 stars 0 forks source link

[Framework] Included views #4

Closed focustense closed 1 month ago

focustense commented 1 month ago

The big daddy of features that makes any UI framework maintainable in the long run is composability: the ability to define lower-level "components" and collect them into higher level "layouts".

StardewUI has many widgets that help perform these roles: CheckBox, ScrollableView, Button and so on. However, it's not possible (or rather, not reasonably practical) to have to extend IView through the API. Users of the framework API are not going to have access to WrapperView or necessarily even know how to use it if they did.

The solution to this is includes, AKA components, user controls, etc. And StardewUI's model of this should ideally be based on assets; that is, any asset name you could pass into IViewEngine.CreateMenuFromMarkup can also be used as the target of an include. This way there is no obscure knowledge required to use it, and we get hot reload and other useful features for free - a major advantage over the web framework situation where there's no invalidation system and they have to be reloaded from the server.

While the implementation is bound to be tricky, the specification is really very simple:

<include name="Mods/MyMod/Views/MyView" *context={{Prop}} />

That is, "create a new view using the markup in the MyView asset, and assign its context to the Prop property of the current context". The context macro (described in #3) is of course optional but probably going to be needed most of the time for included views, since they'll be designed to work with a particular context type.

This is a real element that can be combined into repeaters, conditionals, etc. The simplest way to implement it will be to create an actual view type for it, e.g. a class IncludedView : WrapperView which loads the specified asset in its CreateView. This is, in fact, almost exactly what the DocumentView already does, except that it depends on an already-loaded document whereas the included view needs to encapsulate the loading logic as well, as well as any invalidation for hot reloads.

I think we can kill two birds with one stone here and simply refactor the DocumentView to handle loading and invalidation. That way, the DocumentMenu also gets hot reload for free, whereas currently hot reload is just a theoretical possibility and not actually implemented.

focustense commented 1 month ago

It probably makes sense to consider #6 as a dependency of this one. While it's possible to mock it up in unit tests, it's hard to be completely certain that any arbitrary design for an asset-loading dependency will be entirely compatible with what SMAPI wants. And in any case, #6 has more immediate importance since it's the whole basis of the mod's API.