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
This commit is contained in:
Julian Eisel
2024-10-03 18:45:17 +02:00
parent 3628433e6b
commit 71dee64743
5 changed files with 80 additions and 10 deletions

View File

@@ -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,
)

View File

@@ -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

View File

@@ -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.

View File

@@ -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 */

View File

@@ -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"},