Tool System: split UI label from tool identifiers
authorCampbell Barton <ideasman42@gmail.com>
Fri, 15 Mar 2019 01:45:41 +0000 (12:45 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 15 Mar 2019 03:53:08 +0000 (14:53 +1100)
Prepare for exposing tool registration to the Python API.

- Generated tools can use their own prefix so naming collisions
  won't happen between hard coded & generated tools.
- Add-on authors can use the add-on name as a prefix.

Currently the names match, renaming will happen next.

14 files changed:
release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
release/scripts/presets/keyconfig/keymap_data/blender_default.py
release/scripts/startup/bl_operators/wm.py
release/scripts/startup/bl_ui/space_toolsystem_common.py
release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
release/scripts/startup/bl_ui/space_topbar.py
release/scripts/startup/bl_ui/space_view3d.py
source/blender/editors/interface/interface_query.c
source/blender/editors/interface/interface_region_tooltip.c
source/blender/makesrna/intern/rna_workspace.c
source/blender/makesrna/intern/rna_workspace_api.c
source/blender/windowmanager/WM_toolsystem.h
source/blender/windowmanager/intern/wm_keymap_utils.c
source/blender/windowmanager/intern/wm_toolsystem.c

index 66d0590..0c81278 100644 (file)
@@ -59,7 +59,7 @@ def generate(context, space_type):
         for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context))
         if item is not None
     ]
-    items_all_text = {item_container[0].text for item_container in items_all}
+    items_all_id = {item_container[0].idname for item_container in items_all}
 
     # Press the toolbar popup key again to set the default tool,
     # this is useful because the select box tool is useful as a way
@@ -70,7 +70,7 @@ def generate(context, space_type):
     # TODO: support other tools for modes which don't use this tool.
     tap_reset_tool = "Cursor"
     # Check the tool is available in the current context.
-    if tap_reset_tool not in items_all_text:
+    if tap_reset_tool not in items_all_id:
         use_tap_reset = False
 
     from bl_operators.wm import use_toolbar_release_hack
@@ -102,13 +102,13 @@ def generate(context, space_type):
         for kmi_src in keymap_src.keymap_items:
             # Skip tools that aren't currently shown.
             if (
-                    (kmi_src.idname == "wm.tool_set_by_name") and
-                    (kmi_src.properties.name not in items_all_text)
+                    (kmi_src.idname == "wm.tool_set_by_id") and
+                    (kmi_src.properties.name not in items_all_id)
             ):
                 continue
             keymap.keymap_items.new_from_item(kmi_src)
     del keymap_src
-    del items_all_text
+    del items_all_id
 
 
     kmi_unique_args = set()
@@ -122,7 +122,7 @@ def generate(context, space_type):
     cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
 
     if use_hack_properties:
-        kmi_hack = keymap.keymap_items.new("wm.tool_set_by_name", 'NONE', 'PRESS')
+        kmi_hack = keymap.keymap_items.new("wm.tool_set_by_id", 'NONE', 'PRESS')
         kmi_hack_properties = kmi_hack.properties
         kmi_hack.active = False
 
@@ -148,9 +148,9 @@ def generate(context, space_type):
             # First check for direct assignment, if this tool already has a key, no need to add a new one.
             kmi_hack_properties.name = tap_reset_tool
             kmi_found = wm.keyconfigs.find_item_from_operator(
-                idname="wm.tool_set_by_name",
+                idname="wm.tool_set_by_id",
                 context='INVOKE_REGION_WIN',
-                # properties={"name": item.text},
+                # properties={"name": item.idname},
                 properties=kmi_hack_properties,
                 include={'KEYBOARD'},
             )[1]
@@ -165,7 +165,7 @@ def generate(context, space_type):
         items_all[:] = [
             item_container
             for item_container in items_all
-            if item_container[0].text != tap_reset_tool
+            if item_container[0].idname != tap_reset_tool
         ]
 
     # -----------------------
@@ -179,11 +179,11 @@ def generate(context, space_type):
         # Only check the first item in the tools key-map (a little arbitrary).
         if use_hack_properties:
             # First check for direct assignment.
-            kmi_hack_properties.name = item.text
+            kmi_hack_properties.name = item.idname
             kmi_found = wm.keyconfigs.find_item_from_operator(
-                idname="wm.tool_set_by_name",
+                idname="wm.tool_set_by_id",
                 context='INVOKE_REGION_WIN',
-                # properties={"name": item.text},
+                # properties={"name": item.idname},
                 properties=kmi_hack_properties,
                 include={'KEYBOARD'},
             )[1]
@@ -229,7 +229,7 @@ def generate(context, space_type):
         elif item.keymap is not None:
             km = keyconf_user.keymaps.get(item.keymap[0])
             if km is None:
-                print("Keymap", repr(item.keymap[0]), "not found for tool", item.text)
+                print("Keymap", repr(item.keymap[0]), "not found for tool", item.idname)
                 kmi_found = None
             else:
                 kmi_first = km.keymap_items
@@ -274,12 +274,12 @@ def generate(context, space_type):
         if (
                 (len(kmi_found_type) == 1) or
                 # When a tool is being activated instead of running an operator, just copy the shortcut.
-                (kmi_found.idname in {"wm.tool_set_by_name", "WM_OT_tool_set_by_name"})
+                (kmi_found.idname in {"wm.tool_set_by_id", "WM_OT_tool_set_by_id"})
         ):
             kmi_args = {"type": kmi_found_type, **modifier_keywords_from_item(kmi_found)}
             if kmi_unique_or_pass(kmi_args):
-                kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args)
-                kmi.properties.name = item.text
+                kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
+                kmi.properties.name = item.idname
                 item_container[2] = kmi
 
     # -------------------------------------------------------------------------
@@ -310,8 +310,8 @@ def generate(context, space_type):
                 kmi_args = {"type": kmi_found_type, **modifier_keywords_from_item(kmi_found)}
                 del kmi_args["key_modifier"]
                 if kmi_unique_or_pass(kmi_args):
-                    kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args)
-                    kmi.properties.name = item.text
+                    kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
+                    kmi.properties.name = item.idname
                     item_container[2] = kmi
 
     # -------------------------------------------------------------------------
@@ -331,16 +331,16 @@ def generate(context, space_type):
             item, kmi_found, kmi_exist = item_container
             if kmi_exist:
                 continue
-            kmi_type = item.text[0].upper()
+            kmi_type = item.label[0].upper()
             kmi_tuple = kmi_type_alpha_args_tuple.get(kmi_type)
             if kmi_tuple and kmi_tuple not in kmi_unique_args:
                 kmi_unique_args.add(kmi_tuple)
                 kmi = keymap.keymap_items.new(
-                    idname="wm.tool_set_by_name",
+                    idname="wm.tool_set_by_id",
                     value='PRESS',
                     **kmi_type_alpha_args[kmi_type],
                 )
-                kmi.properties.name = item.text
+                kmi.properties.name = item.idname
                 item_container[2] = kmi
         del kmi_type_alpha_char, kmi_type_alpha_args, kmi_type_alpha_args_tuple
 
@@ -388,8 +388,8 @@ def generate(context, space_type):
                     break
 
             if kmi_args is not None:
-                kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args)
-                kmi.properties.name = item.text
+                kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
+                kmi.properties.name = item.idname
                 item_container[2] = kmi
                 kmi_unique_args.add(kmi_tuple)
 
