Add class LoadedBrush for GimpBrushSelect widget
Summary
This is about the libgimp API (for plugins), not so much about the GIMP app GUI. The GimpBrushSelect widget groups choice of brush with other choices that affect painting. The enhancement is a new class GimpLoadedBrush, which encapsulates these choices.
Background
The v3 GimpBrushSelect widget lets a user choose opacity, spacing, and paint mode (as well as brush) and yields them as separated values. These choices all affect painting, but they are not attributes of a brush. (GimpBrush has a default spacing attribute, but I'm not sure there isn't a global spacing setting.)
In Gimp 2, plugins could declare args of type SF_BRUSH or PF_BRUSH, which meant they received the set of choices from a widget similar to the v3 GimpBrushSelect widget (but implemented separately in PyGimp and ScriptFu.)
With MR !740 (merged), new GimpResource class and GimpBrush subclass are added to libgimp. But a GimpBrush is not what the GimpBrushSelect widget chooses. A PDB procedure that wants something similar to what SF_BRUSH and PF_BRUSH denoted, can't declare an argument of type GimpBrush, or any combination of arguments, and expect GimpProcedureDialog to show the GimpBrushSelect widget.
One symptom or smell is that in the code you keep seeing the same groups of arguments: [brush, opacity, spacing, paint mode.] They can be encapsulated in an object.
The model
A GimpBrush is a subclass of GimpResource, something that can be installed. It affects painting with certain tools, but other things affect the tools/stroking. It is like a real world brush, but dry. The API for GimpBrush is about creating and editing such a thing.
A GimpLoadedBrush would be like a real world brush loaded with paint. How a brush loaded with paint affects an image depends on both the brush and paint attributes. For example, it depends on the opacity of the paint. A GimpLoadedBrush is a struct of things a user chooses, and that a plugin usually sets into the context before a painting operation.
Note that color is still not an attribute of GimpLoadedBrush, but remains a separate choice, not in the GimpSelectBrush widget.
The code changes
Define a new class GimpLoadedBrush. Define it both in libgimp and app/core. Define class method new() to create an instance using initial values from GimpContext. The class will implement GimpConfig so that procedures can declare args of the type and expect GimpProcedureDialog to show them using GimpBrushSelect widget (its current name.)
Define GimpParamSpecLoadedBrush in libgimp/gimpparamspec.h in libgimpg/gimpparamspec-body.c
Refactor GimpBrushSelectButton and GimpBrushSelect to take and yield a GimpLoadedBrush.
Rename GimpBrushSelectButton to GimpSelectLoadedBrush. By reversing the word order, the class methods will appear in the API docs under their own class instead of in the GimpBrush class.
Fix any plugins in the repo that use GimpBrushSelect (gfig.c and script-fu-interface.c, soon to be obsolete.)
Possibly add a method to GimpContext(sic) for setting a GimpLoadedBrush into the context.
Add the class to the PDB code generator, pdbgen. For example gimp_brushes_popup() which should take the new class.
Naming the class
Another name could be used, such as PaintOptions. But I think that name is already used in Gimp core to mean something else. And the name PaintOptions is too abstract.
While the word LoadedBrush conveys more of the real world sense. But unfortunately some might think it means the brush has been loaded into GIMP, i.e. installed, instead of "loaded with paint."
Maybe StyledBrush is a better name. But the word "style" may imply that a StyledBrush can be saved, or cascaded. A LoadedBrush is ephemeral, a user chooses it, a plugin uses it, and then it dissappears. Maybe BrushStyleSheet? I don't really know Gimp's model for styling.
I briefly looked at the model of other painting apps such as Krita and Inkscape.
The benefits
The code is easier to read. I don't think the code is any larger.
There is no change for users.
Plugin authors see a slightly changed API.
The API documents are organized into classes better.
Logistics
I am prototyping this in a branch off MR !740 (merged) because that MR runs into this issue. Instead, I could break it out into a separate commit.