From 71dee6474348bf81a5f3fd57496b310dec7bcb52 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 3 Oct 2024 18:45:17 +0200 Subject: [PATCH] Sculpt/Paint: Asset shelf option to filter brushes by the active tool Part of: https://projects.blender.org/blender/blender/issues/128066 Adds a "Filter by Active Tool" option for the brush asset shelves, enabled by default. If enabled, the asset shelf only shows brushes matching the brush type of the current tool. If the general "Brush" tool is active, only brushes that are not covered by another tool are displayed. The popup brush selector always shows all assets. The option is stored in the Preferences, toggled in the "Display Settings" popover of the asset shelf. Note: This is committed to the 4.3 release branch, which was discussed in advance. Pull Request: https://projects.blender.org/blender/blender/pulls/128450 --- .../startup/bl_ui/properties_paint_common.py | 71 +++++++++++++++++-- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_userdef.cc | 6 +- source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_userdef.cc | 8 +++ 5 files changed, 80 insertions(+), 10 deletions(-) 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"},