@@ -398,8 +398,8 @@ def generate(context, space_type):
                     kmi_args["type"] = key
                     kmi_tuple = dict_as_tuple(kmi_args)
                     if not kmi_tuple in kmi_unique_args:
-                        kmi = keymap.keymap_items.new(idname="wm.tool_set_by_name", value='PRESS', **kmi_args)
-                        kmi.properties.name = item.text
+                        kmi = keymap.keymap_items.new(idname="wm.tool_set_by_id", value='PRESS', **kmi_args)
+                        kmi.properties.name = item.idname
                         kmi_unique_args.add(kmi_tuple)
 
 
@@ -426,7 +426,7 @@ def generate(context, space_type):
             del kmi_toolbar_tuple
 
         kmi = keymap.keymap_items.new(
-            "wm.tool_set_by_name",
+            "wm.tool_set_by_id",
             value='PRESS' if use_toolbar_release_hack else 'DOUBLE_CLICK',
             **kmi_toolbar_args_available,
         )
@@ -445,7 +445,7 @@ def generate(context, space_type):
             # ... or pass through to let the toolbar know we're released.
             # Let the operator know we're released.
             kmi = keymap.keymap_items.new(
-                "wm.tool_set_by_name",
+                "wm.tool_set_by_id",
                 type=kmi_toolbar_type,
                 value='RELEASE',
                 any=True,
index 9b41e4c..91651b7 100644 (file)
@@ -143,11 +143,11 @@ def op_panel(menu, kmi_args, kmi_data=()):
 
 
 def op_tool(tool, kmi_args):
-    return ("wm.tool_set_by_name", kmi_args, {"properties": [("name", tool)]})
+    return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool)]})
 
 
 def op_tool_cycle(tool, kmi_args):
-    return ("wm.tool_set_by_name", kmi_args, {"properties": [("name", tool), ("cycle", True)]})
+    return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool), ("cycle", True)]})
 
 
 # ------------------------------------------------------------------------------
index cf513d6..9e0e090 100644 (file)
@@ -1486,14 +1486,14 @@ class WM_OT_owner_disable(Operator):
 
 
 
-class WM_OT_tool_set_by_name(Operator):
+class WM_OT_tool_set_by_id(Operator):
     """Set the tool by name (for keymaps)"""
-    bl_idname = "wm.tool_set_by_name"
+    bl_idname = "wm.tool_set_by_id"
     bl_label = "Set Tool By Name"
 
     name: StringProperty(
-        name="Text",
-        description="Display name of the tool",
+        name="Identifier",
+        description="Identifier of the tool",
     )
     cycle: BoolProperty(
         name="Cycle",
@@ -1518,8 +1518,8 @@ class WM_OT_tool_set_by_name(Operator):
 
     def execute(self, context):
         from bl_ui.space_toolsystem_common import (
-            activate_by_name,
-            activate_by_name_or_cycle,
+            activate_by_id,
+            activate_by_id_or_cycle,
         )
 
         if self.properties.is_property_set("space_type"):
@@ -1527,7 +1527,7 @@ class WM_OT_tool_set_by_name(Operator):
         else:
             space_type = context.space_data.type
 
-        fn = activate_by_name_or_cycle if self.cycle else activate_by_name
+        fn = activate_by_id_or_cycle if self.cycle else activate_by_id
         if fn(context, space_type, self.name):
             return {'FINISHED'}
         else:
@@ -1800,7 +1800,7 @@ classes = (
     WM_OT_owner_disable,
     WM_OT_owner_enable,
     WM_OT_url_open,
-    WM_OT_tool_set_by_name,
+    WM_OT_tool_set_by_id,
     WM_OT_toolbar,
     WM_MT_splash,
 )
index 72b90e9..e893ee8 100644 (file)
@@ -25,10 +25,10 @@ from bpy.types import (
 __all__ = (
     "ToolDef",
     "ToolSelectPanelHelper",
-    "activate_by_name",
-    "activate_by_name_or_cycle",
-    "description_from_name",
-    "keymap_from_name",
+    "activate_by_id",
+    "activate_by_id_or_cycle",
+    "description_from_id",
+    "keymap_from_id",
 )
 
 # Support reloading icons.
@@ -62,8 +62,10 @@ from collections import namedtuple
 ToolDef = namedtuple(
     "ToolDef",
     (
+        # Unique tool name (withing space & mode context).
+        "idname",
         # The name to display in the interface.
-        "text",
+        "label",
         # Description (for tooltip), when not set, use the description of 'operator',
         # may be a string or a 'function(context, item, keymap) -> string'.
         "description",
@@ -250,10 +252,10 @@ class ToolSelectPanelHelper:
         cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
         if cls is not None:
             tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode)
-            tool_active_text = getattr(tool_active, "name", None)
+            tool_active_id = getattr(tool_active, "idname", None)
             for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context, mode)):
                 if item is not None:
-                    if item.text == tool_active_text:
+                    if item.idname == tool_active_id:
                         if with_icon:
                             icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
                         else:
@@ -262,7 +264,7 @@ class ToolSelectPanelHelper:
         return None, None, 0
 
     @staticmethod
-    def _tool_get_by_name(context, space_type, text):
+    def _tool_get_by_id(context, space_type, idname):
         """
         Return the active Python tool definition and index (if in sub-group, else -1).
         """
@@ -270,7 +272,7 @@ class ToolSelectPanelHelper:
         if cls is not None:
             for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)):
                 if item is not None:
-                    if item.text == text:
+                    if item.idname == idname:
                         return (cls, item, index)
         return None, None, -1
 
@@ -303,12 +305,12 @@ class ToolSelectPanelHelper:
         return None
 
     @staticmethod
-    def _tool_text_from_button(context):
+    def _tool_identifier_from_button(context):
         return context.button_operator.name
 
     @classmethod
-    def _km_action_simple(cls, kc, context_descr, text, keymap_fn):
-        km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {text:s}"
+    def _km_action_simple(cls, kc, context_descr, idname, keymap_fn):
+        km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {idname:s}"
         km = kc.keymaps.get(km_idname)
         if km is None:
             km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW', tool=True)
@@ -353,7 +355,7 @@ class ToolSelectPanelHelper:
             for item in cls._tools_flatten_with_keymap(tools):
                 keymap_data = item.keymap
                 if callable(keymap_data[0]):
-                    cls._km_action_simple(kc, context_descr, item.text, keymap_data)
+                    cls._km_action_simple(kc, context_descr, item.idname, keymap_data)
 
     @classmethod
     def keymap_ui_hierarchy(cls, context_mode):
@@ -472,9 +474,9 @@ class ToolSelectPanelHelper:
         # - ability to click and hold to expose sub-tools.
 
         space_type = context.space_data.type
