User:Djpohly/Theming post-SM5

DISCLAIMER: I do not speak for all of the StepMania developers, and the ideas listed here may never be implemented in StepMania. This is just a place where I can brainstorm about the possibilities of StepMania theming in the future.

Backwards compatibility
Each idea should ideally include a backwards compatibility plan. In the past, major changes to the way themes are made have caused schisms in the community: people used StepMania 3.9 derivatives because there were themes available that could not be used in StepMania 5, for example. This has caused a lot of frustration for developers and users alike, and should be avoided if possible.

Along with this, each version of StepMania should (again, ideally) come with a tool for converting themes from the previous version to use current features. This should be fully automated or almost fully automated, to encourage theme developers to upgrade whenever possible. Such a tool would provide several benefits:
 * Backwards compatibility code need only be maintained for one version back.
 * If a themer has left the community, advanced users can continue to upgrade his/her old themes.
 * Users have no excuse, theme-wise, to stay with old versions of StepMania.

To illustrate, suppose j//forever releases a theme called MEX for the hypothetical StepMania 15. When StepMania 16 is released, MEX will work without modification. Joe User loves MEX and can upgrade immediately to SM16. In the meantime, j//forever can run the upgrade tool (with little or no effort required) and offer a new version of MEX for SM16. For the StepMania 17 release, the developers can then remove any features which were deprecated in SM16, as well as the code for SM15 compatibility. Everybody wins.

Themes and noteskins
Making a theme in SM5 is very different from making a noteskin (or so I am told). We should sit down and have a long, hard look at how we might be able to bring these two closer together. Not only would it make it easier to learn both, but it would likely simplify our codebase if we could do this.

Backwards compatibility: We should include both themes and noteskins in the backwards compatibility plans for the features below. (As I know very little about noteskinning, this will have to stay TBD for now. --djpohly)

BGAnimations and Graphics
There does not appear to be a significant difference between BGAnimations and Graphics, except that they reside in different directories. Either can be implemented in Lua. I propose merging these into a new directory called Objects.

Backwards compatibility: When searching for an object, first look in the Objects directory. If it is not found there, and the theme has a BGAnimations or Graphics directory, look there before moving to the fallback theme.

Directory hierarchy
Objects which are implemented in Lua are generally named according to the form "ScreenName object.lua" or "ScreenName object/default.lua". This puts a large number of files with similar names directly into the Objects directory. Instead of separating components with spaces, we could use directories. This would make smaller directories which are easier to navigate, and it would group related objects together.

Backwards compatibility: A simple possibility is to search for both "ScreenName/object/default.lua" and "ScreenName object/default.lua" whenever loading an object. This is a bit ugly and may impact performance. A cleaner but slightly more complex option would be to have a theme metric in the [Global] section which specifies how a theme organizes its files. If the  metric is false (the default), then the old behavior is used, and if it is true, then the hierarchical behavior is used. (I'm not completely satisfied with this solution, since it might make for ugly code, but it's workable. --djpohly)

Deprecating metrics.ini
Now that StepMania uses Lua for theming, the appearance and behavior of theme elements can be modified directly in the Lua files. This makes it difficult to know whether a certain property needs to be defined in Lua or in. It also leads to confusion when, for example, the  metric from a fallback theme overrides the   property from.

Since Lua objects can include arbitrary properties, it could be very easy to turn metrics into properties of some "metric object." Many (all?) metrics can already accept Lua expressions as a value, so there should be very little difference.

Backwards compatibility: If the theme contains a  file, we convert it to metric objects and merge them into (behind) any Lua metric objects that we have. Converting metrics to Lua could also be automated.

Fallbacks
We can use Lua-style inheritance to implement fallbacks. For example: This has the advantage that falling back is done by Lua itself and does not have to be implemented in the StepMania code.

Backwards compatibility: When creating metric objects from the  file, set the   metamethod accordingly.

Redirect files
Although these don't suck as much as some aspects of theming, they definitely deserve a little thought. Perhaps we could use inheritance here as well.

Backwards compatibility: Depends on what we come up with.

Syntactic sugar
The current syntactic sugar for creating Actors and ActorFrames (see the code) is handy, but it has some idiosyncracies that can confuse themers at first. For example, you have to use the  operator with   but not. Furthermore, there are a number of different styles for adding actors to an ActorFrame:

This is the simplest but least flexible way to set up an ActorFrame. It can be easy to get confused about where you need to use semicolons, commas, or nothing.

This is a very consistent and flexible way to set up an ActorFrame. We can, for instance, add an item conditionally. In addition, it reduces the indentation from the previous method and makes it easier to remember closing braces.

This is like the previous method, except that it uses Lua's efficient built-in method for inserting an item at the end of an array. Unfortunately, it is not as compact as, and it requires an extra set of parentheses.

We could make all of this more consistent by giving the ActorFrame object an  property/function. This would combine the best parts of the second and third methods above, and it would resolve the differences between Def.* and LoadActor:

This might require some changes to the ActorFrame metatable, but it should be possible.

Backwards compatibility: All of the old ways will still work.