From fb74dcc5d69d07f641e318557f679b95e52297a0 Mon Sep 17 00:00:00 2001 From: William Reynish Date: Sat, 14 Dec 2019 18:48:18 +0100 Subject: [PATCH] UI: Brush Settings overhaul This makes a number of changes to the tool settings brush UI: - All brush-related controls are now grouped together, so you can see which items are brush settings are which are not. Previously it was all jumbled together. - The brush picker is in a separate panel, so that you can switch brushes without worrying about the settings, or vice versa. - Custom Icon settings moved from the Display settings(now known as Cursor) to the Brushes panel. - UnifiedPaintSettings panels are removed and the contained options are now next to their relevant setting with a globe icon toggle. This is not displayed in the header. - 2D Falloff and Absolute Jitter toggles were changed into enums, to make it clearer what happens when they are on or off. - Adjust Strength for Spacing option was in the Options panel in some modes, but in the Stroke panel in others. It is now always under Stroke. - Display (now Cursor) panel was reorganized, settings renamed. - 2-option enums are annoying as a drop-down menu, so they are now drawn with expand=True. - Smooth Stroke and Stabilizer options in grease pencil and other paint modes are now both called "Stabilize Stroke", for consistency and clarity. - De-duplicated some drawing code between various painting modes' brush options. I tried to keep de-duplication reasonable and easy to follow. - A few more tweaks - see D5928 for the extensive list. Most of the patch is written by Demeter Dzadik, with some additions by myself Differential Revision: https://developer.blender.org/D5928 Reviewers: Pablo Dobarro, Bastien Montagne, Matias Mendiola --- .../bl_ui/properties_grease_pencil_common.py | 183 +-- .../startup/bl_ui/properties_paint_common.py | 955 ++++++++++++---- release/scripts/startup/bl_ui/space_image.py | 519 ++------- .../startup/bl_ui/space_toolsystem_common.py | 2 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 7 +- release/scripts/startup/bl_ui/space_topbar.py | 129 ++- release/scripts/startup/bl_ui/space_view3d.py | 114 +- .../startup/bl_ui/space_view3d_toolbar.py | 1016 +++++------------ source/blender/makesrna/intern/rna_brush.c | 45 +- 9 files changed, 1367 insertions(+), 1603 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 5fa98c533c3..59c18d4f108 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -131,122 +131,6 @@ class AnnotationDrawingToolsPanel: gpencil_stroke_placement_settings(context, col) -class GreasePencilStrokeEditPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Edit Strokes" - bl_category = "Tools" - bl_region_type = 'TOOLS' - - @classmethod - def poll(cls, context): - if context.gpencil_data is None: - return False - - gpd = context.gpencil_data - return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) - - def draw(self, context): - layout = self.layout - - is_3d_view = context.space_data.type == 'VIEW_3D' - - if not is_3d_view: - layout.label(text="Select:") - col = layout.column(align=True) - col.operator("gpencil.select_all", text="Select All") - col.operator("gpencil.select_box") - col.operator("gpencil.select_circle") - - layout.separator() - - col = layout.column(align=True) - col.operator("gpencil.select_linked") - col.operator("gpencil.select_more") - col.operator("gpencil.select_less") - col.operator("gpencil.select_alternate") - - layout.label(text="Edit:") - row = layout.row(align=True) - row.operator("gpencil.copy", text="Copy") - row.operator("gpencil.paste", text="Paste").type = 'ACTIVE' - row.operator("gpencil.paste", text="Paste by Layer").type = 'LAYER' - - col = layout.column(align=True) - col.operator("gpencil.delete") - col.operator("gpencil.duplicate_move", text="Duplicate") - if is_3d_view: - col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' - col.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps...", property="type") - - layout.separator() - - if not is_3d_view: - col = layout.column(align=True) - col.operator("transform.translate") # icon='MAN_TRANS' - col.operator("transform.rotate") # icon='MAN_ROT' - col.operator("transform.resize", text="Scale") # icon='MAN_SCALE' - - layout.separator() - - layout.separator() - col = layout.column(align=True) - col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction") - col.operator("gpencil.stroke_change_color", text="Assign Material") - - layout.separator() - col = layout.column(align=True) - col.operator("gpencil.stroke_subdivide", text="Subdivide") - row = col.row(align=True) - row.operator("gpencil.stroke_simplify_fixed", text="Simplify") - row.operator("gpencil.stroke_simplify", text="Adaptive") - row.operator("gpencil.stroke_trim", text="Trim") - - col.separator() - - row = col.row(align=True) - row.operator("gpencil.stroke_merge", text="Merge") - row.operator("gpencil.stroke_join", text="Join").type = 'JOIN' - row.operator("gpencil.stroke_join", text="& Copy").type = 'JOINCOPY' - - col.operator("gpencil.stroke_flip", text="Flip Direction") - - if is_3d_view: - layout.separator() - - col = layout.column(align=True) - col.operator_menu_enum("gpencil.stroke_separate", text="Separate...", property="mode") - col.operator("gpencil.stroke_split", text="Split") - - col = layout.column(align=True) - col.label(text="Cleanup:") - col.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") - col.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode") - - -class GreasePencilStrokeSculptPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Sculpt Strokes" - bl_category = "Tools" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.gpencil_sculpt - brush = settings.brush - - layout.template_icon_view(settings, "sculpt_tool", show_labels=True) - - if not self.is_popover: - from bl_ui.properties_paint_common import ( - brush_basic_gpencil_sculpt_settings, - ) - brush_basic_gpencil_sculpt_settings(layout, context, brush) - - class GreasePencilSculptOptionsPanel: bl_label = "Sculpt Strokes" @@ -276,16 +160,35 @@ class GreasePencilSculptOptionsPanel: layout.prop(settings, "use_edit_uv", text="Affect UV") - # GP Object Tool Settings -class GreasePencilAppearancePanel: - bl_label = "Brush Appearance" +class GreasePencilDisplayPanel: + bl_label = "Brush Tip" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): ob = context.active_object - return ob and ob.type == 'GPENCIL' + brush = context.tool_settings.gpencil_paint.brush + if ob and ob.type == 'GPENCIL' and brush: + if context.mode == 'PAINT_GPENCIL': + return brush.gpencil_tool != 'ERASE' + else: + # GP Sculpt and Weight Paint always have Brush Tip panel. + return True + + def draw_header(self, context): + if self.is_popover: return + + if context.mode == 'PAINT_GPENCIL': + brush = context.tool_settings.gpencil_paint.brush + gp_settings = brush.gpencil_settings + + self.layout.prop(gp_settings, "use_cursor", text="") + elif context.mode in ('SCULPT_GPENCIL', 'WEIGHT_GPENCIL'): + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + self.layout.prop(brush, "use_cursor", text="") def draw(self, context): layout = self.layout @@ -299,42 +202,44 @@ class GreasePencilAppearancePanel: brush = tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - sub = layout.column(align=True) - sub.enabled = not brush.use_custom_icon - sub.prop(gp_settings, "gp_icon", text="Icon") + if self.is_popover: + row = layout.row(align=True) + row.prop(gp_settings, "use_cursor", text="") + row.label(text="Display Cursor") - layout.prop(brush, "use_custom_icon") - sub = layout.column() - sub.active = brush.use_custom_icon - sub.prop(brush, "icon_filepath", text="") - - layout.prop(gp_settings, "use_cursor", text="Show Brush") + col = layout.column(align=True) + col.active = gp_settings.use_cursor if brush.gpencil_tool == 'DRAW': - layout.prop(gp_settings, "show_lasso", text="Show Fill Color While Drawing") + col.prop(gp_settings, "show_lasso", text="Show Fill Color While Drawing") if brush.gpencil_tool == 'FILL': - layout.prop(brush, "cursor_color_add", text="Color") + col.prop(brush, "cursor_color_add", text="Cursor Color") elif ob.mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}: settings = tool_settings.gpencil_sculpt brush = settings.brush tool = settings.sculpt_tool + if self.is_popover: + row = layout.row(align=True) + row.prop(brush, "use_cursor", text="") + row.label(text="Display Cursor") + col = layout.column(align=True) - col.prop(brush, "use_cursor", text="Show Brush") + col.active = brush.use_cursor if tool in {'THICKNESS', 'STRENGTH'}: - col.prop(brush, "cursor_color_add", text="Add") - col.prop(brush, "cursor_color_sub", text="Subtract") + col.prop(brush, "cursor_color_add", text="Add Cursor Color") + col.prop(brush, "cursor_color_sub", text="Subtract Cursor Color") elif tool == 'PINCH': - col.prop(brush, "cursor_color_add", text="Pinch") - col.prop(brush, "cursor_color_sub", text="Inflate") + col.prop(brush, "cursor_color_add", text="Pinch Cursor Color") + col.prop(brush, "cursor_color_sub", text="Inflate Cursor Color") elif tool == 'TWIST': - col.prop(brush, "cursor_color_add", text="CCW") - col.prop(brush, "cursor_color_sub", text="CW") + col.prop(brush, "cursor_color_add", text="CCW Cursor Color") + col.prop(brush, "cursor_color_sub", text="CW Cursor Color") else: - col.prop(brush, "cursor_color_add", text="") + col.prop(brush, "cursor_color_add", text="Cursor Color") class GPENCIL_MT_pie_tool_palette(Menu): diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index d8d90fec583..de368bcd20b 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -25,75 +25,462 @@ class UnifiedPaintPanel: # bl_space_type = 'IMAGE_EDITOR' # bl_region_type = 'UI' + @staticmethod + def get_brush_mode(context): + """Get the correct mode for this context. For any context where this returns None, no brush options should be displayed.""" + + if context.mode == 'PARTICLE': + # Particle brush settings currently completely do their own thing. + return None + + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + + if not tool: + # If there is no active tool, then there can't be an active brush. + return None + + if not tool.has_datablock: + # tool.has_datablock is always true for tools that use brushes. + return None + + space_data = context.space_data + tool_settings = context.tool_settings + + if space_data: + space_type = space_data.type + if space_type == 'IMAGE_EDITOR': + if space_data.show_uvedit: + return 'UV_SCULPT' + return 'PAINT_2D' + + if space_type in ('VIEW_3D', 'PROPERTIES'): + if context.mode == 'PAINT_TEXTURE': + if tool_settings.image_paint and tool_settings.image_paint.detect_data(): + return context.mode + else: + return None + return context.mode + @staticmethod def paint_settings(context): tool_settings = context.tool_settings - if context.sculpt_object: + mode = UnifiedPaintPanel.get_brush_mode(context) + + # 3D paint settings + if mode == 'SCULPT': return tool_settings.sculpt - elif context.vertex_paint_object: + elif mode == 'PAINT_VERTEX': return tool_settings.vertex_paint - elif context.weight_paint_object: + elif mode == 'PAINT_WEIGHT': return tool_settings.weight_paint - elif context.image_paint_object: - if (tool_settings.image_paint and tool_settings.image_paint.detect_data()): - return tool_settings.image_paint - - return None - elif context.particle_edit_object: + elif mode == 'PAINT_TEXTURE': + return tool_settings.image_paint + elif mode == 'PARTICLE': return tool_settings.particle_edit - - return None + # 2D paint settings + elif mode == 'PAINT_2D': + return tool_settings.image_paint + elif mode == 'UV_SCULPT': + return tool_settings.uv_sculpt + # Grease Pencil settings + elif mode == 'PAINT_GPENCIL': + return tool_settings.gpencil_paint + elif mode in ('SCULPT_GPENCIL', 'WEIGHT_GPENCIL'): + return tool_settings.gpencil_sculpt @staticmethod - def unified_paint_settings(parent, context): + def prop_unified(layout, context, brush, prop_name, unified_name=None, pressure_name=None, icon='NONE', text=None, slider=False, display_unified_toggle=True): + """ Generalized way of adding brush options to the UI, along with their pen pressure setting and global toggle, if they exist """ + row = layout.row(align=True) ups = context.tool_settings.unified_paint_settings + prop_owner = brush + if(unified_name and getattr(ups, unified_name) and display_unified_toggle): + prop_owner = ups - flow = parent.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + row.prop(prop_owner, prop_name, icon=icon, text=text, slider=slider) - col = flow.column() - col.prop(ups, "use_unified_size", text="Size") - col = flow.column() - col.prop(ups, "use_unified_strength", text="Strength") - if context.weight_paint_object: - col = flow.column() - col.prop(ups, "use_unified_weight", text="Weight") - elif context.vertex_paint_object or context.image_paint_object: - col = flow.column() - col.prop(ups, "use_unified_color", text="Color") - else: - col = flow.column() - col.prop(ups, "use_unified_color", text="Color") + if(pressure_name): + row.prop(brush, pressure_name, text="") - @staticmethod - def prop_unified_size(parent, context, brush, prop_name, *, icon='NONE', text=None, slider=False): - ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_size else brush - parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + if(unified_name and display_unified_toggle): + row.prop(ups, unified_name, text="", icon="WORLD") - @staticmethod - def prop_unified_strength(parent, context, brush, prop_name, *, icon='NONE', text=None, slider=False): - ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_strength else brush - parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) - - @staticmethod - def prop_unified_weight(parent, context, brush, prop_name, *, icon='NONE', text=None, slider=False): - ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_weight else brush - parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + return row @staticmethod def prop_unified_color(parent, context, brush, prop_name, *, text=None): ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_color else brush - parent.prop(ptr, prop_name, text=text) + prop_owner = ups if ups.use_unified_color else brush + parent.prop(prop_owner, prop_name, text=text) @staticmethod def prop_unified_color_picker(parent, context, brush, prop_name, value_slider=True): ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_color else brush - parent.template_color_picker(ptr, prop_name, value_slider=value_slider) + prop_owner = ups if ups.use_unified_color else brush + parent.template_color_picker(prop_owner, prop_name, value_slider=value_slider) + + +### Classes to let various paint modes' panels share code, by sub-classing these classes. ### +class BrushPanel(UnifiedPaintPanel): + @classmethod + def poll(cls, context): + return cls.get_brush_mode(context) is not None + + +class BrushSelectPanel(BrushPanel): + bl_label = "Brushes" + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + brush = settings.brush + + row = layout.row() + large_preview=True + if large_preview: + row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False) + else: + row.column().template_ID(settings, "brush", new="brush.add") + col = row.column() + col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if brush.use_custom_icon: + layout.prop(brush, "icon_filepath", text="") + + +class ColorPalettePanel(BrushPanel): + bl_label = "Color Palette" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + + settings = cls.paint_settings(context) + brush = settings.brush + + if context.space_data.type == 'IMAGE_EDITOR' or context.image_paint_object: + capabilities = brush.image_paint_capabilities + return capabilities.has_color + + elif context.vertex_paint_object: + capabilities = brush.vertex_paint_capabilities + return capabilities.has_color + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + + layout.template_ID(settings, "palette", new="palette.new") + if settings.palette: + layout.template_palette(settings, "palette", color=True) + + +class ClonePanel(BrushPanel): + bl_label = "Clone" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + + settings = cls.paint_settings(context) + + mode = cls.get_brush_mode(context) + if mode in ('PAINT_TEXTURE', 'PAINT_2D'): + brush = settings.brush + return brush.image_tool == 'CLONE' + + def draw_header(self, context): + settings = self.paint_settings(context) + self.layout.prop(settings, "use_clone_layer", text="") + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + brush = settings.brush + + layout.active = settings.use_clone_layer + + ob = context.active_object + col = layout.column() + + if settings.mode == 'MATERIAL': + if len(ob.material_slots) > 1: + col.label(text="Materials") + col.template_list("MATERIAL_UL_matslots", "", + ob, "material_slots", + ob, "active_material_index", rows=2) + + mat = ob.active_material + if mat: + col.label(text="Source Clone Slot") + col.template_list("TEXTURE_UL_texpaintslots", "", + mat, "texture_paint_images", + mat, "paint_clone_slot", rows=2) + + elif settings.mode == 'IMAGE': + mesh = ob.data + + clone_text = mesh.uv_layer_clone.name if mesh.uv_layer_clone else "" + col.label(text="Source Clone Image") + col.template_ID(settings, "clone_image") + col.label(text="Source Clone UV Map") + col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) + + +class TextureMaskPanel(BrushPanel): + bl_label = "Texture Mask" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.tool_settings.image_paint.brush + + col = layout.column() + col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8) + + mask_tex_slot = brush.mask_texture_slot + + # map_mode + layout.row().prop(mask_tex_slot, "mask_map_mode", text="Mask Mapping") + + if mask_tex_slot.map_mode == 'STENCIL': + if brush.mask_texture and brush.mask_texture.type == 'IMAGE': + layout.operator("brush.stencil_fit_image_aspect").mask = True + layout.operator("brush.stencil_reset_transform").mask = True + + col = layout.column() + col.prop(brush, "use_pressure_masking", text="Pressure Masking") + # angle and texture_angle_source + if mask_tex_slot.has_texture_angle: + col = layout.column() + col.prop(mask_tex_slot, "angle", text="Angle") + if mask_tex_slot.has_texture_angle_source: + col.prop(mask_tex_slot, "use_rake", text="Rake") + + if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle: + col.prop(mask_tex_slot, "use_random", text="Random") + if mask_tex_slot.use_random: + col.prop(mask_tex_slot, "random_angle", text="Random Angle") + + # scale and offset + col.prop(mask_tex_slot, "offset") + col.prop(mask_tex_slot, "scale") + + +class StrokePanel(BrushPanel): + bl_label = "Stroke" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + tool_settings = context.tool_settings + mode = self.get_brush_mode(context) + settings = self.paint_settings(context) + brush = settings.brush + + col = layout.column() + + col.prop(brush, "stroke_method") + + if brush.use_anchor: + col.prop(brush, "use_edge_to_edge", text="Edge To Edge") + + if brush.use_airbrush: + col.prop(brush, "rate", text="Rate", slider=True) + + if brush.use_space: + row = col.row(align=True) + row.prop(brush, "spacing", text="Spacing") + row.prop(brush, "use_pressure_spacing", toggle=True, text="") + col.separator() + col.prop(brush, "dash_ratio", text="Dash Ratio") + col.prop(brush, "dash_samples", text="Dash Length") + + if brush.use_line or brush.use_curve: + row = col.row(align=True) + row.prop(brush, "spacing", text="Spacing") + + if brush.use_curve: + col.template_ID(brush, "paint_curve", new="paintcurve.new") + col.operator("paintcurve.draw") + + if mode in ('PAINT_TEXTURE', "PAINT_2D"): + if brush.image_paint_capabilities.has_space_attenuation: + col.prop(brush, "use_space_attenuation") + + if mode == 'SCULPT': + col.row().prop(brush, "use_scene_spacing", text="Spacing Distance", expand=True) + + if brush.sculpt_capabilities.has_space_attenuation: + col.prop(brush, "use_space_attenuation") + + col.separator() + + + if (mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter) or mode != 'SCULPT': + row = col.row(align=True) + if brush.jitter_unit == 'BRUSH': + row.prop(brush, "jitter", slider=True) + else: + row.prop(brush, "jitter_absolute") + row.prop(brush, "use_pressure_jitter", toggle=True, text="") + col.row().prop(brush, "jitter_unit", expand=True) + col.separator() + + col.prop(settings, "input_samples") + + +class SmoothStrokePanel(BrushPanel): + bl_label = "Stabilize Stroke" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + settings = cls.paint_settings(context) + brush = settings.brush + if brush.brush_capabilities.has_smooth_stroke: + return True + + def draw_header(self, context): + settings = self.paint_settings(context) + brush = settings.brush + + self.layout.prop(brush, "use_smooth_stroke", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = self.paint_settings(context) + brush = settings.brush + + col = layout.column() + col.active = brush.use_smooth_stroke + col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + + +class FalloffPanel(BrushPanel): + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + settings = cls.paint_settings(context) + return (settings and settings.brush and settings.brush.curve) + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + mode = self.get_brush_mode(context) + brush = settings.brush + + if brush is None: + return + + col = layout.column(align=True) + row = col.row(align=True) + row.prop(brush, "curve_preset", text="") + + if brush.curve_preset == 'CUSTOM': + layout.template_curve_mapping(brush, "curve", brush=True) + + col = layout.column(align=True) + row = col.row(align=True) + row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' + + if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT'}: + col.separator() + row = col.row(align=True) + row.use_property_split = True + row.use_property_decorate = False + row.prop(brush, "falloff_shape", expand=True) + + +class DisplayPanel(BrushPanel): + bl_label = "Brush Cursor" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + settings = self.paint_settings(context) + if settings and not self.is_popover: + self.layout.prop(settings, "show_brush", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + mode = self.get_brush_mode(context) + settings = self.paint_settings(context) + brush = settings.brush + tex_slot = brush.texture_slot + tex_slot_mask = brush.mask_texture_slot + + if self.is_popover: + row = layout.row(align=True) + row.prop(settings, "show_brush", text="") + row.label(text="Display Cursor") + + col = layout.column() + col.active = brush.brush_capabilities.has_overlay and settings.show_brush + + col.prop(brush, "cursor_color_add", text="Cursor Color") + if mode == 'SCULPT' and brush.sculpt_capabilities.has_secondary_color: + col.prop(brush, "cursor_color_subtract", text="Inverse Cursor Color") + + col.separator() + + row = col.row(align=True) + row.prop(brush, "cursor_overlay_alpha", text="Falloff Opacity") + row.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + row.prop( + brush, "use_cursor_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', + ) + + if mode in ['PAINT_2D', 'PAINT_TEXTURE', 'PAINT_VERTEX', 'SCULPT']: + row = col.row(align=True) + row.prop(brush, "texture_overlay_alpha", text="Texture Opacity") + row.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + if tex_slot.map_mode != 'STENCIL': + row.prop( + brush, "use_primary_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', + ) + + if mode in ['PAINT_TEXTURE', 'PAINT_2D']: + row = col.row(align=True) + row.prop(brush, "mask_overlay_alpha", text="Mask Texture Opacity") + row.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + if tex_slot_mask.map_mode != 'STENCIL': + row.prop( + brush, "use_secondary_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', + ) class VIEW3D_MT_tools_projectpaint_clone(Menu): @@ -108,99 +495,315 @@ class VIEW3D_MT_tools_projectpaint_clone(Menu): props.value = i -def brush_texpaint_common(panel, context, layout, brush, _settings, *, projpaint=False): - col = layout.column() +def brush_settings(layout, context, brush, popover=False): + """ Draw simple brush settings for Sculpt, Texture/Vertex/Weight Paint modes, or skip certain settings for the popover """ - if brush.image_tool == 'FILL' and not projpaint: - col.prop(brush, "fill_threshold", text="Gradient Type", slider=True) + mode = UnifiedPaintPanel.get_brush_mode(context) - elif brush.image_tool == 'SOFTEN': - col.row().prop(brush, "direction", expand=True) - col.prop(brush, "sharp_threshold") - if not projpaint: - col.prop(brush, "blur_kernel_radius") - col.prop(brush, "blur_mode") - elif brush.image_tool == 'MASK': - col.prop(brush, "weight", text="Mask Value", slider=True) + ### Draw simple settings unique to each paint mode. ### + brush_shared_settings(layout, context, brush, popover) - elif brush.image_tool == 'CLONE': - if not projpaint: - col.prop(brush, "clone_image", text="Image") - col.prop(brush, "clone_alpha", text="Alpha") + # Sculpt Mode # + if mode == 'SCULPT': + capabilities = brush.sculpt_capabilities - if not panel.is_popover: - brush_basic_texpaint_settings(col, context, brush) + # normal_radius_factor + layout.prop(brush, "normal_radius_factor", slider=True) + + # auto_smooth_factor and use_inverse_smooth_pressure + if capabilities.has_auto_smooth: + UnifiedPaintPanel.prop_unified(layout, context, brush, "auto_smooth_factor", pressure_name="use_inverse_smooth_pressure", slider=True) + + # topology_rake_factor + if ( + capabilities.has_topology_rake and + context.sculpt_object.use_dynamic_topology_sculpting + ): + layout.prop(brush, "topology_rake_factor", slider=True) + + # normal_weight + if capabilities.has_normal_weight: + layout.prop(brush, "normal_weight", slider=True) + + # crease_pinch_factor + if capabilities.has_pinch_factor: + text = "Pinch" + if brush.sculpt_tool in ('BLOB', 'SNAKE_HOOK'): + text = "Magnify" + layout.prop(brush, "crease_pinch_factor", slider=True, text=text) + + # rake_factor + if capabilities.has_rake_factor: + layout.prop(brush, "rake_factor", slider=True) + + # plane_offset, use_offset_pressure, use_plane_trim, plane_trim + if capabilities.has_plane_offset: + layout.separator() + UnifiedPaintPanel.prop_unified(layout, context, brush, "plane_offset", pressure_name="use_offset_pressure", slider=True) + + layout.prop(brush, "use_plane_trim", text="Plane Trim") + row = layout.row() + row.active = brush.use_plane_trim + row.prop(brush, "plane_trim", slider=True, text="Distance") + layout.separator() + + # height + if capabilities.has_height: + layout.prop(brush, "height", slider=True, text="Height") + + # use_persistent, set_persistent_base + if capabilities.has_persistence: + ob = context.sculpt_object + do_persistent = True + + # not supported yet for this case + for md in ob.modifiers: + if md.type == 'MULTIRES': + do_persistent = False + break + + if do_persistent: + layout.separator() + layout.prop(brush, "use_persistent") + layout.operator("sculpt.set_persistent_base") + layout.separator() + + if brush.sculpt_tool == 'ELASTIC_DEFORM': + layout.separator() + layout.prop(brush, "elastic_deform_type") + layout.prop(brush, "elastic_deform_volume_preservation", slider=True) + layout.separator() + + if brush.sculpt_tool == 'POSE': + row = layout.row() + row.prop(brush, "pose_offset") + + if brush.sculpt_tool == 'SCRAPE': + row = layout.row() + row.prop(brush, "invert_to_scrape_fill", text = "Invert to Fill") + + if brush.sculpt_tool == 'FILL': + row = layout.row() + row.prop(brush, "invert_to_scrape_fill", text = "Invert to Scrape") + + if brush.sculpt_tool == 'GRAB': + layout.prop(brush, "use_grab_active_vertex") + + if brush.sculpt_tool == 'MULTIPLANE_SCRAPE': + col = layout.column() + col.prop(brush, "multiplane_scrape_angle") + col.prop(brush, "use_multiplane_scrape_dynamic") + col.prop(brush, "show_multiplane_scrape_planes_preview") + + if brush.sculpt_tool == 'MASK': + layout.row().prop(brush, "mask_tool", expand=True) + + # 3D and 2D Texture Paint Mode # + elif mode in ('PAINT_TEXTURE', 'PAINT_2D'): + capabilities = brush.image_paint_capabilities + + if brush.image_tool == 'FILL': + if(brush.color_type == 'COLOR' and mode=='PAINT_2D'): # For some reason fill threshold only appears to be implemented in 2D paint. + layout.prop(brush, "fill_threshold", text="Fill Threshold", slider=True) + if(brush.color_type == 'GRADIENT'): + layout.row().prop(brush, "gradient_fill_mode", expand=True) -def brush_texpaint_common_clone(_panel, context, layout, _brush, settings, *, projpaint=False): - ob = context.active_object - col = layout.column() +def brush_shared_settings(layout, context, brush, popover=False): + """ Draw simple brush settings that are shared between different paint modes. """ - if settings.mode == 'MATERIAL': - if len(ob.material_slots) > 1: - col.label(text="Materials") - col.template_list("MATERIAL_UL_matslots", "", - ob, "material_slots", - ob, "active_material_index", rows=2) + mode = UnifiedPaintPanel.get_brush_mode(context) - mat = ob.active_material - if mat: - col.label(text="Source Clone Slot") - col.template_list("TEXTURE_UL_texpaintslots", "", - mat, "texture_paint_images", - mat, "paint_clone_slot", rows=2) + ### Determine which settings to draw. ### + blend_mode = False + size = False + size_mode = False + strength = False + strength_pressure = False + weight = False + direction = False - elif settings.mode == 'IMAGE': - mesh = ob.data + # 3D and 2D Texture Paint # + if mode in ('PAINT_TEXTURE', 'PAINT_2D'): + if not popover: + blend_mode = brush.image_paint_capabilities.has_color + size = brush.image_paint_capabilities.has_radius + strength = strength_pressure = True - clone_text = mesh.uv_layer_clone.name if mesh.uv_layer_clone else "" - col.label(text="Source Clone Image") - col.template_ID(settings, "clone_image") - col.label(text="Source Clone UV Map") - col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) + # Sculpt # + if mode == 'SCULPT': + size_mode = True + if not popover: + size = True + strength = True + strength_pressure = brush.sculpt_capabilities.has_strength_pressure + direction = not brush.sculpt_capabilities.has_direction + + # Vertex Paint # + if mode == 'PAINT_VERTEX': + if not popover: + blend_mode = True + size = True + strength = True + strength_pressure = True + + # Weight Paint # + if mode == 'PAINT_WEIGHT': + if not popover: + size = True + weight = brush.weight_paint_capabilities.has_weight + strength = strength_pressure = True + if(brush.weight_tool=='DRAW'): # Only draw blend mode for the Draw tool, because for other tools it is pointless. D5928#137944 + blend_mode = True + + # UV Sculpt # + if mode == 'UV_SCULPT': + size = True + strength = True + + ### Draw settings. ### + ups = context.scene.tool_settings.unified_paint_settings + + if blend_mode: + layout.prop(brush, "blend", text="Blend") + layout.separator() + + if weight: + UnifiedPaintPanel.prop_unified(layout, context, brush, "weight", unified_name="use_unified_weight", slider=True) + + size_owner = ups if ups.use_unified_size else brush + size_prop = "size" + if size_mode and (size_owner.use_locked_size == 'SCENE'): + size_prop = "unprojected_radius" + if size or size_mode: + if size: + UnifiedPaintPanel.prop_unified(layout, context, brush, size_prop, unified_name="use_unified_size", pressure_name="use_pressure_size", text="Radius", slider=True) + if size_mode: + layout.row().prop(size_owner, "use_locked_size", expand=True) + layout.separator() + + if strength: + pressure_name = "use_pressure_strength" if strength_pressure else None + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", unified_name="use_unified_strength", pressure_name=pressure_name, slider=True) + layout.separator() + + if direction: + layout.row().prop(brush, "direction", expand=True) -def brush_texpaint_common_color(_panel, context, layout, brush, _settings, *, projpaint=False): - UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) +def brush_settings_advanced(layout, context, brush, popover=False): + """Draw advanced brush settings for Sculpt, Texture/Vertex/Weight Paint modes.""" - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") - row.separator() - row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) + mode = UnifiedPaintPanel.get_brush_mode(context) + + # In the popover we want to combine advanced brush settings with non-advanced brush settings. + if popover: + brush_settings(layout, context, brush, popover=True) + layout.separator() + layout.label(text="Advanced:") + + # These options are shared across many modes. + use_accumulate = False + use_frontface = False + + if mode == 'SCULPT': + capabilities = brush.sculpt_capabilities + use_accumulate = capabilities.has_accumulate + use_frontface = True + + # topology automasking + layout.prop(brush, "use_automasking_topology") + + # sculpt plane settings + if capabilities.has_sculpt_plane: + layout.prop(brush, "sculpt_plane") + layout.prop(brush, "use_original_normal") + layout.prop(brush, "use_original_plane") + layout.separator() + + # 3D and 2D Texture Paint # + elif mode in ('PAINT_TEXTURE', 'PAINT_2D'): + capabilities = brush.image_paint_capabilities + use_accumulate = capabilities.has_accumulate + + if mode == 'PAINT_2D': + layout.prop(brush, "use_paint_antialiasing") + else: + layout.prop(brush, "use_alpha") + + # Tool specific settings + if brush.image_tool == 'SOFTEN': + layout.separator() + layout.row().prop(brush, "direction", expand=True) + layout.prop(brush, "sharp_threshold") + if mode == 'PAINT_2D': + layout.prop(brush, "blur_kernel_radius") + layout.prop(brush, "blur_mode") + + elif brush.image_tool == 'MASK': + layout.prop(brush, "weight", text="Mask Value", slider=True) + + elif brush.image_tool == 'CLONE': + if mode == 'PAINT_2D': + layout.prop(brush, "clone_image", text="Image") + layout.prop(brush, "clone_alpha", text="Alpha") + + # Vertex Paint # + elif mode == 'PAINT_VERTEX': + layout.prop(brush, "use_alpha") + if brush.vertex_tool != 'SMEAR': + use_accumulate = True + use_frontface = True + + # Weight Paint + elif mode == 'PAINT_WEIGHT': + if brush.weight_tool != 'SMEAR': + use_accumulate = True + use_frontface = True + + ### Draw shared settings. ### + ups = context.scene.tool_settings.unified_paint_settings + + if use_accumulate: + layout.prop(brush, "use_accumulate") + + if use_frontface: + layout.prop(brush, "use_frontface", text="Front Faces Only") -def brush_texpaint_common_gradient(_panel, context, layout, brush, _settings, *, projpaint=False): - layout.template_color_ramp(brush, "gradient", expand=True) +def draw_color_settings(context, layout, brush, color_type=False): + """Draw color wheel and gradient settings.""" + ups = context.scene.tool_settings.unified_paint_settings - layout.use_property_split = True + if color_type: + row = layout.row() + row.use_property_split = False + row.prop(brush, "color_type", expand=True) - col = layout.column() + # Color wheel + if brush.color_type == 'COLOR': + UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) - if brush.image_tool == 'DRAW': - UnifiedPaintPanel.prop_unified_color(col, context, brush, "secondary_color", text="Background Color") - col.prop(brush, "gradient_stroke_mode", text="Gradient Mapping") - if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: - col.prop(brush, "grad_spacing") - else: # if brush.image_tool == 'FILL': - col.prop(brush, "gradient_fill_mode") + row = layout.row(align=True) + UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") + UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") + row.separator() + row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) + row.prop(ups, "use_unified_color", text="", icon='WORLD') + # Gradient + elif brush.color_type == 'GRADIENT': + layout.template_color_ramp(brush, "gradient", expand=True) + layout.use_property_split = True -def brush_texpaint_common_options(_panel, _context, layout, brush, _settings, *, projpaint=False): - capabilities = brush.image_paint_capabilities + col = layout.column() - col = layout.column() + if brush.image_tool == 'DRAW': + UnifiedPaintPanel.prop_unified(col, context, brush, "secondary_color", unified_name="use_unified_color", text="Background Color", display_unified_toggle=False) - if capabilities.has_accumulate: - col.prop(brush, "use_accumulate") - - if capabilities.has_space_attenuation: - col.prop(brush, "use_space_attenuation") - - if projpaint: - col.prop(brush, "use_alpha") - else: - col.prop(brush, "use_paint_antialiasing") + col.prop(brush, "gradient_stroke_mode", text="Gradient Mapping") + if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: + col.prop(brush, "grad_spacing") # Used in both the View3D toolbar and texture properties @@ -281,94 +884,23 @@ def brush_mask_texture_settings(layout, brush): # scale and offset col.prop(mask_tex_slot, "offset") col.prop(mask_tex_slot, "scale") - -# Basic Brush Options -# -# Share between topbar and brush panel. - - -def brush_basic_wpaint_settings(layout, context, brush, *, compact=False): - capabilities = brush.weight_paint_capabilities - - if capabilities.has_weight: - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_weight(row, context, brush, "weight", slider=True) - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - row.prop(brush, "use_pressure_strength", text="") - - layout.prop(brush, "blend", text="" if compact else "Blend") - - -def brush_basic_vpaint_settings(layout, context, brush, *, compact=False): - capabilities = brush.vertex_paint_capabilities - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - row.prop(brush, "use_pressure_strength", text="") - - if capabilities.has_color: - layout.prop(brush, "blend", text="" if compact else "Blend") - - def brush_basic_texpaint_settings(layout, context, brush, *, compact=False): + """Draw Tool Settings header for Vertex Paint and 2D and 3D Texture Paint modes.""" + # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 capabilities = brush.image_paint_capabilities - if capabilities.has_radius: - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - row.prop(brush, "use_pressure_strength", text="") if capabilities.has_color: + UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") layout.prop(brush, "blend", text="" if compact else "Blend") - -def brush_basic_sculpt_settings(layout, context, brush, *, compact=False): - tool_settings = context.tool_settings - capabilities = brush.sculpt_capabilities - - row = layout.row(align=True) - - ups = tool_settings.unified_paint_settings - if ( - (ups.use_unified_size and ups.use_locked_size == 'SCENE') or - ((not ups.use_unified_size) and brush.use_locked_size == 'SCENE') - ): - UnifiedPaintPanel.prop_unified_size(row, context, brush, "unprojected_radius", slider=True, text="Radius") - else: - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - - row.prop(brush, "use_pressure_size", text="") - - # strength, use_strength_pressure, and use_strength_attenuation - row = layout.row(align=True) - - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - - if capabilities.has_strength_pressure: - row.prop(brush, "use_pressure_strength", text="") - - # direction - if not capabilities.has_direction: - layout.row().prop(brush, "direction", expand=True, **({"text": ""} if compact else {})) + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", pressure_name="use_pressure_size", slider=True, text="Radius") + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", pressure_name="use_pressure_strength") -def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact=True, is_toolbar=False): +def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False): gp_settings = brush.gpencil_settings + tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False) # Brush details if brush.gpencil_tool == 'ERASE': @@ -377,6 +909,8 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY') + row = layout.row(align=True) + row.prop(gp_settings, "eraser_mode", expand=True) if gp_settings.eraser_mode == 'SOFT': row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) @@ -385,6 +919,11 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= row.prop(gp_settings, "eraser_strength_factor") row = layout.row(align=True) row.prop(gp_settings, "eraser_thickness_factor") + + row = layout.row(align=True) + row.prop(gp_settings, "use_cursor", text="Show Brush") + + # FIXME: tools must use their own UI drawing! elif brush.gpencil_tool == 'FILL': row = layout.row(align=True) row.prop(gp_settings, "fill_leak", text="Leak Size") @@ -395,23 +934,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= row = layout.row(align=True) row.prop(gp_settings, "fill_draw_mode", text="Boundary") row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID') - # Fill options - if is_toolbar: - settings = context.tool_settings.gpencil_sculpt - row = layout.row(align=True) - sub = row.row(align=True) - sub.popover( - panel="TOPBAR_PT_gpencil_fill", - text="Fill Options", - ) - else: - row = layout.row(align=True) - row.prop(gp_settings, "fill_factor", text="Resolution") - if gp_settings.fill_draw_mode != 'STROKE': - row = layout.row(align=True) - row.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") - row = layout.row(align=True) - row.prop(gp_settings, "fill_threshold", text="Threshold") + else: # brush.gpencil_tool == 'DRAW': row = layout.row(align=True) row.prop(brush, "size", text="Radius") @@ -427,10 +950,10 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= "builtin.line", "builtin.box", "builtin.circle", - "builtin.polyline", + "builtin.polyline" }: settings = context.tool_settings.gpencil_sculpt - if is_toolbar: + if compact: row = layout.row(align=True) row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA') sub = row.row(align=True) @@ -491,10 +1014,8 @@ def brush_basic_gpencil_weight_settings(layout, _context, brush, *, compact=Fals row = layout.row(align=True) row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - - layout.prop(brush, "use_falloff") - layout.prop(brush, "weight", slider=True) + layout.prop(brush, "use_falloff") classes = ( diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index da1df5bd660..0ccef9c08c8 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -27,12 +27,18 @@ from bpy.types import ( from bl_ui.properties_paint_common import ( UnifiedPaintPanel, brush_texture_settings, - brush_texpaint_common, - brush_texpaint_common_color, - brush_texpaint_common_gradient, - brush_texpaint_common_clone, - brush_texpaint_common_options, - brush_mask_texture_settings, + brush_basic_texpaint_settings, + brush_settings, + brush_settings_advanced, + draw_color_settings, + ClonePanel, + BrushSelectPanel, + TextureMaskPanel, + ColorPalettePanel, + StrokePanel, + SmoothStrokePanel, + FalloffPanel, + DisplayPanel, ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, @@ -44,7 +50,7 @@ from bl_ui.space_toolsystem_common import ( from bpy.app.translations import pgettext_iface as iface_ -class ImagePaintPanel(UnifiedPaintPanel): +class ImagePaintPanel: bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'UI' @@ -59,7 +65,7 @@ class BrushButtonsPanel(UnifiedPaintPanel): return tool_settings.brush -class IMAGE_PT_active_tool(ToolActivePanelHelper, Panel): +class IMAGE_PT_active_tool(Panel, ToolActivePanelHelper): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'UI' bl_category = "Tool" @@ -181,25 +187,6 @@ class IMAGE_MT_select(Menu): layout.operator("uv.select_overlap") -class IMAGE_MT_brush(Menu): - bl_label = "Brush" - - def draw(self, context): - layout = self.layout - tool_settings = context.tool_settings - settings = tool_settings.image_paint - brush = settings.brush - - ups = context.tool_settings.unified_paint_settings - layout.prop(ups, "use_unified_size", text="Unified Size") - layout.prop(ups, "use_unified_strength", text="Unified Strength") - layout.prop(ups, "use_unified_color", text="Unified Color") - layout.separator() - - # Brush tool. - layout.prop_menu_enum(brush, "image_tool") - - class IMAGE_MT_image(Menu): bl_label = "Image" @@ -569,15 +556,16 @@ class IMAGE_HT_tool_header(Header): if tool_mode == 'PAINT': if (tool is not None) and tool.has_datablock: - layout.popover_group( - space_type='IMAGE_EDITOR', - region_type='UI', - context=".paint_common_2d", - category="", - ) + layout.popover("IMAGE_PT_paint_settings_advanced") + layout.popover("IMAGE_PT_paint_stroke") + layout.popover("IMAGE_PT_paint_curve") + layout.popover("IMAGE_PT_tools_brush_display") + layout.popover("IMAGE_PT_tools_brush_texture") + layout.popover("IMAGE_PT_tools_mask_texture") elif tool_mode == 'UV': if (tool is not None) and tool.has_datablock: - layout.popover_group(space_type='IMAGE_EDITOR', region_type='UI', context=".uv_sculpt", category="") + layout.popover("IMAGE_PT_uv_sculpt_curve") + layout.popover("IMAGE_PT_uv_sculpt_options") def draw_mode_settings(self, context): layout = self.layout @@ -601,15 +589,9 @@ class _draw_tool_settings_context_mode: uv_sculpt = tool_settings.uv_sculpt brush = uv_sculpt.brush if brush: - from bl_ui.properties_paint_common import UnifiedPaintPanel - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True) - row.prop(brush, "use_pressure_strength", text="") + # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", pressure_name="use_pressure_size", slider=True) + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", pressure_name="use_pressure_strength", slider=True) @staticmethod def PAINT(context, layout, tool): @@ -623,13 +605,6 @@ class _draw_tool_settings_context_mode: if brush is None: return - from bl_ui.properties_paint_common import ( - UnifiedPaintPanel, - brush_basic_texpaint_settings, - ) - capabilities = brush.image_paint_capabilities - if capabilities.has_color: - UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") brush_basic_texpaint_settings(layout, context, brush, compact=True) @@ -771,8 +746,6 @@ class MASK_MT_editor_menus(Menu): layout.menu("IMAGE_MT_select") if show_maskedit: layout.menu("MASK_MT_select") - if show_paint: - layout.menu("IMAGE_MT_brush") if ima and ima.is_dirty: layout.menu("IMAGE_MT_image", text="Image*") @@ -1101,11 +1074,16 @@ class IMAGE_PT_udim_tiles(Panel): col.operator("image.tile_fill") -class IMAGE_PT_paint(Panel, ImagePaintPanel): - bl_label = "Brush" +class IMAGE_PT_paint_select(Panel, ImagePaintPanel, BrushSelectPanel): + bl_label = "Brushes" bl_context = ".paint_common_2d" bl_category = "Tool" +class IMAGE_PT_paint_settings(Panel, ImagePaintPanel): + bl_context = ".paint_common_2d" + bl_category = "Tool" + bl_label = "Brush Settings" + def draw(self, context): layout = self.layout @@ -1115,17 +1093,31 @@ class IMAGE_PT_paint(Panel, ImagePaintPanel): settings = context.tool_settings.image_paint brush = settings.brush - col = layout.column() - col.template_ID_preview(settings, "brush", new="brush.add", rows=2, cols=6) - if brush: - brush_texpaint_common(self, context, layout, brush, settings) + brush_settings(layout.column(), context, brush, popover=self.is_popover) + + +class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel): + bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" + bl_label = "Advanced" + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + settings = context.tool_settings.image_paint + brush = settings.brush + + brush_settings_advanced(layout.column(), context, brush, self.is_popover) class IMAGE_PT_paint_color(Panel, ImagePaintPanel): - bl_category = "Tool" bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" + bl_parent_id = "IMAGE_PT_paint_settings" + bl_category = "Tool" bl_label = "Color Picker" @classmethod @@ -1141,210 +1133,36 @@ class IMAGE_PT_paint_color(Panel, ImagePaintPanel): settings = context.tool_settings.image_paint brush = settings.brush - layout.prop(brush, "color_type", expand=True) - - if brush.color_type == 'COLOR': - brush_texpaint_common_color(self, context, layout, brush, settings) - elif brush.color_type == 'GRADIENT': - brush_texpaint_common_gradient(self, context, layout, brush, settings) + draw_color_settings(context, layout, brush, color_type=True) -class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel): +class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel, ColorPalettePanel): bl_category = "Tool" bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" + bl_parent_id = "IMAGE_PT_paint_settings" bl_label = "Color Palette" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - capabilities = brush.image_paint_capabilities - return capabilities.has_color - - def draw(self, context): - layout = self.layout - settings = context.tool_settings.image_paint - - layout.template_ID(settings, "palette", new="palette.new") - if settings.palette: - layout.template_palette(settings, "palette", color=True) - - -class IMAGE_PT_paint_clone(Panel, ImagePaintPanel): +class IMAGE_PT_paint_clone(Panel, ImagePaintPanel, ClonePanel): bl_category = "Tool" bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" + bl_parent_id = "IMAGE_PT_paint_settings" bl_label = "Clone from Image/UV Map" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - - return brush.image_tool == 'CLONE' - - def draw_header(self, context): - settings = context.tool_settings.image_paint - self.layout.prop(settings, "use_clone_layer", text="") - - def draw(self, context): - layout = self.layout - settings = context.tool_settings.image_paint - brush = settings.brush - - layout.active = settings.use_clone_layer - - brush_texpaint_common_clone(self, context, layout, brush, settings) -class IMAGE_PT_paint_options(Panel, ImagePaintPanel): - bl_category = "Tool" +class IMAGE_PT_tools_brush_display(Panel, BrushButtonsPanel, DisplayPanel): bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" - bl_label = "Options" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - capabilities = brush.image_paint_capabilities - - return capabilities.has_color - - def draw(self, context): - layout = self.layout - settings = context.tool_settings.image_paint - brush = settings.brush - - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - brush_texpaint_common_options(self, context, layout, brush, settings) - - -class IMAGE_PT_tools_brush_display(BrushButtonsPanel, Panel): - bl_label = "Display" - bl_context = ".paint_common_2d" - bl_options = {'DEFAULT_CLOSED'} - bl_category = "Tool" - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.tool_settings.image_paint - brush = tool_settings.brush - tex_slot = brush.texture_slot - tex_slot_mask = brush.mask_texture_slot - - col = layout.column() - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "cursor_overlay_alpha", text="Curve Alpha") - sub.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - row.prop( - brush, "use_cursor_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', - ) - - col.active = brush.brush_capabilities.has_overlay - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "texture_overlay_alpha", text="Texture Alpha") - sub.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot.map_mode != 'STENCIL': - row.prop( - brush, "use_primary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', - ) - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "mask_overlay_alpha", text="Mask Texture Alpha") - sub.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot_mask.map_mode != 'STENCIL': - row.prop( - brush, "use_secondary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', - ) - - -class IMAGE_PT_tools_brush_display_show_brush(BrushButtonsPanel, Panel): - bl_context = ".paint_common_2d" # dot on purpose (access from topbar) - bl_label = "Show Brush" - bl_parent_id = "IMAGE_PT_tools_brush_display" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" + bl_label = "Brush Tip" bl_options = {'DEFAULT_CLOSED'} - def draw_header(self, context): - settings = context.tool_settings.image_paint - - self.layout.prop(settings, "show_brush", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.image_paint - brush = settings.brush - - col = layout.column() - col.active = settings.show_brush - - if context.sculpt_object and context.tool_settings.sculpt: - if brush.sculpt_capabilities.has_secondary_color: - col.prop(brush, "cursor_color_add", text="Add") - col.prop(brush, "cursor_color_subtract", text="Subtract") - else: - col.prop(brush, "cursor_color_add", text="Color") - else: - col.prop(brush, "cursor_color_add", text="Color") - - -class IMAGE_PT_tools_brush_display_custom_icon(BrushButtonsPanel, Panel): - bl_context = ".paint_common_2d" # dot on purpose (access from topbar) - bl_label = "Custom Icon" - bl_parent_id = "IMAGE_PT_tools_brush_display" - bl_category = "Tool" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - settings = context.tool_settings.image_paint - brush = settings.brush - - self.layout.prop(brush, "use_custom_icon", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.image_paint - brush = settings.brush - - col = layout.column() - col.active = brush.use_custom_icon - col.prop(brush, "icon_filepath", text="") - class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel): bl_label = "Texture" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -1360,135 +1178,36 @@ class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel): brush_texture_settings(col, brush, 0) -class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel): - bl_label = "Texture Mask" +class IMAGE_PT_tools_mask_texture(Panel, BrushButtonsPanel, TextureMaskPanel): bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - - brush = context.tool_settings.image_paint.brush - - col = layout.column() - - col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8) - - brush_mask_texture_settings(col, brush) + bl_label = "Texture Mask" -class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel): +class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel, StrokePanel): bl_label = "Stroke" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - tool_settings = context.tool_settings.image_paint - brush = tool_settings.brush - - layout.use_property_split = True - layout.use_property_decorate = False - - col = layout.column() - - col.prop(brush, "stroke_method") - - if brush.use_anchor: - col.prop(brush, "use_edge_to_edge", text="Edge To Edge") - - if brush.use_airbrush: - col.prop(brush, "rate", text="Rate", slider=True) - - if brush.use_space: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - row.prop(brush, "use_pressure_spacing", toggle=True, text="") - - if brush.use_line or brush.use_curve: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - - if brush.use_curve: - col.template_ID(brush, "paint_curve", new="paintcurve.new") - col.operator("paintcurve.draw") - - row = col.row(align=True) - if brush.use_relative_jitter: - row.prop(brush, "jitter", slider=True) - else: - row.prop(brush, "jitter_absolute") - row.prop(brush, "use_relative_jitter", icon_only=True) - row.prop(brush, "use_pressure_jitter", toggle=True, text="") - - col.prop(tool_settings, "input_samples") - - -class IMAGE_PT_paint_stroke_smooth_stroke(BrushButtonsPanel, Panel): - bl_context = ".paint_common_2d" # dot on purpose (access from topbar) - bl_label = "Smooth Stroke" +class IMAGE_PT_paint_stroke_smooth_stroke(Panel, BrushButtonsPanel, SmoothStrokePanel): + bl_context = ".paint_common_2d" + bl_label = "Stabilize Stroke" bl_parent_id = "IMAGE_PT_paint_stroke" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - if brush.brush_capabilities.has_smooth_stroke: - return True - def draw_header(self, context): - settings = context.tool_settings.image_paint - brush = settings.brush - - self.layout.prop(brush, "use_smooth_stroke", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.image_paint - brush = settings.brush - - col = layout.column() - col.active = brush.use_smooth_stroke - col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) - - -class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel): +class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel, FalloffPanel): bl_label = "Falloff" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - - tool_settings = context.tool_settings.image_paint - brush = tool_settings.brush - - col = layout.column(align=True) - row = col.row(align=True) - row.prop(brush, "curve_preset", text="") - - if brush.curve_preset == 'CUSTOM': - layout.template_curve_mapping(brush, "curve") - - col = layout.column(align=True) - row = col.row(align=True) - row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' - row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' - row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' - row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' - row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' - row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' - class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): bl_context = ".imagepaint_2d" @@ -1507,91 +1226,61 @@ class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): row.prop(ipaint, "tile_x", text="X", toggle=True) row.prop(ipaint, "tile_y", text="Y", toggle=True) - -class IMAGE_PT_uv_sculpt_brush(Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' - bl_context = ".uv_sculpt" # dot on purpose (access from topbar) - bl_category = "Tool" - bl_label = "Brush" - +class UVSculptPanel(UnifiedPaintPanel): @classmethod def poll(cls, context): - sima = context.space_data - # TODO(campbell): nicer way to check if we're in uv sculpt mode. - if sima and sima.show_uvedit: - from bl_ui.space_toolsystem_common import ToolSelectPanelHelper - tool = ToolSelectPanelHelper.tool_active_from_context(context) - if tool.has_datablock: - return True - return False + return cls.get_brush_mode(context) == 'UV_SCULPT' + + +class IMAGE_PT_uv_sculpt_brush_select(Panel, BrushSelectPanel, ImagePaintPanel, UVSculptPanel): + bl_context = ".uv_sculpt" + bl_category = "Tool" + bl_label = "Brushes" + + +class IMAGE_PT_uv_sculpt_brush_settings(Panel, ImagePaintPanel, UVSculptPanel): + bl_context = ".uv_sculpt" + bl_category = "Tool" + bl_label = "Brush Settings" def draw(self, context): - from bl_ui.properties_paint_common import UnifiedPaintPanel layout = self.layout tool_settings = context.tool_settings uvsculpt = tool_settings.uv_sculpt - layout.template_ID(uvsculpt, "brush") - brush = uvsculpt.brush - if not self.is_popover: - if brush: - col = layout.column() - - row = col.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="") - - row = col.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="") - - col = layout.column() - col.prop(tool_settings, "uv_sculpt_lock_borders") - col.prop(tool_settings, "uv_sculpt_all_islands") + brush_settings(layout.column(), context, brush) if brush: if brush.uv_sculpt_tool == 'RELAX': - col.prop(tool_settings, "uv_relax_method") - - col.prop(uvsculpt, "show_brush") + # Although this settings is stored in the scene, it is only used by a single tool, so it doesn't make sense from a user perspective to move it to the Options panel. + layout.prop(tool_settings, "uv_relax_method") -class IMAGE_PT_uv_sculpt_curve(Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' +class IMAGE_PT_uv_sculpt_curve(Panel, FalloffPanel, ImagePaintPanel, UVSculptPanel): bl_context = ".uv_sculpt" # dot on purpose (access from topbar) + bl_parent_id = "IMAGE_PT_uv_sculpt_brush_settings" bl_category = "Tool" bl_label = "Falloff" bl_options = {'DEFAULT_CLOSED'} - poll = IMAGE_PT_uv_sculpt_brush.poll +class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel, UVSculptPanel): + bl_context = ".uv_sculpt" # dot on purpose (access from topbar) + bl_category = "Tool" + bl_label = "Options" def draw(self, context): layout = self.layout tool_settings = context.tool_settings uvsculpt = tool_settings.uv_sculpt - brush = uvsculpt.brush - if brush is not None: - col = layout.column(align=True) - row = col.row(align=True) - row.prop(brush, "curve_preset", text="") - - if brush.curve_preset == 'CUSTOM': - layout.template_curve_mapping(brush, "curve") - - row = layout.row(align=True) - row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' - row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' - row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' - row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' - row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' - row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' + col = layout.column() + col.prop(tool_settings, "uv_sculpt_lock_borders") + col.prop(tool_settings, "uv_sculpt_all_islands") + col.prop(uvsculpt, "show_brush", text="Display Cursor") class ImageScopesPanel: @@ -1765,7 +1454,6 @@ classes = ( IMAGE_MT_view, IMAGE_MT_view_zoom, IMAGE_MT_select, - IMAGE_MT_brush, IMAGE_MT_image, IMAGE_MT_image_invert, IMAGE_MT_uvs, @@ -1797,21 +1485,22 @@ classes = ( IMAGE_PT_view_display, IMAGE_PT_view_display_uv_edit_overlays, IMAGE_PT_view_display_uv_edit_overlays_stretch, - IMAGE_PT_paint, + IMAGE_PT_paint_select, + IMAGE_PT_paint_settings, IMAGE_PT_paint_color, IMAGE_PT_paint_swatches, + IMAGE_PT_paint_settings_advanced, IMAGE_PT_paint_clone, - IMAGE_PT_paint_options, IMAGE_PT_tools_brush_texture, IMAGE_PT_tools_mask_texture, IMAGE_PT_paint_stroke, IMAGE_PT_paint_stroke_smooth_stroke, IMAGE_PT_paint_curve, IMAGE_PT_tools_brush_display, - IMAGE_PT_tools_brush_display_show_brush, - IMAGE_PT_tools_brush_display_custom_icon, IMAGE_PT_tools_imagepaint_symmetry, - IMAGE_PT_uv_sculpt_brush, + IMAGE_PT_uv_sculpt_brush_select, + IMAGE_PT_uv_sculpt_brush_settings, + IMAGE_PT_uv_sculpt_options, IMAGE_PT_uv_sculpt_curve, IMAGE_PT_view_histogram, IMAGE_PT_view_waveform, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 532f5e023b6..65c41bae3f6 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -378,7 +378,7 @@ class ToolSelectPanelHelper: @staticmethod def _tool_active_from_context(context, space_type, mode=None, create=False): - if space_type == 'VIEW_3D': + if space_type in ('VIEW_3D', 'PROPERTIES'): if mode is None: mode = context.mode tool = context.workspace.tools.from_space_view3d_mode(mode, create=create) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index bbcc7097d7c..bd22287aea6 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1135,11 +1135,10 @@ class _defs_weight_paint: def draw_settings(context, layout, tool): brush = context.tool_settings.weight_paint.brush if brush is not None: - from bl_ui.properties_paint_common import UnifiedPaintPanel - UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True) + layout.prop(brush, "weight", slider=True) + layout.prop(brush, "strength", slider=True) props = tool.operator_properties("paint.weight_gradient") - layout.prop(props, "type") + layout.prop(props, "type", expand=True) return dict( idname="builtin.gradient", diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index e69a28d69bf..6316744eca9 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -20,6 +20,7 @@ import bpy from bpy.types import Header, Menu, Panel + class TOPBAR_HT_upper_bar(Header): bl_space_type = 'TOPBAR' @@ -67,7 +68,8 @@ class TOPBAR_HT_upper_bar(Header): layout.template_running_jobs() # Active workspace view-layer is retrieved through window, not through workspace. - layout.template_ID(window, "scene", new="scene.new", unlink="scene.delete") + layout.template_ID(window, "scene", new="scene.new", + unlink="scene.delete") row = layout.row(align=True) row.template_search( @@ -91,9 +93,11 @@ class TOPBAR_PT_tool_settings_extra(Panel): layout = self.layout # Get the active tool - space_type, mode = ToolSelectPanelHelper._tool_key_from_context(context) + space_type, mode = ToolSelectPanelHelper._tool_key_from_context( + context) cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) - item, tool, _ = cls._tool_get_active(context, space_type, mode, with_icon=True) + item, tool, _ = cls._tool_get_active( + context, space_type, mode, with_icon=True) if item is None: return @@ -115,7 +119,8 @@ class TOPBAR_PT_tool_fallback(Panel): ToolSelectPanelHelper.draw_fallback_tool_items(layout, context) if tool_settings.workspace_tool_type == 'FALLBACK': tool = context.tool - ToolSelectPanelHelper.draw_active_tool_fallback(context, layout, tool) + ToolSelectPanelHelper.draw_active_tool_fallback( + context, layout, tool) class TOPBAR_PT_gpencil_layers(Panel): @@ -174,20 +179,25 @@ class TOPBAR_PT_gpencil_layers(Panel): gpl = context.active_gpencil_layer if gpl: - sub.menu("GPENCIL_MT_layer_context_menu", icon='DOWNARROW_HLT', text="") + sub.menu("GPENCIL_MT_layer_context_menu", + icon='DOWNARROW_HLT', text="") if len(gpd.layers) > 1: col.separator() sub = col.column(align=True) - sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' - sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' + sub.operator("gpencil.layer_move", + icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.layer_move", + icon='TRIA_DOWN', text="").type = 'DOWN' col.separator() sub = col.column(align=True) - sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', text="").affect_visibility = True - sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', + text="").affect_visibility = True + sub.operator("gpencil.layer_isolate", icon='LOCKED', + text="").affect_visibility = False class TOPBAR_MT_editor_menus(Menu): @@ -229,7 +239,8 @@ class TOPBAR_MT_app(Menu): layout.separator() - layout.operator("preferences.app_template_install", text="Install Application Template...") + layout.operator("preferences.app_template_install", + text="Install Application Template...") class TOPBAR_MT_file(Menu): @@ -325,7 +336,8 @@ class TOPBAR_MT_file_new(Menu): # Draw application templates. if not use_more: - props = layout.operator("wm.read_homefile", text="General", icon=icon) + props = layout.operator( + "wm.read_homefile", text="General", icon=icon) props.app_template = "" for d in paths: @@ -370,7 +382,8 @@ class TOPBAR_MT_file_defaults(Menu): app_template = None if app_template: - layout.label(text=bpy.path.display_name(app_template, has_ext=False)) + layout.label(text=bpy.path.display_name( + app_template, has_ext=False)) layout.operator("wm.save_homefile") props = layout.operator("wm.read_factory_settings") @@ -384,12 +397,15 @@ class TOPBAR_MT_app_about(Menu): def draw(self, _context): layout = self.layout - layout.operator("wm.url_open_preset", text="Release Notes", icon='URL').type = 'RELEASE_NOTES' + layout.operator("wm.url_open_preset", text="Release Notes", + icon='URL').type = 'RELEASE_NOTES' layout.separator() - layout.operator("wm.url_open_preset", text="Blender Website", icon='URL').type = 'BLENDER' - layout.operator("wm.url_open_preset", text="Credits", icon='URL').type = 'CREDITS' + layout.operator("wm.url_open_preset", + text="Blender Website", icon='URL').type = 'BLENDER' + layout.operator("wm.url_open_preset", text="Credits", + icon='URL').type = 'CREDITS' layout.separator() @@ -404,7 +420,8 @@ class TOPBAR_MT_app_support(Menu): def draw(self, _context): layout = self.layout - layout.operator("wm.url_open_preset", text="Development Fund", icon='FUND').type = 'FUND' + layout.operator("wm.url_open_preset", + text="Development Fund", icon='FUND').type = 'FUND' layout.separator() @@ -417,7 +434,8 @@ class TOPBAR_MT_templates_more(Menu): bl_label = "Templates" def draw(self, context): - bpy.types.TOPBAR_MT_file_new.draw_ex(self.layout, context, use_more=True) + bpy.types.TOPBAR_MT_file_new.draw_ex( + self.layout, context, use_more=True) class TOPBAR_MT_file_import(Menu): @@ -426,7 +444,8 @@ class TOPBAR_MT_file_import(Menu): def draw(self, _context): if bpy.app.build_options.collada: - self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)") + self.layout.operator("wm.collada_import", + text="Collada (Default) (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_import", text="Alembic (.abc)") @@ -437,11 +456,13 @@ class TOPBAR_MT_file_export(Menu): def draw(self, context): if bpy.app.build_options.collada: - self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)") + self.layout.operator("wm.collada_export", + text="Collada (Default) (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_export", text="Alembic (.abc)") if bpy.app.build_options.usd and context.preferences.experimental.use_usd_exporter: - self.layout.operator("wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)") + self.layout.operator( + "wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)") class TOPBAR_MT_file_external_data(Menu): @@ -494,8 +515,10 @@ class TOPBAR_MT_render(Menu): rd = context.scene.render - layout.operator("render.render", text="Render Image", icon='RENDER_STILL').use_viewport = True - props = layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION') + layout.operator("render.render", text="Render Image", + icon='RENDER_STILL').use_viewport = True + props = layout.operator( + "render.render", text="Render Animation", icon='RENDER_ANIMATION') props.animation = True props.use_viewport = True @@ -537,7 +560,8 @@ class TOPBAR_MT_edit(Menu): layout.separator() - layout.operator("wm.search_menu", text="Operator Search...", icon='VIEWZOOM') + layout.operator("wm.search_menu", + text="Operator Search...", icon='VIEWZOOM') layout.separator() @@ -556,7 +580,8 @@ class TOPBAR_MT_edit(Menu): layout.separator() - layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES') + layout.operator("screen.userpref_show", + text="Preferences...", icon='PREFERENCES') class TOPBAR_MT_window(Menu): @@ -576,8 +601,10 @@ class TOPBAR_MT_window(Menu): layout.separator() - layout.operator("screen.workspace_cycle", text="Next Workspace").direction = 'NEXT' - layout.operator("screen.workspace_cycle", text="Previous Workspace").direction = 'PREV' + layout.operator("screen.workspace_cycle", + text="Next Workspace").direction = 'NEXT' + layout.operator("screen.workspace_cycle", + text="Previous Workspace").direction = 'PREV' layout.separator() @@ -604,7 +631,8 @@ class TOPBAR_MT_help(Menu): show_developer = context.preferences.view.show_developer_ui - layout.operator("wm.url_open_preset", text="Manual", icon='HELP').type = 'MANUAL' + layout.operator("wm.url_open_preset", text="Manual", + icon='HELP').type = 'MANUAL' layout.operator( "wm.url_open", text="Tutorials", icon='URL', @@ -637,7 +665,8 @@ class TOPBAR_MT_help(Menu): layout.separator() - layout.operator("wm.url_open_preset", text="Report a Bug", icon='URL').type = 'BUG' + layout.operator("wm.url_open_preset", + text="Report a Bug", icon='URL').type = 'BUG' layout.separator() @@ -666,7 +695,8 @@ class TOPBAR_MT_file_context_menu(Menu): layout.separator() - layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES') + layout.operator("screen.userpref_show", + text="Preferences...", icon='PREFERENCES') class TOPBAR_MT_workspace_menu(Menu): @@ -675,21 +705,26 @@ class TOPBAR_MT_workspace_menu(Menu): def draw(self, _context): layout = self.layout - layout.operator("workspace.duplicate", text="Duplicate", icon='DUPLICATE') + layout.operator("workspace.duplicate", + text="Duplicate", icon='DUPLICATE') if len(bpy.data.workspaces) > 1: layout.operator("workspace.delete", text="Delete", icon='REMOVE') layout.separator() - layout.operator("workspace.reorder_to_front", text="Reorder to Front", icon='TRIA_LEFT_BAR') - layout.operator("workspace.reorder_to_back", text="Reorder to Back", icon='TRIA_RIGHT_BAR') + layout.operator("workspace.reorder_to_front", + text="Reorder to Front", icon='TRIA_LEFT_BAR') + layout.operator("workspace.reorder_to_back", + text="Reorder to Back", icon='TRIA_RIGHT_BAR') layout.separator() # For key binding discoverability. - props = layout.operator("screen.workspace_cycle", text="Previous Workspace") + props = layout.operator("screen.workspace_cycle", + text="Previous Workspace") props.direction = 'PREV' - props = layout.operator("screen.workspace_cycle", text="Next Workspace") + props = layout.operator( + "screen.workspace_cycle", text="Next Workspace") props.direction = 'NEXT' @@ -704,29 +739,8 @@ class TOPBAR_PT_gpencil_primitive(Panel): layout = self.layout # Curve - layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True) - - -# Grease Pencil Fill -class TOPBAR_PT_gpencil_fill(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'HEADER' - bl_label = "Advanced" - - def draw(self, context): - paint = context.tool_settings.gpencil_paint - brush = paint.brush - gp_settings = brush.gpencil_settings - - layout = self.layout - # Fill - row = layout.row(align=True) - row.prop(gp_settings, "fill_factor", text="Resolution") - if gp_settings.fill_draw_mode != 'STROKE': - row = layout.row(align=True) - row.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") - row = layout.row(align=True) - row.prop(gp_settings, "fill_threshold", text="Threshold") + layout.template_curve_mapping( + settings, "thickness_primitive_curve", brush=True) # Only a popover @@ -818,7 +832,6 @@ classes = ( TOPBAR_PT_tool_settings_extra, TOPBAR_PT_gpencil_layers, TOPBAR_PT_gpencil_primitive, - TOPBAR_PT_gpencil_fill, TOPBAR_PT_name, ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a1967d63d2e..fe90cab6e6f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -25,6 +25,7 @@ from bpy.types import ( ) from bl_ui.properties_paint_common import ( UnifiedPaintPanel, + brush_basic_texpaint_settings, ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, @@ -67,7 +68,6 @@ class VIEW3D_HT_tool_header(Header): context, layout, tool_key=('VIEW_3D', tool_mode), ) - # Object Mode Options # ------------------- @@ -79,21 +79,29 @@ class VIEW3D_HT_tool_header(Header): if draw_fn is not None: draw_fn(context, layout, tool) - popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"} + def draw_3d_brush_settings(layout, tool_mode): + layout.popover("VIEW3D_PT_tools_brush_settings_advanced", text="Brush") + if tool_mode != 'PAINT_WEIGHT': + layout.popover("VIEW3D_PT_tools_brush_texture") + if tool_mode == 'PAINT_TEXTURE': + layout.popover("VIEW3D_PT_tools_mask_texture") + layout.popover("VIEW3D_PT_tools_brush_stroke") + layout.popover("VIEW3D_PT_tools_brush_falloff") + layout.popover("VIEW3D_PT_tools_brush_display") # Note: general mode options should be added to 'draw_mode_settings'. if tool_mode == 'SCULPT': if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'PAINT_VERTEX': if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'PAINT_WEIGHT': if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'PAINT_TEXTURE': if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'EDIT_ARMATURE': pass elif tool_mode == 'EDIT_CURVE': @@ -109,11 +117,24 @@ class VIEW3D_HT_tool_header(Header): pass elif tool_mode == 'PAINT_GPENCIL': if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".greasepencil_paint", **popover_kw) + brush = context.tool_settings.gpencil_paint.brush + if brush.gpencil_tool != 'ERASE': + layout.popover("VIEW3D_PT_tools_grease_pencil_brush_advanced") + + if brush.gpencil_tool != 'FILL': + layout.popover("VIEW3D_PT_tools_grease_pencil_brush_stabilizer") + layout.popover("VIEW3D_PT_tools_grease_pencil_brush_random") + layout.popover("VIEW3D_PT_tools_grease_pencil_brushcurves") + + layout.popover("VIEW3D_PT_tools_grease_pencil_paint_appearance") elif tool_mode == 'SCULPT_GPENCIL': - layout.popover_group(context=".greasepencil_sculpt", **popover_kw) + settings = context.tool_settings.gpencil_sculpt + tool = settings.sculpt_tool + if tool in ('SMOOTH', 'RANDOMIZE', 'SMOOTH'): + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options") + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': - layout.popover_group(context=".greasepencil_weight", **popover_kw) + layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance") def draw_mode_settings(self, context): layout = self.layout @@ -230,10 +251,26 @@ class _draw_tool_settings_context_mode: if brush is None: return - from bl_ui.properties_paint_common import ( - brush_basic_sculpt_settings, - ) - brush_basic_sculpt_settings(layout, context, brush, compact=True) + tool_settings = context.tool_settings + capabilities = brush.sculpt_capabilities + + ups = tool_settings.unified_paint_settings + + size = "size" + size_owner = ups if ups.use_unified_size else brush + if size_owner.use_locked_size == 'SCENE': + size = "unprojected_radius" + + # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 + UnifiedPaintPanel.prop_unified(layout, context, brush, size, pressure_name="use_pressure_size", text="Radius", slider=True) + + # strength, use_strength_pressure + pressure_name = "use_pressure_strength" if capabilities.has_strength_pressure else None + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", pressure_name=pressure_name, text="Strength") + + # direction + if not capabilities.has_direction: + layout.row().prop(brush, "direction", expand=True, text="") @staticmethod def PAINT_TEXTURE(context, layout, tool): @@ -247,13 +284,6 @@ class _draw_tool_settings_context_mode: if brush is None: return - from bl_ui.properties_paint_common import ( - UnifiedPaintPanel, - brush_basic_texpaint_settings, - ) - capabilities = brush.image_paint_capabilities - if capabilities.has_color: - UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") brush_basic_texpaint_settings(layout, context, brush, compact=True) @staticmethod @@ -268,14 +298,7 @@ class _draw_tool_settings_context_mode: if brush is None: return - from bl_ui.properties_paint_common import ( - UnifiedPaintPanel, - brush_basic_vpaint_settings, - ) - capabilities = brush.vertex_paint_capabilities - if capabilities.has_color: - UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") - brush_basic_vpaint_settings(layout, context, brush, compact=True) + brush_basic_texpaint_settings(layout, context, brush, compact=True) @staticmethod def PAINT_WEIGHT(context, layout, tool): @@ -288,8 +311,13 @@ class _draw_tool_settings_context_mode: if brush is None: return - from bl_ui.properties_paint_common import brush_basic_wpaint_settings - brush_basic_wpaint_settings(layout, context, brush, compact=True) + # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 + capabilities = brush.weight_paint_capabilities + if capabilities.has_weight: + UnifiedPaintPanel.prop_unified(layout, context, brush, "weight", slider=True) + + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", pressure_name="use_pressure_size", slider=True, text="Radius") + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", pressure_name="use_pressure_strength") @staticmethod def PAINT_GPENCIL(context, layout, tool): @@ -358,7 +386,7 @@ class _draw_tool_settings_context_mode: from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, ) - brush_basic_gpencil_paint_settings(layout, context, brush, tool, compact=True, is_toolbar=True) + brush_basic_gpencil_paint_settings(layout, context, brush, compact=True) @staticmethod def SCULPT_GPENCIL(context, layout, tool): @@ -467,12 +495,11 @@ class VIEW3D_HT_header(Header): show_snap = True else: - from bl_ui.properties_paint_common import UnifiedPaintPanel paint_settings = UnifiedPaintPanel.paint_settings(context) if paint_settings: brush = paint_settings.brush - if brush and brush.stroke_method == 'CURVE': + if brush and hasattr(brush, "stroke_method") and brush.stroke_method == 'CURVE': show_snap = True if show_snap: @@ -6653,6 +6680,7 @@ class VIEW3D_PT_paint_vertex_context_menu(Panel): def draw(self, context): layout = self.layout + brush = context.tool_settings.vertex_paint.brush capabilities = brush.vertex_paint_capabilities @@ -6662,9 +6690,8 @@ class VIEW3D_PT_paint_vertex_context_menu(Panel): UnifiedPaintPanel.prop_unified_color_picker(split, context, brush, "color", value_slider=True) layout.prop(brush, "blend", text="") - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") - + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", unified_name="use_unified_size", pressure_name="use_pressure_size", slider=True) + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", unified_name="use_unified_strength", pressure_name="use_pressure_strength", slider=True) class VIEW3D_PT_paint_texture_context_menu(Panel): # Only for popover, these are dummy values. @@ -6674,6 +6701,7 @@ class VIEW3D_PT_paint_texture_context_menu(Panel): def draw(self, context): layout = self.layout + brush = context.tool_settings.image_paint.brush capabilities = brush.image_paint_capabilities @@ -6684,8 +6712,8 @@ class VIEW3D_PT_paint_texture_context_menu(Panel): layout.prop(brush, "blend", text="") if capabilities.has_radius: - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", unified_name="use_unified_size", pressure_name="use_pressure_size", slider=True) + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", unified_name="use_unified_strength", pressure_name="use_pressure_strength", slider=True) class VIEW3D_PT_paint_weight_context_menu(Panel): @@ -6698,9 +6726,9 @@ class VIEW3D_PT_paint_weight_context_menu(Panel): layout = self.layout brush = context.tool_settings.weight_paint.brush - UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True) - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified(layout, context, brush, "weight", unified_name="use_unified_weight", slider=True) + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", unified_name="use_unified_size", pressure_name="use_pressure_size", slider=True) + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", unified_name="use_unified_strength", pressure_name="use_pressure_strength", slider=True) class VIEW3D_PT_sculpt_context_menu(Panel): @@ -6715,8 +6743,8 @@ class VIEW3D_PT_sculpt_context_menu(Panel): brush = context.tool_settings.sculpt.brush capabilities = brush.sculpt_capabilities - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified(layout, context, brush, "size", unified_name="use_unified_size", pressure_name="use_pressure_size", slider=True) + UnifiedPaintPanel.prop_unified(layout, context, brush, "strength", unified_name="use_unified_strength", pressure_name="use_pressure_strength", slider=True) if capabilities.has_auto_smooth: layout.prop(brush, "auto_smooth_factor", slider=True) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 718365ec99d..6ace1466883 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -19,26 +19,30 @@ # from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, GreasePencilSculptOptionsPanel, - GreasePencilAppearancePanel, + GreasePencilDisplayPanel, ) from bl_ui.properties_paint_common import ( UnifiedPaintPanel, - brush_mask_texture_settings, - brush_texpaint_common, - brush_texpaint_common_color, - brush_texpaint_common_gradient, - brush_texpaint_common_clone, - brush_texpaint_common_options, + BrushSelectPanel, + ClonePanel, + TextureMaskPanel, + ColorPalettePanel, + StrokePanel, + SmoothStrokePanel, + FalloffPanel, + DisplayPanel, brush_texture_settings, + brush_mask_texture_settings, + brush_settings, + brush_settings_advanced, + draw_color_settings, ) from bl_ui.utils import PresetPanel class VIEW3D_MT_brush_context_menu(Menu): - bl_label = "Material Specials" + bl_label = "Brush Specials" def draw(self, context): layout = self.layout @@ -110,9 +114,8 @@ def draw_vpaint_symmetry(layout, vpaint): col.use_property_decorate = False col.prop(vpaint, "radial_symmetry", text="Radial") + # Most of these panels should not be visible in GP edit modes - - def is_not_gpencil_edit_mode(context): is_gpmode = ( context.active_object and @@ -318,27 +321,41 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel): # ********** default tools for paint modes **************** +class TEXTURE_UL_texpaintslots(UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + # mat = data -class View3DPaintPanel(UnifiedPaintPanel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(item, "name", text="", emboss=False, icon_value=icon) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="") + +class View3DPaintPanel(View3DPanel, UnifiedPaintPanel): bl_category = "Tool" +class View3DPaintBrushPanel(View3DPaintPanel): + @classmethod + def poll(cls, context): + mode = cls.get_brush_mode(context) + return mode is not None + + class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Particle tools" + bl_label = "Particle Tool" bl_options = {'HIDE_HEADER'} @classmethod def poll(cls, context): - settings = cls.paint_settings(context) + settings = context.tool_settings.particle_edit return (settings and settings.brush and context.particle_edit_object) def draw(self, context): layout = self.layout - settings = self.paint_settings(context) + settings = context.tool_settings.particle_edit brush = settings.brush tool = settings.tool @@ -371,19 +388,15 @@ class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Brush" +class VIEW3D_PT_tools_brush_select(Panel, View3DPaintBrushPanel, BrushSelectPanel): + bl_context = ".paint_common" + bl_label = "Brushes" - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and - settings.brush and - (context.sculpt_object or - context.vertex_paint_object or - context.weight_paint_object or - context.image_paint_object)) + +# TODO, move to space_view3d.py +class VIEW3D_PT_tools_brush_settings(Panel, View3DPaintBrushPanel): + bl_context = ".paint_common" + bl_label = "Brush Settings" def draw(self, context): layout = self.layout @@ -394,158 +407,29 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): settings = self.paint_settings(context) brush = settings.brush - if not self.is_popover: - row = layout.row() - row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8) - row.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") + brush_settings(layout.column(), context, brush, popover=self.is_popover) - # Sculpt Mode # - if context.sculpt_object and brush: - from bl_ui.properties_paint_common import ( - brush_basic_sculpt_settings, - ) +class VIEW3D_PT_tools_brush_settings_advanced(Panel, View3DPaintBrushPanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" + bl_label = "Advanced" + bl_options = {'DEFAULT_CLOSED'} - capabilities = brush.sculpt_capabilities + def draw(self, context): + layout = self.layout - col = layout.column() + layout.use_property_split = True + layout.use_property_decorate = False # No animation. - if not self.is_popover: - brush_basic_sculpt_settings(col, context, brush) + settings = UnifiedPaintPanel.paint_settings(context) + brush = settings.brush - # normal_radius_factor - col.separator() - row = col.row() - row.prop(brush, "normal_radius_factor", slider=True) - - if brush.sculpt_tool == 'ELASTIC_DEFORM': - col.separator() - row = col.row() - row.prop(brush, "elastic_deform_type") - row = col.row() - row.prop(brush, "elastic_deform_volume_preservation", slider=True) - elif brush.sculpt_tool == 'POSE': - row = col.row() - row.prop(brush, "pose_offset") - row = col.row() - row.prop(brush, "pose_smooth_iterations") - elif brush.sculpt_tool == 'SCRAPE': - row = col.row() - row.prop(brush, "invert_to_scrape_fill", text = "Invert to Fill") - elif brush.sculpt_tool == 'FILL': - row = col.row() - row.prop(brush, "invert_to_scrape_fill", text = "Invert to Scrape") - elif brush.sculpt_tool == 'GRAB': - col.separator() - row = col.row() - row.prop(brush, "use_grab_active_vertex") - elif brush.sculpt_tool == 'MULTIPLANE_SCRAPE': - row = col.row() - row.prop(brush, "multiplane_scrape_angle") - row = col.row() - row.prop(brush, "use_multiplane_scrape_dynamic") - row = col.row() - row.prop(brush, "show_multiplane_scrape_planes_preview") - - # topology_rake_factor - if ( - capabilities.has_topology_rake and - context.sculpt_object.use_dynamic_topology_sculpting - ): - row = col.row() - row.prop(brush, "topology_rake_factor", slider=True) - - # auto_smooth_factor and use_inverse_smooth_pressure - if capabilities.has_auto_smooth: - row = col.row(align=True) - row.prop(brush, "auto_smooth_factor", slider=True) - row.prop(brush, "use_inverse_smooth_pressure", toggle=True, text="") - - # normal_weight - if capabilities.has_normal_weight: - row = col.row(align=True) - row.prop(brush, "normal_weight", slider=True) - - # crease_pinch_factor - if capabilities.has_pinch_factor: - row = col.row(align=True) - if brush.sculpt_tool in {'BLOB', 'SNAKE_HOOK'}: - row.prop(brush, "crease_pinch_factor", slider=True, text="Magnify") - else: - row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch") - - # rake_factor - if capabilities.has_rake_factor: - row = col.row(align=True) - row.prop(brush, "rake_factor", slider=True) - - if brush.sculpt_tool == 'MASK': - col.prop(brush, "mask_tool") - - # plane_offset, use_offset_pressure, use_plane_trim, plane_trim - if capabilities.has_plane_offset: - row = col.row(align=True) - row.prop(brush, "plane_offset", slider=True) - row.prop(brush, "use_offset_pressure", text="") - - col.separator() - - row = col.row() - row.prop(brush, "use_plane_trim", text="Plane Trim") - row = col.row() - row.active = brush.use_plane_trim - row.prop(brush, "plane_trim", slider=True, text="Distance") - - # height - if capabilities.has_height: - row = col.row() - row.prop(brush, "height", slider=True, text="Height") - - # use_persistent, set_persistent_base - if capabilities.has_persistence: - ob = context.sculpt_object - do_persistent = True - - # not supported yet for this case - for md in ob.modifiers: - if md.type == 'MULTIRES': - do_persistent = False - break - - if do_persistent: - col.prop(brush, "use_persistent") - col.operator("sculpt.set_persistent_base") - - # Texture Paint Mode # - - elif context.image_paint_object and brush: - brush_texpaint_common(self, context, layout, brush, settings, projpaint=True) - - # Weight Paint Mode # - elif context.weight_paint_object and brush: - from bl_ui.properties_paint_common import ( - brush_basic_wpaint_settings, - ) - - col = layout.column() - - if not self.is_popover: - brush_basic_wpaint_settings(col, context, brush) - - # Vertex Paint Mode # - elif context.vertex_paint_object and brush: - from bl_ui.properties_paint_common import ( - brush_basic_vpaint_settings, - ) - - col = layout.column() - - if not self.is_popover: - brush_basic_vpaint_settings(col, context, brush) + brush_settings_advanced(layout.column(), context, brush, self.is_popover) class VIEW3D_PT_tools_brush_color(Panel, View3DPaintPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Color Picker" @classmethod @@ -565,138 +449,22 @@ class VIEW3D_PT_tools_brush_color(Panel, View3DPaintPanel): settings = self.paint_settings(context) brush = settings.brush - if context.vertex_paint_object: - brush_texpaint_common_color(self, context, layout, brush, settings, projpaint=True) - - else: - layout.prop(brush, "color_type", expand=True) - - if brush.color_type == 'COLOR': - brush_texpaint_common_color(self, context, layout, brush, settings, projpaint=True) - elif brush.color_type == 'GRADIENT': - brush_texpaint_common_gradient(self, context, layout, brush, settings, projpaint=True) + draw_color_settings(context, layout, brush, color_type = not context.vertex_paint_object) -class VIEW3D_PT_tools_brush_swatches(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" +class VIEW3D_PT_tools_brush_swatches(Panel, View3DPaintPanel, ColorPalettePanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Color Palette" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - brush = settings.brush - if context.image_paint_object: - capabilities = brush.image_paint_capabilities - return capabilities.has_color - elif context.vertex_paint_object: - capabilities = brush.vertex_paint_capabilities - return capabilities.has_color - - def draw(self, context): - layout = self.layout - settings = self.paint_settings(context) - - layout.template_ID(settings, "palette", new="palette.new") - if settings.palette: - layout.template_palette(settings, "palette", color=True) - - -class VIEW3D_PT_tools_brush_clone(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" +class VIEW3D_PT_tools_brush_clone(Panel, View3DPaintPanel, ClonePanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Clone from Paint Slot" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - brush = settings.brush - - return brush.image_tool == 'CLONE' - - def draw_header(self, context): - settings = self.paint_settings(context) - self.layout.prop(settings, "use_clone_layer", text="") - - def draw(self, context): - layout = self.layout - settings = self.paint_settings(context) - brush = settings.brush - - layout.active = settings.use_clone_layer - - brush_texpaint_common_clone(self, context, layout, brush, settings, projpaint=True) - - -class VIEW3D_PT_tools_brush_options(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" - bl_label = "Options" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - tool_settings = context.tool_settings - settings = self.paint_settings(context) - brush = settings.brush - capabilities = brush.sculpt_capabilities - - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - col = layout.column() - - if context.image_paint_object and brush: - brush_texpaint_common_options(self, context, layout, brush, settings, projpaint=True) - - elif context.sculpt_object and brush: - col.prop(brush, "use_automasking_topology") - if capabilities.has_accumulate: - col.prop(brush, "use_accumulate") - - UnifiedPaintPanel.prop_unified_size(col, context, brush, "use_locked_size") - - if capabilities.has_sculpt_plane: - col.prop(brush, "sculpt_plane") - col.prop(brush, "use_original_normal") - col.prop(brush, "use_original_plane") - - col.prop(brush, "use_frontface", text="Front Faces Only") - col.prop(brush, "use_projected") - - elif context.weight_paint_object and brush: - - if brush.weight_tool != 'SMEAR': - col.prop(brush, "use_accumulate") - - col.prop(brush, "use_frontface", text="Front Faces Only") - col.prop(brush, "use_projected") - col.prop(tool_settings, "use_auto_normalize", text="Auto Normalize") - col.prop(tool_settings, "use_multipaint", text="Multi-Paint") - - elif context.vertex_paint_object and brush: - - if brush.vertex_tool != 'SMEAR': - col.prop(brush, "use_accumulate") - - col.prop(brush, "use_alpha") - col.prop(brush, "use_frontface", text="Front Faces Only") - col.prop(brush, "use_projected") - - -class TEXTURE_UL_texpaintslots(UIList): - def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): - # mat = data - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.prop(item, "name", text="", emboss=False, icon_value=icon) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="") - class VIEW3D_MT_tools_projectpaint_uvlayer(Menu): bl_label = "Clone Layer" @@ -782,9 +550,8 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel): layout.separator() layout.operator("image.save_all_modified", text="Save All Images", icon='FILE_TICK') + # TODO, move to space_view3d.py - - class VIEW3D_PT_stencil_projectpaint(View3DPanel, Panel): bl_category = "Tool" bl_context = ".imagepaint" # dot on purpose (access from topbar) @@ -836,73 +603,17 @@ class VIEW3D_PT_stencil_projectpaint(View3DPanel, Panel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush_display(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Display" +class VIEW3D_PT_tools_brush_display(Panel, View3DPaintBrushPanel, DisplayPanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" + bl_label = "Cursor" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and - settings.brush and - (context.sculpt_object or - context.vertex_paint_object or - context.weight_paint_object or - context.image_paint_object)) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - tex_slot = brush.texture_slot - tex_slot_mask = brush.mask_texture_slot - - col = layout.column() - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "cursor_overlay_alpha", text="Curve Alpha") - sub.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - row.prop( - brush, "use_cursor_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', - ) - - col.active = brush.brush_capabilities.has_overlay - - if context.image_paint_object or context.sculpt_object or context.vertex_paint_object: - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "texture_overlay_alpha", text="Texture Alpha") - sub.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot.map_mode != 'STENCIL': - row.prop( - brush, "use_primary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', - ) - - if context.image_paint_object: - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "mask_overlay_alpha", text="Mask Texture Alpha") - sub.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot_mask.map_mode != 'STENCIL': - row.prop( - brush, "use_secondary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', - ) - # TODO, move to space_view3d.py class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Texture" bl_options = {'DEFAULT_CLOSED'} @@ -926,9 +637,10 @@ class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_mask_texture(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_mask_texture(Panel, View3DPaintPanel, TextureMaskPanel): bl_category = "Tool" bl_context = ".imagepaint" # dot on purpose (access from topbar) + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Texture Mask" bl_options = {'DEFAULT_CLOSED'} @@ -950,151 +662,27 @@ class VIEW3D_PT_tools_mask_texture(Panel, View3DPaintPanel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush_stroke(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_brush_stroke(Panel, View3DPaintPanel, StrokePanel): bl_context = ".paint_common" # dot on purpose (access from topbar) bl_label = "Stroke" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and - settings.brush and - (context.sculpt_object or - context.vertex_paint_object or - context.weight_paint_object or - context.image_paint_object)) - def draw(self, context): - layout = self.layout - - settings = self.paint_settings(context) - brush = settings.brush - layout.use_property_split = True - layout.use_property_decorate = False - - col = layout.column() - - col.prop(brush, "stroke_method") - - if brush.use_anchor: - col.prop(brush, "use_edge_to_edge", text="Edge To Edge") - - if brush.use_airbrush: - col.prop(brush, "rate", text="Rate", slider=True) - - if brush.use_space: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - row.prop(brush, "use_pressure_spacing", toggle=True, text="") - col.prop(brush, "dash_ratio") - col.prop(brush, "dash_samples") - - if brush.use_line or brush.use_curve: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - col.prop(brush, "dash_ratio") - col.prop(brush, "dash_samples") - - if brush.use_curve: - col.template_ID(brush, "paint_curve", new="paintcurve.new") - col.operator("paintcurve.draw") - - if context.sculpt_object: - - if brush.sculpt_capabilities.has_space_attenuation: - col.prop(brush, "use_space_attenuation") - - col.prop(brush, "use_scene_spacing") - - if brush.sculpt_capabilities.has_jitter: - - row = col.row(align=True) - if brush.use_relative_jitter: - row.prop(brush, "jitter", slider=True) - else: - row.prop(brush, "jitter_absolute") - row.prop(brush, "use_relative_jitter", icon_only=True) - row.prop(brush, "use_pressure_jitter", toggle=True, text="") - - else: - - row = col.row(align=True) - if brush.use_relative_jitter: - row.prop(brush, "jitter", slider=True) - else: - row.prop(brush, "jitter_absolute") - row.prop(brush, "use_relative_jitter", icon_only=True) - row.prop(brush, "use_pressure_jitter", toggle=True, text="") - - col.prop(settings, "input_samples") - - -class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, SmoothStrokePanel): bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Smooth Stroke" + bl_label = "Stabilize Stroke" bl_parent_id = "VIEW3D_PT_tools_brush_stroke" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - brush = settings.brush - if brush.brush_capabilities.has_smooth_stroke: - return True - - def draw_header(self, context): - settings = self.paint_settings(context) - brush = settings.brush - - self.layout.prop(brush, "use_smooth_stroke", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column() - col.active = brush.use_smooth_stroke - col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) - # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Falloff" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and settings.brush and settings.brush.curve) - - def draw(self, context): - layout = self.layout - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column(align=True) - row = col.row(align=True) - row.prop(brush, "curve_preset", text="") - - if brush.curve_preset == 'CUSTOM': - layout.template_curve_mapping(brush, "curve", brush=True) - - col = layout.column(align=True) - row = col.row(align=True) - row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' - row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' - row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' - row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' - row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' - row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' - class VIEW3D_PT_tools_brush_falloff_frontface(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) @@ -1265,8 +853,6 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): col.operator("object.voxel_remesh", text="Remesh") # TODO, move to space_view3d.py - - class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_label = "Options" @@ -1293,24 +879,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col = flow.column() col.prop(sculpt, "use_deform_only") - -class VIEW3D_PT_sculpt_options_unified(Panel, View3DPaintPanel): - bl_context = ".sculpt_mode" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_sculpt_options" - bl_label = "Unified Brush" - - @classmethod - def poll(cls, context): - return (context.sculpt_object and context.tool_settings.sculpt) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - self.unified_paint_settings(layout, context) - - class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_parent_id = "VIEW3D_PT_sculpt_options" @@ -1409,64 +977,6 @@ class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel): draw = VIEW3D_PT_sculpt_symmetry.draw -class VIEW3D_PT_tools_brush_display_show_brush(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Show Brush" - bl_parent_id = "VIEW3D_PT_tools_brush_display" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - settings = self.paint_settings(context) - - self.layout.prop(settings, "show_brush", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column() - col.active = settings.show_brush - - if context.sculpt_object and context.tool_settings.sculpt: - if brush.sculpt_capabilities.has_secondary_color: - col.prop(brush, "cursor_color_add", text="Add") - col.prop(brush, "cursor_color_subtract", text="Subtract") - else: - col.prop(brush, "cursor_color_add", text="Color") - else: - col.prop(brush, "cursor_color_add", text="Color") - - -class VIEW3D_PT_tools_brush_display_custom_icon(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Custom Icon" - bl_parent_id = "VIEW3D_PT_tools_brush_display" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - settings = self.paint_settings(context) - brush = settings.brush - - self.layout.prop(brush, "use_custom_icon", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column() - col.active = brush.use_custom_icon - col.prop(brush, "icon_filepath", text="") - # ********** default tools for weight-paint **************** @@ -1512,6 +1022,10 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): wpaint = tool_settings.weight_paint col = layout.column() + + col.prop(tool_settings, "use_auto_normalize", text="Auto Normalize") + col.prop(tool_settings, "use_multipaint", text="Multi-Paint") + col.prop(wpaint, "use_group_restrict") obj = context.weight_paint_object @@ -1523,19 +1037,6 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): row.prop(mesh, "use_mirror_topology") -class VIEW3D_PT_tools_weightpaint_options_unified(Panel, View3DPaintPanel): - bl_context = ".weightpaint" - bl_label = "Unified Brush" - bl_parent_id = "VIEW3D_PT_tools_weightpaint_options" - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - self.unified_paint_settings(layout, context) - # ********** default tools for vertex-paint **************** @@ -1545,16 +1046,16 @@ class VIEW3D_PT_tools_vertexpaint_options(Panel, View3DPaintPanel): bl_label = "Options" bl_options = {'DEFAULT_CLOSED'} + @classmethod + def poll(self, context): + # This is currently unused, since there aren't any Vertex Paint mode specific options. + return False + def draw(self, context): layout = self.layout - - layout.label(text="Unified Brush") - layout.use_property_split = True layout.use_property_decorate = False - self.unified_paint_settings(layout, context) - # TODO, move to space_view3d.py class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel): @@ -1676,19 +1177,6 @@ class VIEW3D_PT_tools_imagepaint_options(View3DPaintPanel, Panel): col.prop(ipaint, "use_backface_culling", text="Backface Culling") -class VIEW3D_PT_tools_imagepaint_options_unified(Panel, View3DPaintPanel): - bl_context = ".imagepaint" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_imagepaint_options" - bl_label = "Unified Brush" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - self.unified_paint_settings(layout, context) - - class VIEW3D_PT_tools_imagepaint_options_cavity(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) bl_label = "Cavity Mask" @@ -1719,13 +1207,14 @@ class VIEW3D_PT_imagepaint_options(View3DPaintPanel): @classmethod def poll(cls, context): + # This is currently unused, since there aren't any Vertex Paint mode specific options. + return False return (context.image_paint_object and context.tool_settings.image_paint) def draw(self, context): layout = self.layout - - col = layout.column() - self.unified_paint_settings(col, context) + layout.use_property_split = True + layout.use_property_decorate = False class VIEW3D_MT_tools_projectpaint_stencil(Menu): @@ -1842,15 +1331,13 @@ class VIEW3D_PT_tools_particlemode_options_display(View3DPanel, Panel): # Grease Pencil drawing brushes -class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): +class GreasePencilPanel: bl_context = ".greasepencil_paint" - bl_label = "Brush" bl_category = "Tool" @classmethod def poll(cls, context): - is_3d_view = context.space_data.type == 'VIEW_3D' - if is_3d_view: + if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): if context.gpencil_data is None: return False @@ -1859,6 +1346,10 @@ class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): else: return True + +class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPanel): + bl_label = "Brushes" + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -1868,15 +1359,40 @@ class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): gpencil_paint = tool_settings.gpencil_paint row = layout.row() - col = row.column() - col.template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) + row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) col = row.column() + col.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="") + + if context.mode == 'PAINT_GPENCIL': + brush = tool_settings.gpencil_paint.brush + gp_settings = brush.gpencil_settings + + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if(brush.use_custom_icon): + layout.row().prop(brush, "icon_filepath", text="") + else: + layout.row().prop(gp_settings, "gp_icon", text="Icon") + + +class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPanel): + bl_label = "Brush Settings" + + # What is the point of brush presets? Seems to serve the exact same purpose as brushes themselves?? + def draw_header_preset(self, _context): + VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + tool_settings = context.scene.tool_settings + gpencil_paint = tool_settings.gpencil_paint + brush = gpencil_paint.brush - sub = col.column(align=True) - sub.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="") - if brush is not None: gp_settings = brush.gpencil_settings @@ -1895,63 +1411,68 @@ class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, ) - tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False) - brush_basic_gpencil_paint_settings(layout, context, brush, tool, compact=True, is_toolbar=False) + brush_basic_gpencil_paint_settings(layout, context, brush, compact=False) -# Grease Pencil drawing brushes options -class VIEW3D_PT_tools_grease_pencil_brush_option(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): bl_context = ".greasepencil_paint" - bl_label = "Options" + bl_label = "Advanced" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' bl_category = "Tool" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL'} - - def draw_header_preset(self, _context): - VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout) + return brush is not None and brush.gpencil_tool != 'ERASE' def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - brush = context.tool_settings.gpencil_paint.brush + tool_settings = context.scene.tool_settings + gpencil_paint = tool_settings.gpencil_paint + brush = gpencil_paint.brush + gp_settings = brush.gpencil_settings + col = layout.column(align=True) if brush is not None: - gp_settings = brush.gpencil_settings - col = layout.column(align=True) - col.prop(gp_settings, "input_samples") - col.separator() + if brush.gpencil_tool != 'FILL': + col.prop(gp_settings, "input_samples") + col.separator() - col.prop(gp_settings, "active_smooth_factor") - col.separator() + col.prop(gp_settings, "active_smooth_factor") + col.separator() - col.prop(gp_settings, "angle", slider=True) - col.prop(gp_settings, "angle_factor", text="Factor", slider=True) + col.prop(gp_settings, "angle", slider=True) + col.prop(gp_settings, "angle_factor", text="Factor", slider=True) - ob = context.object - if ob and brush.gpencil_settings.use_material_pin is False: - ma = ob.active_material - elif brush.gpencil_settings.material: - ma = brush.gpencil_settings.material - else: + ob = context.object ma = None + if ob and brush.gpencil_settings.use_material_pin is False: + ma = ob.active_material + elif brush.gpencil_settings.material: + ma = brush.gpencil_settings.material - col.separator() - subcol = col.column(align=True) - if ma and ma.grease_pencil.mode == 'LINE': - subcol.enabled = False - subcol.prop(gp_settings, "gradient_factor", slider=True) - subcol.prop(gp_settings, "gradient_shape") + col.separator() + subcol = col.column(align=True) + if ma and ma.grease_pencil.mode == 'LINE': + subcol.enabled = False + subcol.prop(gp_settings, "gradient_factor", slider=True) + subcol.prop(gp_settings, "gradient_shape") + + elif brush.gpencil_tool == 'FILL': + col.prop(gp_settings, "fill_factor", text="Resolution") + if gp_settings.fill_draw_mode != 'STROKE': + col.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") + col.prop(gp_settings, "fill_threshold", text="Threshold") -class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(Panel, View3DPanel): bl_context = ".greasepencil_paint" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' - bl_label = "Stabilize" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' + bl_label = "Stabilize Stroke" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -1961,6 +1482,8 @@ class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): return brush is not None and brush.gpencil_tool == 'DRAW' def draw_header(self, context): + if self.is_popover: return + brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings self.layout.prop(gp_settings, "use_settings_stabilizer", text="") @@ -1972,24 +1495,34 @@ class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - layout.active = gp_settings.use_settings_stabilizer - layout.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - layout.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + if self.is_popover: + row = layout.row() + row.prop(gp_settings, "use_settings_stabilizer", text="") + row.label(text=self.bl_label) + + col = layout.column() + col.active = gp_settings.use_settings_stabilizer + + col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) -class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_post_processing(View3DPanel, Panel): bl_context = ".greasepencil_paint" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' bl_label = "Post-Processing" bl_category = "Tool" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool != 'ERASE' + return brush is not None and brush.gpencil_tool not in ('ERASE', 'FILL') def draw_header(self, context): + if self.is_popover: return + brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings self.layout.prop(gp_settings, "use_settings_postprocess", text="") @@ -2001,30 +1534,37 @@ class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - layout.active = gp_settings.use_settings_postprocess - col = layout.column(align=True) - col.prop(gp_settings, "pen_smooth_factor") - col.prop(gp_settings, "pen_smooth_steps") + if self.is_popover: + row = layout.row() + row.prop(gp_settings, "use_settings_postprocess", text="") + row.label(text=self.bl_label) - col = layout.column(align=True) - col.prop(gp_settings, "pen_thick_smooth_factor") - col.prop(gp_settings, "pen_thick_smooth_steps", text="Iterations") + col = layout.column() + col.active = gp_settings.use_settings_postprocess - col = layout.column(align=True) - col.prop(gp_settings, "pen_subdivision_steps") - col.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) + col1 = col.column(align=True) + col1.prop(gp_settings, "pen_smooth_factor") + col1.prop(gp_settings, "pen_smooth_steps") - col = layout.column(align=True) - col.prop(gp_settings, "simplify_factor") + col1 = col.column(align=True) + col1.prop(gp_settings, "pen_thick_smooth_factor") + col1.prop(gp_settings, "pen_thick_smooth_steps", text="Iterations") - col = layout.column(align=True) - col.prop(gp_settings, "trim") + col1 = col.column(align=True) + col1.prop(gp_settings, "pen_subdivision_steps") + col1.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) + + col1 = col.column(align=True) + col1.prop(gp_settings, "simplify_factor") + + col1 = col.column(align=True) + col1.prop(gp_settings, "trim") class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): bl_context = ".greasepencil_paint" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' bl_label = "Randomize" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -2032,9 +1572,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): @classmethod def poll(cls, context): brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool != 'ERASE' + return brush is not None and brush.gpencil_tool not in ('ERASE', 'FILL') def draw_header(self, context): + if self.is_popover: return + brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings self.layout.prop(gp_settings, "use_settings_random", text="") @@ -2046,13 +1588,20 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - layout.active = gp_settings.use_settings_random - layout.prop(gp_settings, "random_pressure", text="Pressure", slider=True) - layout.prop(gp_settings, "random_strength", text="Strength", slider=True) - layout.prop(gp_settings, "uv_random", text="UV", slider=True) + if self.is_popover: + row = layout.row() + row.prop(gp_settings, "use_settings_random", text="") + row.label(text=self.bl_label) - row = layout.row(align=True) + col = layout.column() + col.active = gp_settings.use_settings_random + + col.prop(gp_settings, "random_pressure", text="Pressure", slider=True) + col.prop(gp_settings, "random_strength", text="Strength", slider=True) + col.prop(gp_settings, "uv_random", text="UV", slider=True) + + row = col.row(align=True) row.prop(gp_settings, "pen_jitter", slider=True) row.prop(gp_settings, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') @@ -2060,6 +1609,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): # Grease Pencil drawingcurves class VIEW3D_PT_tools_grease_pencil_brushcurves(View3DPanel, Panel): bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' bl_label = "Curves" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -2124,12 +1674,6 @@ class VIEW3D_PT_tools_grease_pencil_brushcurves_jitter(View3DPanel, Panel): use_negative_slope=True) -# Grease Pencil stroke editing tools -class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'VIEW_3D' - bl_category = "Tool" - - # Grease Pencil stroke interpolation tools class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): bl_space_type = 'VIEW_3D' @@ -2178,18 +1722,47 @@ class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): # Grease Pencil stroke sculpting tools -class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, View3DPanel, Panel): + +class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel): bl_context = ".greasepencil_sculpt" - bl_category = "Tools" - bl_label = "Brush" + bl_label = "Brushes" bl_category = "Tool" + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + layout.template_icon_view(settings, "sculpt_tool", show_labels=True) + + +class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel): + bl_context = ".greasepencil_sculpt" + bl_category = "Tool" + bl_label = "Brush Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + if not self.is_popover: + from bl_ui.properties_paint_common import ( + brush_basic_gpencil_sculpt_settings, + ) + brush_basic_gpencil_sculpt_settings(layout, context, brush) # Grease Pencil weight painting tools -class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel): + +class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel): bl_context = ".greasepencil_weight" - bl_category = "Tools" - bl_label = "Brush" + bl_label = "Brushes" bl_category = "Tool" def draw(self, context): @@ -2202,42 +1775,58 @@ class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel): layout.template_icon_view(settings, "weight_tool", show_labels=True) - col = layout.column() + +class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel): + bl_context = ".greasepencil_weight" + bl_category = "Tool" + bl_label = "Brush Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + if not self.is_popover: from bl_ui.properties_paint_common import ( brush_basic_gpencil_weight_settings, ) - brush_basic_gpencil_weight_settings(col, context, brush) + brush_basic_gpencil_weight_settings(layout, context, brush) + + +class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, Panel, View3DPanel): + bl_context = ".greasepencil_sculpt" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' + bl_category = "Tool" + bl_label = "Sculpt Strokes" # Grease Pencil Brush Appearance (one for each mode) -class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_paint" - bl_label = "Display" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' + bl_label = "Cursor" bl_category = "Tool" -class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_sculpt" - bl_label = "Display" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' + bl_label = "Cursor" bl_category = "Tool" -class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, View3DPanel, Panel): - bl_context = ".greasepencil_sculpt" - bl_label = "Sculpt Strokes" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt' - bl_category = "Tool" - - -class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_weight" - bl_label = "Display" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_weight_paint_settings' bl_category = "Tool" + bl_label = "Cursor" -class VIEW3D_PT_gpencil_brush_presets(PresetPanel, Panel): +class VIEW3D_PT_gpencil_brush_presets(Panel, PresetPanel): """Brush settings""" bl_label = "Brush Presets" preset_subdir = "gpencil_brush" @@ -2255,12 +1844,14 @@ classes = ( VIEW3D_PT_tools_curveedit_options_stroke, VIEW3D_PT_tools_armatureedit_options, VIEW3D_PT_tools_posemode_options, + VIEW3D_PT_slots_projectpaint, - VIEW3D_PT_tools_brush, + VIEW3D_PT_tools_brush_select, + VIEW3D_PT_tools_brush_settings, VIEW3D_PT_tools_brush_color, VIEW3D_PT_tools_brush_swatches, + VIEW3D_PT_tools_brush_settings_advanced, VIEW3D_PT_tools_brush_clone, - VIEW3D_PT_tools_brush_options, TEXTURE_UL_texpaintslots, VIEW3D_MT_tools_projectpaint_uvlayer, VIEW3D_PT_stencil_projectpaint, @@ -2272,49 +1863,52 @@ classes = ( VIEW3D_PT_tools_brush_falloff_frontface, VIEW3D_PT_tools_brush_falloff_normal, VIEW3D_PT_tools_brush_display, - VIEW3D_PT_tools_brush_display_show_brush, - VIEW3D_PT_tools_brush_display_custom_icon, + VIEW3D_PT_sculpt_dyntopo, VIEW3D_PT_sculpt_dyntopo_remesh, VIEW3D_PT_sculpt_voxel_remesh, VIEW3D_PT_sculpt_symmetry, VIEW3D_PT_sculpt_symmetry_for_topbar, VIEW3D_PT_sculpt_options, - VIEW3D_PT_sculpt_options_unified, VIEW3D_PT_sculpt_options_gravity, + VIEW3D_PT_tools_weightpaint_symmetry, VIEW3D_PT_tools_weightpaint_symmetry_for_topbar, VIEW3D_PT_tools_weightpaint_options, - VIEW3D_PT_tools_weightpaint_options_unified, + VIEW3D_PT_tools_vertexpaint_symmetry, VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar, VIEW3D_PT_tools_vertexpaint_options, + VIEW3D_PT_tools_imagepaint_symmetry, VIEW3D_PT_tools_imagepaint_options, VIEW3D_PT_tools_imagepaint_options_cavity, - VIEW3D_PT_tools_imagepaint_options_unified, VIEW3D_PT_tools_imagepaint_options_external, VIEW3D_MT_tools_projectpaint_stencil, + VIEW3D_PT_tools_particlemode, VIEW3D_PT_tools_particlemode_options, VIEW3D_PT_tools_particlemode_options_shapecut, VIEW3D_PT_tools_particlemode_options_display, VIEW3D_PT_gpencil_brush_presets, - VIEW3D_PT_tools_grease_pencil_brush, - VIEW3D_PT_tools_grease_pencil_brush_option, + VIEW3D_PT_tools_grease_pencil_brush_select, VIEW3D_PT_tools_grease_pencil_brush_settings, + VIEW3D_PT_tools_grease_pencil_brush_advanced, + VIEW3D_PT_tools_grease_pencil_brush_post_processing, VIEW3D_PT_tools_grease_pencil_brush_stabilizer, VIEW3D_PT_tools_grease_pencil_brush_random, VIEW3D_PT_tools_grease_pencil_brushcurves, VIEW3D_PT_tools_grease_pencil_brushcurves_sensitivity, VIEW3D_PT_tools_grease_pencil_brushcurves_strength, VIEW3D_PT_tools_grease_pencil_brushcurves_jitter, - VIEW3D_PT_tools_grease_pencil_sculpt, - VIEW3D_PT_tools_grease_pencil_weight_paint, VIEW3D_PT_tools_grease_pencil_paint_appearance, + VIEW3D_PT_tools_grease_pencil_sculpt_select, + VIEW3D_PT_tools_grease_pencil_sculpt_settings, VIEW3D_PT_tools_grease_pencil_sculpt_options, VIEW3D_PT_tools_grease_pencil_sculpt_appearance, + VIEW3D_PT_tools_grease_pencil_weight_paint_select, + VIEW3D_PT_tools_grease_pencil_weight_paint_settings, VIEW3D_PT_tools_grease_pencil_weight_appearance, VIEW3D_PT_tools_grease_pencil_interpolate, ) diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 2d70950ce7b..5b683ffd80e 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1596,6 +1596,22 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_jitter_unit_items[] = { + {BRUSH_ABSOLUTE_JITTER, "VIEW", 0, "View", "Jitterring happens in screen space, in pixels"}, + {0, "BRUSH", 0, "Brush", "Jitterring happens relative to the brush size"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem falloff_shape_unit_items[] = { + {0, "SPHERE", 0, "Sphere", "Apply brush influence in a Sphere, outwards from the center"}, + {PAINT_FALLOFF_SHAPE_TUBE, + "PROJECTED", + 0, + "Projected", + "Apply brush influence in a 2D circle, projected from the view"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem brush_curve_preset_items[] = { {BRUSH_CURVE_CUSTOM, "CUSTOM", ICON_RNDCURVE, "Custom", ""}, {BRUSH_CURVE_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""}, @@ -1704,6 +1720,19 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "jitter_unit", PROP_ENUM, PROP_NONE); /* as an enum */ + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, brush_jitter_unit_items); + RNA_def_property_ui_text( + prop, "Jitter Unit", "Jitter in screen space or relative to brush size"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "falloff_shape", PROP_ENUM, PROP_NONE); /* as an enum */ + RNA_def_property_enum_bitflag_sdna(prop, NULL, "falloff_shape"); + RNA_def_property_enum_items(prop, falloff_shape_unit_items); + RNA_def_property_ui_text(prop, "Falloff Shape", "Use projected or spherical falloff"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* number values */ prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); RNA_def_property_int_funcs(prop, NULL, "rna_Brush_set_size", NULL); @@ -1993,13 +2022,6 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Brush_update"); /* flag */ - /* This is an enum but its unlikely we add other shapes, so expose as a boolean. */ - prop = RNA_def_property(srna, "use_projected", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "falloff_shape", PAINT_FALLOFF_SHAPE_TUBE); - RNA_def_property_ui_text( - prop, "2D Falloff", "Apply brush influence in 2D circle instead of a sphere"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "use_airbrush", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_AIRBRUSH); RNA_def_property_ui_text( @@ -2045,7 +2067,7 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "use_paint_antialiasing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "sampling_flag", BRUSH_PAINT_ANTIALIASING); - RNA_def_property_ui_text(prop, "Antialasing", "Smooths the edges of the strokes"); + RNA_def_property_ui_text(prop, "Anti-Aliasing", "Smooths the edges of the strokes"); prop = RNA_def_property(srna, "use_multiplane_scrape_dynamic", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_MULTIPLANE_SCRAPE_DYNAMIC); @@ -2117,13 +2139,6 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Inverse Smooth Pressure", "Lighter pressure causes more smoothing to be applied"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "use_relative_jitter", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", BRUSH_ABSOLUTE_JITTER); - RNA_def_property_ui_icon(prop, ICON_UNLOCKED, true); - RNA_def_property_ui_text( - prop, "Absolute Jitter", "Jittering happens in screen space, not relative to brush size"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "use_plane_trim", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_PLANE_TRIM); RNA_def_property_ui_text(prop, "Use Plane Trim", "Enable Plane Trim");