Goals of this task:
- Create an API to create new tools.
- Create an API to register tools in the toolbar.
Create new Tool
Every tool is a namedtuple called ToolDef currently. That is a nice internal format but does not work well in an API due to the amount of attributes.
There is a wrapper called ToolDef.from_fn that makes it easier to create new tools. This function is used can be used as a decorator. Every decorated function then turns into a tool definition.
The advantage of that approach is that it makes tool definitions very compact. However, personally I think that this does not work well in an API because "why should I define a function to create a tool".
A better approach could be to use concepts that are used in other parts of the API already: classes.
Unfortunately the concept of a class does not fit exactly, because there aren't really instances of a tool. The tool would be the class itself.
A combination of the current tool syntax and classes could fit best. Below is an example of how that API might look like.
from bpy. tools import ToolDefinition @ToolDefinition class CursorTool: label = "Cursor" description = "Set the cursor location" icon = "ops.generic.cursor" keymap = ... # to be discussed def draw_settings(self, context, layout): props = self.operator_properties("view3d.cursor3d") layout.prop(props, "use_depth") layout.prop(props, "orientation")
This approach also allows individual attributes to be normal values or functions (e.g. there can be a function to generate the description dynamically).
We could also change it so that CursorTool has to inherit ToolDefinition. However I prefer to use a decorator here because of the following reasons.
- It differentiates it from Panels, Menus, etc. which work quite differently.
- Allows us to run code when the new tool is created. This code can check if everything is setup correctly and can add missing attributes. Although this can probably be done with __init_subclass__ as well, haven't tried it yet.
- A decorator can transform the class completely, potentially even convert it into a namedtuple if necessary.
Since tools are (currently?) very lightweight, tools don't have to be registered. The "registration" happens when it is added to the toolbar.
Add Tool to Toolbar
I use the following terminology, not sure if other terminology is established already.
- Toolbar: The toolbar (I know, recursive definition) within one specific mode. E.g. the toolbar in curve edit and mesh edit mode are completely separate toolbars.
- Toolbar Entry: A single icon in the toolbar. A toolbar entry can contain an arbitrary amound of tools. If it has 0, it will be hidden. If it has more than 1, a submenu will be displayed.
- Entry Block: Can contain an arbitrary amount toolbar entries. A toolbar can have multiple entry blocks. Entry blocks are separated in the UI.
For users to be able to use a tool, it has to be in a toolbar. The goal is to allow addon developers to do the following.
- Add a new entry block to a toolbar.
- Add a toolbar entry to an existing or new entry block.
- Add a tool to an existing or new toolbar entry.
We do not support creating a new toolbar. Only existing toolbars can be extended (for now).
In the current design it is difficult to allow all these operations because toolbars are mainly tuples, which means they are immutable. Changing this is not too difficult but then we face the bigger problem: How can we identify toolbars, entry blocks and entries? Also, should the identifier be just a string or some python object? I guess we will use strings, because they are used in many other areas in Blender already.
- Toolbar: This is mostly a solved problem because there is already the tool mode. The main question is whether the space type should become part of the identifier. E.g. is the identifier just EDIT_ARMATURE or VIEW3D.EDIT_ARMATURE?
- Entry Block: Currently there is no concept of an identifier for entry blocks. This would have to be added to support the second kind of operation. An example block identifier could be EDIT_MESH.ADD_MESH which contains the Add Cube and possibly other tools.
- Entry: An entry also does not have an identifier currently. However these identifiers are easier to come up with because the scope is much smaller. In most cases the identifier could just be the name/label of the tool. A full entry identifier could look like EDIT_MESH.MANIPULATE.SPIN. We might also want to flatten the namespace out a little so that entry blocks and entries themselves are in the same namespace. Then EDIT_MESH.SPIN would be an entry while EDIT_MESH.ADD_MESH is a block. Don't know how likely name collisions are.
Once the identifier issue is solved, the API could look like so:
import bpy bpy.tools.register(BLOCK_OR_ENTRY_IDENTIFIER, tool) bpy.tools.unregister(BLOCK_OR_ENTRY_IDENTIFIER, tool)
The same API could be used by Blender itself to setup the default toolbars.
The order of tools within an entry and the order of entries within a block depends entirely on the registration order and cannot be changed using the API for now. This could be added later if it proofs to be useful, however it would be better to implement support for custom reordering. Panels are handled similarly; the order of panels can be changed by the user but not by an addon (afaik).