Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYimingWu <xp8110@outlook.com>2020-02-01 05:25:32 +0300
committerYimingWu <xp8110@outlook.com>2020-02-01 05:25:32 +0300
commitb47883a990ee68e659a8a8b44729be9b8e0d002f (patch)
tree24a7733807992fc84445d30b63deaedfe1ab40a1 /release/scripts/startup/bl_ui
parentb5abbc40a07041af91dca5d0a4acd8e5f1518c91 (diff)
parentd9ec25844b4ac3143775615469fe69b27105c108 (diff)
Merge remote-tracking branch 'origin/master' into temp-lanpr-review
Diffstat (limited to 'release/scripts/startup/bl_ui')
-rw-r--r--release/scripts/startup/bl_ui/__init__.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py193
-rw-r--r--release/scripts/startup/bl_ui/properties_mask_common.py41
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py1048
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py10
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_cloth.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py21
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py1206
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py692
-rw-r--r--release/scripts/startup/bl_ui/space_clip.py36
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py1
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py2
-rw-r--r--release/scripts/startup/bl_ui/space_graph.py4
-rw-r--r--release/scripts/startup/bl_ui/space_image.py572
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py2
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py2
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_common.py39
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py15
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py143
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py884
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py345
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py1055
23 files changed, 3197 insertions, 3122 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index ac5d1a7ac30..8fc59ca493a 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -54,10 +54,9 @@ _modules = [
"properties_physics_common",
"properties_physics_dynamicpaint",
"properties_physics_field",
- "properties_physics_fluid",
"properties_physics_rigidbody",
"properties_physics_rigidbody_constraint",
- "properties_physics_smoke",
+ "properties_physics_fluid",
"properties_physics_softbody",
"properties_render",
"properties_output",
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index c872b9acd4a..d4b2c39bd5e 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -945,7 +945,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.prop(md, "angle")
col.prop(md, "limits", slider=True)
- def SMOKE(self, layout, _ob, _md):
+ def FLUID(self, layout, _ob, _md):
layout.label(text="Settings are inside the Physics tab")
def SMOOTH(self, layout, ob, md):
@@ -2414,6 +2414,7 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
row.prop(md, "layer_pass", text="Pass")
row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT')
+
classes = (
DATA_PT_modifiers,
DATA_PT_gpencil_modifiers,
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..45cb10bb3bd 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"
@@ -278,14 +162,36 @@ class GreasePencilSculptOptionsPanel:
# 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
+ return False
+
+ 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 +205,36 @@ 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")
-
- if tool in {'THICKNESS', 'STRENGTH'}:
- col.prop(brush, "cursor_color_add", text="Add")
- col.prop(brush, "cursor_color_sub", text="Subtract")
- elif tool == 'PINCH':
- col.prop(brush, "cursor_color_add", text="Pinch")
- col.prop(brush, "cursor_color_sub", text="Inflate")
- elif tool == 'TWIST':
- col.prop(brush, "cursor_color_add", text="CCW")
- col.prop(brush, "cursor_color_sub", text="CW")
- else:
- col.prop(brush, "cursor_color_add", text="")
+ col.active = brush.use_cursor
+
+ col.prop(brush, "cursor_color_add", text="Cursor Color")
+ if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
+ col.prop(brush, "cursor_color_sub", text="Inverse Cursor Color")
class GPENCIL_MT_pie_tool_palette(Menu):
@@ -601,7 +501,7 @@ class GPENCIL_MT_move_to_layer(Menu):
gpl_active = context.active_gpencil_layer
tot_layers = len(gpd.layers)
i = tot_layers - 1
- while(i >= 0):
+ while i >= 0:
gpl = gpd.layers[i]
if gpl.info == gpl_active.info:
icon = 'GREASEPENCIL'
@@ -811,12 +711,11 @@ class GreasePencilToolsPanel:
bl_options = {'DEFAULT_CLOSED'}
@classmethod
- def poll(cls, context):
+ def poll(cls, _context):
# XXX - disabled in 2.8 branch.
+ # return (context.gpencil_data is not None)
return False
- return (context.gpencil_data is not None)
-
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_mask_common.py b/release/scripts/startup/bl_ui/properties_mask_common.py
index 94d7ac2c91e..3342fe1985a 100644
--- a/release/scripts/startup/bl_ui/properties_mask_common.py
+++ b/release/scripts/startup/bl_ui/properties_mask_common.py
@@ -22,6 +22,33 @@
# menus are referenced `as is`
from bpy.types import Menu, UIList
+from bpy.app.translations import contexts as i18n_contexts
+
+
+# Use by both image & clip context menus.
+def draw_mask_context_menu(layout, context):
+ layout.operator_menu_enum("mask.handle_type_set", "type")
+ layout.operator("mask.switch_direction")
+ layout.operator("mask.cyclic_toggle")
+
+ layout.separator()
+ layout.operator("mask.copy_splines", icon='COPYDOWN')
+ layout.operator("mask.paste_splines", icon='PASTEDOWN')
+
+ layout.separator()
+
+ layout.operator("mask.shape_key_rekey", text="Re-key Shape Points")
+ layout.operator("mask.feather_weight_clear")
+ layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation")
+
+ layout.separator()
+
+ layout.operator("mask.parent_set")
+ layout.operator("mask.parent_clear")
+
+ layout.separator()
+
+ layout.operator("mask.delete")
class MASK_UL_layers(UIList):
@@ -320,6 +347,19 @@ class MASK_MT_mask(Menu):
layout.menu("MASK_MT_animation")
+class MASK_MT_add(Menu):
+ bl_idname = "MASK_MT_add"
+ bl_label = "Add"
+ bl_translation_context = i18n_contexts.operator_default
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator("mask.primitive_circle_add", text="Circle", icon='MESH_CIRCLE')
+ layout.operator("mask.primitive_square_add", text="Square", icon='MESH_PLANE')
+
+
class MASK_MT_visibility(Menu):
bl_label = "Show/Hide"
@@ -383,6 +423,7 @@ class MASK_MT_select(Menu):
classes = (
MASK_UL_layers,
MASK_MT_mask,
+ MASK_MT_add,
MASK_MT_visibility,
MASK_MT_transform,
MASK_MT_animation,
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index d8d90fec583..327df079d3b 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -26,74 +26,487 @@ class UnifiedPaintPanel:
# 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."""
+ mode = context.mode
+
+ if 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'
+ elif space_type in {'VIEW_3D', 'PROPERTIES'}:
+ if mode == 'PAINT_TEXTURE':
+ if tool_settings.image_paint:
+ return mode
+ else:
+ return None
+ return mode
+ return None
+
+ @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
-
+ # 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
return None
@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,
+ header=False,
+ ):
+ """ 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):
+ prop_owner = ups
- flow = parent.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- 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")
+ row.prop(prop_owner, prop_name, icon=icon, text=text, slider=slider)
- @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 pressure_name:
+ row.prop(brush, pressure_name, text="")
- @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)
+ if unified_name and not header:
+ # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281
+ row.prop(ups, unified_name, text="", icon="WORLD")
- @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="")
+
+ if brush is not None:
+ 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
+ return False
+
+ 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'
+ return False
+
+ 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)
+
+ 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
+
+ mode = self.get_brush_mode(context)
+ settings = self.paint_settings(context)
+ brush = settings.brush
+
+ col = layout.column()
+
+ col.prop(brush, "stroke_method")
+ col.separator()
+
+ 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 mode == 'SCULPT':
+ col.row().prop(brush, "use_scene_spacing", text="Spacing Distance", expand=True)
+
+ if mode in {'PAINT_TEXTURE', 'PAINT_2D', 'SCULPT'}:
+ if brush.image_paint_capabilities.has_space_attenuation or brush.sculpt_capabilities.has_space_attenuation:
+ col.prop(brush, "use_space_attenuation")
+
+ if brush.use_curve:
+ col.separator()
+ col.template_ID(brush, "paint_curve", new="paintcurve.new")
+ col.operator("paintcurve.draw")
+ col.separator()
+
+ if brush.use_space:
+ col.separator()
+ row = col.row(align=True)
+ col.prop(brush, "dash_ratio", text="Dash Ratio")
+ col.prop(brush, "dash_samples", text="Dash Length")
+
+ if (mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter) or mode != 'SCULPT':
+ col.separator()
+ 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
+ return False
+
+ 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 +521,363 @@ 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,
+ )
-def brush_texpaint_common_clone(_panel, context, layout, _brush, settings, *, projpaint=False):
- ob = context.active_object
- col = layout.column()
+ # 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,
+ )
- 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)
+ 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':
+ # For some reason fill threshold only appears to be implemented in 2D paint.
+ if brush.color_type == 'COLOR':
+ if mode == 'PAINT_2D':
+ layout.prop(brush, "fill_threshold", text="Fill Threshold", slider=True)
+ elif brush.color_type == 'GRADIENT':
+ layout.row().prop(brush, "gradient_fill_mode", expand=True)
+
+
+def brush_shared_settings(layout, context, brush, popover=False):
+ """ Draw simple brush settings that are shared between different paint modes. """
+
+ mode = UnifiedPaintPanel.get_brush_mode(context)
+
+ ### Determine which settings to draw. ###
+ blend_mode = False
+ size = False
+ size_mode = False
+ strength = False
+ strength_pressure = False
+ weight = False
+ direction = False
+
+ # 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
+
+ # 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
+ # Only draw blend mode for the Draw tool, because for other tools it is pointless. D5928#137944
+ if brush.weight_tool == 'DRAW':
+ 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_settings_advanced(layout, context, brush, popover=False):
+ """Draw advanced brush settings for Sculpt, Texture/Vertex/Weight Paint modes."""
+
+ 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")
- 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)
+ # 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 settings.mode == 'IMAGE':
- mesh = ob.data
+ elif brush.image_tool == 'MASK':
+ layout.prop(brush, "weight", text="Mask Value", slider=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)
+ 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
-def brush_texpaint_common_color(_panel, context, layout, brush, _settings, *, projpaint=False):
- UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True)
+ # Weight Paint
+ elif mode == 'PAINT_WEIGHT':
+ if brush.weight_tool != 'SMEAR':
+ use_accumulate = True
+ use_frontface = True
- 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)
+ # Draw shared 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)
- layout.use_property_split = 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
- col = layout.column()
+ if color_type:
+ row = layout.row()
+ row.use_property_split = False
+ row.prop(brush, "color_type", expand=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")
+ # Color wheel
+ if brush.color_type == 'COLOR':
+ UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True)
+ 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)
-def brush_texpaint_common_options(_panel, _context, layout, brush, _settings, *, projpaint=False):
- capabilities = brush.image_paint_capabilities
+ layout.use_property_split = True
- col = layout.column()
+ col = layout.column()
- if capabilities.has_accumulate:
- col.prop(brush, "use_accumulate")
+ if brush.image_tool == 'DRAW':
+ UnifiedPaintPanel.prop_unified(
+ col,
+ context,
+ brush,
+ "secondary_color",
+ unified_name="use_unified_color",
+ text="Background Color",
+ header=True,
+ )
- 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
@@ -282,93 +959,40 @@ def brush_mask_texture_settings(layout, brush):
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."""
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 {}))
-
-
-def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact=True, is_toolbar=False):
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "size",
+ pressure_name="use_pressure_size",
+ unified_name="use_unified_size",
+ slider=True,
+ text="Radius",
+ header=True
+ )
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "strength",
+ pressure_name="use_pressure_strength",
+ unified_name="use_unified_strength",
+ header=True
+ )
+
+
+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 +1001,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 +1011,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="Display Cursor")
+
+ # 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")
@@ -392,26 +1023,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact=
row.prop(brush, "size", text="Thickness")
row = layout.row(align=True)
row.prop(gp_settings, "fill_simplify_level", text="Simplify")
- 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 +1039,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 +1103,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/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index 076b1f2592c..21abf8bb34c 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -55,7 +55,7 @@ def particle_panel_poll(cls, context):
if not settings:
return False
- return settings.is_fluid is False and (engine in cls.COMPAT_ENGINES)
+ return (settings.is_fluid is False) and (engine in cls.COMPAT_ENGINES)
def particle_get_settings(context):
@@ -119,6 +119,7 @@ def find_modifier(ob, psys):
if md.type == 'PARTICLE_SYSTEM':
if md.particle_system == psys:
return md
+ return None
class PARTICLE_UL_particle_systems(bpy.types.UIList):
@@ -159,7 +160,10 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel):
@classmethod
def poll(cls, context):
engine = context.engine
- return (context.particle_system or context.object or context.space_data.pin_id) and (engine in cls.COMPAT_ENGINES)
+ return (
+ (context.particle_system or context.object or context.space_data.pin_id) and
+ (engine in cls.COMPAT_ENGINES)
+ )
def draw(self, context):
layout = self.layout
@@ -203,7 +207,7 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel):
col = layout.column()
- if part.is_fluid is False:
+ if (part.is_fluid is False):
row = col.row()
row.enabled = particle_panel_enabled(context, psys)
row.template_ID(psys, "settings", new="particle.new")
diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py
index cd7e99f255c..d9713cb8608 100644
--- a/release/scripts/startup/bl_ui/properties_physics_cloth.py
+++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py
@@ -160,6 +160,7 @@ class PHYSICS_PT_cloth_damping(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop(cloth, "bending_damping", text="Bending")
+
class PHYSICS_PT_cloth_internal_springs(PhysicButtonsPanel, Panel):
bl_label = "Internal Springs"
bl_parent_id = 'PHYSICS_PT_cloth_physical_properties'
@@ -201,6 +202,7 @@ class PHYSICS_PT_cloth_internal_springs(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop(cloth, "internal_compression_stiffness_max", text="Max Compression")
+
class PHYSICS_PT_cloth_pressure(PhysicButtonsPanel, Panel):
bl_label = "Pressure"
bl_parent_id = 'PHYSICS_PT_cloth_physical_properties'
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index b69f2233035..5397020a521 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -99,8 +99,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
physics_add(col, context.soft_body, "Soft Body", 'SOFT_BODY', 'MOD_SOFT', True)
if obj.type == 'MESH':
- physics_add(col, context.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True)
- physics_add(col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', True)
+ physics_add(col, context.fluid, "Fluid", 'FLUID', 'MOD_FLUIDSIM', True)
physics_add_special(
col, obj.rigid_body, "Rigid Body",
@@ -118,7 +117,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
)
-# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc.
+# cache-type can be 'PSYS' 'HAIR' 'FLUID' etc.
def point_cache_ui(self, cache, enabled, cachetype):
layout = self.layout
@@ -141,10 +140,10 @@ def point_cache_ui(self, cache, enabled, cachetype):
col.operator("ptcache.add", icon='ADD', text="")
col.operator("ptcache.remove", icon='REMOVE', text="")
- if cachetype in {'PSYS', 'HAIR', 'SMOKE'}:
+ if cachetype in {'PSYS', 'HAIR', 'FLUID'}:
col = layout.column()
- if cachetype == 'SMOKE':
+ if cachetype == 'FLUID':
col.prop(cache, "use_library_path", text="Use Library Path")
col.prop(cache, "use_external")
@@ -160,14 +159,14 @@ def point_cache_ui(self, cache, enabled, cachetype):
col.alignment = 'RIGHT'
col.label(text=cache_info)
else:
- if cachetype in {'SMOKE', 'DYNAMIC_PAINT'}:
+ if cachetype in {'FLUID', 'DYNAMIC_PAINT'}:
if not is_saved:
col = layout.column(align=True)
col.alignment = 'RIGHT'
col.label(text="Cache is disabled until the file is saved")
layout.enabled = False
- if not cache.use_external or cachetype == 'SMOKE':
+ if not cache.use_external or cachetype == 'FLUID':
col = layout.column(align=True)
if cachetype not in {'PSYS', 'DYNAMIC_PAINT'}:
@@ -175,18 +174,18 @@ def point_cache_ui(self, cache, enabled, cachetype):
col.prop(cache, "frame_start", text="Simulation Start")
col.prop(cache, "frame_end")
- if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
+ if cachetype not in {'FLUID', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
col.prop(cache, "frame_step")
cache_info = cache.info
- if cachetype != 'SMOKE' and cache_info: # avoid empty space.
+ if cachetype != 'FLUID' and cache_info: # avoid empty space.
col = layout.column(align=True)
col.alignment = 'RIGHT'
col.label(text=cache_info)
can_bake = True
- if cachetype not in {'SMOKE', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
+ if cachetype not in {'FLUID', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
if not is_saved:
col = layout.column(align=True)
col.alignment = 'RIGHT'
@@ -269,7 +268,7 @@ def effector_weights_ui(self, weights, weight_type):
col.prop(weights, "curve_guide", slider=True)
col.prop(weights, "texture", slider=True)
- if weight_type != 'SMOKE':
+ if weight_type != 'FLUID':
col.prop(weights, "smokeflow", slider=True)
col = flow.column()
diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py
index b9e690629d1..6b0dd7ac36f 100644
--- a/release/scripts/startup/bl_ui/properties_physics_fluid.py
+++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py
@@ -21,16 +21,18 @@
import bpy
from bpy.types import (
Panel,
+ Menu,
+)
+from .properties_physics_common import (
+ effector_weights_ui,
)
-from bpy.app.translations import pgettext_iface as iface_
-from bl_ui.utils import PresetPanel
-class FLUID_PT_presets(PresetPanel, Panel):
+class FLUID_MT_presets(Menu):
bl_label = "Fluid Presets"
preset_subdir = "fluid"
preset_operator = "script.execute_preset"
- preset_add_operator = "fluid.preset_add"
+ draw = Menu.draw_preset
class PhysicButtonsPanel:
@@ -39,28 +41,59 @@ class PhysicButtonsPanel:
bl_context = "physics"
@staticmethod
+ def check_domain_has_unbaked_guide(domain):
+ return (
+ domain.use_guide and not domain.has_cache_baked_guide and
+ ((domain.guide_source == 'EFFECTOR') or
+ (domain.guide_source == 'DOMAIN' and not domain.guide_parent))
+ )
+
+ @staticmethod
def poll_fluid(context):
ob = context.object
if not ((ob and ob.type == 'MESH') and (context.fluid)):
return False
- return (bpy.app.build_options.mod_fluid)
+ md = context.fluid
+ return md and (context.fluid.fluid_type != 'NONE')
+
+ @staticmethod
+ def poll_fluid_domain(context):
+ if not PhysicButtonsPanel.poll_fluid(context):
+ return False
+
+ md = context.fluid
+ return md and (md.fluid_type == 'DOMAIN')
@staticmethod
- def poll_fluid_settings(context):
- if not (PhysicButtonsPanel.poll_fluid(context)):
+ def poll_gas_domain(context):
+ if not PhysicButtonsPanel.poll_fluid(context):
return False
md = context.fluid
- return md and md.settings and (md.settings.type != 'NONE')
+ if md and (md.fluid_type == 'DOMAIN'):
+ domain = md.domain_settings
+ return domain.domain_type in {'GAS'}
+ return False
@staticmethod
- def poll_fluid_domain(context):
+ def poll_liquid_domain(context):
if not PhysicButtonsPanel.poll_fluid(context):
return False
md = context.fluid
- return md and md.settings and (md.settings.type == 'DOMAIN')
+ if md and (md.fluid_type == 'DOMAIN'):
+ domain = md.domain_settings
+ return domain.domain_type in {'LIQUID'}
+ return False
+
+ @staticmethod
+ def poll_fluid_flow(context):
+ if not PhysicButtonsPanel.poll_fluid(context):
+ return False
+
+ md = context.fluid
+ return md and (md.fluid_type == 'FLOW')
class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel):
@@ -70,325 +103,903 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel):
@classmethod
def poll(cls, context):
ob = context.object
- return (ob and ob.type == 'MESH') and context.engine in cls.COMPAT_ENGINES and (context.fluid)
+ return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.fluid)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
- if not bpy.app.build_options.mod_fluid:
- col = layout.column()
+ if not bpy.app.build_options.fluid:
+ col = layout.column(align=True)
col.alignment = 'RIGHT'
- col.label(text="Built without fluids")
+ col.label(text="Built without Fluid modifier")
return
-
md = context.fluid
- fluid = md.settings
- col = layout.column()
- col.prop(fluid, "type")
+ layout.prop(md, "fluid_type")
-class PHYSICS_PT_fluid_flow(PhysicButtonsPanel, Panel):
- bl_label = "Flow"
- bl_parent_id = "PHYSICS_PT_fluid"
+class PHYSICS_PT_settings(PhysicButtonsPanel, Panel):
+ bl_label = "Settings"
+ bl_parent_id = 'PHYSICS_PT_fluid'
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
- md = context.fluid
- fluid = md.settings
-
- if not PhysicButtonsPanel.poll_fluid_settings(context):
+ if not PhysicButtonsPanel.poll_fluid(context):
return False
- return fluid.type in {'INFLOW', 'OUTFLOW', 'CONTROL'} and (context.engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- md = context.fluid
- fluid = md.settings
- self.layout.prop(fluid, "use", text="")
+ return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
- fluid = md.settings
+ ob = context.object
+ scene = context.scene
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
+ if md.fluid_type == 'DOMAIN':
+ domain = md.domain_settings
- flow.active = fluid.use
+ # Deactivate UI if guides are enabled but not baked yet.
+ layout.active = not self.check_domain_has_unbaked_guide(domain)
- if fluid.type == 'INFLOW':
- col = flow.column()
- col.prop(fluid, "volume_initialization", text="Volume Initialization")
- col.prop(fluid, "use_animated_mesh")
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
- row = col.row()
- row.active = not fluid.use_animated_mesh
- row.prop(fluid, "use_local_coords")
+ row = layout.row()
+ row.enabled = not is_baking_any and not has_baked_data
+ row.prop(domain, "domain_type", expand=False)
- col = flow.column()
- col.prop(fluid, "inflow_velocity", text="Inflow Velocity")
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_data
- elif fluid.type == 'OUTFLOW':
col = flow.column()
- col.prop(fluid, "volume_initialization", text="Volume Initialization")
+ col.prop(domain, "resolution_max", text="Resolution Divisions")
+ col.prop(domain, "time_scale", text="Time Scale")
+ col.prop(domain, "cfl_condition", text="CFL Number")
col = flow.column()
- col.prop(fluid, "use_animated_mesh")
+ col.prop(domain, "use_adaptive_timesteps")
+ col1 = col.column(align=True)
+ col1.enabled = domain.use_adaptive_timesteps
+ col1.prop(domain, "timesteps_max", text="Timesteps Maximum")
+ col1.prop(domain, "timesteps_min", text="Minimum")
- elif fluid.type == 'CONTROL':
- col = flow.column()
- col.prop(fluid, "quality", slider=True)
- col.prop(fluid, "use_reverse_frames")
+ col.separator()
col = flow.column()
- col.prop(fluid, "start_time", text="Time Start")
- col.prop(fluid, "end_time", text="End")
+ if scene.use_gravity:
+ sub = col.column()
+ sub.enabled = False
+ sub.prop(domain, "gravity", text="Using Scene Gravity", icon='SCENE_DATA')
+ else:
+ col.prop(domain, "gravity", text="Gravity")
+ # TODO (sebbas): Clipping var useful for manta openvdb caching?
+ # col.prop(domain, "clipping", text="Empty Space")
+
+ if domain.cache_type == 'MODULAR':
+ col.separator()
+ split = layout.split()
- col.separator()
+ bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
+ if domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_data", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_data", text="Free")
+ elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
+ split.enabled = False
+ split.operator("fluid.pause_bake", text="Baking Data - ESC to pause")
+ elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
+ split.operator("fluid.bake_data", text="Bake Data")
+ else:
+ split.operator("fluid.free_data", text="Free Data")
+
+ elif md.fluid_type == 'FLOW':
+ flow = md.flow_settings
+
+ row = layout.row()
+ row.prop(flow, "flow_type", expand=False)
+
+ grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+
+ col = grid.column()
+ col.prop(flow, "flow_behavior", expand=False)
+ if flow.flow_behavior in {'INFLOW'}:
+ col.prop(flow, "use_inflow", text="Use Inflow")
+
+ col.prop(flow, "subframes", text="Sampling Substeps")
+
+ if not flow.flow_behavior == 'OUTFLOW' and flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}:
+
+ if flow.flow_type in {'SMOKE', 'BOTH'}:
+ col.prop(flow, "smoke_color", text="Smoke Color")
+
+ col = grid.column(align=True)
+ col.prop(flow, "use_absolute", text="Absolute Density")
+
+ if flow.flow_type in {'SMOKE', 'BOTH'}:
+ col.prop(flow, "temperature", text="Initial Temperature")
+ col.prop(flow, "density", text="Density")
+
+ if flow.flow_type in {'FIRE', 'BOTH'}:
+ col.prop(flow, "fuel_amount", text="Fuel")
+
+ col.separator()
+ col.prop_search(flow, "density_vertex_group", ob, "vertex_groups", text="Vertex Group")
+
+ elif md.fluid_type == 'EFFECTOR':
+ effector_settings = md.effector_settings
+
+ row = layout.row()
+ row.prop(effector_settings, "effector_type")
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
- col.prop(fluid, "attraction_strength", text="Attraction Strength")
- col.prop(fluid, "attraction_radius", text="Radius")
- col.separator()
+ col.prop(effector_settings, "use_plane_init", text="Is Planar")
+ col.prop(effector_settings, "surface_distance", text="Surface Thickness")
- col = flow.column(align=True)
- col.prop(fluid, "velocity_strength", text="Velocity Strength")
- col.prop(fluid, "velocity_radius", text="Radius")
+ if effector_settings.effector_type == 'GUIDE':
+ col.prop(effector_settings, "velocity_factor", text="Velocity Factor")
+ col = flow.column()
+ col.prop(effector_settings, "guide_mode", text="Guide Mode")
-class PHYSICS_PT_fluid_settings(PhysicButtonsPanel, Panel):
- bl_label = "Settings"
- bl_parent_id = "PHYSICS_PT_fluid"
+class PHYSICS_PT_borders(PhysicButtonsPanel, Panel):
+ bl_label = "Border Collisions"
+ bl_parent_id = 'PHYSICS_PT_settings'
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
+ if not PhysicButtonsPanel.poll_fluid_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
md = context.fluid
- fluid = md.settings
+ domain = md.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_data
+
+ col = flow.column()
+ col.prop(domain, "use_collision_border_front", text="Front")
+ col = flow.column()
+ col.prop(domain, "use_collision_border_back", text="Back")
+ col = flow.column()
+ col.prop(domain, "use_collision_border_right", text="Right")
+ col = flow.column()
+ col.prop(domain, "use_collision_border_left", text="Left")
+ col = flow.column()
+ col.prop(domain, "use_collision_border_top", text="Top")
+ col = flow.column()
+ col.prop(domain, "use_collision_border_bottom", text="Bottom")
+
- if not PhysicButtonsPanel.poll_fluid_settings(context):
+class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
+ bl_label = "Smoke"
+ bl_parent_id = 'PHYSICS_PT_settings'
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_gas_domain(context):
return False
- return fluid.type in {'DOMAIN', 'FLUID', 'OBSTACLE', 'PARTICLE'} and (context.engine in cls.COMPAT_ENGINES)
+
+ return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
md = context.fluid
- fluid = md.settings
+ domain = md.domain_settings
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
- if fluid.type not in {'NONE', 'DOMAIN', 'PARTICLE', 'FLUID', 'OBSTACLE'}:
- flow.active = fluid.use
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_data
- if fluid.type == 'DOMAIN':
- col = flow.column()
+ col = flow.column()
+ col.prop(domain, "alpha")
+ col.prop(domain, "beta", text="Temperature Diff.")
+ col = flow.column()
+ col.prop(domain, "vorticity")
- if bpy.app.build_options.openmp:
- col.prop(fluid, "threads", text="Simulation Threads")
- col.separator()
- col.prop(fluid, "resolution", text="Final Resolution")
- col.prop(fluid, "preview_resolution", text="Preview")
+class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel):
+ bl_label = "Dissolve"
+ bl_parent_id = 'PHYSICS_PT_smoke'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
- col.separator()
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_gas_domain(context):
+ return False
- col = flow.column()
- col.prop(fluid, "render_display_mode", text="Render Display")
- col.prop(fluid, "viewport_display_mode", text="Viewport")
+ return (context.engine in cls.COMPAT_ENGINES)
- col.separator()
+ def draw_header(self, context):
+ md = context.fluid
+ domain = md.domain_settings
- col = flow.column()
- sub = col.column(align=True)
- sub.prop(fluid, "start_time", text="Time Start")
- sub.prop(fluid, "end_time", text="End")
- col.prop(fluid, "simulation_rate", text="Speed")
+ self.layout.prop(domain, "use_dissolve_smoke", text="")
- col = flow.column()
- col.prop(fluid, "use_speed_vectors")
- col.prop(fluid, "use_reverse_frames")
- col.prop(fluid, "frame_offset", text="Offset")
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
- elif fluid.type == 'FLUID':
- col = flow.column()
- col.prop(fluid, "volume_initialization", text="Volume Initialization")
- col.prop(fluid, "use_animated_mesh")
+ md = context.fluid
+ domain = md.domain_settings
- col = flow.column()
- col.prop(fluid, "initial_velocity", text="Initial Velocity")
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
- elif fluid.type == 'OBSTACLE':
- col = flow.column()
- col.prop(fluid, "volume_initialization", text="Volume Initialization")
- col.prop(fluid, "use_animated_mesh")
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_data
- col = flow.column()
- subcol = col.column()
- subcol.enabled = not fluid.use_animated_mesh
- subcol.prop(fluid, "slip_type", text="Slip Type")
+ layout.active = domain.use_dissolve_smoke
- if fluid.slip_type == 'PARTIALSLIP':
- subcol.prop(fluid, "partial_slip_factor", text="Amount", slider=True)
+ col = flow.column()
+ col.prop(domain, "dissolve_speed", text="Time")
- col.prop(fluid, "impact_factor", text="Impact Factor")
+ col = flow.column()
+ col.prop(domain, "use_dissolve_smoke_log", text="Slow")
- elif fluid.type == 'PARTICLE':
- col = flow.column()
- col.prop(fluid, "particle_influence", text="Influence Size")
- col.prop(fluid, "alpha_influence", text="Alpha")
- col = flow.column()
- col.prop(fluid, "use_drops")
- col.prop(fluid, "use_floats")
- col.prop(fluid, "show_tracer")
+class PHYSICS_PT_fire(PhysicButtonsPanel, Panel):
+ bl_label = "Fire"
+ bl_parent_id = 'PHYSICS_PT_settings'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_gas_domain(context):
+ return False
-class PHYSICS_PT_fluid_particle_cache(PhysicButtonsPanel, Panel):
- bl_label = "Cache"
- bl_parent_id = "PHYSICS_PT_fluid"
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ md = context.fluid
+ domain = md.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_data
+
+ col = flow.column()
+ col.prop(domain, "burning_rate", text="Reaction Speed")
+ col = flow.column()
+ col.prop(domain, "flame_smoke", text="Flame Smoke")
+ col = flow.column()
+ col.prop(domain, "flame_vorticity", text="Flame Vorticity")
+ col = flow.column()
+ col.prop(domain, "flame_ignition", text="Temperature Ignition")
+ col = flow.column()
+ col.prop(domain, "flame_max_temp", text="Maximum Temperature")
+ col = flow.column()
+ col.prop(domain, "flame_smoke_color", text="Flame Color")
+
+
+class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel):
+ bl_label = "Liquid"
+ bl_parent_id = 'PHYSICS_PT_settings'
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_liquid_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+ self.layout.prop(md, "use_flip_particles", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ md = context.fluid
+ domain = md.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+
+ col = flow.column()
+ col0 = col.column()
+ col0.enabled = not is_baking_any and not has_baked_data
+ col0.prop(domain, "simulation_method", expand=False)
+ col0.prop(domain, "flip_ratio", text="FLIP Ratio")
+ col0.prop(domain, "particle_radius", text="Particle Radius")
+
+ col1 = flow.column(align=True)
+ col1.enabled = not is_baking_any and not has_baked_data
+ col1.prop(domain, "particle_max", text="Particles Maximum")
+ col1.prop(domain, "particle_min", text="Minimum")
+
+ col1 = flow.column()
+ col1.enabled = not is_baking_any and not has_baked_data
+ col1.prop(domain, "particle_number", text="Particle Sampling")
+ col1.prop(domain, "particle_band_width", text="Narrow Band Width")
+ col1.prop(domain, "particle_randomness", text="Particle Randomness")
+
+ col2 = flow.column()
+ col2.enabled = not is_baking_any and not has_baked_data
+ col2.prop(domain, "use_fractions", text="Fractional Obstacles")
+ col3 = col2.column()
+ col3.enabled = domain.use_fractions and col2.enabled
+ col3.prop(domain, "fractions_threshold", text="Obstacle-Fluid Threshold")
+
+
+class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel):
+ bl_label = "Flow Source"
+ bl_parent_id = 'PHYSICS_PT_settings'
+ bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
- if not PhysicButtonsPanel.poll_fluid_settings(context):
+ if not PhysicButtonsPanel.poll_fluid_flow(context):
return False
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ ob = context.object
+ flow = context.fluid.flow_settings
+
+ col = layout.column()
+ col.prop(flow, "flow_source", expand=False, text="Flow Source")
+ if flow.flow_source == 'PARTICLES':
+ col.prop_search(flow, "particle_system", ob, "particle_systems", text="Particle System")
+
+ grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+
+ col = grid.column()
+ if flow.flow_source == 'MESH':
+ col.prop(flow, "use_plane_init", text="Is Planar")
+ col.prop(flow, "surface_distance", text="Surface Thickness")
+ if flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}:
+ col = grid.column()
+ col.prop(flow, "volume_density", text="Volume Density")
+
+ if flow.flow_source == 'PARTICLES':
+ col.prop(flow, "use_particle_size", text="Set Size")
+ sub = col.column()
+ sub.active = flow.use_particle_size
+ sub.prop(flow, "particle_size")
+
+
+class PHYSICS_PT_flow_initial_velocity(PhysicButtonsPanel, Panel):
+ bl_label = "Initial Velocity"
+ bl_parent_id = 'PHYSICS_PT_settings'
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_fluid_flow(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw_header(self, context):
md = context.fluid
- return md and md.settings and (md.settings.type == 'PARTICLE') and (context.engine in cls.COMPAT_ENGINES)
+ flow_smoke = md.flow_settings
+
+ self.layout.prop(flow_smoke, "use_initial_velocity", text="")
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
+
+ md = context.fluid
+ flow_smoke = md.flow_settings
+
+ flow.active = flow_smoke.use_initial_velocity
+
+ col = flow.column()
+ col.prop(flow_smoke, "velocity_factor")
+
+ if flow_smoke.flow_source == 'MESH':
+ col.prop(flow_smoke, "velocity_normal")
+ # col.prop(flow_smoke, "velocity_random")
+ col = flow.column()
+ col.prop(flow_smoke, "velocity_coord")
+
+
+class PHYSICS_PT_flow_texture(PhysicButtonsPanel, Panel):
+ bl_label = "Texture"
+ bl_parent_id = 'PHYSICS_PT_settings'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_fluid_flow(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw_header(self, context):
md = context.fluid
- fluid = md.settings
+ flow_smoke = md.flow_settings
+
+ self.layout.prop(flow_smoke, "use_texture", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+
+ ob = context.object
+ flow_smoke = context.fluid.flow_settings
+
+ sub = flow.column()
+ sub.active = flow_smoke.use_texture
+ sub.prop(flow_smoke, "noise_texture")
+ sub.prop(flow_smoke, "texture_map_type", text="Mapping")
+
+ col = flow.column()
+ sub = col.column()
+ sub.active = flow_smoke.use_texture
+
+ if flow_smoke.texture_map_type == 'UV':
+ sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers")
+
+ if flow_smoke.texture_map_type == 'AUTO':
+ sub.prop(flow_smoke, "texture_size")
+
+ sub.prop(flow_smoke, "texture_offset")
+
+
+class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel):
+ bl_label = "Adaptive Domain"
+ bl_parent_id = 'PHYSICS_PT_fluid'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_gas_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+ domain = context.fluid.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_any = domain.has_cache_baked_any
- layout.prop(fluid, "filepath", text="")
+ self.layout.enabled = not is_baking_any and not has_baked_any
+ self.layout.prop(md, "use_adaptive_domain", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ domain = context.fluid.domain_settings
+ layout.active = domain.use_adaptive_domain
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_any = domain.has_cache_baked_any
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
+ flow.enabled = not is_baking_any and not has_baked_any
+
+ col = flow.column()
+ col.prop(domain, "additional_res", text="Add Resolution")
+ col.prop(domain, "adapt_margin")
+
+ col.separator()
+
+ col = flow.column()
+ col.prop(domain, "adapt_threshold", text="Threshold")
-class PHYSICS_PT_domain_bake(PhysicButtonsPanel, Panel):
- bl_label = "Bake"
+class PHYSICS_PT_noise(PhysicButtonsPanel, Panel):
+ bl_label = "Noise"
bl_parent_id = 'PHYSICS_PT_fluid'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
- if not PhysicButtonsPanel.poll_fluid_domain(context):
+ if not PhysicButtonsPanel.poll_gas_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+ domain = context.fluid.domain_settings
+ is_baking_any = domain.is_cache_baking_any
+ self.layout.enabled = not is_baking_any
+ self.layout.prop(md, "use_noise", text="")
+
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
- md = context.fluid
- fluid = md.settings
+ domain = context.fluid.domain_settings
- row = layout.row(align=True)
- row.alignment = 'RIGHT'
- row.label(text="Cache Path")
+ # Deactivate UI if guides are enabled but not baked yet.
+ layout.active = domain.use_noise and not self.check_domain_has_unbaked_guide(domain)
- layout.prop(fluid, "filepath", text="")
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_noise = domain.has_cache_baked_noise
- # odd formatting here so translation script can extract string
- layout.operator(
- "fluid.bake", text=iface_("Bake (Req. Memory: %s)") % fluid.memory_estimate,
- translate=False, icon='MOD_FLUIDSIM'
- )
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_noise
+
+ col = flow.column()
+ col.prop(domain, "noise_scale", text="Upres Factor")
+ # TODO (sebbas): Mantaflow only supports wavelet noise. Maybe get rid of noise type field.
+ col.prop(domain, "noise_type", text="Noise Method")
+
+ col = flow.column()
+ col.prop(domain, "noise_strength", text="Strength")
+ col.prop(domain, "noise_pos_scale", text="Scale")
+ col.prop(domain, "noise_time_anim", text="Time")
+ if domain.cache_type == 'MODULAR':
+ col.separator()
-class PHYSICS_PT_domain_gravity(PhysicButtonsPanel, Panel):
- bl_label = "World"
+ split = layout.split()
+ split.enabled = domain.has_cache_baked_data
+
+ bake_incomplete = (domain.cache_frame_pause_noise < domain.cache_frame_end)
+ if domain.has_cache_baked_noise and not domain.is_cache_baking_noise and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_noise", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_noise", text="Free")
+ elif not domain.has_cache_baked_noise and domain.is_cache_baking_noise:
+ split.enabled = False
+ split.operator("fluid.pause_bake", text="Baking Noise - ESC to pause")
+ elif not domain.has_cache_baked_noise and not domain.is_cache_baking_noise:
+ split.operator("fluid.bake_noise", text="Bake Noise")
+ else:
+ split.operator("fluid.free_noise", text="Free Noise")
+
+
+class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel):
+ bl_label = "Mesh"
bl_parent_id = 'PHYSICS_PT_fluid'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
- if not PhysicButtonsPanel.poll_fluid_domain(context):
+ if not PhysicButtonsPanel.poll_liquid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+ domain = context.fluid.domain_settings
+ is_baking_any = domain.is_cache_baking_any
+ self.layout.enabled = not is_baking_any
+ self.layout.prop(md, "use_mesh", text="")
+
def draw(self, context):
layout = self.layout
layout.use_property_split = True
- fluid = context.fluid.settings
- scene = context.scene
+ domain = context.fluid.domain_settings
- col = layout.column()
+ # Deactivate UI if guides are enabled but not baked yet.
+ layout.active = domain.use_mesh and not self.check_domain_has_unbaked_guide(domain)
- use_gravity = scene.use_gravity
- use_units = scene.unit_settings.system != 'NONE'
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_mesh = domain.has_cache_baked_mesh
- if use_gravity or use_units:
- s_gravity = " Gravity" if use_gravity else ""
- s_units = " Units" if use_units else ""
- s_and = " and " if use_gravity and use_units else ""
- warn = f"Using {s_gravity}{s_and}{s_units} from Scene"
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_mesh
- sub = col.column()
- sub.alignment = 'RIGHT'
- sub.label(text=warn)
+ col = flow.column()
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
+ col.prop(domain, "mesh_scale", text="Upres Factor")
+ col.prop(domain, "mesh_particle_radius", text="Particle Radius")
col = flow.column()
- sub = col.column()
- sub.enabled = not use_gravity
- sub.prop(fluid, "gravity", text="Gravity")
+ col.prop(domain, "use_speed_vectors", text="Use Speed Vectors")
- sub = col.column()
- sub.enabled = not use_units
- sub.prop(fluid, "simulation_scale", text="Scene Size Meters" if use_units else "World Size Meters")
+ col.separator()
+ col.prop(domain, "mesh_generator", text="Mesh Generator")
+
+ if domain.mesh_generator in {'IMPROVED'}:
+ col = flow.column(align=True)
+ col.prop(domain, "mesh_smoothen_pos", text="Smoothing Positive")
+ col.prop(domain, "mesh_smoothen_neg", text="Negative")
+
+ col = flow.column(align=True)
+ col.prop(domain, "mesh_concave_upper", text="Concavity Upper")
+ col.prop(domain, "mesh_concave_lower", text="Lower")
+
+ # TODO (sebbas): for now just interpolate any upres grids, ie not sampling highres grids
+ #col.prop(domain, "highres_sampling", text="Flow Sampling:")
+
+ if domain.cache_type == 'MODULAR':
+ col.separator()
+
+ split = layout.split()
+ split.enabled = domain.has_cache_baked_data
+
+ bake_incomplete = (domain.cache_frame_pause_mesh < domain.cache_frame_end)
+ if domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_mesh", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_mesh", text="Free")
+ elif not domain.has_cache_baked_mesh and domain.is_cache_baking_mesh:
+ split.enabled = False
+ split.operator("fluid.pause_bake", text="Baking Mesh - ESC to pause")
+ elif not domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh:
+ split.operator("fluid.bake_mesh", text="Bake Mesh")
+ else:
+ split.operator("fluid.free_mesh", text="Free Mesh")
+
+
+class PHYSICS_PT_particles(PhysicButtonsPanel, Panel):
+ bl_label = "Particles"
+ bl_parent_id = 'PHYSICS_PT_fluid'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_liquid_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ domain = context.fluid.domain_settings
+
+ # Deactivate UI if guides are enabled but not baked yet.
+ layout.active = not self.check_domain_has_unbaked_guide(domain)
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_particles = domain.has_cache_baked_particles
+ using_particles = domain.use_spray_particles or domain.use_foam_particles or domain.use_bubble_particles
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any
+
+ sndparticle_combined_export = domain.sndparticle_combined_export
+ col = flow.column()
+ col.enabled = sndparticle_combined_export in {'OFF', 'FOAM + BUBBLES'}
+ col.prop(domain, "use_spray_particles", text="Spray")
+ col = flow.column()
+ col.enabled = sndparticle_combined_export in {'OFF', 'SPRAY + BUBBLES'}
+ col.prop(domain, "use_foam_particles", text="Foam")
+ col = flow.column()
+ col.enabled = sndparticle_combined_export in {'OFF', 'SPRAY + FOAM'}
+ col.prop(domain, "use_bubble_particles", text="Bubbles")
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_particles and using_particles
+
+ col = flow.column()
+ col.prop(domain, "sndparticle_combined_export")
+ col.prop(domain, "particle_scale", text="Upres Factor")
+ col.separator()
+
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_tau_max_wc", text="Wave Crest Potential Maximum")
+ col.prop(domain, "sndparticle_tau_min_wc", text="Minimum")
+ col.separator()
+
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_tau_max_ta", text="Trapped Air Potential Maximum")
+ col.prop(domain, "sndparticle_tau_min_ta", text="Minimum")
+ col.separator()
+
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_tau_max_k", text="Kinetic Energy Potential Maximum")
+ col.prop(domain, "sndparticle_tau_min_k", text="Minimum")
+ col.separator()
+
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_potential_radius", text="Potential Radius")
+ col.prop(domain, "sndparticle_update_radius", text="Particle Update Radius")
+ col.separator()
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_k_wc", text="Wave Crest Particle Sampling")
+ col.prop(domain, "sndparticle_k_ta", text="Trapped Air Particle Sampling")
+ col.separator()
+
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_l_max", text="Particle Life Maximum")
+ col.prop(domain, "sndparticle_l_min", text="Minimum")
+ col.separator()
+
+ col = flow.column(align=True)
+ col.prop(domain, "sndparticle_k_b", text="Bubble Buoyancy")
+ col.prop(domain, "sndparticle_k_d", text="Bubble Drag")
col.separator()
col = flow.column()
- col.prop(fluid, "grid_levels", text="Optimization", slider=True)
- col.prop(fluid, "compressibility", slider=True)
+ col.prop(domain, "sndparticle_boundary", text="Particles in Boundary:")
+ if domain.cache_type == 'MODULAR':
+ col.separator()
-class PHYSICS_PT_domain_viscosity(PhysicButtonsPanel, Panel):
- bl_label = "Viscosity"
+ split = layout.split()
+ split.enabled = (
+ domain.has_cache_baked_data and
+ (domain.use_spray_particles or
+ domain.use_bubble_particles or
+ domain.use_foam_particles or
+ domain.use_tracer_particles)
+ )
+
+ bake_incomplete = (domain.cache_frame_pause_particles < domain.cache_frame_end)
+ if domain.has_cache_baked_particles and not domain.is_cache_baking_particles and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_particles", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_particles", text="Free")
+ elif not domain.has_cache_baked_particles and domain.is_cache_baking_particles:
+ split.enabled = False
+ split.operator("fluid.pause_bake", text="Baking Particles - ESC to pause")
+ elif not domain.has_cache_baked_particles and not domain.is_cache_baking_particles:
+ split.operator("fluid.bake_particles", text="Bake Particles")
+ else:
+ split.operator("fluid.free_particles", text="Free Particles")
+
+
+class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel):
+ bl_label = "Diffusion"
bl_parent_id = 'PHYSICS_PT_fluid'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
+ # Fluid diffusion only enabled for liquids (surface tension and viscosity not relevant for smoke)
+ if not PhysicButtonsPanel.poll_liquid_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ domain = context.fluid.domain_settings
+
+ # Deactivate UI if guides are enabled but not baked yet.
+ layout.active = not self.check_domain_has_unbaked_guide(domain)
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_any = domain.has_cache_baked_any
+ has_baked_data = domain.has_cache_baked_data
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data
+
+ row = flow.row()
+
+ col = row.column()
+ col.label(text="Viscosity Presets:")
+ col.menu("FLUID_MT_presets", text=bpy.types.FLUID_MT_presets.bl_label)
+
+ col = row.column(align=True)
+ col.operator("fluid.preset_add", text="", icon='ADD')
+ col.operator("fluid.preset_add", text="", icon='REMOVE').remove_active = True
+
+ col = flow.column(align=True)
+ col.prop(domain, "viscosity_base", text="Base")
+ col.prop(domain, "viscosity_exponent", text="Exponent", slider=True)
+
+ col = flow.column()
+ col.prop(domain, "domain_size", text="Real World Size")
+ col.prop(domain, "surface_tension", text="Surface Tension")
+
+
+class PHYSICS_PT_guide(PhysicButtonsPanel, Panel):
+ bl_label = "Guides"
+ bl_parent_id = 'PHYSICS_PT_fluid'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
+
+ @classmethod
+ def poll(cls, context):
if not PhysicButtonsPanel.poll_fluid_domain(context):
return False
return (context.engine in cls.COMPAT_ENGINES)
- def draw_header_preset(self, _context):
- FLUID_PT_presets.draw_panel_header(self.layout)
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+ domain = context.fluid.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+
+ self.layout.enabled = not is_baking_any
+ self.layout.prop(md, "use_guide", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
- fluid = context.fluid.settings
+ domain = context.fluid.domain_settings
+
+ layout.active = domain.use_guide
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_data = domain.has_cache_baked_data
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_data
col = flow.column()
- col.prop(fluid, "viscosity_base", text="Base")
+ col.prop(domain, "guide_alpha", text="Weight")
+ col.prop(domain, "guide_beta", text="Size")
+ col.prop(domain, "guide_vel_factor", text="Velocity Factor")
col = flow.column()
- col.prop(fluid, "viscosity_exponent", text="Exponent", slider=True)
+ col.prop(domain, "guide_source", text="Velocity Source")
+ if domain.guide_source == 'DOMAIN':
+ col.prop(domain, "guide_parent", text="Guide Parent")
+ if domain.cache_type == 'MODULAR':
+ col.separator()
-class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, Panel):
- bl_label = "Boundary"
+ if domain.guide_source == 'EFFECTOR':
+ split = layout.split()
+ bake_incomplete = (domain.cache_frame_pause_guide < domain.cache_frame_end)
+ if domain.has_cache_baked_guide and not domain.is_cache_baking_guide and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_guides", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_guides", text="Free")
+ elif not domain.has_cache_baked_guide and domain.is_cache_baking_guide:
+ split.operator("fluid.pause_bake", text="Pause Guides")
+ elif not domain.has_cache_baked_guide and not domain.is_cache_baking_guide:
+ split.operator("fluid.bake_guides", text="Bake Guides")
+ else:
+ split.operator("fluid.free_guides", text="Free Guides")
+
+
+class PHYSICS_PT_collections(PhysicButtonsPanel, Panel):
+ bl_label = "Collections"
bl_parent_id = 'PHYSICS_PT_fluid'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@@ -403,26 +1014,126 @@ class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
- fluid = context.fluid.settings
+ domain = context.fluid.domain_settings
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+
+ col = flow.column()
+ col.prop(domain, "fluid_group", text="Flow")
+
+ # col.prop(domain, "effector_group", text="Forces")
+ col.prop(domain, "effector_group", text="Effector")
+
+
+class PHYSICS_PT_cache(PhysicButtonsPanel, Panel):
+ bl_label = "Cache"
+ bl_parent_id = 'PHYSICS_PT_fluid'
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_fluid_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+
+ md = context.fluid
+ domain = context.fluid.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_any = domain.has_cache_baked_any
+
+ col = layout.column()
+ col.prop(domain, "cache_directory", text="")
+ col.enabled = not is_baking_any
+
+ layout.use_property_split = True
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
col = flow.column()
- col.prop(fluid, "slip_type", text="Type")
+ col.prop(domain, "cache_type", expand=False)
+ col.enabled = not is_baking_any
+ col = flow.column(align=True)
col.separator()
- if fluid.slip_type == 'PARTIALSLIP':
- col.prop(fluid, "partial_slip_factor", slider=True, text="Amount")
+ col.prop(domain, "cache_frame_start", text="Frame Start")
+ col.prop(domain, "cache_frame_end", text="End")
+ col.enabled = not is_baking_any
+
+ col.separator()
col = flow.column()
- col.prop(fluid, "surface_smooth", text="Surface Smoothing")
- col.prop(fluid, "surface_subdivisions", text="Subdivisions")
- col.prop(fluid, "use_surface_noobs")
+ col.enabled = not is_baking_any and not has_baked_any
+ col.prop(domain, "cache_data_format", text="Data File Format")
+ if md.domain_settings.domain_type in {'GAS'}:
+ if domain.use_noise:
+ col.prop(domain, "cache_noise_format", text="Noise File Format")
-class PHYSICS_PT_domain_particles(PhysicButtonsPanel, Panel):
- bl_label = "Particles"
+ if md.domain_settings.domain_type in {'LIQUID'}:
+ # File format for all particle systemes (FLIP and secondary)
+ col.prop(domain, "cache_particle_format", text="Particle File Format")
+
+ if domain.use_mesh:
+ col.prop(domain, "cache_mesh_format", text="Mesh File Format")
+
+ if domain.cache_type == 'FINAL':
+
+ col.separator()
+ split = layout.split()
+
+ bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end)
+ if domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete:
+ col = split.column()
+ col.operator("fluid.bake_all", text="Resume")
+ col = split.column()
+ col.operator("fluid.free_all", text="Free")
+ elif domain.is_cache_baking_data and not domain.has_cache_baked_data:
+ split.enabled = False
+ split.operator("fluid.pause_bake", text="Baking All - ESC to pause")
+ elif not domain.has_cache_baked_data and not domain.is_cache_baking_data:
+ split.operator("fluid.bake_all", text="Bake All")
+ else:
+ split.operator("fluid.free_all", text="Free All")
+
+
+class PHYSICS_PT_export(PhysicButtonsPanel, Panel):
+ bl_label = "Advanced"
+ bl_parent_id = 'PHYSICS_PT_cache'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_fluid_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ domain = context.fluid.domain_settings
+
+ is_baking_any = domain.is_cache_baking_any
+ has_baked_any = domain.has_cache_baked_any
+
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.enabled = not is_baking_any and not has_baked_any
+
+ col = flow.column()
+ col.prop(domain, "export_manta_script", text="Export Mantaflow Script")
+
+
+class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel):
+ bl_label = "Field Weights"
bl_parent_id = 'PHYSICS_PT_fluid'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@@ -435,32 +1146,141 @@ class PHYSICS_PT_domain_particles(PhysicButtonsPanel, Panel):
return (context.engine in cls.COMPAT_ENGINES)
def draw(self, context):
+ domain = context.fluid.domain_settings
+ effector_weights_ui(self, domain.effector_weights, 'SMOKE')
+
+
+class PHYSICS_PT_viewport_display(PhysicButtonsPanel, Panel):
+ bl_label = "Viewport Display"
+ bl_parent_id = 'PHYSICS_PT_fluid'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return (PhysicButtonsPanel.poll_gas_domain(context))
+
+ def draw(self, context):
layout = self.layout
layout.use_property_split = True
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
- fluid = context.fluid.settings
+ domain = context.fluid.domain_settings
col = flow.column()
- col.prop(fluid, "tracer_particles", text="Tracer")
+ col.prop(domain, "display_thickness")
+
+ col.separator()
+
+ col.prop(domain, "slice_method", text="Slicing")
+
+ slice_method = domain.slice_method
+ axis_slice_method = domain.axis_slice_method
+
+ do_axis_slicing = (slice_method == 'AXIS_ALIGNED')
+ do_full_slicing = (axis_slice_method == 'FULL')
+
+ col = col.column()
+ col.enabled = do_axis_slicing
+ col.prop(domain, "axis_slice_method")
col = flow.column()
- col.prop(fluid, "generate_particles", text="Generate")
+ sub = col.column()
+ sub.enabled = not do_full_slicing and do_axis_slicing
+ sub.prop(domain, "slice_axis")
+ sub.prop(domain, "slice_depth")
+
+ row = col.row()
+ row.enabled = do_full_slicing or not do_axis_slicing
+ row.prop(domain, "slice_per_voxel")
+
+ col.prop(domain, "display_interpolation")
+
+
+class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel):
+ bl_label = "Color Mapping"
+ bl_parent_id = 'PHYSICS_PT_viewport_display'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return (PhysicButtonsPanel.poll_gas_domain(context))
+
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+
+ self.layout.prop(md, "use_color_ramp", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ domain = context.fluid.domain_settings
+ col = layout.column()
+ col.enabled = domain.use_color_ramp
+
+ col.prop(domain, "coba_field")
+
+ col.use_property_split = False
+
+ col = col.column()
+ col.template_color_ramp(domain, "color_ramp", expand=True)
+
+
+class PHYSICS_PT_viewport_display_debug(PhysicButtonsPanel, Panel):
+ bl_label = "Debug Velocity"
+ bl_parent_id = 'PHYSICS_PT_viewport_display'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return (PhysicButtonsPanel.poll_gas_domain(context))
+
+ def draw_header(self, context):
+ md = context.fluid.domain_settings
+
+ self.layout.prop(md, "show_velocity", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
+
+ domain = context.fluid.domain_settings
+
+ col = flow.column()
+ col.enabled = domain.show_velocity
+ col.prop(domain, "vector_display_type", text="Display As")
+ col.prop(domain, "vector_scale")
classes = (
- FLUID_PT_presets,
+ FLUID_MT_presets,
PHYSICS_PT_fluid,
- PHYSICS_PT_fluid_settings,
- PHYSICS_PT_fluid_flow,
- PHYSICS_PT_fluid_particle_cache,
- PHYSICS_PT_domain_bake,
- PHYSICS_PT_domain_boundary,
- PHYSICS_PT_domain_particles,
- PHYSICS_PT_domain_gravity,
- PHYSICS_PT_domain_viscosity,
+ PHYSICS_PT_settings,
+ PHYSICS_PT_borders,
+ PHYSICS_PT_smoke,
+ PHYSICS_PT_smoke_dissolve,
+ PHYSICS_PT_fire,
+ PHYSICS_PT_liquid,
+ PHYSICS_PT_flow_source,
+ PHYSICS_PT_flow_initial_velocity,
+ PHYSICS_PT_flow_texture,
+ PHYSICS_PT_adaptive_domain,
+ PHYSICS_PT_noise,
+ PHYSICS_PT_mesh,
+ PHYSICS_PT_particles,
+ PHYSICS_PT_diffusion,
+ PHYSICS_PT_guide,
+ PHYSICS_PT_collections,
+ PHYSICS_PT_cache,
+ PHYSICS_PT_export,
+ PHYSICS_PT_field_weights,
+ PHYSICS_PT_viewport_display,
+ PHYSICS_PT_viewport_display_color,
+ PHYSICS_PT_viewport_display_debug,
)
+
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
deleted file mode 100644
index 057e7ddf211..00000000000
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ /dev/null
@@ -1,692 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-# <pep8 compliant>
-
-import bpy
-from bpy.types import (
- Panel,
-)
-from bl_ui.properties_physics_common import (
- point_cache_ui,
- effector_weights_ui,
-)
-
-
-class PhysicButtonsPanel:
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "physics"
-
- @staticmethod
- def poll_smoke(context):
- ob = context.object
- if not ((ob and ob.type == 'MESH') and (context.smoke)):
- return False
-
- md = context.smoke
- return md and (context.smoke.smoke_type != 'NONE') and (bpy.app.build_options.mod_smoke)
-
- @staticmethod
- def poll_smoke_domain(context):
- if not PhysicButtonsPanel.poll_smoke(context):
- return False
-
- md = context.smoke
- return md and (md.smoke_type == 'DOMAIN')
-
-
-class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
- bl_label = "Smoke"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- ob = context.object
- return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.smoke)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- if not bpy.app.build_options.mod_smoke:
- col = layout.column(align=True)
- col.alignment = 'RIGHT'
- col.label(text="Built without Smoke modifier")
- return
-
- md = context.smoke
-
- layout.prop(md, "smoke_type")
-
-
-class PHYSICS_PT_smoke_settings(PhysicButtonsPanel, Panel):
- bl_label = "Settings"
- bl_parent_id = 'PHYSICS_PT_smoke'
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- md = context.smoke
- ob = context.object
-
- if md.smoke_type == 'DOMAIN':
- domain = md.domain_settings
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- col = flow.column()
- col.enabled = (not domain.point_cache.is_baked)
- col.prop(domain, "resolution_max", text="Resolution Divisions")
- col.prop(domain, "time_scale", text="Time Scale")
-
- col.separator()
-
- col = flow.column()
- sub = col.row()
- sub.enabled = (not domain.point_cache.is_baked)
- sub.prop(domain, "collision_extents", text="Border Collisions")
-
- # This can be tweaked after baking, for render.
- col.prop(domain, "clipping", text="Empty Space")
-
- elif md.smoke_type == 'FLOW':
- flow_smoke = md.flow_settings
-
- col = layout.column()
- col.prop(flow_smoke, "smoke_flow_type", expand=False)
-
- col.separator()
-
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
- col = flow.column()
-
- if flow_smoke.smoke_flow_type != 'OUTFLOW':
- col.prop(flow_smoke, "smoke_flow_source", expand=False, text="Flow Source")
-
- if flow_smoke.smoke_flow_source == 'PARTICLES':
- col.prop_search(
- flow_smoke, "particle_system", ob, "particle_systems",
- text="Particle System"
- )
- else:
- col.prop(flow_smoke, "surface_distance")
- col.prop(flow_smoke, "volume_density")
-
- col = flow.column()
- col.prop(flow_smoke, "use_absolute")
-
- if flow_smoke.smoke_flow_type in {'SMOKE', 'BOTH'}:
- col.prop(flow_smoke, "density")
- col.prop(flow_smoke, "temperature", text="Temperature Diff.")
-
- col.separator()
-
- col = flow.column()
- col.prop(flow_smoke, "smoke_color")
-
- if flow_smoke.smoke_flow_type in {'FIRE', 'BOTH'}:
- col.prop(flow_smoke, "fuel_amount")
-
- col.prop(flow_smoke, "subframes", text="Sampling Subframes")
-
- col.separator()
-
- col.prop_search(flow_smoke, "density_vertex_group", ob, "vertex_groups", text="Vertex Group")
-
- elif md.smoke_type == 'COLLISION':
- coll = md.coll_settings
-
- col = layout.column()
- col.prop(coll, "collision_type")
-
-
-class PHYSICS_PT_smoke_settings_initial_velocity(PhysicButtonsPanel, Panel):
- bl_label = "Initial Velocity"
- bl_parent_id = 'PHYSICS_PT_smoke_settings'
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke(context):
- return False
-
- md = context.smoke
- return (md and (md.smoke_type == 'FLOW')
- and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW'
- and context.engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- md = context.smoke
- flow_smoke = md.flow_settings
-
- self.layout.prop(flow_smoke, "use_initial_velocity", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
-
- md = context.smoke
- flow_smoke = md.flow_settings
-
- flow.active = flow_smoke.use_initial_velocity
-
- col = flow.column(align=True)
- col.prop(flow_smoke, "velocity_factor")
-
- if flow_smoke.smoke_flow_source == 'MESH':
- col = flow.column()
- col.prop(flow_smoke, "velocity_normal")
- # sub.prop(flow_smoke, "velocity_random")
-
-
-class PHYSICS_PT_smoke_settings_particle_size(PhysicButtonsPanel, Panel):
- bl_label = "Particle Size"
- bl_parent_id = 'PHYSICS_PT_smoke_settings'
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke(context):
- return False
-
- md = context.smoke
- return (md and (md.smoke_type == 'FLOW')
- and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW'
- and md.flow_settings.smoke_flow_source == 'PARTICLES'
- and context.engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- md = context.smoke
- flow_smoke = md.flow_settings
-
- self.layout.prop(flow_smoke, "use_particle_size", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- md = context.smoke
- flow_smoke = md.flow_settings
-
- layout.active = flow_smoke.use_particle_size
-
- layout.prop(flow_smoke, "particle_size")
-
-
-class PHYSICS_PT_smoke_behavior(PhysicButtonsPanel, Panel):
- bl_label = "Behavior"
- bl_parent_id = 'PHYSICS_PT_smoke_settings'
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- md = context.smoke
- domain = md.domain_settings
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- flow.enabled = (not domain.point_cache.is_baked)
-
- col = flow.column()
- col.prop(domain, "alpha")
- col.prop(domain, "beta", text="Temperature Diff.")
- col = flow.column()
- col.prop(domain, "vorticity")
-
-
-class PHYSICS_PT_smoke_behavior_dissolve(PhysicButtonsPanel, Panel):
- bl_label = "Dissolve"
- bl_parent_id = 'PHYSICS_PT_smoke_behavior'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- md = context.smoke
- domain = md.domain_settings
-
- self.layout.prop(domain, "use_dissolve_smoke", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- md = context.smoke
- domain = md.domain_settings
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- flow.enabled = (not domain.point_cache.is_baked)
-
- layout.active = domain.use_dissolve_smoke
-
- col = flow.column()
- col.prop(domain, "dissolve_speed", text="Time")
-
- col = flow.column()
- col.prop(domain, "use_dissolve_smoke_log", text="Slow")
-
-
-class PHYSICS_PT_smoke_flow_texture(PhysicButtonsPanel, Panel):
- bl_label = "Texture"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke(context):
- return False
-
- md = context.smoke
- return (md and (md.smoke_type == 'FLOW')
- and (md.flow_settings.smoke_flow_source == 'MESH')
- and (context.engine in cls.COMPAT_ENGINES))
-
- def draw_header(self, context):
- md = context.smoke
- flow_smoke = md.flow_settings
-
- self.layout.prop(flow_smoke, "use_texture", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- ob = context.object
- flow_smoke = context.smoke.flow_settings
-
- sub = flow.column()
- sub.active = flow_smoke.use_texture
- sub.prop(flow_smoke, "noise_texture")
- sub.prop(flow_smoke, "texture_map_type", text="Mapping")
-
- col = flow.column()
- sub = col.column()
- sub.active = flow_smoke.use_texture
-
- if flow_smoke.texture_map_type == 'UV':
- sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers")
-
- if flow_smoke.texture_map_type == 'AUTO':
- sub.prop(flow_smoke, "texture_size")
-
- sub.prop(flow_smoke, "texture_offset")
-
-
-class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
- bl_label = "Flames"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- domain = context.smoke.domain_settings
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- flow.enabled = (not domain.point_cache.is_baked)
-
- col = flow.column()
- col.prop(domain, "burning_rate", text="Reaction Speed")
- col.prop(domain, "flame_smoke")
- col.prop(domain, "flame_vorticity")
-
- col.separator()
-
- col = flow.column(align=True)
- col.prop(domain, "flame_ignition", text="Temperature Ignition")
- col.prop(domain, "flame_max_temp")
-
- col.separator()
-
- sub = col.column()
- sub.prop(domain, "flame_smoke_color")
-
-
-class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
- bl_label = "Adaptive Domain"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- md = context.smoke.domain_settings
-
- self.layout.prop(md, "use_adaptive_domain", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- domain = context.smoke.domain_settings
- layout.active = domain.use_adaptive_domain
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
- flow.enabled = (not domain.point_cache.is_baked)
-
- col = flow.column()
- col.prop(domain, "additional_res", text="Add Resolution")
- col.prop(domain, "adapt_margin")
-
- col.separator()
-
- col = flow.column()
- col.prop(domain, "adapt_threshold", text="Threshold")
-
-
-class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
- bl_label = "High Resolution"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- md = context.smoke.domain_settings
-
- self.layout.prop(md, "use_high_resolution", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- md = context.smoke.domain_settings
- layout.active = md.use_high_resolution
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
-
- col = flow.column()
- col.enabled = not md.point_cache.is_baked
- col.prop(md, "amplify", text="Resolution Divisions")
- col.prop(md, "highres_sampling", text="Flow Sampling")
-
- col.separator()
-
- col = flow.column()
- col.enabled = not md.point_cache.is_baked
- col.prop(md, "noise_type", text="Noise Method")
- col.prop(md, "strength")
-
- layout.prop(md, "show_high_resolution")
-
-
-class PHYSICS_PT_smoke_collections(PhysicButtonsPanel, Panel):
- bl_label = "Collections"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- domain = context.smoke.domain_settings
-
- col = layout.column()
- col.prop(domain, "fluid_collection", text="Flow")
-
- # col = layout.column()
- # col.prop(domain, "effector_collection", text="Effector")
- col.prop(domain, "collision_collection", text="Collision")
-
-
-class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
- bl_label = "Cache"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
-
- domain = context.smoke.domain_settings
- cache_file_format = domain.cache_file_format
-
- col = flow.column()
- col.prop(domain, "cache_file_format")
-
- if cache_file_format == 'POINTCACHE':
- col = flow.column()
- col.prop(domain, "point_cache_compress_type", text="Compression")
- col.separator()
-
- elif cache_file_format == 'OPENVDB':
- if not bpy.app.build_options.openvdb:
- row = layout.row(align=True)
- row.alignment = 'RIGHT'
- row.label(text="Built without OpenVDB support")
- return
-
- col = flow.column()
- col.prop(domain, "openvdb_cache_compress_type", text="Compression")
- col.prop(domain, "data_depth", text="Data Depth")
- col.separator()
-
- cache = domain.point_cache
- point_cache_ui(self, cache, (cache.is_baked is False), 'SMOKE')
-
-
-class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
- bl_label = "Field Weights"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
-
- @classmethod
- def poll(cls, context):
- if not PhysicButtonsPanel.poll_smoke_domain(context):
- return False
-
- return (context.engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- domain = context.smoke.domain_settings
- effector_weights_ui(self, domain.effector_weights, 'SMOKE')
-
-
-class PHYSICS_PT_smoke_viewport_display(PhysicButtonsPanel, Panel):
- bl_label = "Viewport Display"
- bl_parent_id = 'PHYSICS_PT_smoke'
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- return (PhysicButtonsPanel.poll_smoke_domain(context))
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
-
- domain = context.smoke.domain_settings
-
- col = flow.column()
- col.prop(domain, "display_thickness")
-
- col.separator()
-
- col.prop(domain, "slice_method", text="Slicing")
-
- slice_method = domain.slice_method
- axis_slice_method = domain.axis_slice_method
-
- do_axis_slicing = (slice_method == 'AXIS_ALIGNED')
- do_full_slicing = (axis_slice_method == 'FULL')
-
- col = col.column()
- col.enabled = do_axis_slicing
- col.prop(domain, "axis_slice_method")
-
- col = flow.column()
- sub = col.column()
- sub.enabled = not do_full_slicing and do_axis_slicing
- sub.prop(domain, "slice_axis")
- sub.prop(domain, "slice_depth")
-
- row = col.row()
- row.enabled = do_full_slicing or not do_axis_slicing
- row.prop(domain, "slice_per_voxel")
-
- col.prop(domain, "display_interpolation")
-
-
-class PHYSICS_PT_smoke_viewport_display_color(PhysicButtonsPanel, Panel):
- bl_label = "Color Mapping"
- bl_parent_id = 'PHYSICS_PT_smoke_viewport_display'
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- return (PhysicButtonsPanel.poll_smoke_domain(context))
-
- def draw_header(self, context):
- md = context.smoke.domain_settings
-
- self.layout.prop(md, "use_color_ramp", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
-
- domain = context.smoke.domain_settings
- col = layout.column()
- col.enabled = domain.use_color_ramp
-
- col.prop(domain, "coba_field")
-
- col.use_property_split = False
-
- col = col.column()
- col.template_color_ramp(domain, "color_ramp", expand=True)
-
-
-class PHYSICS_PT_smoke_viewport_display_debug(PhysicButtonsPanel, Panel):
- bl_label = "Debug Velocity"
- bl_parent_id = 'PHYSICS_PT_smoke_viewport_display'
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- return (PhysicButtonsPanel.poll_smoke_domain(context))
-
- def draw_header(self, context):
- md = context.smoke.domain_settings
-
- self.layout.prop(md, "show_velocity", text="")
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
-
- domain = context.smoke.domain_settings
-
- col = flow.column()
- col.enabled = domain.show_velocity
- col.prop(domain, "vector_display_type", text="Display As")
- col.prop(domain, "vector_scale")
-
-
-classes = (
- PHYSICS_PT_smoke,
- PHYSICS_PT_smoke_settings,
- PHYSICS_PT_smoke_settings_initial_velocity,
- PHYSICS_PT_smoke_settings_particle_size,
- PHYSICS_PT_smoke_behavior,
- PHYSICS_PT_smoke_behavior_dissolve,
- PHYSICS_PT_smoke_adaptive_domain,
- PHYSICS_PT_smoke_cache,
- PHYSICS_PT_smoke_field_weights,
- PHYSICS_PT_smoke_fire,
- PHYSICS_PT_smoke_flow_texture,
- PHYSICS_PT_smoke_collections,
- PHYSICS_PT_smoke_highres,
- PHYSICS_PT_smoke_viewport_display,
- PHYSICS_PT_smoke_viewport_display_color,
- PHYSICS_PT_smoke_viewport_display_debug,
-)
-
-
-if __name__ == "__main__": # only for live edit.
- from bpy.utils import register_class
- for cls in classes:
- register_class(cls)
diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py
index 9afdcdff9a5..f93629a4f03 100644
--- a/release/scripts/startup/bl_ui/space_clip.py
+++ b/release/scripts/startup/bl_ui/space_clip.py
@@ -1445,15 +1445,6 @@ class CLIP_MT_select_grouped(Menu):
layout.operator_enum("clip.select_grouped", "group")
-class CLIP_MT_mask_handle_type_menu(Menu):
- bl_label = "Set Handle Type"
-
- def draw(self, _context):
- layout = self.layout
-
- layout.operator_enum("mask.handle_type_set", "type")
-
-
class CLIP_MT_tracking_context_menu(Menu):
bl_label = "Context Menu"
@@ -1507,30 +1498,8 @@ class CLIP_MT_tracking_context_menu(Menu):
layout.operator("clip.delete_track")
elif mode == 'MASK':
-
- layout.menu("CLIP_MT_mask_handle_type_menu")
- layout.operator("mask.switch_direction")
- layout.operator("mask.cyclic_toggle")
-
- layout.separator()
-
- layout.operator("mask.copy_splines", icon='COPYDOWN')
- layout.operator("mask.paste_splines", icon='PASTEDOWN')
-
- layout.separator()
-
- layout.operator("mask.shape_key_rekey", text="Re-key Shape Points")
- layout.operator("mask.feather_weight_clear")
- layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation")
-
- layout.separator()
-
- layout.operator("mask.parent_set")
- layout.operator("mask.parent_clear")
-
- layout.separator()
-
- layout.operator("mask.delete")
+ from .properties_mask_common import draw_mask_context_menu
+ draw_mask_context_menu(layout, context)
class CLIP_PT_camera_presets(PresetPanel, Panel):
@@ -1801,7 +1770,6 @@ classes = (
CLIP_MT_tracking_pie,
CLIP_MT_reconstruction_pie,
CLIP_MT_solving_pie,
- CLIP_MT_mask_handle_type_menu
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index b8f86788b4c..5a73ff094a2 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -657,6 +657,7 @@ class DOPESHEET_MT_snap_pie(Menu):
pie.operator("action.snap", text="Nearest Second").type = 'NEAREST_SECOND'
pie.operator("action.snap", text="Nearest Marker").type = 'NEAREST_MARKER'
+
class LayersDopeSheetPanel:
bl_space_type = 'DOPESHEET_EDITOR'
bl_region_type = 'UI'
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 543a45e85c2..8dd0eaf5445 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -162,7 +162,7 @@ def panel_poll_is_upper_region(region):
class FILEBROWSER_UL_dir(UIList):
- def draw_item(self, _context, layout, _data, item, icon, _active_data, active_propname, _index):
+ def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
direntry = item
# space = context.space_data
diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index 968492db374..2e853a287ea 100644
--- a/release/scripts/startup/bl_ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -294,7 +294,9 @@ class GRAPH_MT_key(Menu):
operator_context = layout.operator_context
layout.operator("graph.decimate", text="Decimate (Ratio)").mode = 'RATIO'
- # Using the modal operation doesn't make sense for this variant as we do not have a modal mode for it, so just execute it.
+
+ # Using the modal operation doesn't make sense for this variant
+ # as we do not have a modal mode for it, so just execute it.
layout.operator_context = 'EXEC_DEFAULT'
layout.operator("graph.decimate", text="Decimate (Allowed Change)").mode = 'ERROR'
layout.operator_context = operator_context
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 5af9fed83f8..c6f490f9d26 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"
@@ -244,7 +231,7 @@ class IMAGE_MT_image(Menu):
if ima and not show_render:
if ima.packed_file:
- if len(ima.filepath):
+ if ima.filepath:
layout.separator()
layout.operator("image.unpack", text="Unpack")
else:
@@ -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,26 @@ 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)
- UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="")
-
- row = layout.row(align=True)
- UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True)
- UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="")
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "size",
+ pressure_name="use_pressure_size",
+ unified_name="use_unified_size",
+ slider=True,
+ header=True
+ )
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "strength",
+ pressure_name="use_pressure_strength",
+ unified_name="use_unified_strength",
+ slider=True,
+ header=True
+ )
@staticmethod
def PAINT(context, layout, tool):
@@ -623,13 +622,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)
@@ -763,7 +755,6 @@ class MASK_MT_editor_menus(Menu):
show_uvedit = sima.show_uvedit
show_maskedit = sima.show_maskedit
- show_paint = sima.show_paint
layout.menu("IMAGE_MT_view")
@@ -771,8 +762,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*")
@@ -792,44 +781,13 @@ class IMAGE_MT_mask_context_menu(Menu):
@classmethod
def poll(cls, context):
sima = context.space_data
- return (sima.show_maskedit)
+ return sima.show_maskedit
def draw(self, context):
layout = self.layout
- sima = context.space_data
-
- if not sima.mask:
- layout.operator("mask.new")
- layout.separator()
- layout.operator("mask.primitive_circle_add", icon='MESH_CIRCLE')
- layout.operator("mask.primitive_square_add", icon='MESH_PLANE')
- else:
- layout.operator_menu_enum("mask.handle_type_set", "type")
- layout.operator("mask.switch_direction")
- layout.operator("mask.cyclic_toggle")
-
- layout.separator()
- layout.operator("mask.primitive_circle_add", icon='MESH_CIRCLE')
- layout.operator("mask.primitive_square_add", icon='MESH_PLANE')
-
- layout.separator()
- layout.operator("mask.copy_splines", icon='COPYDOWN')
- layout.operator("mask.paste_splines", icon='PASTEDOWN')
+ from .properties_mask_common import draw_mask_context_menu
+ draw_mask_context_menu(layout, context)
- layout.separator()
-
- layout.operator("mask.shape_key_rekey", text="Re-key Shape Points")
- layout.operator("mask.feather_weight_clear")
- layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation")
-
- layout.separator()
-
- layout.operator("mask.parent_set")
- layout.operator("mask.parent_clear")
-
- layout.separator()
-
- layout.operator("mask.delete")
# -----------------------------------------------------------------------------
# Mask (similar code in space_clip.py, keep in sync)
@@ -1064,7 +1022,7 @@ class IMAGE_PT_render_slots(Panel):
class IMAGE_UL_udim_tiles(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ def draw_item(self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index):
tile = item
layout.prop(tile, "label", text="", emboss=False)
@@ -1101,11 +1059,17 @@ 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,98 +1079,33 @@ 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_color(Panel, ImagePaintPanel):
- bl_category = "Tool"
+class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel):
bl_context = ".paint_common_2d"
- bl_parent_id = "IMAGE_PT_paint"
- bl_label = "Color Picker"
-
- @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.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)
-
-
-class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel):
+ bl_parent_id = "IMAGE_PT_paint_settings"
bl_category = "Tool"
- bl_context = ".paint_common_2d"
- bl_parent_id = "IMAGE_PT_paint"
- 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
+ bl_label = "Advanced"
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):
- bl_category = "Tool"
- bl_context = ".paint_common_2d"
- bl_parent_id = "IMAGE_PT_paint"
- 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="")
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
- def draw(self, context):
- layout = self.layout
settings = context.tool_settings.image_paint
brush = settings.brush
- layout.active = settings.use_clone_layer
+ brush_settings_advanced(layout.column(), context, brush, self.is_popover)
- brush_texpaint_common_clone(self, context, layout, brush, settings)
-
-class IMAGE_PT_paint_options(Panel, ImagePaintPanel):
- bl_category = "Tool"
+class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
bl_context = ".paint_common_2d"
- bl_parent_id = "IMAGE_PT_paint"
- bl_label = "Options"
- bl_options = {'DEFAULT_CLOSED'}
+ bl_parent_id = "IMAGE_PT_paint_settings"
+ bl_category = "Tool"
+ bl_label = "Color Picker"
@classmethod
def poll(cls, context):
@@ -1221,130 +1120,36 @@ class IMAGE_PT_paint_options(Panel, ImagePaintPanel):
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)
+ draw_color_settings(context, layout, brush, color_type=True)
-class IMAGE_PT_tools_brush_display(BrushButtonsPanel, Panel):
- bl_label = "Display"
+class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel, ColorPalettePanel):
+ bl_category = "Tool"
bl_context = ".paint_common_2d"
+ bl_parent_id = "IMAGE_PT_paint_settings"
+ bl_label = "Color Palette"
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"
+class IMAGE_PT_paint_clone(Panel, ImagePaintPanel, ClonePanel):
bl_category = "Tool"
- 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")
+ bl_context = ".paint_common_2d"
+ bl_parent_id = "IMAGE_PT_paint_settings"
+ bl_label = "Clone from Image/UV Map"
-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"
+class IMAGE_PT_tools_brush_display(Panel, BrushButtonsPanel, DisplayPanel):
+ bl_context = ".paint_common_2d"
+ 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
- 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 +1165,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"
@@ -1508,90 +1214,64 @@ class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel):
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 +1445,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 +1476,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_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index 7bf203d8e39..11fb20d8b38 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -48,7 +48,7 @@ class OUTLINER_HT_header(Header):
if display_mode == 'SEQUENCE':
row = layout.row(align=True)
- row.prop(space, "use_sync_select", icon="UV_SYNC_SELECT", text="")
+ row.prop(space, "use_sync_select", icon='UV_SYNC_SELECT', text="")
row = layout.row(align=True)
if display_mode in {'SCENES', 'VIEW_LAYER'}:
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index b4f841d2eb8..097564444d0 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -510,7 +510,7 @@ class SEQUENCER_MT_add(Menu):
col.enabled = selected_sequences_len(context) >= 2
col = layout.column()
- col.operator_menu_enum("sequencer.fades_add", "type", text="Fade", icon="IPO_EASE_IN_OUT")
+ col.operator_menu_enum("sequencer.fades_add", "type", text="Fade", icon='IPO_EASE_IN_OUT')
col.enabled = selected_sequences_len(context) >= 1
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py
index 532f5e023b6..7b0a769ae62 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_common.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py
@@ -218,49 +218,27 @@ class ToolSelectPanelHelper:
@staticmethod
def _tools_flatten(tools):
- """
- Flattens, skips None and calls generators.
- """
for item in tools:
- if item is None:
- yield None
- elif type(item) is tuple:
- for sub_item in item:
- if sub_item is None:
- yield None
- elif _item_is_fn(sub_item):
- yield from sub_item(context)
- else:
- yield sub_item
+ if type(item) is tuple:
+ yield from item
else:
- if _item_is_fn(item):
- yield from item(context)
- else:
- yield item
+ # May be None.
+ yield item
@staticmethod
def _tools_flatten_with_tool_index(tools):
for item in tools:
- if item is None:
- yield None, -1
- elif type(item) is tuple:
+ if type(item) is tuple:
i = 0
for sub_item in item:
if sub_item is None:
yield None, -1
- elif _item_is_fn(sub_item):
- for item_dyn in sub_item(context):
- yield item_dyn, i
- i += 1
else:
yield sub_item, i
i += 1
else:
- if _item_is_fn(item):
- for item_dyn in item(context):
- yield item_dyn, -1
- else:
- yield item, -1
+ # May be None.
+ yield item, -1
@classmethod
def _tool_get_active(cls, context, space_type, mode, with_icon=False):
@@ -378,7 +356,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)
@@ -676,7 +654,6 @@ class ToolSelectPanelHelper:
space_type = context.space_data.type
return ToolSelectPanelHelper._tool_active_from_context(context, space_type)
-
@staticmethod
def draw_active_tool_fallback(
context, layout, tool,
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 639fb2a31a4..5f017e61db7 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -594,7 +594,7 @@ class _defs_edit_mesh:
props = tool.operator_properties("mesh.bevel")
region_type = context.region.type
- if extra == False:
+ if not extra:
if props.offset_type == 'PERCENT':
layout.prop(props, "offset_pct")
else:
@@ -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",
@@ -1203,7 +1202,7 @@ class _defs_image_uv_transform:
idname="builtin.move",
label="Move",
icon="ops.transform.translate",
- # widget="VIEW3D_GGT_xform_gizmo",
+ widget="IMAGE_GGT_gizmo2d_translate",
operator="transform.translate",
keymap="Image Editor Tool: Uv, Move",
)
@@ -1214,7 +1213,7 @@ class _defs_image_uv_transform:
idname="builtin.rotate",
label="Rotate",
icon="ops.transform.rotate",
- # widget="VIEW3D_GGT_xform_gizmo",
+ widget="IMAGE_GGT_gizmo2d_rotate",
operator="transform.rotate",
keymap="Image Editor Tool: Uv, Rotate",
)
@@ -1225,7 +1224,7 @@ class _defs_image_uv_transform:
idname="builtin.scale",
label="Scale",
icon="ops.transform.resize",
- # widget="VIEW3D_GGT_xform_gizmo",
+ widget="IMAGE_GGT_gizmo2d_resize",
operator="transform.resize",
keymap="Image Editor Tool: Uv, Scale",
)
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 2e2c5adb970..7dbf49d01e3 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,18 @@ 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_cleanup(Menu):
+ bl_label = "Clean Up"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.separator()
+
+ layout.operator("outliner.orphans_purge")
class TOPBAR_MT_file(Menu):
@@ -270,6 +291,7 @@ class TOPBAR_MT_file(Menu):
layout.separator()
layout.menu("TOPBAR_MT_file_external_data")
+ layout.menu("TOPBAR_MT_file_cleanup")
layout.separator()
@@ -325,7 +347,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 +393,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 +408,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 +431,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 +445,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 +455,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)")
@@ -435,11 +465,15 @@ class TOPBAR_MT_file_export(Menu):
bl_idname = "TOPBAR_MT_file_export"
bl_label = "Export"
- def draw(self, _context):
+ 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)")
class TOPBAR_MT_file_external_data(Menu):
@@ -492,8 +526,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
@@ -535,7 +571,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()
@@ -554,7 +591,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):
@@ -574,8 +612,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()
@@ -602,7 +642,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',
@@ -635,7 +676,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()
@@ -664,7 +706,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):
@@ -673,21 +716,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'
@@ -702,29 +750,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
@@ -807,6 +834,7 @@ classes = (
TOPBAR_MT_file_import,
TOPBAR_MT_file_export,
TOPBAR_MT_file_external_data,
+ TOPBAR_MT_file_cleanup,
TOPBAR_MT_file_previews,
TOPBAR_MT_edit,
TOPBAR_MT_render,
@@ -816,7 +844,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_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index bf39cbda391..9527c7f4de8 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -27,6 +27,9 @@ from bpy.app.translations import pgettext_iface as iface_
from bpy.app.translations import contexts as i18n_contexts
+# -----------------------------------------------------------------------------
+# Main Header
+
class USERPREF_HT_header(Header):
bl_space_type = 'PREFERENCES'
@@ -60,6 +63,9 @@ class USERPREF_HT_header(Header):
self.draw_buttons(layout, context)
+# -----------------------------------------------------------------------------
+# Main Navigation Bar
+
class USERPREF_PT_navigation_bar(Panel):
bl_label = "Preferences Navigation"
bl_space_type = 'PREFERENCES'
@@ -128,47 +134,53 @@ class USERPREF_PT_save_preferences(Panel):
USERPREF_HT_header.draw_buttons(layout, context)
+# -----------------------------------------------------------------------------
+# Min-In Helpers
+
# Panel mix-in.
-class PreferencePanel:
+class CenterAlignMixIn:
"""
Base class for panels to center align contents with some horizontal margin.
- Deriving classes need to implement a ``draw_props(context, layout)`` function.
+ Deriving classes need to implement a ``draw_centered(context, layout)`` function.
"""
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
-
def draw(self, context):
layout = self.layout
width = context.region.width
ui_scale = context.preferences.system.ui_scale
+ # No horizontal margin if region is rather small.
+ is_wide = width > (350 * ui_scale)
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
row = layout.row()
- if width > (350 * ui_scale): # No horizontal margin if region is rather small.
+ if is_wide:
row.label() # Needed so col below is centered.
col = row.column()
col.ui_units_x = 50
- # draw_props implemented by deriving classes.
- self.draw_props(context, col)
+ # Implemented by sub-classes.
+ self.draw_centered(context, col)
- if width > (350 * ui_scale): # No horizontal margin if region is rather small.
+ if is_wide:
row.label() # Needed so col above is centered.
-class USERPREF_PT_interface_display(PreferencePanel, Panel):
- bl_label = "Display"
+# -----------------------------------------------------------------------------
+# Interface Panels
+
+class InterfacePanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "interface"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INTERFACE')
- def draw_props(self, context, layout):
+class USERPREF_PT_interface_display(InterfacePanel, CenterAlignMixIn, Panel):
+ bl_label = "Display"
+
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -188,16 +200,11 @@ class USERPREF_PT_interface_display(PreferencePanel, Panel):
flow.prop(view, "show_large_cursors")
-class USERPREF_PT_interface_text(PreferencePanel, Panel):
+class USERPREF_PT_interface_text(InterfacePanel, CenterAlignMixIn, Panel):
bl_label = "Text Rendering"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INTERFACE')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -212,14 +219,13 @@ class USERPREF_PT_interface_text(PreferencePanel, Panel):
flow.prop(view, "font_path_ui_mono")
-class USERPREF_PT_interface_translation(PreferencePanel, Panel):
+class USERPREF_PT_interface_translation(InterfacePanel, CenterAlignMixIn, Panel):
bl_label = "Translation"
bl_translation_context = i18n_contexts.id_windowmanager
@classmethod
def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INTERFACE') and bpy.app.build_options.international
+ return bpy.app.build_options.international
def draw_header(self, context):
prefs = context.preferences
@@ -227,7 +233,7 @@ class USERPREF_PT_interface_translation(PreferencePanel, Panel):
self.layout.prop(view, "use_international_fonts", text="")
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -242,15 +248,10 @@ class USERPREF_PT_interface_translation(PreferencePanel, Panel):
flow.prop(view, "use_translate_new_dataname", text="New Data")
-class USERPREF_PT_interface_editors(PreferencePanel, Panel):
+class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel):
bl_label = "Editors"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INTERFACE')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
system = prefs.system
@@ -265,17 +266,12 @@ class USERPREF_PT_interface_editors(PreferencePanel, Panel):
flow.prop(view, "factor_display_type")
-class USERPREF_PT_interface_temporary_windows(PreferencePanel, Panel):
+class USERPREF_PT_interface_temporary_windows(InterfacePanel, CenterAlignMixIn, Panel):
bl_label = "Temporary Windows"
bl_parent_id = "USERPREF_PT_interface_editors"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INTERFACE')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -285,22 +281,15 @@ class USERPREF_PT_interface_temporary_windows(PreferencePanel, Panel):
flow.prop(view, "filebrowser_display_type", text="File Browser")
-class USERPREF_PT_interface_menus(Panel):
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
+class USERPREF_PT_interface_menus(InterfacePanel, Panel):
bl_label = "Menus"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INTERFACE')
-
def draw(self, context):
pass
-class USERPREF_PT_interface_menus_mouse_over(PreferencePanel, Panel):
+class USERPREF_PT_interface_menus_mouse_over(InterfacePanel, CenterAlignMixIn, Panel):
bl_label = "Open on Mouse Over"
bl_parent_id = "USERPREF_PT_interface_menus"
@@ -310,7 +299,7 @@ class USERPREF_PT_interface_menus_mouse_over(PreferencePanel, Panel):
self.layout.prop(view, "use_mouse_over_open", text="")
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -322,11 +311,11 @@ class USERPREF_PT_interface_menus_mouse_over(PreferencePanel, Panel):
flow.prop(view, "open_sublevel_delay", text="Sub Level")
-class USERPREF_PT_interface_menus_pie(PreferencePanel, Panel):
+class USERPREF_PT_interface_menus_pie(InterfacePanel, CenterAlignMixIn, Panel):
bl_label = "Pie Menus"
bl_parent_id = "USERPREF_PT_interface_menus"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -340,25 +329,27 @@ class USERPREF_PT_interface_menus_pie(PreferencePanel, Panel):
flow.prop(view, "pie_menu_confirm")
-class USERPREF_PT_edit_objects(Panel):
- bl_label = "Objects"
+# -----------------------------------------------------------------------------
+# Editing Panels
+
+class EditingPanel:
bl_space_type = 'PREFERENCES'
bl_region_type = 'WINDOW'
+ bl_context = "editing"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EDITING')
+
+class USERPREF_PT_edit_objects(EditingPanel, Panel):
+ bl_label = "Objects"
def draw(self, context):
pass
-class USERPREF_PT_edit_objects_new(PreferencePanel, Panel):
+class USERPREF_PT_edit_objects_new(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "New Objects"
bl_parent_id = "USERPREF_PT_edit_objects"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -369,11 +360,11 @@ class USERPREF_PT_edit_objects_new(PreferencePanel, Panel):
flow.prop(edit, "use_enter_edit_mode", text="Enter Edit Mode")
-class USERPREF_PT_edit_objects_duplicate_data(PreferencePanel, Panel):
+class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Duplicate Data"
bl_parent_id = "USERPREF_PT_edit_objects"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -398,15 +389,10 @@ class USERPREF_PT_edit_objects_duplicate_data(PreferencePanel, Panel):
col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil")
-class USERPREF_PT_edit_cursor(PreferencePanel, Panel):
+class USERPREF_PT_edit_cursor(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "3D Cursor"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EDITING')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -416,16 +402,11 @@ class USERPREF_PT_edit_cursor(PreferencePanel, Panel):
flow.prop(edit, "use_cursor_lock_adjust")
-class USERPREF_PT_edit_gpencil(PreferencePanel, Panel):
+class USERPREF_PT_edit_gpencil(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Grease Pencil"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EDITING')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -435,15 +416,10 @@ class USERPREF_PT_edit_gpencil(PreferencePanel, Panel):
flow.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance")
-class USERPREF_PT_edit_annotations(PreferencePanel, Panel):
+class USERPREF_PT_edit_annotations(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Annotations"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EDITING')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -453,16 +429,11 @@ class USERPREF_PT_edit_annotations(PreferencePanel, Panel):
flow.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius")
-class USERPREF_PT_edit_weight_paint(PreferencePanel, Panel):
+class USERPREF_PT_edit_weight_paint(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Weight Paint"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EDITING')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -473,16 +444,11 @@ class USERPREF_PT_edit_weight_paint(PreferencePanel, Panel):
col.template_color_ramp(view, "weight_color_range", expand=True)
-class USERPREF_PT_edit_misc(PreferencePanel, Panel):
+class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel):
bl_label = "Miscellaneous"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EDITING')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -492,15 +458,19 @@ class USERPREF_PT_edit_misc(PreferencePanel, Panel):
flow.prop(edit, "node_margin", text="Node Auto-offset Margin")
-class USERPREF_PT_animation_timeline(PreferencePanel, Panel):
- bl_label = "Timeline"
+# -----------------------------------------------------------------------------
+# Animation Panels
+
+class AnimationPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "animation"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'ANIMATION')
- def draw_props(self, context, layout):
+class USERPREF_PT_animation_timeline(AnimationPanel, CenterAlignMixIn, Panel):
+ bl_label = "Timeline"
+
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
edit = prefs.edit
@@ -521,15 +491,10 @@ class USERPREF_PT_animation_timeline(PreferencePanel, Panel):
flow.prop(view, "view_frame_keyframes")
-class USERPREF_PT_animation_keyframes(PreferencePanel, Panel):
+class USERPREF_PT_animation_keyframes(AnimationPanel, CenterAlignMixIn, Panel):
bl_label = "Keyframes"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'ANIMATION')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -539,11 +504,11 @@ class USERPREF_PT_animation_keyframes(PreferencePanel, Panel):
flow.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed")
-class USERPREF_PT_animation_autokey(PreferencePanel, Panel):
+class USERPREF_PT_animation_autokey(AnimationPanel, CenterAlignMixIn, Panel):
bl_label = "Auto-Keyframing"
bl_parent_id = "USERPREF_PT_animation_keyframes"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -554,15 +519,10 @@ class USERPREF_PT_animation_autokey(PreferencePanel, Panel):
flow.prop(edit, "use_auto_keying", text="Enable in New Scenes")
-class USERPREF_PT_animation_fcurves(PreferencePanel, Panel):
+class USERPREF_PT_animation_fcurves(AnimationPanel, CenterAlignMixIn, Panel):
bl_label = "F-Curves"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'ANIMATION')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
edit = prefs.edit
@@ -575,15 +535,19 @@ class USERPREF_PT_animation_fcurves(PreferencePanel, Panel):
flow.prop(edit, "use_insertkey_xyz_to_rgb", text="XYZ to RGB")
-class USERPREF_PT_system_sound(PreferencePanel, Panel):
- bl_label = "Sound"
+# -----------------------------------------------------------------------------
+# System Panels
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'SYSTEM')
+class SystemPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "system"
+
+
+class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel):
+ bl_label = "Sound"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
system = prefs.system
@@ -597,15 +561,10 @@ class USERPREF_PT_system_sound(PreferencePanel, Panel):
sub.prop(system, "audio_sample_format", text="Sample Format")
-class USERPREF_PT_system_cycles_devices(PreferencePanel, Panel):
+class USERPREF_PT_system_cycles_devices(SystemPanel, CenterAlignMixIn, Panel):
bl_label = "Cycles Render Devices"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'SYSTEM')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
col = layout.column()
@@ -624,15 +583,55 @@ class USERPREF_PT_system_cycles_devices(PreferencePanel, Panel):
# col.row().prop(system, "opensubdiv_compute_type", text="")
-class USERPREF_PT_viewport_display(PreferencePanel, Panel):
- bl_label = "Display"
+class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel):
+ bl_label = "Memory & Limits"
- @classmethod
- def poll(cls, context):
+ def draw_centered(self, context, layout):
prefs = context.preferences
- return (prefs.active_section == 'VIEWPORT')
+ system = prefs.system
+ edit = prefs.edit
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
+ flow.prop(edit, "undo_steps", text="Undo Steps")
+ flow.prop(edit, "undo_memory_limit", text="Undo Memory Limit")
+ flow.prop(edit, "use_global_undo")
+
+ layout.separator()
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
+ flow.prop(system, "memory_cache_limit", text="Sequencer Cache Limit")
+ flow.prop(system, "scrollback", text="Console Scrollback Lines")
+
+ layout.separator()
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
+ flow.prop(system, "texture_time_out", text="Texture Time Out")
+ flow.prop(system, "texture_collection_rate", text="Garbage Collection Rate")
+
+ layout.separator()
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
+ flow.prop(system, "vbo_time_out", text="Vbo Time Out")
+ flow.prop(system, "vbo_collection_rate", text="Garbage Collection Rate")
+
+
+# -----------------------------------------------------------------------------
+# Viewport Panels
+
+class ViewportPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "viewport"
+
+
+class USERPREF_PT_viewport_display(ViewportPanel, CenterAlignMixIn, Panel):
+ bl_label = "Display"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
@@ -660,15 +659,10 @@ class USERPREF_PT_viewport_display(PreferencePanel, Panel):
col.prop(view, "mini_axis_brightness", text="Brightness")
-class USERPREF_PT_viewport_quality(PreferencePanel, Panel):
+class USERPREF_PT_viewport_quality(ViewportPanel, CenterAlignMixIn, Panel):
bl_label = "Quality"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'VIEWPORT')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
system = prefs.system
@@ -680,15 +674,10 @@ class USERPREF_PT_viewport_quality(PreferencePanel, Panel):
flow.prop(system, "use_edit_mode_smooth_wire")
-class USERPREF_PT_viewport_textures(PreferencePanel, Panel):
+class USERPREF_PT_viewport_textures(ViewportPanel, CenterAlignMixIn, Panel):
bl_label = "Textures"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'VIEWPORT')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
system = prefs.system
@@ -700,16 +689,11 @@ class USERPREF_PT_viewport_textures(PreferencePanel, Panel):
flow.prop(system, "image_draw_method", text="Image Display Method")
-class USERPREF_PT_viewport_selection(PreferencePanel, Panel):
+class USERPREF_PT_viewport_selection(ViewportPanel, CenterAlignMixIn, Panel):
bl_label = "Selection"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'VIEWPORT')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
system = prefs.system
@@ -718,45 +702,13 @@ class USERPREF_PT_viewport_selection(PreferencePanel, Panel):
flow.prop(system, "use_select_pick_depth")
-class USERPREF_PT_system_memory(PreferencePanel, Panel):
- bl_label = "Memory & Limits"
-
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'SYSTEM')
-
- def draw_props(self, context, layout):
- prefs = context.preferences
- system = prefs.system
- edit = prefs.edit
-
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
-
- flow.prop(edit, "undo_steps", text="Undo Steps")
- flow.prop(edit, "undo_memory_limit", text="Undo Memory Limit")
- flow.prop(edit, "use_global_undo")
-
- layout.separator()
-
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
-
- flow.prop(system, "memory_cache_limit", text="Sequencer Cache Limit")
- flow.prop(system, "scrollback", text="Console Scrollback Lines")
-
- layout.separator()
-
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+# -----------------------------------------------------------------------------
+# Theme Panels
- flow.prop(system, "texture_time_out", text="Texture Time Out")
- flow.prop(system, "texture_collection_rate", text="Garbage Collection Rate")
-
- layout.separator()
-
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
-
- flow.prop(system, "vbo_time_out", text="Vbo Time Out")
- flow.prop(system, "vbo_collection_rate", text="Garbage Collection Rate")
+class ThemePanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "themes"
class USERPREF_MT_interface_theme_presets(Menu):
@@ -775,17 +727,10 @@ class USERPREF_MT_interface_theme_presets(Menu):
bpy.ops.preferences.reset_default_theme()
-class USERPREF_PT_theme(Panel):
- bl_space_type = 'PREFERENCES'
+class USERPREF_PT_theme(ThemePanel, Panel):
bl_label = "Themes"
- bl_region_type = 'WINDOW'
bl_options = {'HIDE_HEADER'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'THEMES')
-
def draw(self, _context):
layout = self.layout
@@ -801,17 +746,10 @@ class USERPREF_PT_theme(Panel):
row.operator("preferences.reset_default_theme", text="Reset", icon='LOOP_BACK')
-class USERPREF_PT_theme_user_interface(PreferencePanel, Panel):
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
+class USERPREF_PT_theme_user_interface(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "User Interface"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'THEMES')
-
def draw_header(self, _context):
layout = self.layout
@@ -822,9 +760,8 @@ class USERPREF_PT_theme_user_interface(PreferencePanel, Panel):
# Base class for dynamically defined widget color panels.
+# This is not registered.
class PreferenceThemeWidgetColorPanel:
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
bl_parent_id = "USERPREF_PT_theme_user_interface"
def draw(self, context):
@@ -851,15 +788,10 @@ class PreferenceThemeWidgetColorPanel:
col.prop(widget_style, "roundness")
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'THEMES')
-
+# Base class for dynamically defined widget color panels.
+# This is not registered.
class PreferenceThemeWidgetShadePanel:
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
def draw(self, context):
theme = context.preferences.themes[0]
@@ -882,12 +814,12 @@ class PreferenceThemeWidgetShadePanel:
self.layout.prop(widget_style, "show_shaded", text="")
-class USERPREF_PT_theme_interface_state(PreferencePanel, Panel):
+class USERPREF_PT_theme_interface_state(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "State"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "USERPREF_PT_theme_user_interface"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
theme = context.preferences.themes[0]
ui_state = theme.user_interface.wcol_state
@@ -917,12 +849,12 @@ class USERPREF_PT_theme_interface_state(PreferencePanel, Panel):
col.prop(ui_state, "blend")
-class USERPREF_PT_theme_interface_styles(PreferencePanel, Panel):
+class USERPREF_PT_theme_interface_styles(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "Styles"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "USERPREF_PT_theme_user_interface"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
theme = context.preferences.themes[0]
ui = theme.user_interface
@@ -937,12 +869,12 @@ class USERPREF_PT_theme_interface_styles(PreferencePanel, Panel):
flow.prop(ui, "widget_emboss")
-class USERPREF_PT_theme_interface_gizmos(PreferencePanel, Panel):
+class USERPREF_PT_theme_interface_gizmos(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "Axis & Gizmo Colors"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "USERPREF_PT_theme_user_interface"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
theme = context.preferences.themes[0]
ui = theme.user_interface
@@ -962,12 +894,12 @@ class USERPREF_PT_theme_interface_gizmos(PreferencePanel, Panel):
col.prop(ui, "gizmo_b")
-class USERPREF_PT_theme_interface_icons(PreferencePanel, Panel):
+class USERPREF_PT_theme_interface_icons(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "Icon Colors"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "USERPREF_PT_theme_user_interface"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
theme = context.preferences.themes[0]
ui = theme.user_interface
@@ -983,15 +915,10 @@ class USERPREF_PT_theme_interface_icons(PreferencePanel, Panel):
flow.prop(ui, "icon_border_intensity")
-class USERPREF_PT_theme_text_style(PreferencePanel, Panel):
+class USERPREF_PT_theme_text_style(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "Text Style"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'THEMES')
-
@staticmethod
def _ui_font_style(layout, font_style):
layout.use_property_split = True
@@ -1015,7 +942,7 @@ class USERPREF_PT_theme_text_style(PreferencePanel, Panel):
layout.label(icon='FONTPREVIEW')
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
style = context.preferences.ui_styles[0]
layout.label(text="Panel Title")
@@ -1032,21 +959,16 @@ class USERPREF_PT_theme_text_style(PreferencePanel, Panel):
self._ui_font_style(layout, style.widget_label)
-class USERPREF_PT_theme_bone_color_sets(PreferencePanel, Panel):
+class USERPREF_PT_theme_bone_color_sets(ThemePanel, CenterAlignMixIn, Panel):
bl_label = "Bone Color Sets"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'THEMES')
-
def draw_header(self, _context):
layout = self.layout
layout.label(icon='COLOR')
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
theme = context.preferences.themes[0]
layout.use_property_split = True
@@ -1063,9 +985,8 @@ class USERPREF_PT_theme_bone_color_sets(PreferencePanel, Panel):
# Base class for dynamically defined theme-space panels.
+# This is not registered.
class PreferenceThemeSpacePanel:
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
# not essential, hard-coded UI delimiters for the theme layout
ui_delimiters = {
@@ -1137,11 +1058,6 @@ class PreferenceThemeSpacePanel:
data = getattr(data, datapath_item)
PreferenceThemeSpacePanel._theme_generic(layout, data, self.theme_area)
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'THEMES')
-
class ThemeGenericClassGenerator():
@@ -1172,7 +1088,7 @@ class ThemeGenericClassGenerator():
for (name, wcol) in wcols:
panel_id = "USERPREF_PT_theme_interface_" + wcol
- yield type(panel_id, (PreferenceThemeWidgetColorPanel, Panel), {
+ yield type(panel_id, (PreferenceThemeWidgetColorPanel, ThemePanel, Panel), {
"bl_label": name,
"bl_options": {'DEFAULT_CLOSED'},
"draw": PreferenceThemeWidgetColorPanel.draw,
@@ -1180,7 +1096,7 @@ class ThemeGenericClassGenerator():
})
panel_shade_id = "USERPREF_PT_theme_interface_shade_" + wcol
- yield type(panel_shade_id, (PreferenceThemeWidgetShadePanel, Panel), {
+ yield type(panel_shade_id, (PreferenceThemeWidgetShadePanel, ThemePanel, Panel), {
"bl_label": "Shaded",
"bl_options": {'DEFAULT_CLOSED'},
"bl_parent_id": panel_id,
@@ -1204,7 +1120,7 @@ class ThemeGenericClassGenerator():
for prop in props_ls:
new_datapath = datapath + "." + prop.identifier if datapath else prop.identifier
panel_id = parent_id + "_" + prop.identifier
- yield type(panel_id, (PreferenceThemeSpacePanel, Panel), {
+ yield type(panel_id, (PreferenceThemeSpacePanel, ThemePanel, Panel), {
"bl_label": rna_type.properties[prop.identifier].name,
"bl_parent_id": parent_id,
"bl_options": {'DEFAULT_CLOSED'},
@@ -1213,7 +1129,12 @@ class ThemeGenericClassGenerator():
"datapath": new_datapath,
})
- yield from generate_child_panel_classes_recurse(panel_id, prop.fixed_type, theme_area, new_datapath)
+ yield from generate_child_panel_classes_recurse(
+ panel_id,
+ prop.fixed_type,
+ theme_area,
+ new_datapath,
+ )
yield from generate_child_panel_classes_recurse(parent_id, rna_type, theme_area, datapath)
@@ -1227,7 +1148,7 @@ class ThemeGenericClassGenerator():
panel_id = "USERPREF_PT_theme_" + theme_area.identifier.lower()
# Generate panel-class from theme_area
- yield type(panel_id, (PreferenceThemeSpacePanel, Panel), {
+ yield type(panel_id, (PreferenceThemeSpacePanel, ThemePanel, Panel), {
"bl_label": theme_area.name,
"bl_options": {'DEFAULT_CLOSED'},
"draw_header": PreferenceThemeSpacePanel.draw_header,
@@ -1242,29 +1163,24 @@ class ThemeGenericClassGenerator():
theme_area, theme_area.identifier.lower())
+# -----------------------------------------------------------------------------
+# File Paths Panels
+
# Panel mix-in.
class FilePathsPanel:
bl_space_type = 'PREFERENCES'
bl_region_type = 'WINDOW'
+ bl_context = "file_paths"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'FILE_PATHS')
+
+class USERPREF_PT_file_paths_data(FilePathsPanel, Panel):
+ bl_label = "Data"
def draw(self, context):
layout = self.layout
-
layout.use_property_split = True
layout.use_property_decorate = False
- self.draw_props(context, layout)
-
-
-class USERPREF_PT_file_paths_data(FilePathsPanel, Panel):
- bl_label = "Data"
-
- def draw_props(self, context, _layout):
paths = context.preferences.filepaths
col = self.layout.column()
@@ -1278,7 +1194,11 @@ class USERPREF_PT_file_paths_data(FilePathsPanel, Panel):
class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):
bl_label = "Render"
- def draw_props(self, context, _layout):
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
paths = context.preferences.filepaths
col = self.layout.column()
@@ -1289,7 +1209,11 @@ class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):
class USERPREF_PT_file_paths_applications(FilePathsPanel, Panel):
bl_label = "Applications"
- def draw_props(self, context, layout):
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
paths = context.preferences.filepaths
col = layout.column()
@@ -1305,14 +1229,18 @@ class USERPREF_PT_file_paths_development(FilePathsPanel, Panel):
@classmethod
def poll(cls, context):
prefs = context.preferences
- return (prefs.active_section == 'FILE_PATHS') and prefs.view.show_developer_ui
+ return prefs.view.show_developer_ui
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
- def draw_props(self, context, layout):
paths = context.preferences.filepaths
layout.prop(paths, "i18n_branches_directory", text="I18n Branches")
-class USERPREF_PT_saveload_autorun(PreferencePanel, Panel):
+class USERPREF_PT_saveload_autorun(FilePathsPanel, Panel):
bl_label = "Auto Run Python Scripts"
bl_parent_id = "USERPREF_PT_saveload_blend"
@@ -1343,15 +1271,19 @@ class USERPREF_PT_saveload_autorun(PreferencePanel, Panel):
row.operator("wm.userpref_autoexec_path_remove", text="", icon='X', emboss=False).index = i
-class USERPREF_PT_saveload_blend(PreferencePanel, Panel):
- bl_label = "Blend Files"
+# -----------------------------------------------------------------------------
+# Save/Load Panels
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'SAVE_LOAD')
+class SaveLoadPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "save_load"
+
+
+class USERPREF_PT_saveload_blend(SaveLoadPanel, CenterAlignMixIn, Panel):
+ bl_label = "Blend Files"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
paths = prefs.filepaths
view = prefs.view
@@ -1373,11 +1305,11 @@ class USERPREF_PT_saveload_blend(PreferencePanel, Panel):
flow.prop(paths, "recent_files")
-class USERPREF_PT_saveload_blend_autosave(PreferencePanel, Panel):
+class USERPREF_PT_saveload_blend_autosave(SaveLoadPanel, CenterAlignMixIn, Panel):
bl_label = "Auto Save"
bl_parent_id = "USERPREF_PT_saveload_blend"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
paths = prefs.filepaths
@@ -1389,15 +1321,10 @@ class USERPREF_PT_saveload_blend_autosave(PreferencePanel, Panel):
sub.prop(paths, "auto_save_time", text="Timer (mins)")
-class USERPREF_PT_saveload_file_browser(PreferencePanel, Panel):
+class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel):
bl_label = "File Browser"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'SAVE_LOAD')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
paths = prefs.filepaths
@@ -1409,85 +1336,19 @@ class USERPREF_PT_saveload_file_browser(PreferencePanel, Panel):
flow.prop(paths, "hide_system_bookmarks")
-class USERPREF_PT_ndof_settings(Panel):
- bl_label = "3D Mouse Settings"
- bl_space_type = 'TOPBAR' # dummy.
- bl_region_type = 'HEADER'
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False # No animation.
-
- input_prefs = context.preferences.inputs
-
- is_view3d = context.space_data.type == 'VIEW_3D'
-
- col = layout.column(align=True)
- col.prop(input_prefs, "ndof_sensitivity")
- col.prop(input_prefs, "ndof_orbit_sensitivity")
- col.prop(input_prefs, "ndof_deadzone")
-
- if is_view3d:
- layout.separator()
- layout.prop(input_prefs, "ndof_show_guide")
-
- layout.separator()
- layout.label(text="Orbit Style")
- layout.row().prop(input_prefs, "ndof_view_navigate_method", text="Navigate")
- layout.row().prop(input_prefs, "ndof_view_rotate_method", text="Orbit")
- layout.separator()
-
- layout.label(text="Orbit Options")
- split = layout.split(factor=0.6)
- row = split.row()
- row.alignment = 'RIGHT'
- row.label(text="Invert Axis")
- row = split.row(align=True)
- for text, attr in (
- ("X", "ndof_rotx_invert_axis"),
- ("Y", "ndof_roty_invert_axis"),
- ("Z", "ndof_rotz_invert_axis"),
- ):
- row.prop(input_prefs, attr, text=text, toggle=True)
-
- # view2d use pan/zoom
- layout.separator()
- layout.label(text="Pan Options")
-
- split = layout.split(factor=0.6)
- row = split.row()
- row.alignment = 'RIGHT'
- row.label(text="Invert Axis")
- row = split.row(align=True)
- for text, attr in (
- ("X", "ndof_panx_invert_axis"),
- ("Y", "ndof_pany_invert_axis"),
- ("Z", "ndof_panz_invert_axis"),
- ):
- row.prop(input_prefs, attr, text=text, toggle=True)
+# -----------------------------------------------------------------------------
+# Input Panels
- layout.prop(input_prefs, "ndof_pan_yz_swap_axis")
-
- layout.label(text="Zoom Options")
- layout.prop(input_prefs, "ndof_zoom_invert")
-
- if is_view3d:
- layout.separator()
- layout.label(text="Fly/Walk Options")
- layout.prop(input_prefs, "ndof_fly_helicopter")
- layout.prop(input_prefs, "ndof_lock_horizon")
+class InputPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "input"
-class USERPREF_PT_input_keyboard(PreferencePanel, Panel):
+class USERPREF_PT_input_keyboard(InputPanel, CenterAlignMixIn, Panel):
bl_label = "Keyboard"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INPUT')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
inputs = prefs.inputs
@@ -1495,15 +1356,10 @@ class USERPREF_PT_input_keyboard(PreferencePanel, Panel):
layout.prop(inputs, "use_numeric_input_advanced")
-class USERPREF_PT_input_mouse(PreferencePanel, Panel):
+class USERPREF_PT_input_mouse(InputPanel, CenterAlignMixIn, Panel):
bl_label = "Mouse"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'INPUT')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
import sys
prefs = context.preferences
inputs = prefs.inputs
@@ -1524,15 +1380,63 @@ class USERPREF_PT_input_mouse(PreferencePanel, Panel):
flow.prop(inputs, "move_threshold")
-class USERPREF_PT_navigation_orbit(PreferencePanel, Panel):
- bl_label = "Orbit & Pan"
+class USERPREF_PT_input_tablet(InputPanel, CenterAlignMixIn, Panel):
+ bl_label = "Tablet"
+
+ def draw_centered(self, context, layout):
+ prefs = context.preferences
+ inputs = prefs.inputs
+
+ import sys
+ if sys.platform[:3] == "win":
+ layout.prop(inputs, "tablet_api")
+ layout.separator()
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
+ flow.prop(inputs, "pressure_threshold_max")
+ flow.prop(inputs, "pressure_softness")
+
+
+class USERPREF_PT_input_ndof(InputPanel, CenterAlignMixIn, Panel):
+ bl_label = "NDOF"
+ bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
prefs = context.preferences
- return (prefs.active_section == 'NAVIGATION')
+ inputs = prefs.inputs
+ return inputs.use_ndof
+
+ def draw_centered(self, context, layout):
+ prefs = context.preferences
+ inputs = prefs.inputs
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+
+ flow.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity")
+ flow.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity")
+ flow.prop(inputs, "ndof_deadzone", text="Deadzone")
- def draw_props(self, context, layout):
+ layout.separator()
+
+ flow.row().prop(inputs, "ndof_view_navigate_method", expand=True)
+ flow.row().prop(inputs, "ndof_view_rotate_method", expand=True)
+
+
+# -----------------------------------------------------------------------------
+# Navigation Panels
+
+class NavigationPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "navigation"
+
+
+class USERPREF_PT_navigation_orbit(NavigationPanel, CenterAlignMixIn, Panel):
+ bl_label = "Orbit & Pan"
+
+ def draw_centered(self, context, layout):
import sys
prefs = context.preferences
inputs = prefs.inputs
@@ -1558,15 +1462,10 @@ class USERPREF_PT_navigation_orbit(PreferencePanel, Panel):
flow.prop(view, "rotation_angle")
-class USERPREF_PT_navigation_zoom(PreferencePanel, Panel):
+class USERPREF_PT_navigation_zoom(NavigationPanel, CenterAlignMixIn, Panel):
bl_label = "Zoom"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'NAVIGATION')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
inputs = prefs.inputs
@@ -1582,15 +1481,10 @@ class USERPREF_PT_navigation_zoom(PreferencePanel, Panel):
flow.prop(inputs, "use_zoom_to_mouse")
-class USERPREF_PT_navigation_fly_walk(PreferencePanel, Panel):
+class USERPREF_PT_navigation_fly_walk(NavigationPanel, CenterAlignMixIn, Panel):
bl_label = "Fly & Walk"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'NAVIGATION')
-
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
inputs = prefs.inputs
@@ -1600,7 +1494,7 @@ class USERPREF_PT_navigation_fly_walk(PreferencePanel, Panel):
flow.prop(inputs, "use_camera_lock_parent")
-class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel, Panel):
+class USERPREF_PT_navigation_fly_walk_navigation(NavigationPanel, CenterAlignMixIn, Panel):
bl_label = "Walk"
bl_parent_id = "USERPREF_PT_navigation_fly_walk"
bl_options = {'DEFAULT_CLOSED'}
@@ -1610,7 +1504,7 @@ class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel, Panel):
prefs = context.preferences
return prefs.inputs.navigation_mode == 'WALK'
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
inputs = prefs.inputs
walk = inputs.walk_navigation
@@ -1626,7 +1520,7 @@ class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel, Panel):
sub.prop(walk, "walk_speed_factor")
-class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel, Panel):
+class USERPREF_PT_navigation_fly_walk_gravity(NavigationPanel, CenterAlignMixIn, Panel):
bl_label = "Gravity"
bl_parent_id = "USERPREF_PT_navigation_fly_walk"
bl_options = {'DEFAULT_CLOSED'}
@@ -1643,7 +1537,7 @@ class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel, Panel):
self.layout.prop(walk, "use_gravity", text="")
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
inputs = prefs.inputs
walk = inputs.walk_navigation
@@ -1656,53 +1550,84 @@ class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel, Panel):
flow.prop(walk, "jump_height")
-class USERPREF_PT_input_tablet(PreferencePanel, Panel):
- bl_label = "Tablet"
+# Special case, this is only exposed as a popover.
+class USERPREF_PT_ndof_settings(Panel):
+ bl_label = "3D Mouse Settings"
+ bl_space_type = 'TOPBAR' # dummy.
+ bl_region_type = 'HEADER'
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return prefs.active_section == 'INPUT'
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
- def draw_props(self, context, layout):
- prefs = context.preferences
- inputs = prefs.inputs
+ input_prefs = context.preferences.inputs
- import sys
- if sys.platform[:3] == "win":
- layout.prop(inputs, "tablet_api")
+ is_view3d = context.space_data.type == 'VIEW_3D'
+
+ col = layout.column(align=True)
+ col.prop(input_prefs, "ndof_sensitivity")
+ col.prop(input_prefs, "ndof_orbit_sensitivity")
+ col.prop(input_prefs, "ndof_deadzone")
+
+ if is_view3d:
layout.separator()
+ layout.prop(input_prefs, "ndof_show_guide")
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ layout.separator()
+ layout.label(text="Orbit Style")
+ layout.row().prop(input_prefs, "ndof_view_navigate_method", text="Navigate")
+ layout.row().prop(input_prefs, "ndof_view_rotate_method", text="Orbit")
+ layout.separator()
- flow.prop(inputs, "pressure_threshold_max")
- flow.prop(inputs, "pressure_softness")
+ layout.label(text="Orbit Options")
+ split = layout.split(factor=0.6)
+ row = split.row()
+ row.alignment = 'RIGHT'
+ row.label(text="Invert Axis")
+ row = split.row(align=True)
+ for text, attr in (
+ ("X", "ndof_rotx_invert_axis"),
+ ("Y", "ndof_roty_invert_axis"),
+ ("Z", "ndof_rotz_invert_axis"),
+ ):
+ row.prop(input_prefs, attr, text=text, toggle=True)
+ # view2d use pan/zoom
+ layout.separator()
+ layout.label(text="Pan Options")
-class USERPREF_PT_input_ndof(PreferencePanel, Panel):
- bl_label = "NDOF"
- bl_options = {'DEFAULT_CLOSED'}
+ split = layout.split(factor=0.6)
+ row = split.row()
+ row.alignment = 'RIGHT'
+ row.label(text="Invert Axis")
+ row = split.row(align=True)
+ for text, attr in (
+ ("X", "ndof_panx_invert_axis"),
+ ("Y", "ndof_pany_invert_axis"),
+ ("Z", "ndof_panz_invert_axis"),
+ ):
+ row.prop(input_prefs, attr, text=text, toggle=True)
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- inputs = prefs.inputs
- return prefs.active_section == 'INPUT' and inputs.use_ndof
+ layout.prop(input_prefs, "ndof_pan_yz_swap_axis")
- def draw_props(self, context, layout):
- prefs = context.preferences
- inputs = prefs.inputs
+ layout.label(text="Zoom Options")
+ layout.prop(input_prefs, "ndof_zoom_invert")
- flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ if is_view3d:
+ layout.separator()
+ layout.label(text="Fly/Walk Options")
+ layout.prop(input_prefs, "ndof_fly_helicopter")
+ layout.prop(input_prefs, "ndof_lock_horizon")
- flow.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity")
- flow.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity")
- flow.prop(inputs, "ndof_deadzone", text="Deadzone")
- layout.separator()
+# -----------------------------------------------------------------------------
+# Key-Map Editor Panels
- flow.row().prop(inputs, "ndof_view_navigate_method", expand=True)
- flow.row().prop(inputs, "ndof_view_rotate_method", expand=True)
+class KeymapPanel:
+ bl_space_type = 'PREFERENCES'
+ bl_region_type = 'WINDOW'
+ bl_context = "keymap"
class USERPREF_MT_keyconfigs(Menu):
@@ -1714,17 +1639,10 @@ class USERPREF_MT_keyconfigs(Menu):
Menu.draw_preset(self, context)
-class USERPREF_PT_keymap(Panel):
- bl_space_type = 'PREFERENCES'
+class USERPREF_PT_keymap(KeymapPanel, Panel):
bl_label = "Keymap"
- bl_region_type = 'WINDOW'
bl_options = {'HIDE_HEADER'}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'KEYMAP')
-
def draw(self, context):
from rna_keymap_ui import draw_keymaps
@@ -1740,10 +1658,17 @@ class USERPREF_PT_keymap(Panel):
# print("runtime", time.time() - start)
-class USERPREF_PT_addons(Panel):
+# -----------------------------------------------------------------------------
+# Add-On Panels
+
+class AddOnPanel:
bl_space_type = 'PREFERENCES'
- bl_label = "Add-ons"
bl_region_type = 'WINDOW'
+ bl_context = "addons"
+
+
+class USERPREF_PT_addons(AddOnPanel, Panel):
+ bl_label = "Add-ons"
bl_options = {'HIDE_HEADER'}
_support_icon_mapping = {
@@ -1752,11 +1677,6 @@ class USERPREF_PT_addons(Panel):
'TESTING': 'EXPERIMENTAL',
}
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'ADDONS')
-
@staticmethod
def is_user_addon(mod, user_addon_paths):
import os
@@ -2027,14 +1947,16 @@ class USERPREF_PT_addons(Panel):
row.label(text=module_name, translate=False)
-class StudioLightPanelMixin():
+# -----------------------------------------------------------------------------
+# Studio Light Panels
+
+class StudioLightPanel:
bl_space_type = 'PREFERENCES'
bl_region_type = 'WINDOW'
+ bl_context = "lights"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'LIGHTS')
+
+class StudioLightPanelMixin:
def _get_lights(self, prefs):
return [light for light in prefs.studio_lights if light.is_user_defined and light.type == self.sl_type]
@@ -2070,7 +1992,7 @@ class StudioLightPanelMixin():
box.label(text=studio_light.name)
-class USERPREF_PT_studiolight_matcaps(Panel, StudioLightPanelMixin):
+class USERPREF_PT_studiolight_matcaps(StudioLightPanel, StudioLightPanelMixin, Panel):
bl_label = "MatCaps"
sl_type = 'MATCAP'
@@ -2080,7 +2002,7 @@ class USERPREF_PT_studiolight_matcaps(Panel, StudioLightPanelMixin):
layout.separator()
-class USERPREF_PT_studiolight_world(Panel, StudioLightPanelMixin):
+class USERPREF_PT_studiolight_world(StudioLightPanel, StudioLightPanelMixin, Panel):
bl_label = "LookDev HDRIs"
sl_type = 'WORLD'
@@ -2090,7 +2012,7 @@ class USERPREF_PT_studiolight_world(Panel, StudioLightPanelMixin):
layout.separator()
-class USERPREF_PT_studiolight_lights(Panel, StudioLightPanelMixin):
+class USERPREF_PT_studiolight_lights(StudioLightPanel, StudioLightPanelMixin, Panel):
bl_label = "Studio Lights"
sl_type = 'STUDIO'
@@ -2102,14 +2024,13 @@ class USERPREF_PT_studiolight_lights(Panel, StudioLightPanelMixin):
layout.separator()
-class USERPREF_PT_studiolight_light_editor(Panel):
+class USERPREF_PT_studiolight_light_editor(StudioLightPanel, Panel):
bl_label = "Editor"
bl_parent_id = "USERPREF_PT_studiolight_lights"
- bl_space_type = 'PREFERENCES'
- bl_region_type = 'WINDOW'
bl_options = {'DEFAULT_CLOSED'}
- def opengl_light_buttons(self, layout, light):
+ @staticmethod
+ def opengl_light_buttons(layout, light):
col = layout.column()
col.active = light.use
@@ -2156,33 +2077,28 @@ class USERPREF_PT_studiolight_light_editor(Panel):
layout.prop(system, "light_ambient")
+# -----------------------------------------------------------------------------
+# Experimental Panels
+
class ExperimentalPanel:
bl_space_type = 'PREFERENCES'
bl_region_type = 'WINDOW'
+ bl_context = "experimental"
url_prefix = "https://developer.blender.org/"
- @classmethod
- def poll(cls, context):
- prefs = context.preferences
- return (prefs.active_section == 'EXPERIMENTAL')
-
- def draw(self, context):
- layout = self.layout
-
- layout.use_property_split = True
- layout.use_property_decorate = False
-
- self.draw_props(context, layout)
-
class USERPREF_PT_experimental_ui(ExperimentalPanel, Panel):
bl_label = "User Interface"
- def draw_props(self, context, layout):
+ def draw(self, context):
prefs = context.preferences
experimental = prefs.experimental
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
task = "T66304"
split = layout.split(factor=0.66)
col = split.column()
@@ -2198,7 +2114,7 @@ class USERPREF_PT_experimental_ui(ExperimentalPanel, Panel):
class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel):
bl_label = "Virtual Reality"
- def draw_props(self, context, layout):
+ def draw_centered(self, context, layout):
prefs = context.preferences
experimental = prefs.experimental
@@ -2218,6 +2134,33 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel):
"""
+class USERPREF_PT_experimental_usd(ExperimentalPanel, Panel):
+ bl_label = "Universal Scene Description"
+
+ @classmethod
+ def poll(cls, context):
+ # Only show the panel if Blender was actually built with USD support.
+ return getattr(bpy.app.build_options, "usd", False)
+
+ def draw(self, context):
+ prefs = context.preferences
+ experimental = prefs.experimental
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ split = layout.split(factor=0.66)
+ col = split.split()
+ col.prop(experimental, "use_usd_exporter", text="USD Exporter")
+ col = split.split()
+ url = "https://devtalk.blender.org/t/universal-scene-description-usd-exporter-feedback/10920"
+ col.operator("wm.url_open", text='Give Feedback', icon='URL').url = url
+
+
+# -----------------------------------------------------------------------------
+# Class Registration
+
# Order of registration defines order in UI,
# so dynamically generated classes are 'injected' in the intended order.
classes = (
@@ -2300,6 +2243,7 @@ classes = (
USERPREF_PT_studiolight_world,
USERPREF_PT_experimental_ui,
+ USERPREF_PT_experimental_usd,
# Popovers.
USERPREF_PT_ndof_settings,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index cebfdf9196c..48e40554cfb 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
# -------------------
@@ -77,23 +77,31 @@ class VIEW3D_HT_tool_header(Header):
# (obviously separated for from the users POV)
draw_fn = getattr(_draw_tool_settings_context_mode, tool_mode, None)
if draw_fn is not None:
- draw_fn(context, layout, tool)
-
- popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"}
+ is_valid_context = draw_fn(context, layout, 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)
+ if is_valid_context:
+ 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)
+ if is_valid_context:
+ 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)
+ if is_valid_context:
+ 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)
+ if is_valid_context:
+ draw_3d_brush_settings(layout, tool_mode)
elif tool_mode == 'EDIT_ARMATURE':
pass
elif tool_mode == 'EDIT_CURVE':
@@ -108,12 +116,24 @@ class VIEW3D_HT_tool_header(Header):
# layout.popover_group(context=".paint_common", **popover_kw)
pass
elif tool_mode == 'PAINT_GPENCIL':
- if (tool is not None) and tool.has_datablock:
- layout.popover_group(context=".greasepencil_paint", **popover_kw)
+ if is_valid_context:
+ 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_stroke")
+ 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
@@ -221,80 +241,138 @@ class _draw_tool_settings_context_mode:
@staticmethod
def SCULPT(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
paint = context.tool_settings.sculpt
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
- return
+ return False
- from bl_ui.properties_paint_common import (
- brush_basic_sculpt_settings,
+ 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"
+
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ size,
+ pressure_name="use_pressure_size",
+ unified_name="use_unified_size",
+ text="Radius",
+ slider=True,
+ header=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,
+ unified_name="use_unified_strength",
+ text="Strength",
+ header=True
)
- brush_basic_sculpt_settings(layout, context, brush, compact=True)
+
+ # direction
+ if not capabilities.has_direction:
+ layout.row().prop(brush, "direction", expand=True, text="")
+
+ return True
@staticmethod
def PAINT_TEXTURE(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
paint = context.tool_settings.image_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
- return
+ return False
- 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)
+ return True
+
@staticmethod
def PAINT_VERTEX(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
paint = context.tool_settings.vertex_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
- return
+ return False
- 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)
+
+ return True
@staticmethod
def PAINT_WEIGHT(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
paint = context.tool_settings.weight_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
- return
+ return False
+
+ capabilities = brush.weight_paint_capabilities
+ if capabilities.has_weight:
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "weight",
+ unified_name="use_unified_weight",
+ slider=True,
+ header=True
+ )
- from bl_ui.properties_paint_common import brush_basic_wpaint_settings
- brush_basic_wpaint_settings(layout, context, brush, compact=True)
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "size",
+ pressure_name="use_pressure_size",
+ unified_name="use_unified_size",
+ slider=True,
+ text="Radius",
+ header=True
+ )
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "strength",
+ pressure_name="use_pressure_strength",
+ unified_name="use_unified_strength",
+ header=True
+ )
+
+ return True
@staticmethod
def PAINT_GPENCIL(context, layout, tool):
if tool is None:
- return
+ return False
# is_paint = True
# FIXME: tools must use their own UI drawing!
@@ -311,14 +389,14 @@ class _draw_tool_settings_context_mode:
elif tool.idname == "Cutter":
row = layout.row(align=True)
row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
- return
+ return False
elif not tool.has_datablock:
- return
+ return False
paint = context.tool_settings.gpencil_paint
brush = paint.brush
if brush is None:
- return
+ return False
gp_settings = brush.gpencil_settings
@@ -358,12 +436,14 @@ 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)
+
+ return True
@staticmethod
def SCULPT_GPENCIL(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
tool_settings = context.tool_settings
settings = tool_settings.gpencil_sculpt
brush = settings.brush
@@ -373,10 +453,12 @@ class _draw_tool_settings_context_mode:
)
brush_basic_gpencil_sculpt_settings(layout, context, brush, compact=True)
+ return True
+
@staticmethod
def WEIGHT_GPENCIL(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
tool_settings = context.tool_settings
settings = tool_settings.gpencil_sculpt
brush = settings.brush
@@ -386,38 +468,44 @@ class _draw_tool_settings_context_mode:
)
brush_basic_gpencil_weight_settings(layout, context, brush, compact=True)
+ return True
+
@staticmethod
def PARTICLE(context, layout, tool):
if (tool is None) or (not tool.has_datablock):
- return
+ return False
# See: 'VIEW3D_PT_tools_brush', basically a duplicate
settings = context.tool_settings.particle_edit
brush = settings.brush
tool = settings.tool
- if tool != 'NONE':
- layout.prop(brush, "size", slider=True)
- if tool == 'ADD':
- layout.prop(brush, "count")
+ if tool == 'NONE':
+ return False
- layout.prop(settings, "use_default_interpolate")
- layout.prop(brush, "steps", slider=True)
- layout.prop(settings, "default_key_count", slider=True)
- else:
- layout.prop(brush, "strength", slider=True)
-
- if tool == 'LENGTH':
- layout.row().prop(brush, "length_mode", expand=True)
- elif tool == 'PUFF':
- layout.row().prop(brush, "puff_mode", expand=True)
- layout.prop(brush, "use_puff_volume")
- elif tool == 'COMB':
- row = layout.row()
- row.active = settings.is_editable
- row.prop(settings, "use_emitter_deflect", text="Deflect Emitter")
- sub = row.row(align=True)
- sub.active = settings.use_emitter_deflect
- sub.prop(settings, "emitter_distance", text="Distance")
+ layout.prop(brush, "size", slider=True)
+ if tool == 'ADD':
+ layout.prop(brush, "count")
+
+ layout.prop(settings, "use_default_interpolate")
+ layout.prop(brush, "steps", slider=True)
+ layout.prop(settings, "default_key_count", slider=True)
+ else:
+ layout.prop(brush, "strength", slider=True)
+
+ if tool == 'LENGTH':
+ layout.row().prop(brush, "length_mode", expand=True)
+ elif tool == 'PUFF':
+ layout.row().prop(brush, "puff_mode", expand=True)
+ layout.prop(brush, "use_puff_volume")
+ elif tool == 'COMB':
+ row = layout.row()
+ row.active = settings.is_editable
+ row.prop(settings, "use_emitter_deflect", text="Deflect Emitter")
+ sub = row.row(align=True)
+ sub.active = settings.use_emitter_deflect
+ sub.prop(settings, "emitter_distance", text="Distance")
+
+ return True
class VIEW3D_HT_header(Header):
@@ -467,12 +555,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:
@@ -1042,6 +1129,12 @@ class VIEW3D_MT_view(Menu):
layout.operator("render.opengl", text="Viewport Render Image", icon='RENDER_STILL')
layout.operator("render.opengl", text="Viewport Render Animation", icon='RENDER_ANIMATION').animation = True
+ props = layout.operator("render.opengl",
+ text="Viewport Render Keyframes",
+ icon='RENDER_ANIMATION',
+ )
+ props.animation = True
+ props.render_keyed_only = True
layout.separator()
@@ -2591,7 +2684,7 @@ class VIEW3D_MT_object_quick_effects(Menu):
layout.operator("object.quick_fur")
layout.operator("object.quick_explode")
layout.operator("object.quick_smoke")
- layout.operator("object.quick_fluid")
+ layout.operator("object.quick_liquid")
class VIEW3D_MT_object_showhide(Menu):
@@ -5474,9 +5567,10 @@ class VIEW3D_PT_shading_render_pass(Panel):
@classmethod
def poll(cls, context):
- return (context.space_data.shading.type == 'MATERIAL'
- or (context.engine in cls.COMPAT_ENGINES
- and context.space_data.shading.type == 'RENDERED'))
+ return (
+ (context.space_data.shading.type == 'MATERIAL') or
+ (context.engine in cls.COMPAT_ENGINES and context.space_data.shading.type == 'RENDERED')
+ )
def draw(self, context):
shading = context.space_data.shading
@@ -6652,6 +6746,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
@@ -6661,8 +6756,24 @@ 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):
@@ -6673,6 +6784,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
@@ -6683,8 +6795,24 @@ 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):
@@ -6697,9 +6825,32 @@ 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):
@@ -6714,8 +6865,24 @@ 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..f0c4aaa9344 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 @@
# <pep8 compliant>
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
-
+# 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
@@ -319,26 +322,42 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel):
# ********** default tools for paint modes ****************
-class View3DPaintPanel(UnifiedPaintPanel):
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
+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 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 +390,20 @@ 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"
+
+
+# TODO, move to space_view3d.py
+class VIEW3D_PT_tools_brush_settings(Panel, View3DPaintBrushPanel):
+ bl_context = ".paint_common"
+ bl_label = "Brush Settings"
@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))
+ return settings and settings.brush is not None
def draw(self, context):
layout = self.layout
@@ -394,309 +414,67 @@ 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="")
-
- # Sculpt Mode #
- if context.sculpt_object and brush:
- from bl_ui.properties_paint_common import (
- brush_basic_sculpt_settings,
- )
-
- capabilities = brush.sculpt_capabilities
-
- col = layout.column()
-
- if not self.is_popover:
- brush_basic_sculpt_settings(col, context, 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 #
+ brush_settings(layout.column(), context, brush, popover=self.is_popover)
- 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()
+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'}
- if not self.is_popover:
- brush_basic_wpaint_settings(col, context, brush)
+ def draw(self, context):
+ layout = self.layout
- # Vertex Paint Mode #
- elif context.vertex_paint_object and brush:
- from bl_ui.properties_paint_common import (
- brush_basic_vpaint_settings,
- )
+ layout.use_property_split = True
+ layout.use_property_decorate = False # No animation.
- col = layout.column()
+ settings = UnifiedPaintPanel.paint_settings(context)
+ brush = settings.brush
- 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
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
+ return False
+
def draw(self, context):
layout = self.layout
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"
@@ -718,8 +496,7 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel):
@classmethod
def poll(cls, context):
brush = context.tool_settings.image_paint.brush
- ob = context.active_object
- return (brush is not None and ob is not None)
+ return (brush is not None and context.active_object is not None)
def draw(self, context):
layout = self.layout
@@ -782,9 +559,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
-
+# 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 +612,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 +646,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 +671,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)
@@ -1264,9 +861,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
col.prop(mesh, "use_remesh_preserve_paint_mask")
col.operator("object.voxel_remesh", text="Remesh")
-# TODO, move to space_view3d.py
-
+# 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"
@@ -1294,23 +890,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
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 +988,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 +1033,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 +1048,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 +1057,16 @@ class VIEW3D_PT_tools_vertexpaint_options(Panel, View3DPaintPanel):
bl_label = "Options"
bl_options = {'DEFAULT_CLOSED'}
- def draw(self, context):
- layout = self.layout
-
- layout.label(text="Unified Brush")
+ @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.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 +1188,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"
@@ -1718,14 +1217,15 @@ class VIEW3D_PT_imagepaint_options(View3DPaintPanel):
bl_label = "Options"
@classmethod
- def poll(cls, context):
- return (context.image_paint_object and context.tool_settings.image_paint)
+ 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):
+ 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):
@@ -1783,6 +1283,8 @@ class VIEW3D_PT_tools_particlemode_options(View3DPanel, Panel):
col = layout.column(align=True)
col.active = pe.is_editable
col.prop(ob.data, "use_mirror_x")
+ if pe.tool == 'ADD':
+ col.prop(ob.data, "use_mirror_topology")
col.separator()
col.prop(pe, "use_preserve_length", text="Preserve Strand Lengths")
col.prop(pe, "use_preserve_root", text="Preserve Root Positions")
@@ -1842,15 +1344,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 +1359,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,14 +1372,38 @@ 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()
- brush = gpencil_paint.brush
+ col.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="")
+
+ if context.mode == 'PAINT_GPENCIL':
+ brush = tool_settings.gpencil_paint.brush
+ if brush is not None:
+ 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="")
+
- sub = col.column(align=True)
- sub.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="")
+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
if brush is not None:
gp_settings = brush.gpencil_settings
@@ -1895,63 +1423,87 @@ 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':
+ row = col.row(align=True)
+ row.prop(gp_settings, "fill_draw_mode", text="Boundary")
+ row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID')
+ col.separator()
+ 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_stroke(Panel, View3DPanel):
+ bl_context = ".greasepencil_paint"
+ bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings'
+ bl_label = "Stroke"
+ 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 == 'DRAW'
+
+ def draw(self, context):
+ layout = self.layout
-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_stroke'
+ bl_label = "Stabilize Stroke"
bl_category = "Tool"
bl_options = {'DEFAULT_CLOSED'}
@@ -1961,6 +1513,9 @@ 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 +1527,35 @@ 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_stroke'
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 +1567,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_stroke'
bl_label = "Randomize"
bl_category = "Tool"
bl_options = {'DEFAULT_CLOSED'}
@@ -2032,9 +1605,12 @@ 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 +1622,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 +1643,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 +1708,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 +1756,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
+
+ 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):
@@ -2198,46 +1805,60 @@ class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel):
layout.use_property_decorate = False
settings = context.tool_settings.gpencil_sculpt
- brush = settings.brush
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)
-# Grease Pencil Brush Appearance (one for each mode)
-class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilAppearancePanel, View3DPanel, Panel):
- bl_context = ".greasepencil_paint"
- bl_label = "Display"
+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"
-class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilAppearancePanel, View3DPanel, Panel):
- bl_context = ".greasepencil_sculpt"
- bl_label = "Display"
+# Grease Pencil Brush Appearance (one for each mode)
+class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel):
+ bl_context = ".greasepencil_paint"
+ bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings'
+ bl_label = "Cursor"
bl_category = "Tool"
-class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, View3DPanel, Panel):
+class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilDisplayPanel, Panel, View3DPanel):
bl_context = ".greasepencil_sculpt"
- bl_label = "Sculpt Strokes"
- bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt'
+ bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings'
+ bl_label = "Cursor"
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 +1876,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 +1895,53 @@ 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_stabilizer,
+ VIEW3D_PT_tools_grease_pencil_brush_advanced,
+ VIEW3D_PT_tools_grease_pencil_brush_stroke,
+ VIEW3D_PT_tools_grease_pencil_brush_post_processing,
VIEW3D_PT_tools_grease_pencil_brush_random,
+ VIEW3D_PT_tools_grease_pencil_brush_stabilizer,
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,
)