[Bf-blender-cvs] [269adfdf234] temp-sculpt-brush-channel: temp-sculpt-brush-channel: Refactor MAKE_PROP macro

Joseph Eagar noreply at git.blender.org
Wed Aug 17 02:05:09 CEST 2022


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



More information about the Bf-blender-cvs mailing list