diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py index 7adcd799c0f..96832cbd9c7 100644 --- a/release/scripts/modules/bl_keymap_utils/io.py +++ b/release/scripts/modules/bl_keymap_utils/io.py @@ -22,6 +22,7 @@ # Export Functions __all__ = ( + "_init_properties_from_data", # Shared with gizmo default property initialization. "keyconfig_export_as_data", "keyconfig_import_from_data", "keyconfig_init_from_data", @@ -244,20 +245,24 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False): # ----------------------------------------------------------------------------- # Import Functions +# +# NOTE: unlike export, this runs on startup. +# Take care making changes that could impact performance. -def _kmi_props_setattr(kmi_props, attr, value): - if type(value) is list: - kmi_subprop = getattr(kmi_props, attr) - for subattr, subvalue in value: - _kmi_props_setattr(kmi_subprop, subattr, subvalue) - return - - try: - setattr(kmi_props, attr, value) - except AttributeError: - print(f"Warning: property '{attr}' not found in keymap item '{kmi_props.__class__.__name__}'") - except Exception as ex: - print(f"Warning: {ex!r}") +def _init_properties_from_data(base_props, base_value): + assert(type(base_value) is list) + for attr, value in base_value: + if type(value) is list: + base_props.property_unset(attr) + props = getattr(base_props, attr) + _init_properties_from_data(props, value) + else: + try: + setattr(base_props, attr, value) + except AttributeError: + print(f"Warning: property '{attr}' not found in item '{base_props.__class__.__name__}'") + except Exception as ex: + print(f"Warning: {ex!r}") def keymap_init_from_data(km, km_items, is_modal=False): @@ -271,8 +276,7 @@ def keymap_init_from_data(km, km_items, is_modal=False): if kmi_props_data is not None: kmi_props = kmi.properties assert type(kmi_props_data) is list - for attr, value in kmi_props_data: - _kmi_props_setattr(kmi_props, attr, value) + _init_properties_from_data(kmi_props, kmi_props_data) def keyconfig_init_from_data(kc, keyconfig_data): diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index 1fe73f50639..afa04a18ef6 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -859,6 +859,7 @@ def register_tool(tool_cls, *, after=None, separator=False, group=False): "icon": getattr(tool_cls, "bl_icon", None), "cursor": getattr(tool_cls, "bl_cursor", None), "widget": getattr(tool_cls, "bl_widget", None), + "widget_properties": getattr(tool_cls, "bl_widget_properties", None), "keymap": getattr(tool_cls, "bl_keymap", None), "data_block": getattr(tool_cls, "bl_data_block", None), "operator": getattr(tool_cls, "bl_operator", None), diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index cde430c1e6f..74e0f242299 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -75,6 +75,8 @@ ToolDef = namedtuple( "icon", # An optional cursor to use when this tool is active. "cursor", + # The properties to use for the widget. + "widget_properties", # An optional gizmo group to activate when the tool is set or None for no gizmo. "widget", # Optional key-map for tool, possible values are: @@ -132,6 +134,7 @@ def from_dict(kw_args): "icon": None, "cursor": None, "widget": None, + "widget_properties": None, "keymap": None, "data_block": None, "operator": None, @@ -939,6 +942,21 @@ class WM_MT_toolsystem_submenu(Menu): ).name = item.idname +def _kmi_props_setattr(kmi_props, attr, value): + if type(value) is list: + kmi_subprop = getattr(kmi_props, attr) + for subattr, subvalue in value: + _kmi_props_setattr(kmi_subprop, subattr, subvalue) + return + + try: + setattr(kmi_props, attr, value) + except AttributeError: + print(f"Warning: property '{attr}' not found in keymap item '{kmi_props.__class__.__name__}'") + except Exception as ex: + print(f"Warning: {ex!r}") + + def _activate_by_item(context, space_type, item, index, *, as_fallback=False): cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) tool = ToolSelectPanelHelper._tool_active_from_context(context, space_type, create=True) @@ -983,11 +1001,13 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False): item_fallback, _index = cls._tool_get_active_by_index(context, select_index) # End calculating fallback. + gizmo_group = item.widget or "" + tool.setup( idname=item.idname, keymap=item.keymap[0] if item.keymap is not None else "", cursor=item.cursor or 'DEFAULT', - gizmo_group=item.widget or "", + gizmo_group=gizmo_group, data_block=item.data_block or "", operator=item.operator or "", index=index, @@ -995,6 +1015,24 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False): keymap_fallback=(item_fallback and item_fallback.keymap and item_fallback.keymap[0]) or "", ) + if ( + (gizmo_group != "") and + (props := tool.gizmo_group_properties(gizmo_group)) + ): + if props is None: + print("Error:", gizmo_group, "could not access properties!") + else: + for key in props.bl_rna.properties.keys(): + props.property_unset(key) + + gizmo_properties = item.widget_properties + if gizmo_properties is not None: + if not isinstance(gizmo_properties, list): + raise Exception("expected a list, not a %r" % type(gizmo_properties)) + + from bl_keymap_utils.io import _init_properties_from_data + _init_properties_from_data(props, gizmo_properties) + WindowManager = bpy.types.WindowManager handle_map = _activate_by_item._cursor_draw_handle diff --git a/release/scripts/templates_py/ui_tool_simple.py b/release/scripts/templates_py/ui_tool_simple.py index fc239093b9c..fa81b3b58a9 100644 --- a/release/scripts/templates_py/ui_tool_simple.py +++ b/release/scripts/templates_py/ui_tool_simple.py @@ -53,14 +53,38 @@ class MyOtherTool(WorkSpaceTool): layout.prop(props, "mode") +class MyWidgetTool(WorkSpaceTool): + bl_space_type = 'VIEW_3D' + bl_context_mode = 'OBJECT' + + bl_idname = "my_template.my_gizmo_translate" + bl_label = "My Gizmo Tool" + bl_description = "Short description" + bl_icon = "ops.transform.translate" + bl_widget="VIEW3D_GGT_tool_generic_handle_free" + bl_widget_properties=[ + ("radius", 75.0), + ("backdrop_fill_alpha", 0.0), + ] + bl_keymap = ( + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + ) + + def draw_settings(context, layout, tool): + props = tool.operator_properties("transform.translate") + layout.prop(props, "mode") + + def register(): bpy.utils.register_tool(MyTool, after={"builtin.scale_cage"}, separator=True, group=True) bpy.utils.register_tool(MyOtherTool, after={MyTool.bl_idname}) + bpy.utils.register_tool(MyWidgetTool, after={MyTool.bl_idname}) def unregister(): bpy.utils.unregister_tool(MyTool) bpy.utils.unregister_tool(MyOtherTool) + bpy.utils.unregister_tool(MyWidgetTool) if __name__ == "__main__": diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index ad91af73a71..0e0d59764e5 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -36,6 +36,7 @@ #include "WM_toolsystem.h" #include "RNA_access.h" +#include "RNA_define.h" #include "WM_api.h" #include "WM_message.h" @@ -47,6 +48,9 @@ static const char *handle_normal_id; static const char *handle_free_id; +static const float handle_normal_radius_default = 100.0f; +static const float handle_free_radius_default = 36.0f; + /* -------------------------------------------------------------------- */ /** \name Generic Tool * \{ */ @@ -72,6 +76,7 @@ static bool WIDGETGROUP_tool_generic_poll(const bContext *C, wmGizmoGroupType *g static wmGizmo *tool_generic_create_gizmo(const bContext *C, wmGizmoGroup *gzgroup) { + wmGizmo *gz = WM_gizmo_new("GIZMO_GT_button_2d", gzgroup, NULL); gz->flag |= WM_GIZMO_OPERATOR_TOOL_INIT; @@ -82,8 +87,17 @@ static wmGizmo *tool_generic_create_gizmo(const bContext *C, wmGizmoGroup *gzgro RNA_enum_set(gz->ptr, "icon", ICON_NONE); + bToolRef *tref = WM_toolsystem_ref_from_context((bContext *)C); + PointerRNA gzgt_ptr; + const bool gzgt_ptr_is_valid = WM_toolsystem_ref_properties_get_from_gizmo_group( + tref, gzgroup->type, &gzgt_ptr); + if (gzgroup->type->idname == handle_normal_id) { - gz->scale_basis = 0.12f; + const float radius = (gzgt_ptr_is_valid ? RNA_float_get(&gzgt_ptr, "radius") : + handle_normal_radius_default) / + 12.0f; + + gz->scale_basis = radius / U.gizmo_size; gz->matrix_offset[3][2] -= 12.0; RNA_enum_set(gz->ptr, "draw_options", @@ -91,16 +105,20 @@ static wmGizmo *tool_generic_create_gizmo(const bContext *C, wmGizmoGroup *gzgro ED_GIZMO_BUTTON_SHOW_OUTLINE)); } else { - gz->scale_basis = 0.16f * 3; + const float radius = gzgt_ptr_is_valid ? RNA_float_get(&gzgt_ptr, "radius") : + handle_free_radius_default; + + gz->scale_basis = radius / U.gizmo_size; RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP); /* Make the center low alpha. */ WM_gizmo_set_line_width(gz, 2.0f); - RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.125f); + RNA_float_set(gz->ptr, + "backdrop_fill_alpha", + gzgt_ptr_is_valid ? RNA_float_get(&gzgt_ptr, "backdrop_fill_alpha") : 0.125f); } - bToolRef *tref = WM_toolsystem_ref_from_context((bContext *)C); wmWindowManager *wm = CTX_wm_manager(C); struct wmKeyConfig *kc = wm->defaultconf; @@ -206,6 +224,16 @@ void VIEW3D_GGT_tool_generic_handle_normal(wmGizmoGroupType *gzgt) gzgt->setup = WIDGETGROUP_tool_generic_setup; gzgt->refresh = WIDGETGROUP_tool_generic_refresh; gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe; + + RNA_def_float(gzgt->srna, + "radius", + handle_normal_radius_default, + 0.0f, + 1000.0, + "Radius", + "Radius in pixels", + 0.0f, + 1000.0f); } void VIEW3D_GGT_tool_generic_handle_free(wmGizmoGroupType *gzgt) @@ -224,6 +252,18 @@ void VIEW3D_GGT_tool_generic_handle_free(wmGizmoGroupType *gzgt) gzgt->setup = WIDGETGROUP_tool_generic_setup; gzgt->refresh = WIDGETGROUP_tool_generic_refresh; gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe; + + RNA_def_float(gzgt->srna, + "radius", + handle_free_radius_default, + 0.0f, + 1000.0, + "Radius", + "Radius in pixels", + 0.0f, + 1000.0f); + RNA_def_float( + gzgt->srna, "backdrop_fill_alpha", 0.125, 0.0f, 1.0f, "Backdrop Alpha", "", 0.0f, 1.0f); } /** \} */