-        tool_active_text = getattr(
+        tool_active_id = getattr(
             ToolSelectPanelHelper._tool_active_from_context(context, space_type),
-            "name", None,
+            "idname", None,
         )
 
         if detect_layout:
@@ -497,7 +499,7 @@ class ToolSelectPanelHelper:
                 for i, sub_item in enumerate(item):
                     if sub_item is None:
                         continue
-                    is_active = (sub_item.text == tool_active_text)
+                    is_active = (sub_item.idname == tool_active_id)
                     if is_active:
                         index = i
                         break
@@ -505,9 +507,9 @@ class ToolSelectPanelHelper:
 
                 if is_active:
                     # not ideal, write this every time :S
-                    cls._tool_group_active[item[0].text] = index
+                    cls._tool_group_active[item[0].idname] = index
                 else:
-                    index = cls._tool_group_active.get(item[0].text, 0)
+                    index = cls._tool_group_active.get(item[0].idname, 0)
 
                 item = item[index]
                 use_menu = True
@@ -515,26 +517,26 @@ class ToolSelectPanelHelper:
                 index = -1
                 use_menu = False
 
-            is_active = (item.text == tool_active_text)
+            is_active = (item.idname == tool_active_id)
             icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
 
             sub = ui_gen.send(False)
 
             if use_menu:
                 sub.operator_menu_hold(
-                    "wm.tool_set_by_name",
-                    text=item.text if show_text else "",
+                    "wm.tool_set_by_id",
+                    text=item.label if show_text else "",
                     depress=is_active,
                     menu="WM_MT_toolsystem_submenu",
                     icon_value=icon_value,
-                ).name = item.text
+                ).name = item.idname
             else:
                 sub.operator(
-                    "wm.tool_set_by_name",
-                    text=item.text if show_text else "",
+                    "wm.tool_set_by_id",
+                    text=item.label if show_text else "",
                     depress=is_active,
                     icon_value=icon_value,
-                ).name = item.text
+                ).name = item.idname
         # Signal to finish any remaining layout edits.
         ui_gen.send(None)
 
@@ -564,7 +566,7 @@ class ToolSelectPanelHelper:
             return None
         # Note: we could show 'item.text' here but it makes the layout jitter when switching tools.
         # Add some spacing since the icon is currently assuming regular small icon size.
-        layout.label(text="    " + item.text if show_tool_name else " ", icon_value=icon_value)
+        layout.label(text="    " + item.label if show_tool_name else " ", icon_value=icon_value)
         draw_settings = item.draw_settings
         if draw_settings is not None:
             draw_settings(context, layout, tool)
@@ -581,11 +583,11 @@ class WM_MT_toolsystem_submenu(Menu):
         # Lookup the tool definitions based on the space-type.
         cls = ToolSelectPanelHelper._tool_class_from_space_type(context.space_data.type)
         if cls is not None:
-            button_text = ToolSelectPanelHelper._tool_text_from_button(context)
+            button_identifier = ToolSelectPanelHelper._tool_identifier_from_button(context)
             for item_group in cls.tools_from_context(context):
                 if type(item_group) is tuple:
                     for sub_item in item_group:
-                        if (sub_item is not None) and (sub_item.text == button_text):
+                        if (sub_item is not None) and (sub_item.idname == button_identifier):
                             return cls, item_group
         return None, None
 
@@ -605,16 +607,16 @@ class WM_MT_toolsystem_submenu(Menu):
                 continue
             icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
             layout.operator(
-                "wm.tool_set_by_name",
-                text=item.text,
+                "wm.tool_set_by_id",
+                text=item.label,
                 icon_value=icon_value,
-            ).name = item.text
+            ).name = item.idname
 
 
 def _activate_by_item(context, space_type, item, index):
     tool = ToolSelectPanelHelper._tool_active_from_context(context, space_type, create=True)
     tool.setup(
-        name=item.text,
+        idname=item.idname,
         keymap=item.keymap[0] if item.keymap is not None else "",
         cursor=item.cursor or 'DEFAULT',
         gizmo_group=item.widget or "",
@@ -639,52 +641,52 @@ def _activate_by_item(context, space_type, item, index):
 _activate_by_item._cursor_draw_handle = {}
 
 
-def activate_by_name(context, space_type, text):
-    _cls, item, index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
+def activate_by_id(context, space_type, text):
+    _cls, item, index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, text)
     if item is None:
         return False
     _activate_by_item(context, space_type, item, index)
     return True
 
 
-def activate_by_name_or_cycle(context, space_type, text, offset=1):
+def activate_by_id_or_cycle(context, space_type, idname, offset=1):
 
     # Only cycle when the active tool is activated again.
-    cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
+    cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
     if item is None:
         return False
 
     tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type)
-    text_active = getattr(tool_active, "name", None)
+    id_active = getattr(tool_active, "idname", None)
 
-    text_current = ""
+    id_current = ""
     for item_group in cls.tools_from_context(context):
         if type(item_group) is tuple:
-            index_current = cls._tool_group_active.get(item_group[0].text, 0)
+            index_current = cls._tool_group_active.get(item_group[0].idname, 0)
             for sub_item in item_group:
-                if sub_item.text == text:
-                    text_current = item_group[index_current].text
+                if sub_item.idname == idname:
+                    id_current = item_group[index_current].idname
                     break
-            if text_current:
+            if id_current:
                 break
 
-    if text_current == "":
-        return activate_by_name(context, space_type, text)
-    if text_active != text_current:
-        return activate_by_name(context, space_type, text_current)
+    if id_current == "":
+        return activate_by_id(context, space_type, idname)
+    if id_active != id_current:
+        return activate_by_id(context, space_type, id_current)
 
     index_found = (tool_active.index + offset) % len(item_group)
 
-    cls._tool_group_active[item_group[0].text] = index_found
+    cls._tool_group_active[item_group[0].idname] = index_found
 
     item_found = item_group[index_found]
     _activate_by_item(context, space_type, item_found, index_found)
     return True
 
 
-def description_from_name(context, space_type, text, *, use_operator=True):
+def description_from_id(context, space_type, idname, *, use_operator=True):
     # Used directly for tooltips.
-    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
+    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
     if item is None:
         return False
 
@@ -714,9 +716,15 @@ def description_from_name(context, space_type, text, *, use_operator=True):
     return ""
 
 
-def keymap_from_name(context, space_type, text):
+def item_from_id(context, space_type, idname):
     # Used directly for tooltips.
-    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
+    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
+    return item
+
+
+def keymap_from_id(context, space_type, idname):
+    # Used directly for tooltips.
+    _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
     if item is None:
         return False
 
