Commit: 269adfdf23400ec91c0bb18e2c1618ca1aa6e1fa Author: Joseph Eagar Date: Tue Aug 16 16:59:23 2022 -0700 Branches: temp-sculpt-brush-channel https://developer.blender.org/rB269adfdf23400ec91c0bb18e2c1618ca1aa6e1fa
temp-sculpt-brush-channel: Refactor MAKE_PROP macro * MAKE_PROP macro now defines which properties belong to which brush tools and where in the UI (workspace, header, right-click menu) they are visible in. * Added all brush RNA properties to brush_channel_define.h, still have to add the code for evaluating input mappings (except radius). NOTE: the categories are a work in progress. * The brush channel API now takes a ePaintMode in addition to a tool integer. * Added a second Brush Settings panel that's dynamically generated from the ui visiblity flags and categories. * Adding support for PROP_BOOLEAN to the RNA wrapper. =================================================================== M release/scripts/startup/bl_ui/properties_paint_common.py M release/scripts/startup/bl_ui/space_view3d.py M release/scripts/startup/bl_ui/space_view3d_toolbar.py M source/blender/blenkernel/BKE_brush_channel.h M source/blender/blenkernel/intern/brush.cc M source/blender/blenkernel/intern/brush_channel.cc M source/blender/blenkernel/intern/brush_channel_define.h M source/blender/editors/sculpt_paint/paint_ops.c M source/blender/makesdna/DNA_scene_types.h M source/blender/makesrna/intern/rna_brush_channels.c M source/blender/makesrna/intern/rna_scene.c =================================================================== diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9b36f65ded5..da0058db008 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1,6 +1,250 @@ # SPDX-License-Identifier: GPL-2.0-or-later from bpy.types import Menu, Panel +builtin_channel_categories = [ + "Basic", "Paint", "Smooth", "Cloth", "Automasking" +] + +class DynamicBrushCategoryPanel(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Tool" + + @classmethod + def poll(self, context): + ok = context.mode == "SCULPT" and context.tool_settings.sculpt and context.tool_settings.sculpt.brush + ok = ok and len(self.get_channels(context)) > 0 + + return ok + + @classmethod + def get_channels(self, context): + brush = context.tool_settings.sculpt.brush + + idname = self.get_category(self) + + #for ch in brush.channels: + # print(ch.idname, ch.show_in_workspace) + + channels = list(filter(lambda ch: ch.show_in_workspace and ch.category == idname, brush.channels)) + channels.sort(key=lambda ch: ch.ui_order) + + return channels + + def draw(self, context): + layout = self.layout + brush = context.tool_settings.sculpt.brush + + idname = self.get_category() + opt = self.get_options() + + layout.use_property_split = True + + channels = self.get_channels(context) + group = DynamicPaintPanelGen.getGroup(self) + + for ch in channels: + name = ch.name + + # Handle size/unprojected_radius hack. + if ch.idname == "size" and brush.use_locked_size == "SCENE": + continue + if ch.idname == "unprojected_radius": + if brush.use_locked_size != "SCENE": + continue + name = "Radius" + + inserts = group.getInserts(ch.idname) if group else [] + + ok = ch.show_in_workspace + ok = ok and ch.category == idname + + if not ok: + continue + + row = layout if len(inserts) == 0 else layout.row(align=True) + + UnifiedPaintPanel.channel_unified(row, + context, + brush, + ch.idname, + slider=True, + ui_editing=opt["ui_editing"], + show_reorder=opt["show_reorder"], + show_mappings=opt["show_mappings"], + text=name) + + for item in inserts: + if item.sameLine: + item.cb(row) + + for item in inserts: + if not item.sameLine: + item.cb(layout) + +class InsertAfterItem: + def __init__(self, cb, sameLine): + self.cb = cb + self.sameLine = sameLine + +class DynamicPaintPanelGen: + class Group: + def __init__(self, idname, name, prefix, parent): + self.idname = idname + self.name = name + self.prefix = prefix + self.rnaclass = None + self.parent = parent + self.options = {} + self.insert_cbs = {} + + def getInserts(self, key): + return self.insert_cbs[key] if key in self.insert_cbs else [] + + def insertEachAfter(self, insertdict): + #return + for key in insertdict.keys(): + item = insertdict[key] + + if isinstance(item, dict): + callback = item["callback"] + sameLine = item["sameLine"] if "sameLine" in item else False + else: + callback = item + sameLine = False + + self.insertAfter(key, callback, sameLine) + + def insertAfter(self, key, cb, sameLine=True): + """ + example: + group.insertAfter("color", lambda layout: layout.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False)) + """ + if key not in self.insert_cbs: + self.insert_cbs[key] = [] + + self.insert_cbs[key].append(InsertAfterItem(cb, sameLine)) + + groups = {} + + @staticmethod + def getGroup(panel): + idname = panel.bl_idname + + return DynamicPaintPanelGen.groups[idname] if idname in DynamicPaintPanelGen.groups else None + + @staticmethod + def ensureCategory(idname, name=None, prefix="VIEW3D_PT_brush_category_", parent=None, show_reorder=False, ui_editing=False, show_mappings=None): + if name is None: + name = idname + + groupid = prefix + idname.lower() + + if groupid in DynamicPaintPanelGen.groups: + return DynamicPaintPanelGen.groups[groupid] + + group = DynamicPaintPanelGen.Group(idname, name, prefix, parent) + DynamicPaintPanelGen.groups[groupid] = group + + group.options = { + "ui_editing": ui_editing, + "show_reorder": show_reorder, + "show_mappings" : show_mappings + } + + def callback(): + DynamicPaintPanelGen.createPanel(group) + pass + + import bpy + bpy.app.timers.register(callback) + + return group + + @staticmethod + def get(idname, prefix): + return DynamicPaintPanelGen.groups[idname] + + @staticmethod + def createPanel(group): + from bpy.utils import register_class, unregister_class + + from bpy.types import Panel + global classes + + name = group.prefix + group.idname.lower() + name1 = name + name2 = "" + + for c in name: + n = ord(c) + + ok = n >= ord("a") and n <= ord("z") + ok = ok or (n >= ord("A") and n <= ord("Z")) + ok = ok or (n >= ord("0") and n <= ord("9")) + ok = ok or c == "_" + + if not ok: + c = "_" + + name2 += c + name = name2 + + for cls in classes[:]: + if cls.bl_rna.identifier == name: + try: + unregister_class(cls) + except: + print("failed to unregister", name) + + classes.remove(cls) + + if group.parent: + parent = 'bl_parent_id = "%s"' % group.parent + else: + parent = "" + + opt = repr(group.options) + + code = """ + +global classes + +class CLASSNAME (DynamicBrushCategoryPanel): + bl_label = "LABEL" + PARENT + + def get_category(self): + return "IDNAME" + + def get_options(self): + return OPT + +register_class(CLASSNAME) +classes.append(CLASSNAME) + +""".strip().replace("CLASSNAME", name).replace("PARENT", parent).replace("LABEL", group.name).replace("OPT", opt) + code = code.replace("IDNAME", group.idname) + + exec(code) + +#custom UI code that is inserted +#for different brush channel properties +insertAfters = { + "color" : { + "sameLine" : True, + "callback" : lambda layout: layout.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) + } +} + +#pre create category panels in correct order +for cat in builtin_channel_categories: + DynamicPaintPanelGen.ensureCategory(cat, cat, parent="VIEW3D_PT_tools_brush_settings_channels", prefix="VIEW3D_PT_brush_category_", + ui_editing=False, show_reorder=True, show_mappings=True).insertEachAfter(insertAfters) + #DynamicPaintPanelGen.ensureCategory(cat, cat, prefix="VIEW3D_PT_brush_category_edit_", + # parent="VIEW3D_PT_tools_brush_settings_channels_preview").insertEachAfter(insertAfters) + + def template_curve(layout, base, propname, full_path, use_negative_slope=None): layout.template_curve_mapping(base, propname, brush=True, use_negative_slope=use_negative_slope) @@ -23,6 +267,14 @@ class BRUSH_PT_channel_panel(Panel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' + @classmethod + def poll(cls, context): + if not context.object: + return False + + mode = context.object.mode + return mode in {"SCULPT", "VERTEX_PAINT", "WEIGHT_PAINT", "TEXTURE_PAINT"} + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -124,6 +376,28 @@ class UnifiedPaintPanel: return mode return None + @staticmethod + def get_channel(context, brush, prop_name, toolsettings_only=False, need_path=False): + ch = brush.channels[prop_name] if prop_name in brush.channels else None + unified_ch = context.tool_settings.unified_channels[prop_name] + + path = None + inherit = False + + if ch: + path = "sculpt.brush.channels[\"%s\"]" % prop_name + inherit = ch.inherit or (unified_ch.unified and not ch.disable_unified) + + if not ch or inherit or toolsettings_only: + ch = context.tool_settings.unified_channels[prop_name] + path = "tool_settings.unified_channels[\"%s\"]" % prop_name + + if need_path: + path = "tool_settings." + path + return (ch, path) + else: + return ch + @staticmethod def paint_settings(context): tool_settings = context.tool_settings @@ -185,8 +459,7 @@ class UnifiedPaintPanel: elif ui_editing is None: ui_editing = True - #XXX - if 1: #not context.tool_settings.unified_paint_settings.brush_editor_mode: + if not context.tool_settings.brush_editor_mode: ui_editing = False show_reorder = False @@ -1384,6 +1657,45 @@ def brush_shared_settings(layout, context, brush, popover=False): if direction: layout.row().prop(brush, "direction", expand=True) +def get_ui_channels(channels, filterkeys=["show_in_workspace"]): + ret = [] + + for ch in channels: + ok = len(filterkeys) == 0 + for key in filterkeys: + if getat @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs