diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 6eb78a3fc80..a9cf2a9708b 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later import bpy -from bpy.types import Menu +from bpy.types import Menu, Panel class BrushAssetShelf: @@ -18,12 +18,43 @@ class BrushAssetShelf: def poll(cls, context): return hasattr(context, "object") and context.object and context.object.mode == cls.mode + @classmethod + def has_tool_with_brush_type(cls, context, brush_type): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + space_type = context.space_data.type + + brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items + + tool_helper_cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + for item in ToolSelectPanelHelper._tools_flatten( + tool_helper_cls.tools_from_context(context, mode=context.mode), + ): + if item is None: + continue + if item.idname in { + "builtin.arc", + "builtin.curve", + "builtin.line", + "builtin.box", + "builtin.circle", + "builtin.polyline", + }: + continue + if item.options is None or ('USE_BRUSHES' not in item.options): + continue + if item.brush_type is not None: + if brush_type_items[item.brush_type].value == brush_type: + return True + + return False + + @classmethod def brush_type_poll(cls, context, asset): from bl_ui.space_toolsystem_common import ToolSelectPanelHelper tool = ToolSelectPanelHelper.tool_active_from_context(context) - if not tool or tool.brush_type == 'ANY': + if not tool: return True if not cls.brush_type_prop or not cls.tool_prop: return True @@ -33,9 +64,14 @@ class BrushAssetShelf: # certain brush type. if asset_brush_type is None: return False - brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items - return brush_type_items[asset_brush_type].identifier == tool.brush_type + # For the general brush that supports any brush type, filter out brushes that show up for + # other tools already. + if tool.brush_type == 'ANY': + return not cls.has_tool_with_brush_type(context, asset_brush_type) + + brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items + return brush_type_items[tool.brush_type].value == asset_brush_type @classmethod def asset_poll(cls, asset): @@ -45,12 +81,13 @@ class BrushAssetShelf: return False context = bpy.context + prefs = context.preferences is_asset_shelf_region = context.region and context.region.type == 'ASSET_SHELF' - # Show all brushes in the permanent asset shelf region. Otherwise filter out brushes that + # Show all brushes in the popup asset shelves. Otherwise filter out brushes that # are incompatible with the tool. - if not is_asset_shelf_region and not cls.brush_type_poll(context, asset): - return False + if is_asset_shelf_region and prefs.view.use_filter_brushes_by_tool: + return cls.brush_type_poll(context, asset) return True @@ -112,6 +149,25 @@ class BrushAssetShelf: ) +class VIEW3D_PT_brush_asset_shelf_filter(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Filter" + bl_parent_id = "ASSETSHELF_PT_display" + + @classmethod + def poll(cls, context): + if context.asset_shelf is None: + return False + return context.asset_shelf.bl_idname == BrushAssetShelf.get_shelf_name_from_context(context) + + def draw(self, context): + layout = self.layout + prefs = context.preferences + + layout.prop(prefs.view, "use_filter_brushes_by_tool", text="By Active Tool") + + class UnifiedPaintPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' @@ -1828,6 +1884,7 @@ def brush_basic_grease_pencil_vertex_settings(layout, context, brush, *, compact classes = ( + VIEW3D_PT_brush_asset_shelf_filter, VIEW3D_MT_tools_projectpaint_clone, ) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 7b3bcfe5e39..1c722615b30 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 29 +#define BLENDER_FILE_SUBVERSION 30 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 9da16394428..d3623c085e4 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -946,7 +946,7 @@ void blo_do_versions_userdef(UserDef *userdef) if (!USER_VERSION_ATLEAST(400, 24)) { /* Clear deprecated USER_MENUFIXEDORDER user flag for reuse. */ - userdef->uiflag &= ~USER_UIFLAG_UNUSED_4; + userdef->uiflag &= ~(1 << 23); } if (!USER_VERSION_ATLEAST(400, 26)) { @@ -1064,6 +1064,10 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->sequencer_editor_flag |= USER_SEQ_ED_CONNECT_STRIPS_BY_DEFAULT; } + if (!USER_VERSION_ATLEAST(403, 30)) { + userdef->uiflag |= USER_FILTER_BRUSHES_BY_TOOL; + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a USER_VERSION_ATLEAST check. diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 7831efd3456..012f2ec026a 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1294,7 +1294,8 @@ typedef enum eUserpref_UI_Flag { USER_ZOOM_TO_MOUSEPOS = (1 << 20), USER_SHOW_FPS = (1 << 21), USER_REGISTER_ALL_USERS = (1 << 22), - USER_UIFLAG_UNUSED_4 = (1 << 23), /* Cleared. */ + /** Actually implemented in .py. */ + USER_FILTER_BRUSHES_BY_TOOL = (1 << 23), USER_CONTINUOUS_MOUSE = (1 << 24), USER_ZOOM_INVERT = (1 << 25), USER_ZOOM_HORIZ = (1 << 26), /* for CONTINUE and DOLLY zoom */ diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index b8092e202a6..aa468079815 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -5323,6 +5323,14 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, nullptr, "uiflag", USER_PLAINMENUS); RNA_def_property_ui_text(prop, "Toolbox Column Layout", "Use a column layout for toolbox"); + prop = RNA_def_property(srna, "use_filter_brushes_by_tool", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "uiflag", USER_FILTER_BRUSHES_BY_TOOL); + RNA_def_property_ui_text(prop, + "Filter Brushes by Tool", + "Only show brushes applicable for the currently active tool in the " + "asset shelf. Stored in the Preferences, which may have to be saved " + "manually if Auto-Save Preferences is disabled"); + static const EnumPropertyItem header_align_items[] = { {0, "NONE", 0, "Keep Existing", "Keep existing header alignment"}, {USER_HEADER_FROM_PREF, "TOP", 0, "Top", "Top aligned on load"},