index 0784f73..b0b8327 100644 (file)
@@ -47,13 +47,14 @@ def generate_from_enum_ex(
     tool_defs = []
     for enum in type.bl_rna.properties[attr].enum_items_static:
         name = enum.name
-        identifier = enum.identifier
+        idname = enum.identifier
         tool_defs.append(
             ToolDef.from_dict(
                 dict(
-                    text=name,
-                    icon=icon_prefix + identifier.lower(),
-                    data_block=identifier,
+                    idname=name,
+                    label=name,
+                    icon=icon_prefix + idname.lower(),
+                    data_block=idname,
                     **tooldef_keywords,
                 )
             )
@@ -85,7 +86,8 @@ class _defs_view3d_generic:
             layout.prop(props, "use_depth")
             layout.prop(props, "orientation")
         return dict(
-            text="Cursor",
+            idname="Cursor",
+            label="Cursor",
             description=(
                 "Set the cursor location, drag to transform"
             ),
@@ -97,7 +99,8 @@ class _defs_view3d_generic:
     @ToolDef.from_fn
     def cursor_click():
         return dict(
-            text="None",
+            idname="None",
+            label="None",
             icon="ops.generic.cursor",
             keymap=(),
         )
@@ -123,7 +126,8 @@ class _defs_view3d_generic:
                 kmi_to_string_or_none(kmi_remove),
             )
         return dict(
-            text="Measure",
+            idname="Measure",
+            label="Measure",
             description=description,
             icon="ops.view3d.ruler",
             widget="VIEW3D_GGT_ruler",
@@ -175,7 +179,8 @@ class _defs_annotate:
     @ToolDef.from_fn.with_args(draw_settings=draw_settings_common)
     def scribble(*, draw_settings):
         return dict(
-            text="Annotate",
+            idname="Annotate",
+            label="Annotate",
             icon="ops.gpencil.draw",
             cursor='PAINT_BRUSH',
             keymap="Generic Tool: Annotate",
@@ -185,7 +190,8 @@ class _defs_annotate:
     @ToolDef.from_fn.with_args(draw_settings=draw_settings_common)
     def line(*, draw_settings):
         return dict(
-            text="Annotate Line",
+            idname="Annotate Line",
+            label="Annotate Line",
             icon="ops.gpencil.draw.line",
             cursor='CROSSHAIR',
             keymap="Generic Tool: Annotate Line",
@@ -195,7 +201,8 @@ class _defs_annotate:
     @ToolDef.from_fn.with_args(draw_settings=draw_settings_common)
     def poly(*, draw_settings):
         return dict(
-            text="Annotate Polygon",
+            idname="Annotate Polygon",
+            label="Annotate Polygon",
             icon="ops.gpencil.draw.poly",
             cursor='CROSSHAIR',
             keymap="Generic Tool: Annotate Polygon",
@@ -209,7 +216,8 @@ class _defs_annotate:
             prefs = context.preferences
             layout.prop(prefs.edit, "grease_pencil_eraser_radius", text="Radius")
         return dict(
-            text="Annotate Eraser",
+            idname="Annotate Eraser",
+            label="Annotate Eraser",
             icon="ops.gpencil.draw.eraser",
             cursor='CROSSHAIR',  # XXX: Always show brush circle when enabled
             keymap="Generic Tool: Annotate Eraser",
@@ -224,7 +232,8 @@ class _defs_transform:
         def draw_settings(context, layout, tool):
             _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 1)
         return dict(
-            text="Move",
+            idname="Move",
+            label="Move",
             # cursor='SCROLL_XY',
             icon="ops.transform.translate",
             widget="TRANSFORM_GGT_gizmo",
@@ -238,7 +247,8 @@ class _defs_transform:
         def draw_settings(context, layout, tool):
             _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 2)
         return dict(
-            text="Rotate",
+            idname="Rotate",
+            label="Rotate",
             # cursor='SCROLL_XY',
             icon="ops.transform.rotate",
             widget="TRANSFORM_GGT_gizmo",
@@ -252,7 +262,8 @@ class _defs_transform:
         def draw_settings(context, layout, tool):
             _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 3)
         return dict(
-            text="Scale",
+            idname="Scale",
+            label="Scale",
             # cursor='SCROLL_XY',
             icon="ops.transform.resize",
             widget="TRANSFORM_GGT_gizmo",
@@ -266,7 +277,8 @@ class _defs_transform:
         def draw_settings(context, layout, tool):
             _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 3)
         return dict(
-            text="Scale Cage",
+            idname="Scale Cage",
+            label="Scale Cage",
             icon="ops.transform.resize.cage",
             widget="VIEW3D_GGT_xform_cage",
             operator="transform.resize",
@@ -287,7 +299,8 @@ class _defs_transform:
             _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 1)
 
         return dict(
-            text="Transform",
+            idname="Transform",
+            label="Transform",
             description=(
                 "Supports any combination of grab, rotate & scale at once"
             ),
@@ -305,7 +318,8 @@ class _defs_view3d_select:
         def draw_settings(context, layout, tool):
             pass
         return dict(
-            text="Select",
+            idname="Select",
+            label="Select",
             icon="ops.generic.select",
             widget=None,
             keymap="3D View Tool: Select",
@@ -318,7 +332,8 @@ class _defs_view3d_select:
             props = tool.operator_properties("view3d.select_box")
             layout.prop(props, "mode", expand=True)
         return dict(
-            text="Select Box",
+            idname="Select Box",
+            label="Select Box",
             icon="ops.generic.select_box",
             widget=None,
             keymap="3D View Tool: Select Box",
@@ -331,7 +346,8 @@ class _defs_view3d_select:
             props = tool.operator_properties("view3d.select_lasso")
             layout.prop(props, "mode", expand=True)
         return dict(
-            text="Select Lasso",
+            idname="Select Lasso",
+            label="Select Lasso",
             icon="ops.generic.select_lasso",
             widget=None,
             keymap="3D View Tool: Select Lasso",
@@ -352,7 +368,8 @@ class _defs_view3d_select:
             draw_circle_2d(xy, (1.0,) * 4, radius, 32)
 
         return dict(
-            text="Select Circle",
+            idname="Select Circle",
+            label="Select Circle",
             icon="ops.generic.select_circle",
             widget=None,
             keymap="3D View Tool: Select Circle",
@@ -370,7 +387,8 @@ class _defs_edit_armature:
     @ToolDef.from_fn
     def roll():
         return dict(
-            text="Roll",
+            idname="Roll",
+            label="Roll",
             icon="ops.armature.bone.roll",
             widget=None,
             keymap=(),
@@ -379,7 +397,8 @@ class _defs_edit_armature:
     @ToolDef.from_fn
     def bone_envelope():
         return dict(
-            text="Bone Envelope",
+            idname="Bone Envelope",
+            label="Bone Envelope",
             icon="ops.transform.bone_envelope",
             widget=None,
             keymap=(),
@@ -388,7 +407,8 @@ class _defs_edit_armature:
     @ToolDef.from_fn
     def bone_size():
         return dict(
-            text="Bone Size",
+            idname="Bone Size",
+            label="Bone Size",
             icon="ops.transform.bone_size",
             widget=None,
             keymap=(),
@@ -397,7 +417,8 @@ class _defs_edit_armature:
     @ToolDef.from_fn
     def extrude():
         return dict(
-            text="Extrude",
+            idname="Extrude",
+            label="Extrude",
             icon="ops.armature.extrude_move",
             widget="VIEW3D_GGT_xform_extrude",
             keymap=(),
@@ -407,7 +428,8 @@ class _defs_edit_armature:
     @ToolDef.from_fn
     def extrude_cursor():
         return dict(
-            text="Extrude to Cursor",
+            idname="Extrude to Cursor",
+            label="Extrude to Cursor",
             icon="ops.armature.extrude_cursor",
             widget=None,
             keymap=(),
@@ -419,7 +441,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def cube_add():
         return dict(
-            text="Add Cube",
+            idname="Add Cube",
+            label="Add Cube",
             icon="ops.mesh.primitive_cube_add_gizmo",
             description=(
                 "Add cube to mesh interactively"
@@ -436,7 +459,8 @@ class _defs_edit_mesh:
             layout.prop(props_macro, "use_fill")
 
         return dict(
-            text="Rip Region",
+            idname="Rip Region",
+            label="Rip Region",
             icon="ops.mesh.rip",
             widget=None,
             keymap=(),
@@ -446,7 +470,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def rip_edge():
         return dict(
-            text="Rip Edge",
+            idname="Rip Edge",
+            label="Rip Edge",
             icon="ops.mesh.rip_edge",
             widget=None,
             keymap=(),
@@ -455,7 +480,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def poly_build():
         return dict(
-            text="Poly Build",
+            idname="Poly Build",
+            label="Poly Build",
             icon="ops.mesh.polybuild_hover",
             widget="VIEW3D_GGT_mesh_preselect_elem",
             keymap=(),
@@ -468,7 +494,8 @@ class _defs_edit_mesh:
             layout.prop(props, "correct_uv")
 
         return dict(
-            text="Edge Slide",
+            idname="Edge Slide",
+            label="Edge Slide",
             icon="ops.transform.edge_slide",
             widget=None,
             keymap=(),
@@ -482,7 +509,8 @@ class _defs_edit_mesh:
             layout.prop(props, "correct_uv")
 
         return dict(
-            text="Vertex Slide",
+            idname="Vertex Slide",
+            label="Vertex Slide",
             icon="ops.transform.vert_slide",
             widget=None,
             keymap=(),
@@ -498,7 +526,8 @@ class _defs_edit_mesh:
             layout.prop(props, "axis")
 
         return dict(
-            text="Spin",
+            idname="Spin",
+            label="Spin",
             icon="ops.mesh.spin",
             widget="MESH_GGT_spin",
             keymap=(),
@@ -514,7 +543,8 @@ class _defs_edit_mesh:
             layout.prop(props, "axis")
 
         return dict(
-            text="Spin Duplicates",
+            idname="Spin Duplicates",
+            label="Spin Duplicates",
             icon="ops.mesh.spin.duplicate",
             widget="MESH_GGT_spin",
             keymap=(),
@@ -531,7 +561,8 @@ class _defs_edit_mesh:
             layout.prop(props, "use_relative_offset")
 
         return dict(
-            text="Inset Faces",
+            idname="Inset Faces",
+            label="Inset Faces",
             icon="ops.mesh.inset",
             widget=None,
             keymap=(),
@@ -548,7 +579,8 @@ class _defs_edit_mesh:
             layout.prop(props, "vertex_only")
 
         return dict(
-            text="Bevel",
+            idname="Bevel",
+            label="Bevel",
             icon="ops.mesh.bevel",
             widget=None,
             keymap=(),
@@ -558,7 +590,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def extrude():
         return dict(
-            text="Extrude Region",
+            idname="Extrude Region",
+            label="Extrude Region",
             # The operator description isn't useful in this case, give our own.
             description=(
                 "Extrude freely or along an axis"
@@ -578,7 +611,8 @@ class _defs_edit_mesh:
             props_macro = props.TRANSFORM_OT_shrink_fatten
             layout.prop(props_macro, "use_even_offset")
         return dict(
-            text="Extrude Along Normals",
+            idname="Extrude Along Normals",
+            label="Extrude Along Normals",
             icon="ops.mesh.extrude_region_shrink_fatten",
             widget=None,
             operator="mesh.extrude_region_shrink_fatten",
@@ -589,7 +623,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def extrude_individual():
         return dict(
-            text="Extrude Individual",
+            idname="Extrude Individual",
+            label="Extrude Individual",
             icon="ops.mesh.extrude_faces_move",
             widget=None,
             keymap=(),
@@ -602,7 +637,8 @@ class _defs_edit_mesh:
             layout.prop(props, "rotate_source")
 
         return dict(
-            text="Extrude to Cursor",
+            idname="Extrude to Cursor",
+            label="Extrude to Cursor",
             icon="ops.mesh.dupli_extrude_cursor",
             widget=None,
             keymap=(),
@@ -620,7 +656,8 @@ class _defs_edit_mesh:
             layout.prop(props_macro, "correct_uv")
 
         return dict(
-            text="Loop Cut",
+            idname="Loop Cut",
+            label="Loop Cut",
             icon="ops.mesh.loopcut_slide",
             widget="VIEW3D_GGT_mesh_preselect_edgering",
             keymap=(),
@@ -630,7 +667,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def offset_edge_loops_slide():
         return dict(
-            text="Offset Edge Loop Cut",
+            idname="Offset Edge Loop Cut",
+            label="Offset Edge Loop Cut",
             icon="ops.mesh.offset_edge_loops_slide",
             widget=None,
             keymap=(),
@@ -642,7 +680,8 @@ class _defs_edit_mesh:
             props = tool.operator_properties("mesh.vertices_smooth")
             layout.prop(props, "repeat")
         return dict(
-            text="Smooth",
+            idname="Smooth",
+            label="Smooth",
             icon="ops.mesh.vertices_smooth",
             widget="WM_GGT_value_operator_redo",
             keymap=(),
@@ -657,7 +696,8 @@ class _defs_edit_mesh:
             layout.prop(props, "normal")
             layout.prop(props, "seed")
         return dict(
-            text="Randomize",
+            idname="Randomize",
+            label="Randomize",
             icon="ops.transform.vertex_random",
             widget="WM_GGT_value_operator_redo",
             keymap=(),
@@ -672,7 +712,8 @@ class _defs_edit_mesh:
             layout.prop(props, "shear_axis", expand=True)
             _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 2)
         return dict(
-            text="Shear",
+            idname="Shear",
+            label="Shear",
             icon="ops.transform.shear",
             widget="VIEW3D_GGT_xform_shear",
             keymap=(),
@@ -682,7 +723,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def tosphere():
         return dict(
-            text="To Sphere",
+            idname="To Sphere",
+            label="To Sphere",
             icon="ops.transform.tosphere",
             widget=None,
             keymap=(),
@@ -695,7 +737,8 @@ class _defs_edit_mesh:
             layout.prop(props, "use_even_offset")
 
         return dict(
-            text="Shrink/Fatten",
+            idname="Shrink/Fatten",
+            label="Shrink/Fatten",
             icon="ops.transform.shrink_fatten",
             widget=None,
             keymap=(),
@@ -705,7 +748,8 @@ class _defs_edit_mesh:
     @ToolDef.from_fn
     def push_pull():
         return dict(
-            text="Push/Pull",
+            idname="Push/Pull",
+            label="Push/Pull",
             icon="ops.transform.push_pull",
             widget=None,
             keymap=(),
@@ -719,7 +763,8 @@ class _defs_edit_mesh:
             layout.prop(props, "only_selected")
 
         return dict(
-            text="Knife",
+            idname="Knife",
+            label="Knife",
             icon="ops.mesh.knife_tool",
             widget=None,
             keymap=(),
@@ -735,7 +780,8 @@ class _defs_edit_mesh:
             layout.prop(props, "clear_outer")
             layout.prop(props, "threshold")
         return dict(
-            text="Bisect",
+            idname="Bisect",
+            label="Bisect",
             icon="ops.mesh.bisect",
             widget=None,
             keymap=(),
@@ -766,7 +812,8 @@ class _defs_edit_curve:
                 col.prop(cps, "corner_angle")
 
         return dict(
-            text="Draw",
+            idname="Draw",
+            label="Draw",
             cursor='PAINT_BRUSH',
             icon="ops.curve.draw",
             widget=None,
@@ -777,7 +824,8 @@ class _defs_edit_curve:
     @ToolDef.from_fn
     def extrude():
         return dict(
-            text="Extrude",
+            idname="Extrude",
+            label="Extrude",
             icon="ops.curve.extrude_move",
             widget="VIEW3D_GGT_xform_extrude",
             keymap=(),
@@ -787,7 +835,8 @@ class _defs_edit_curve:
     @ToolDef.from_fn
     def extrude_cursor():
         return dict(
-            text="Extrude Cursor",
+            idname="Extrude Cursor",
+            label="Extrude Cursor",
             icon="ops.curve.extrude_cursor",
             widget=None,
             keymap=(),
@@ -796,7 +845,8 @@ class _defs_edit_curve:
     @ToolDef.from_fn
     def tilt():
         return dict(
-            text="Tilt",
+            idname="Tilt",
+            label="Tilt",
             icon="ops.transform.tilt",
             widget=None,
             keymap=(),
@@ -805,7 +855,8 @@ class _defs_edit_curve:
     @ToolDef.from_fn
     def curve_radius():
         return dict(
-            text="Radius",
+            idname="Radius",
+            label="Radius",
             description=(
                 "Expand or contract the radius of the selected curve points"
             ),
@@ -822,7 +873,8 @@ class _defs_edit_curve:
             layout.prop(props, "normal")
             layout.prop(props, "seed")
         return dict(
-            text="Randomize",
+            idname="Randomize",
+            label="Randomize",
             icon="ops.curve.vertex_random",
             widget="WM_GGT_value_operator_redo",
             keymap=(),
@@ -835,7 +887,8 @@ class _defs_pose:
     @ToolDef.from_fn
     def breakdown():
         return dict(
-            text="Breakdowner",
+            idname="Breakdowner",
+            label="Breakdowner",
             icon="ops.pose.breakdowner",
             widget=None,
             keymap=(),
@@ -844,7 +897,8 @@ class _defs_pose:
     @ToolDef.from_fn
     def push():
         return dict(
-            text="Push",
+            idname="Push",
+            label="Push",
             icon="ops.pose.push",
             widget=None,
             keymap=(),
@@ -853,7 +907,8 @@ class _defs_pose:
     @ToolDef.from_fn
     def relax():
         return dict(
-            text="Relax",
+            idname="Relax",
+            label="Relax",
             icon="ops.pose.relax",
             widget=None,
             keymap=(),
@@ -886,7 +941,8 @@ class _defs_sculpt:
     @ToolDef.from_fn
     def hide_border():
         return dict(
-            text="Box Hide",
+            idname="Box Hide",
+            label="Box Hide",
             icon="ops.sculpt.border_hide",
             widget=None,
             keymap=(),
@@ -895,7 +951,8 @@ class _defs_sculpt:
     @ToolDef.from_fn
     def mask_border():
         return dict(
-            text="Box Mask",
+            idname="Box Mask",
+            label="Box Mask",
             icon="ops.sculpt.border_mask",
             widget=None,
             keymap=(),
@@ -960,7 +1017,8 @@ class _defs_weight_paint:
     @ToolDef.from_fn
     def sample_weight():
         return dict(
-            text="Sample Weight",
+            idname="Sample Weight",
+            label="Sample Weight",
             icon="ops.paint.weight_sample",
             widget=None,
             keymap=(),
@@ -969,7 +1027,8 @@ class _defs_weight_paint:
     @ToolDef.from_fn
     def sample_weight_group():
         return dict(
-            text="Sample Vertex Group",
+            idname="Sample Vertex Group",
+            label="Sample Vertex Group",
             icon="ops.paint.weight_sample_group",
             widget=None,
             keymap=(),
@@ -987,7 +1046,8 @@ class _defs_weight_paint:
             layout.prop(props, "type")
 
         return dict(
-            text="Gradient",
+            idname="Gradient",
+            label="Gradient",
             icon="ops.paint.weight_gradient",
             widget=None,
             keymap=(),
@@ -1009,7 +1069,8 @@ class _defs_image_generic:
     @ToolDef.from_fn
     def cursor():
         return dict(
-            text="Cursor",
+            idname="Cursor",
+            label="Cursor",
             description=(
                 "Set the cursor location, drag to transform"
             ),
@@ -1025,7 +1086,8 @@ class _defs_image_generic:
             props = tool.operator_properties("image.sample")
             layout.prop(props, "size")
         return dict(
-            text="Sample",
+            idname="Sample",
+            label="Sample",
             description=(
                 "Sample pixel values under the cursor"
             ),
@@ -1040,7 +1102,8 @@ class _defs_image_uv_transform:
     @ToolDef.from_fn
     def transform():
         return dict(
-            text="Transform",
+            idname="Transform",
+            label="Transform",
             description=(
                 "Supports any combination of grab, rotate & scale at once"
             ),
@@ -1057,7 +1120,8 @@ class _defs_image_uv_select:
         def draw_settings(context, layout, tool):
             pass
         return dict(
-            text="Select",
+            idname="Select",
+            label="Select",
             icon="ops.generic.select",
             widget=None,
             keymap=(),
@@ -1070,7 +1134,8 @@ class _defs_image_uv_select:
             props = tool.operator_properties("uv.select_box")
             layout.prop(props, "mode", expand=True)
         return dict(
-            text="Select Box",
+            idname="Select Box",
+            label="Select Box",
             icon="ops.generic.select_box",
             widget=None,
             keymap=(),
@@ -1083,7 +1148,8 @@ class _defs_image_uv_select:
             props = tool.operator_properties("uv.select_lasso")
             layout.prop(props, "mode", expand=True)
         return dict(
-            text="Select Lasso",
+            idname="Select Lasso",
+            label="Select Lasso",
             icon="ops.generic.select_lasso",
             widget=None,
             keymap=(),
@@ -1097,7 +1163,8 @@ class _defs_image_uv_select:
             layout.prop(props, "mode", expand=True)
             layout.prop(props, "radius")
         return dict(
-            text="Select Circle",
+            idname="Select Circle",
+            label="Select Circle",
             icon="ops.generic.select_circle",
             widget=None,
             keymap=(),
@@ -1134,7 +1201,8 @@ class _defs_gpencil_paint:
     @ToolDef.from_fn
     def cutter():
         return dict(
-            text="Cutter",
+            idname="Cutter",
+            label="Cutter",
             icon="ops.gpencil.stroke_cutter",
             cursor='KNIFE',
             widget=None,
@@ -1144,7 +1212,8 @@ class _defs_gpencil_paint:
     @ToolDef.from_fn
     def line():
         return dict(
-            text="Line",
+            idname="Line",
+            label="Line",
             icon="ops.gpencil.primitive_line",
             cursor='CROSSHAIR',
             widget=None,
@@ -1154,7 +1223,8 @@ class _defs_gpencil_paint:
     @ToolDef.from_fn
     def box():
         return dict(
-            text="Box",
+            idname="Box",
+            label="Box",
             icon="ops.gpencil.primitive_box",
             cursor='CROSSHAIR',
             widget=None,
@@ -1164,7 +1234,8 @@ class _defs_gpencil_paint:
     @ToolDef.from_fn
     def circle():
         return dict(
-            text="Circle",
+            idname="Circle",
+            label="Circle",
             icon="ops.gpencil.primitive_circle",
             cursor='CROSSHAIR',
             widget=None,
@@ -1174,7 +1245,8 @@ class _defs_gpencil_paint:
     @ToolDef.from_fn
     def arc():
         return dict(
-            text="Arc",
+            idname="Arc",
+            label="Arc",
             icon="ops.gpencil.primitive_arc",
             cursor='CROSSHAIR',
             widget=None,
@@ -1184,7 +1256,8 @@ class _defs_gpencil_paint:
     @ToolDef.from_fn
     def curve():
         return dict(
-            text="Curve",
+            idname="Curve",
+            label="Curve",
             icon="ops.gpencil.primitive_curve",
             cursor='CROSSHAIR',
             widget=None,
@@ -1196,7 +1269,8 @@ class _defs_gpencil_edit:
     @ToolDef.from_fn
     def bend():
         return dict(
-            text="Bend",
+            idname="Bend",
+            label="Bend",
             icon="ops.gpencil.edit_bend",
             widget=None,
             keymap=(),
@@ -1207,7 +1281,8 @@ class _defs_gpencil_edit:
         def draw_settings(context, layout, tool):
             layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
         return dict(
-            text="Select",
+            idname="Select",
+            label="Select",
             icon="ops.generic.select",
             widget=None,
             keymap=(),
@@ -1221,7 +1296,8 @@ class _defs_gpencil_edit:
             layout.prop(props, "mode", expand=True)
             layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
         return dict(
-            text="Select Box",
+            idname="Select Box",
+            label="Select Box",
             icon="ops.generic.select_box",
             widget=None,
             keymap=(),
@@ -1235,7 +1311,8 @@ class _defs_gpencil_edit:
             layout.prop(props, "mode", expand=True)
             layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
         return dict(
-            text="Select Lasso",
+            idname="Select Lasso",
+            label="Select Lasso",
             icon="ops.generic.select_lasso",
             widget=None,
             keymap=(),
@@ -1250,7 +1327,8 @@ class _defs_gpencil_edit:
             layout.prop(props, "radius")
             layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
         return dict(
-            text="Select Circle",
+            idname="Select Circle",
+            label="Select Circle",
             icon="ops.generic.select_circle",
             widget=None,
             keymap=(),
@@ -1260,7 +1338,8 @@ class _defs_gpencil_edit:
     @ToolDef.from_fn
     def radius():
         return dict(
-            text="Radius",
+            idname="Radius",
+            label="Radius",
             description=(
                 "Expand or contract the radius of the selected points"
             ),
@@ -1273,7 +1352,8 @@ class _defs_gpencil_edit:
     @ToolDef.from_fn
     def shear():
         return dict(
-            text="Shear",
+            idname="Shear",
+            label="Shear",
             icon="ops.gpencil.edit_shear",
             widget=None,
             keymap=(),
@@ -1282,7 +1362,8 @@ class _defs_gpencil_edit:
     @ToolDef.from_fn
     def tosphere():
         return dict(
-            text="To Sphere",
+            idname="To Sphere",
+            label="To Sphere",
             icon="ops.transform.tosphere",
             widget=None,
             keymap=(),
@@ -1291,7 +1372,8 @@ class _defs_gpencil_edit:
     @ToolDef.from_fn
     def extrude():
         return dict(
-            text="Extrude",
+            idname="Extrude",
+            label="Extrude",
             icon="ops.gpencil.extrude_move",
             widget="VIEW3D_GGT_xform_extrude",
             keymap=(),
@@ -1330,7 +1412,8 @@ class _defs_node_select:
         def draw_settings(context, layout, tool):
             pass
         return dict(
-            text="Select",
+            idname="Select",
+            label="Select",
             icon="ops.generic.select",
             widget=None,
             keymap="Node Tool: Select",
@@ -1344,7 +1427,8 @@ class _defs_node_select:
             layout.prop(props, "mode", expand=True)
             pass
         return dict(
-            text="Select Box",
+            idname="Select Box",
+            label="Select Box",
             icon="ops.generic.select_box",
             widget=None,
             keymap="Node Tool: Select Box",
@@ -1357,7 +1441,8 @@ class _defs_node_select:
             props = tool.operator_properties("node.select_lasso")
             layout.prop(props, "mode", expand=True)
         return dict(
-            text="Select Lasso",
+            idname="Select Lasso",
+            label="Select Lasso",
             icon="ops.generic.select_lasso",
             widget=None,
             keymap="Node Tool: Select Lasso",
@@ -1371,7 +1456,8 @@ class _defs_node_select:
             layout.prop(props, "mode", expand=True)
             layout.prop(props, "radius")
         return dict(
-            text="Select Circle",
+            idname="Select Circle",
+            label="Select Circle",
             icon="ops.generic.select_circle",
             widget=None,
             keymap="Node Tool: Select Circle",
@@ -1384,7 +1470,8 @@ class _defs_node_edit:
     @ToolDef.from_fn
     def links_cut():
         return dict(
-            text="Links Cut",
+            idname="Links Cut",
+            label="Links Cut",
             icon="ops.node.links_cut",
             widget=None,
             keymap="Node Tool: Links Cut",
index d248252..93a0351 100644 (file)
@@ -295,9 +295,10 @@ class _draw_left_context_mode:
                 return
 
             is_paint = True
-            if tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}:
+            # FIXME: tools must use their own UI drawing!
+            if tool.idname in {"Line", "Box", "Circle", "Arc", "Curve"}:
                 is_paint = False
-            elif tool.name == "Cutter":
+            elif tool.idname == "Cutter":
                 row = layout.row(align=True)
                 row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
                 return
@@ -349,7 +350,8 @@ class _draw_left_context_mode:
             )
             brush_basic_gpencil_paint_settings(layout, context, brush, compact=True)
 
-            if tool.name in {"Arc", "Curve", "Line", "Box", "Circle"}:
+            # FIXME: tools must use their own UI drawing!
+            if tool.idname in {"Arc", "Curve", "Line", "Box", "Circle"}:
                 settings = context.tool_settings.gpencil_sculpt
                 row = layout.row(align=True)
                 row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA')
index f11a934..d98647b 100644 (file)
@@ -255,7 +255,9 @@ class VIEW3D_HT_header(Header):
             )
 
         if object_mode == 'PAINT_GPENCIL':
-            if context.workspace.tools.from_space_view3d_mode(object_mode).name == "Draw":
+            # FIXME: this is bad practice!
+            # Tool options are to be displayed in the topbar.
+            if context.workspace.tools.from_space_view3d_mode(object_mode).idname == "Draw":
                 settings = tool_settings.gpencil_sculpt.guide
                 row = layout.row(align=True)
                 row.prop(settings, "use_guide", text="", icon='GRID')
index d4d5e3d..ad83bce 100644 (file)
@@ -83,7 +83,7 @@ bool UI_but_is_tool(const uiBut *but)
        if (but->optype != NULL) {
                static wmOperatorType *ot = NULL;
                if (ot == NULL) {
-                       ot = WM_operatortype_find("WM_OT_tool_set_by_name", false);
+                       ot = WM_operatortype_find("WM_OT_tool_set_by_id", false);
                }
                if (but->optype == ot) {
                        return true;
index 0da96cc..629de55 100644 (file)
@@ -364,7 +364,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
                return NULL;
        }
 
-       if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_name")) {
+       if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_id")) {
                return NULL;
        }
 
@@ -373,9 +373,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
                return NULL;
        }
 
-       char tool_name[MAX_NAME];
-       RNA_string_get(but->opptr, "name", tool_name);
-       BLI_assert(tool_name[0] != '\0');
+       char tool_id[MAX_NAME];
+       RNA_string_get(but->opptr, "name", tool_id);
+       BLI_assert(tool_id[0] != '\0');
 
        /* We have a tool, now extract the info. */
        uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
@@ -387,13 +387,43 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
 
        /* Title (when icon-only). */
        if (but->drawstr[0] == '\0') {
-               uiTooltipField *field = text_field_add(
-                       data, &(uiTooltipFormat){
-                           .style = UI_TIP_STYLE_NORMAL,
-                           .color_id = UI_TIP_LC_MAIN,
-                           .is_pad = true,
-                       });
-               field->text = BLI_strdup(tool_name);
+               const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+               char expr[256];
+               SNPRINTF(
+                       expr,
+                       "bl_ui.space_toolsystem_common.item_from_id("
+                       "bpy.context, "
+                       "bpy.context.space_data.type, "
+                       "'%s').label",
+                       tool_id);
+               char *expr_result = NULL;
+               bool is_error = false;
+               if (BPY_execute_string_as_string(C, expr_imports, expr, true, &expr_result)) {
+                       if (STREQ(expr_result, "")) {
+                               MEM_freeN(expr_result);
+                               expr_result = NULL;
+                       }
+               }
+               else {
+                       /* Note, this is an exceptional case, we could even remove it
+                        * however there have been reports of tooltips failing, so keep it for now. */
+                       expr_result = BLI_strdup("Internal error!");
+                       is_error = true;
+               }
+
+               if (expr_result != NULL) {
+                       uiTooltipField *field = text_field_add(
+                               data, &(uiTooltipFormat){
+                                   .style = UI_TIP_STYLE_NORMAL,
+                                   .color_id = UI_TIP_LC_MAIN,
+                                   .is_pad = true,
+                               });
+                       field->text = expr_result;
+
+                       if (UNLIKELY(is_error)) {
+                               field->format.color_id = UI_TIP_LC_ALERT;
+                       }
+               }
        }
 
        /* Tip. */
@@ -402,11 +432,11 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
                char expr[256];
                SNPRINTF(
                        expr,
-                       "bl_ui.space_toolsystem_common.description_from_name("
+                       "bl_ui.space_toolsystem_common.description_from_id("
                        "bpy.context, "
                        "bpy.context.space_data.type, "
                        "'%s') + '.'",
-                       tool_name);
+                       tool_id);
 
                char *expr_result = NULL;
                bool is_error = false;
@@ -461,7 +491,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
                        const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
                        if (tool_attr != NULL) {
                                const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
-                               const int i = RNA_enum_from_name(items, tool_name);
+                               const int i = RNA_enum_from_name(items, tool_id);
                                if (i != -1) {
                                        wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true);
                                        PointerRNA op_props;
@@ -504,9 +534,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
                                                wmKeyMap *keymap = (wmKeyMap *)expr_result;
                                                for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
                                                        if (STREQ(kmi->idname, but->optype->idname)) {
-                                                               char tool_name_test[MAX_NAME];
-                                                               RNA_string_get(kmi->ptr, "name", tool_name_test);
-                                                               if (STREQ(tool_name, tool_name_test)) {
+                                                               char tool_id_test[MAX_NAME];
+                                                               RNA_string_get(kmi->ptr, "name", tool_id_test);
+                                                               if (STREQ(tool_id, tool_id_test)) {
                                                                        char buf[128];
                                                                        WM_keymap_item_to_string(kmi, false, buf, sizeof(buf));
                                                                        shortcut = BLI_sprintfN("%s, %s", shortcut_toolbar, buf);
@@ -543,12 +573,12 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
                SNPRINTF(
                        expr,
                        "getattr("
-                       "bl_ui.space_toolsystem_common.keymap_from_name("
+                       "bl_ui.space_toolsystem_common.keymap_from_id("
                        "bpy.context, "
                        "bpy.context.space_data.type, "
                        "'%s'), "
                        "'as_pointer', lambda: 0)()",
-                       tool_name);
+                       tool_id);
 
                intptr_t expr_result = 0;
                if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) {
index 3ae3f43..ab0af6c 100644 (file)
@@ -234,9 +234,8 @@ static void rna_def_workspace_tool(BlenderRNA *brna)
        RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
        RNA_def_struct_ui_text(srna, "Work Space Tool", "");
 
-       prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
-       RNA_def_property_string_sdna(prop, NULL, "idname");
-       RNA_def_property_ui_text(prop, "Name", "");
+       prop = RNA_def_property(srna, "idname", PROP_STRING, PROP_NONE);
+       RNA_def_property_ui_text(prop, "Identifier", "");
        RNA_def_struct_name_property(srna, prop);
 
        prop = RNA_def_property(srna, "index", PROP_INT, PROP_NONE);
index 3f1a30a..f82b1ec 100644 (file)
@@ -133,7 +133,7 @@ void RNA_api_workspace_tool(StructRNA *srna)
        RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT);
        RNA_def_function_ui_description(func, "Set the tool settings");
 
-       parm = RNA_def_string(func, "name", NULL, KMAP_MAX_NAME, "Name", "");
+       parm = RNA_def_string(func, "idname", NULL, MAX_NAME, "Identifier", "");
        RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
 
        /* 'bToolRef_Runtime' */
index 7f9b17d..762c25b 100644 (file)
@@ -54,7 +54,7 @@ struct bToolRef *WM_toolsystem_ref_find(struct WorkSpace *workspace, const bTool
 bool WM_toolsystem_ref_ensure(
         struct WorkSpace *workspace, const bToolKey *tkey,
         struct bToolRef **r_tref);
-struct bToolRef *WM_toolsystem_ref_set_by_name(
+struct bToolRef *WM_toolsystem_ref_set_by_id(
         struct bContext *C, struct WorkSpace *workspace, const bToolKey *tkey,
         const char *name, bool cycle);
 
index b387cd0..6e70511 100644 (file)
@@ -69,7 +69,7 @@ wmKeyMapItem *WM_keymap_add_panel(wmKeyMap *keymap, const char *idname, int type
 /* tool wrapper for WM_keymap_add_item */
 wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier)
 {
-       wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_tool_set_by_name", type, val, modifier, keymodifier);
+       wmKeyMapItem *kmi = WM_keymap_add_item(keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier);
        RNA_string_set(kmi->ptr, "name", idname);
        return kmi;
 }
@@ -169,7 +169,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
        if (STRPREFIX(opname, "WM_OT") ||
            STRPREFIX(opname, "ED_OT_undo"))
        {
-               if (STREQ(opname, "WM_OT_tool_set_by_name")) {
+               if (STREQ(opname, "WM_OT_tool_set_by_id")) {
                        km = WM_keymap_guess_from_context(C);
                }
 
index 1c8bea1..75bcee5 100644 (file)
@@ -728,11 +728,11 @@ static void toolsystem_refresh_screen_from_active_tool(
        }
 }
 
-bToolRef *WM_toolsystem_ref_set_by_name(
+bToolRef *WM_toolsystem_ref_set_by_id(
         bContext *C, WorkSpace *workspace, const bToolKey *tkey,
         const char *name, bool cycle)
 {
-       wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_name", false);
+       wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_id", false);
        /* On startup, Python operatores are not yet loaded. */
        if (ot == NULL) {
                return NULL;
@@ -775,7 +775,7 @@ static void toolsystem_reinit_with_toolref(
                .space_type = tref->space_type,
                .mode = tref->mode,
        };
-       WM_toolsystem_ref_set_by_name(C, workspace, &tkey, tref->idname, false);
+       WM_toolsystem_ref_set_by_id(C, workspace, &tkey, tref->idname, false);
 }
 
 static const char *toolsystem_default_tool(const bToolKey *tkey)