diff options
author | Jacques Lucke <jacques@blender.org> | 2021-06-14 13:44:13 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2021-06-14 13:44:13 +0300 |
commit | dddcf1e9bbf4a6d1f4ff03eaf0cb7e9228b18ec5 (patch) | |
tree | c20defa7efd54c933d20a296abefe567909bb6c0 /release/scripts/startup | |
parent | 3b162b7c185d089e93d892169a458d552196b7b6 (diff) | |
parent | c9dc55301cd7903b7ef7c045d337ada29aa809a1 (diff) |
Merge branch 'master' into temp-compact-node-prototypetemp-compact-node-prototype
Diffstat (limited to 'release/scripts/startup')
46 files changed, 832 insertions, 396 deletions
diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index ab56b24d186..81a28964389 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -355,7 +355,8 @@ class UpdateAnimatedTransformConstraint(Operator): use_convert_to_radians: BoolProperty( name="Convert to Radians", - description="Convert fcurves/drivers affecting rotations to radians (Warning: use this only once!)", + description="Convert f-curves/drivers affecting rotations to radians.\n" + "Warning: Use this only once", default=True, ) diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py index 73391f94c85..45d1ea98a8a 100644 --- a/release/scripts/startup/bl_operators/clip.py +++ b/release/scripts/startup/bl_operators/clip.py @@ -18,7 +18,6 @@ # <pep8 compliant> import bpy -import os from bpy.types import Operator from bpy.props import FloatProperty from mathutils import ( @@ -207,8 +206,8 @@ class CLIP_OT_filter_tracks(Operator): @classmethod def poll(cls, context): - space = context.space_data - return (space.type == 'CLIP_EDITOR') and space.clip + sc = context.space_data + return sc and (sc.type == 'CLIP_EDITOR') and sc.clip def execute(self, context): num_tracks = self._filter_values(context, self.track_threshold) @@ -222,8 +221,8 @@ class CLIP_OT_set_active_clip(Operator): @classmethod def poll(cls, context): - space = context.space_data - return space.type == 'CLIP_EDITOR' and space.clip + sc = context.space_data + return sc and (sc.type == 'CLIP_EDITOR') and sc.clip def execute(self, context): clip = context.space_data.clip @@ -269,8 +268,8 @@ class CLIP_OT_track_to_empty(Operator): @classmethod def poll(cls, context): - space = context.space_data - return space.type == 'CLIP_EDITOR' and space.clip + sc = context.space_data + return sc and (sc.type == 'CLIP_EDITOR') and sc.clip def execute(self, context): sc = context.space_data @@ -294,7 +293,7 @@ class CLIP_OT_bundles_to_mesh(Operator): @classmethod def poll(cls, context): sc = context.space_data - return (sc.type == 'CLIP_EDITOR') and sc.clip + return sc and (sc.type == 'CLIP_EDITOR') and sc.clip def execute(self, context): from bpy_extras.io_utils import unpack_list @@ -342,12 +341,8 @@ class CLIP_OT_delete_proxy(Operator): @classmethod def poll(cls, context): - if context.space_data.type != 'CLIP_EDITOR': - return False - sc = context.space_data - - return sc.clip + return sc and (sc.type == 'CLIP_EDITOR') and sc.clip def invoke(self, context, event): wm = context.window_manager @@ -356,6 +351,7 @@ class CLIP_OT_delete_proxy(Operator): @staticmethod def _rmproxy(abspath): + import os import shutil if not os.path.exists(abspath): @@ -367,6 +363,7 @@ class CLIP_OT_delete_proxy(Operator): os.remove(abspath) def execute(self, context): + import os sc = context.space_data clip = sc.clip if clip.use_proxy_custom_directory: @@ -423,12 +420,8 @@ class CLIP_OT_set_viewport_background(Operator): @classmethod def poll(cls, context): - if context.space_data.type != 'CLIP_EDITOR': - return False - sc = context.space_data - - return sc.clip + return sc and (sc.type == 'CLIP_EDITOR') and sc.clip def execute(self, context): sc = context.space_data @@ -562,13 +555,11 @@ class CLIP_OT_setup_tracking_scene(Operator): @classmethod def poll(cls, context): sc = context.space_data - - if sc.type != 'CLIP_EDITOR': - return False - - clip = sc.clip - - return clip and clip.tracking.reconstruction.is_valid + if sc and sc.type == 'CLIP_EDITOR': + clip = sc.clip + if clip and clip.tracking.reconstruction.is_valid: + return True + return False @staticmethod def _setupScene(context): @@ -1017,13 +1008,11 @@ class CLIP_OT_track_settings_as_default(Operator): @classmethod def poll(cls, context): sc = context.space_data - - if sc.type != 'CLIP_EDITOR': - return False - - clip = sc.clip - - return clip and clip.tracking.tracks.active + if sc and sc.type == 'CLIP_EDITOR': + clip = sc.clip + if clip and clip.tracking.tracks.active: + return True + return False def execute(self, context): sc = context.space_data @@ -1067,11 +1056,12 @@ class CLIP_OT_track_settings_to_track(Operator): @classmethod def poll(cls, context): - space = context.space_data - if space.type != 'CLIP_EDITOR': - return False - clip = space.clip - return clip and clip.tracking.tracks.active + sc = context.space_data + if sc and sc.type == 'CLIP_EDITOR': + clip = sc.clip + if clip and clip.tracking.tracks.active: + return True + return False def execute(self, context): space = context.space_data diff --git a/release/scripts/startup/bl_operators/constraint.py b/release/scripts/startup/bl_operators/constraint.py index 49fc6a04112..f18f3bb3a49 100644 --- a/release/scripts/startup/bl_operators/constraint.py +++ b/release/scripts/startup/bl_operators/constraint.py @@ -33,6 +33,11 @@ class CONSTRAINT_OT_add_target(Operator): bl_label = "Add Target" bl_options = {'UNDO', 'INTERNAL'} + @classmethod + def poll(cls, context): + constraint = getattr(context, "constraint", None) + return constraint + def execute(self, context): context.constraint.targets.new() return {'FINISHED'} @@ -46,6 +51,11 @@ class CONSTRAINT_OT_remove_target(Operator): index: IntProperty() + @classmethod + def poll(cls, context): + constraint = getattr(context, "constraint", None) + return constraint + def execute(self, context): tgts = context.constraint.targets tgts.remove(tgts[self.index]) @@ -58,6 +68,11 @@ class CONSTRAINT_OT_normalize_target_weights(Operator): bl_label = "Normalize Weights" bl_options = {'UNDO', 'INTERNAL'} + @classmethod + def poll(cls, context): + constraint = getattr(context, "constraint", None) + return constraint + def execute(self, context): tgts = context.constraint.targets total = sum(t.weight for t in tgts) diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index 0c7a2a01b7a..71ef89a066b 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -42,8 +42,8 @@ def geometry_node_group_empty_new(): def geometry_modifier_poll(context): ob = context.object - # Test object support for geometry node modifier (No volume, curve, or hair object support yet) - if not ob or ob.type not in {'MESH', 'POINTCLOUD'}: + # Test object support for geometry node modifier (No curve, or hair object support yet) + if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME'}: return False return True diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py index 5fca3e194d7..ddbfe7845b1 100644 --- a/release/scripts/startup/bl_operators/mesh.py +++ b/release/scripts/startup/bl_operators/mesh.py @@ -219,7 +219,7 @@ class MeshSelectNext(Operator): if find_adjacent.select_next(bm, self.report): bm.select_flush_mode() - bmesh.update_edit_mesh(me, False) + bmesh.update_edit_mesh(me, loop_triangles=False) return {'FINISHED'} @@ -244,7 +244,7 @@ class MeshSelectPrev(Operator): if find_adjacent.select_prev(bm, self.report): bm.select_flush_mode() - bmesh.update_edit_mesh(me, False) + bmesh.update_edit_mesh(me, loop_triangles=False) return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index 52484f8ffc8..17e17273432 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -20,7 +20,6 @@ from __future__ import annotations import bpy -import nodeitems_utils from bpy.types import ( Operator, PropertyGroup, @@ -121,7 +120,7 @@ class NodeAddOperator: def poll(cls, context): space = context.space_data # needs active node editor and a tree to add nodes to - return ((space.type == 'NODE_EDITOR') and + return (space and (space.type == 'NODE_EDITOR') and space.edit_tree and not space.edit_tree.library) # Default execute simply adds a node @@ -195,6 +194,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator): # Create an enum list from node items def node_enum_items(self, context): + import nodeitems_utils + enum_items = NODE_OT_add_search._enum_item_hack enum_items.clear() @@ -210,6 +211,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator): # Look up the item based on index def find_node_item(self, context): + import nodeitems_utils + node_item = int(self.node_item) for index, item in enumerate(nodeitems_utils.node_items_iter(context)): if index == node_item: @@ -262,7 +265,7 @@ class NODE_OT_collapse_hide_unused_toggle(Operator): def poll(cls, context): space = context.space_data # needs active node editor and a tree - return ((space.type == 'NODE_EDITOR') and + return (space and (space.type == 'NODE_EDITOR') and (space.edit_tree and not space.edit_tree.library)) def execute(self, context): @@ -293,7 +296,7 @@ class NODE_OT_tree_path_parent(Operator): def poll(cls, context): space = context.space_data # needs active node editor and a tree - return (space.type == 'NODE_EDITOR' and len(space.path) > 1) + return (space and (space.type == 'NODE_EDITOR') and len(space.path) > 1) def execute(self, context): space = context.space_data @@ -302,7 +305,6 @@ class NODE_OT_tree_path_parent(Operator): return {'FINISHED'} - class NODE_OT_expose_input_socket(Operator): '''Expose socket''' bl_idname = "node.expose_input_socket" @@ -324,6 +326,65 @@ class NODE_OT_expose_input_socket(Operator): return {'FINISHED'} +class NODE_OT_active_preview_toggle(Operator): + '''Toggle active preview state of node''' + bl_idname = "node.active_preview_toggle" + bl_label = "Toggle Active Preview" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + space = context.space_data + if space is None: + return False + if space.type != 'NODE_EDITOR': + return False + if space.edit_tree is None: + return False + if space.edit_tree.nodes.active is None: + return False + return True + + def execute(self, context): + node_editor = context.space_data + ntree = node_editor.edit_tree + active_node = ntree.nodes.active + + if active_node.active_preview: + self.disable_preview(context, ntree, active_node) + else: + self.enable_preview(context, node_editor, ntree, active_node) + + return {'FINISHED'} + + def enable_preview(self, context, node_editor, ntree, active_node): + spreadsheets = self.find_unpinned_spreadsheets(context) + + for spreadsheet in spreadsheets: + spreadsheet.set_geometry_node_context(node_editor, active_node) + + for node in ntree.nodes: + node.active_preview = False + active_node.active_preview = True + + def disable_preview(self, context, ntree, active_node): + spreadsheets = self.find_unpinned_spreadsheets(context) + for spreadsheet in spreadsheets: + spreadsheet.context_path.clear() + + active_node.active_preview = False + + def find_unpinned_spreadsheets(self, context): + spreadsheets = [] + for window in context.window_manager.windows: + for area in window.screen.areas: + space = area.spaces.active + if space.type == 'SPREADSHEET' and not space.is_pinned: + spreadsheets.append(space) + return spreadsheets + + + classes = ( NodeSetting, @@ -333,4 +394,5 @@ classes = ( NODE_OT_collapse_hide_unused_toggle, NODE_OT_tree_path_parent, NODE_OT_expose_input_socket, + NODE_OT_active_preview_toggle, ) diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 5a388047ddd..d61bed71cab 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -133,7 +133,7 @@ class SelectCamera(Operator): scene = context.scene view_layer = context.view_layer view = context.space_data - if view.type == 'VIEW_3D' and view.use_local_camera: + if view and view.type == 'VIEW_3D' and view.use_local_camera: camera = view.camera else: camera = scene.camera diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index cedbe542287..3189f3b3376 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -114,9 +114,7 @@ class AddPresetBase: filename = self.as_filename(name) target_path = os.path.join("presets", self.preset_subdir) - target_path = bpy.utils.user_resource('SCRIPTS', - target_path, - create=True) + target_path = bpy.utils.user_resource('SCRIPTS', path=target_path, create=True) if not target_path: self.report({'WARNING'}, "Failed to create presets path") diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py index 6c29c07c62e..6d60c58cc3a 100644 --- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py +++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py @@ -22,7 +22,6 @@ import bpy from bpy.types import Operator -import os from bpy.app.translations import pgettext_tip as tip_ @@ -62,6 +61,7 @@ class PlayRenderedAnim(Operator): bl_options = {'REGISTER'} def execute(self, context): + import os import subprocess from shlex import quote @@ -130,6 +130,7 @@ class PlayRenderedAnim(Operator): "-s", str(frame_start), "-e", str(frame_end), "-j", str(scene.frame_step), + "-c", str(prefs.system.memory_cache_limit), file, ] cmd.extend(opts) diff --git a/release/scripts/startup/bl_operators/spreadsheet.py b/release/scripts/startup/bl_operators/spreadsheet.py index 91fca883bb5..5cc83d4eddd 100644 --- a/release/scripts/startup/bl_operators/spreadsheet.py +++ b/release/scripts/startup/bl_operators/spreadsheet.py @@ -34,13 +34,45 @@ class SPREADSHEET_OT_toggle_pin(Operator): def execute(self, context): space = context.space_data - if space.pinned_id: - space.pinned_id = None + if space.is_pinned: + self.unpin(context) else: - space.pinned_id = context.active_object - + self.pin(context) return {'FINISHED'} + def pin(self, context): + space = context.space_data + space.is_pinned = True + + def unpin(self, context): + space = context.space_data + space.is_pinned = False + + space.context_path.clear() + + # Try to find a node with an active preview in any open editor. + if space.object_eval_state == 'EVALUATED': + node_editors = self.find_geometry_node_editors(context) + for node_editor in node_editors: + ntree = node_editor.edit_tree + for node in ntree.nodes: + if node.active_preview: + space.set_geometry_node_context(node_editor, node) + return + + def find_geometry_node_editors(self, context): + editors = [] + for window in context.window_manager.windows: + for area in window.screen.areas: + space = area.spaces.active + if space.type != 'NODE_EDITOR': + continue + if space.edit_tree is None: + continue + if space.edit_tree.type == 'GEOMETRY': + editors.append(space) + return editors + classes = ( SPREADSHEET_OT_toggle_pin, diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 7547184dc04..623bf583a74 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -90,7 +90,7 @@ class PREFERENCES_OT_copy_prev(Operator): @classmethod def _old_version_path(cls, version): - return bpy.utils.resource_path('USER', version[0], version[1]) + return bpy.utils.resource_path('USER', major=version[0], minor=version[1]) @classmethod def previous_version(cls): @@ -99,6 +99,15 @@ class PREFERENCES_OT_copy_prev(Operator): version = bpy.app.version version_new = ((version[0] * 100) + version[1]) version_old = ((version[0] * 100) + version[1]) - 1 + + # Special case, remove when the version is > 3.0. + if version_new == 300: + version_new = 294 + version_old = 293 + else: + print("TODO: remove exception!") + # End special case. + # Ensure we only try to copy files from a point release. # The check below ensures the second numbers match. while (version_new % 100) // 10 == (version_old % 100) // 10: @@ -144,7 +153,7 @@ class PREFERENCES_OT_copy_prev(Operator): def execute(self, _context): import shutil - shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True) + shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True, symlinks=True) # reload preferences and recent-files.txt bpy.ops.wm.read_userpref() @@ -217,7 +226,11 @@ class PREFERENCES_OT_keyconfig_import(Operator): config_name = basename(self.filepath) - path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", "keyconfig"), create=True) + path = bpy.utils.user_resource( + 'SCRIPTS', + path=os.path.join("presets", "keyconfig"), + create=True, + ) path = os.path.join(path, config_name) try: @@ -520,7 +533,11 @@ class PREFERENCES_OT_theme_install(Operator): xmlfile = self.filepath - path_themes = bpy.utils.user_resource('SCRIPTS', "presets/interface_theme", create=True) + path_themes = bpy.utils.user_resource( + 'SCRIPTS', + path=os.path.join("presets", "interface_theme"), + create=True, + ) if not path_themes: self.report({'ERROR'}, "Failed to get themes path") @@ -581,7 +598,7 @@ class PREFERENCES_OT_addon_install(Operator): name="Target Path", items=( ('DEFAULT', "Default", ""), - ('PREFS', "User Prefs", ""), + ('PREFS', "Preferences", ""), ), ) @@ -613,8 +630,8 @@ class PREFERENCES_OT_addon_install(Operator): pyfile = self.filepath if self.target == 'DEFAULT': - # don't use bpy.utils.script_paths("addons") because we may not be able to write to it. - path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True) + # Don't use `bpy.utils.script_paths(path="addons")` because we may not be able to write to it. + path_addons = bpy.utils.user_resource('SCRIPTS', path="addons", create=True) else: path_addons = context.preferences.filepaths.script_directory if path_addons: @@ -873,7 +890,8 @@ class PREFERENCES_OT_app_template_install(Operator): filepath = self.filepath path_app_templates = bpy.utils.user_resource( - 'SCRIPTS', os.path.join("startup", "bl_app_templates_user"), + 'SCRIPTS', + path=os.path.join("startup", "bl_app_templates_user"), create=True, ) @@ -979,7 +997,7 @@ class PREFERENCES_OT_studiolight_install(Operator): prefs = context.preferences path_studiolights = os.path.join("studiolights", self.type.lower()) - path_studiolights = bpy.utils.user_resource('DATAFILES', path_studiolights, create=True) + path_studiolights = bpy.utils.user_resource('DATAFILES', path=path_studiolights, create=True) if not path_studiolights: self.report({'ERROR'}, "Failed to create Studio Light path") return {'CANCELLED'} @@ -1025,7 +1043,11 @@ class PREFERENCES_OT_studiolight_new(Operator): wm = context.window_manager filename = bpy.path.ensure_ext(self.filename, ".sl") - path_studiolights = bpy.utils.user_resource('DATAFILES', os.path.join("studiolights", "studio"), create=True) + path_studiolights = bpy.utils.user_resource( + 'DATAFILES', + path=os.path.join("studiolights", "studio"), + create=True, + ) if not path_studiolights: self.report({'ERROR'}, "Failed to get Studio Light path") return {'CANCELLED'} diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py index 1b801f77e07..90131109e24 100644 --- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py +++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py @@ -223,7 +223,7 @@ def extend(obj, EXTEND_MODE): for f_triple in walk_face(f_act): apply_uv(*f_triple) - bmesh.update_edit_mesh(me, False) + bmesh.update_edit_mesh(me, loop_triangles=False) return STATUS_OK diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 29c17711c2a..6ba8750e9df 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -628,7 +628,7 @@ class LightMapPack(Operator): name="New Image", description=( "Assign new images for every mesh (only one if " - "shared tex space enabled)" + "Share Texture Space is enabled)" ), default=False, ) diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py index ff5bcdb034f..0fa82e36d20 100644 --- a/release/scripts/startup/bl_operators/view3d.py +++ b/release/scripts/startup/bl_operators/view3d.py @@ -208,7 +208,8 @@ class VIEW3D_OT_transform_gizmo_set(Operator): @classmethod def poll(cls, context): - return context.area.type == 'VIEW_3D' + area = context.area + return area and (area.type == 'VIEW_3D') def execute(self, context): space_data = context.space_data diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 2f97942faa4..2cc7b828c11 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -95,6 +95,76 @@ def context_path_validate(context, data_path): return value +def context_path_to_rna_property(context, data_path): + from bl_rna_utils.data_path import property_definition_from_data_path + rna_prop = property_definition_from_data_path(context, "." + data_path) + if rna_prop is not None: + return rna_prop + return None + + +def context_path_decompose(data_path): + # Decompose a data_path into 3 components: + # base_path, prop_attr, prop_item, where: + # `"foo.bar["baz"].fiz().bob.buz[10][2]"`, returns... + # `("foo.bar["baz"].fiz().bob", "buz", "[10][2]")` + # + # This is useful as we often want the base and the property, ignoring any item access. + # Note that item access includes function calls since these aren't properties. + # + # Note that the `.` is removed from the start of the first and second values, + # this is done because `.attr` isn't convenient to use as an argument, + # also the convention is not to include this within the data paths or the operator logic for `bpy.ops.wm.*`. + from bl_rna_utils.data_path import decompose_data_path + path_split = decompose_data_path("." + data_path) + + # Find the last property that isn't a function call. + value_prev = "" + i = len(path_split) + while (i := i - 1) >= 0: + value = path_split[i] + if value.startswith("."): + if not value_prev.startswith("("): + break + value_prev = value + + if i != -1: + base_path = "".join(path_split[:i]) + prop_attr = path_split[i] + prop_item = "".join(path_split[i + 1:]) + + if base_path: + assert(base_path.startswith(".")) + base_path= base_path[1:] + if prop_attr: + assert(prop_attr.startswith(".")) + prop_attr = prop_attr[1:] + else: + # If there are no properties, everything is an item. + # Note that should not happen in practice with values which are added onto `context`, + # include since it's correct to account for this case and not doing so will create a confusing exception. + base_path = "" + prop_attr = "" + prop_item = "".join(path_split) + + return (base_path, prop_attr, prop_item) + + +def description_from_data_path(base, data_path, *, prefix, value=Ellipsis): + if context_path_validate(base, data_path) is Ellipsis: + return None + + if ( + (rna_prop := context_path_to_rna_property(base, data_path)) and + (description := rna_prop.description) + ): + description = "%s: %s" % (prefix, description) + if value != Ellipsis: + description = "%s\n%s: %s" % (description, iface_("Value"), str(value)) + return description + return None + + def operator_value_is_undo(value): if value in {None, Ellipsis}: return False @@ -120,12 +190,9 @@ def operator_value_is_undo(value): def operator_path_is_undo(context, data_path): - # note that if we have data paths that use strings this could fail - # luckily we don't do this! - # - # When we can't find the data owner assume no undo is needed. - data_path_head = data_path.rpartition(".")[0] + data_path_head, _, _ = context_path_decompose(data_path) + # When we can't find the data owner assume no undo is needed. if not data_path_head: return False @@ -168,6 +235,10 @@ class WM_OT_context_set_boolean(Operator): default=True, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + execute = execute_context_assign @@ -185,6 +256,10 @@ class WM_OT_context_set_int(Operator): # same as enum ) relative: rna_relative_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value) + execute = execute_context_assign @@ -201,6 +276,10 @@ class WM_OT_context_scale_float(Operator): default=1.0, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value) + def execute(self, context): data_path = self.data_path if context_path_validate(context, data_path) is Ellipsis: @@ -235,6 +314,10 @@ class WM_OT_context_scale_int(Operator): options={'SKIP_SAVE'}, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value) + def execute(self, context): data_path = self.data_path if context_path_validate(context, data_path) is Ellipsis: @@ -274,6 +357,10 @@ class WM_OT_context_set_float(Operator): # same as enum ) relative: rna_relative_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value) + execute = execute_context_assign @@ -290,6 +377,10 @@ class WM_OT_context_set_string(Operator): # same as enum maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + execute = execute_context_assign @@ -306,6 +397,10 @@ class WM_OT_context_set_enum(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + execute = execute_context_assign @@ -322,6 +417,10 @@ class WM_OT_context_set_value(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + def execute(self, context): data_path = self.data_path if context_path_validate(context, data_path) is Ellipsis: @@ -339,6 +438,13 @@ class WM_OT_context_toggle(Operator): data_path: rna_path_prop module: rna_module_prop + @classmethod + def description(cls, context, props): + # Currently unsupported, it might be possible to extract this. + if props.module: + return None + return description_from_data_path(context, props.data_path, prefix=iface_("Toggle")) + def execute(self, context): data_path = self.data_path @@ -375,6 +481,11 @@ class WM_OT_context_toggle_enum(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + value = "(%r, %r)" % (props.value_1, props.value_2) + return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"), value=value) + def execute(self, context): data_path = self.data_path @@ -406,6 +517,10 @@ class WM_OT_context_cycle_int(Operator): reverse: rna_reverse_prop wrap: rna_wrap_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Cycle")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -442,6 +557,10 @@ class WM_OT_context_cycle_enum(Operator): reverse: rna_reverse_prop wrap: rna_wrap_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Cycle")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -450,22 +569,11 @@ class WM_OT_context_cycle_enum(Operator): orig_value = value - # Have to get rna enum values - rna_struct_str, rna_prop_str = data_path.rsplit('.', 1) - i = rna_prop_str.find('[') - - # just in case we get "context.foo.bar[0]" - if i != -1: - rna_prop_str = rna_prop_str[0:i] - - rna_struct = eval("context.%s.rna_type" % rna_struct_str) - - rna_prop = rna_struct.properties[rna_prop_str] - + rna_prop = context_path_to_rna_property(context, data_path) if type(rna_prop) != bpy.types.EnumProperty: raise Exception("expected an enum property") - enums = rna_struct.properties[rna_prop_str].enum_items.keys() + enums = rna_prop.enum_items.keys() orig_index = enums.index(orig_value) # Have the info we need, advance to the next item. @@ -498,6 +606,10 @@ class WM_OT_context_cycle_array(Operator): data_path: rna_path_prop reverse: rna_reverse_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Cycle")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -523,6 +635,10 @@ class WM_OT_context_menu_enum(Operator): data_path: rna_path_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Menu")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -530,15 +646,15 @@ class WM_OT_context_menu_enum(Operator): if value is Ellipsis: return {'PASS_THROUGH'} - base_path, prop_string = data_path.rsplit(".", 1) + base_path, prop_attr, _ = context_path_decompose(data_path) value_base = context_path_validate(context, base_path) - prop = value_base.bl_rna.properties[prop_string] + rna_prop = context_path_to_rna_property(context, data_path) def draw_cb(self, context): layout = self.layout - layout.prop(value_base, prop_string, expand=True) + layout.prop(value_base, prop_attr, expand=True) - context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon) + context.window_manager.popup_menu(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon) return {'FINISHED'} @@ -550,6 +666,10 @@ class WM_OT_context_pie_enum(Operator): data_path: rna_path_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu")) + def invoke(self, context, event): wm = context.window_manager data_path = self.data_path @@ -558,15 +678,15 @@ class WM_OT_context_pie_enum(Operator): if value is Ellipsis: return {'PASS_THROUGH'} - base_path, prop_string = data_path.rsplit(".", 1) + base_path, prop_attr, _ = context_path_decompose(data_path) value_base = context_path_validate(context, base_path) - prop = value_base.bl_rna.properties[prop_string] + rna_prop = context_path_to_rna_property(context, data_path) def draw_cb(self, context): layout = self.layout - layout.prop(value_base, prop_string, expand=True) + layout.prop(value_base, prop_attr, expand=True) - wm.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event) + wm.popup_menu_pie(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon, event=event) return {'FINISHED'} @@ -587,11 +707,15 @@ class WM_OT_operator_pie_enum(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu")) + def invoke(self, context, event): wm = context.window_manager data_path = self.data_path - prop_string = self.prop_string + prop_attr = self.prop_string # same as eval("bpy.ops." + data_path) op_mod_str, ob_id_str = data_path.split(".", 1) @@ -607,7 +731,7 @@ class WM_OT_operator_pie_enum(Operator): def draw_cb(self, context): layout = self.layout pie = layout.menu_pie() - pie.operator_enum(data_path, prop_string) + pie.operator_enum(data_path, prop_attr) wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event) @@ -631,17 +755,17 @@ class WM_OT_context_set_id(Operator): value = self.value data_path = self.data_path - # match the pointer type from the target property to bpy.data.* + # Match the pointer type from the target property to `bpy.data.*` # so we lookup the correct list. - data_path_base, data_path_prop = data_path.rsplit(".", 1) - data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop] - data_prop_rna_type = data_prop_rna.fixed_type + + rna_prop = context_path_to_rna_property(context, data_path) + rna_prop_fixed_type = rna_prop.fixed_type id_iter = None for prop in bpy.data.rna_type.properties: if prop.rna_type.identifier == "CollectionProperty": - if prop.fixed_type == data_prop_rna_type: + if prop.fixed_type == rna_prop_fixed_type: id_iter = prop.identifier break @@ -976,7 +1100,7 @@ class WM_OT_path_open(Operator): return {'FINISHED'} -def _wm_doc_get_id(doc_id, do_url=True, url_prefix="", report=None): +def _wm_doc_get_id(doc_id, *, do_url=True, url_prefix="", report=None): def operator_exists_pair(a, b): # Not fast, this is only for docs. @@ -1066,7 +1190,7 @@ class WM_OT_doc_view_manual(Operator): doc_id: doc_id @staticmethod - def _find_reference(rna_id, url_mapping, verbose=True): + def _find_reference(rna_id, url_mapping, *, verbose=True): if verbose: print("online manual check for: '%s'... " % rna_id) from fnmatch import fnmatchcase @@ -1402,7 +1526,7 @@ class WM_OT_properties_edit(Operator): self.default = "" # setup defaults - prop_ui = rna_idprop_ui_prop_get(item, prop, False) # don't create + prop_ui = rna_idprop_ui_prop_get(item, prop, create=False) if prop_ui: self.min = prop_ui.get("min", -1000000000) self.max = prop_ui.get("max", 1000000000) @@ -1786,7 +1910,7 @@ class WM_OT_toolbar(Operator): return context.space_data is not None @staticmethod - def keymap_from_toolbar(context, space_type, use_fallback_keys=True, use_reset=True): + def keymap_from_toolbar(context, space_type, *, use_fallback_keys=True, use_reset=True): from bl_ui.space_toolsystem_common import ToolSelectPanelHelper from bl_keymap_utils import keymap_from_toolbar @@ -2087,7 +2211,7 @@ class WM_OT_batch_rename(Operator): actions: CollectionProperty(type=BatchRenameAction) @staticmethod - def _data_from_context(context, data_type, only_selected, check_context=False): + def _data_from_context(context, data_type, only_selected, *, check_context=False): mode = context.mode scene = context.scene diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py index 186314f9591..c5c35121135 100644 --- a/release/scripts/startup/bl_ui/properties_collection.py +++ b/release/scripts/startup/bl_ui/properties_collection.py @@ -77,6 +77,7 @@ class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel): class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel): bl_label = "Line Art" + bl_order = 10 def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index e835e577953..a88def34767 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -245,6 +245,7 @@ class ConstraintButtonsPanel: sub.prop(con, "max_z", text="Max") row.label(icon='BLANK1') + layout.prop(con, "euler_order", text="Order") layout.prop(con, "use_transform_limit") self.space_template(layout, con, target=False, owner=True) diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 4cdcab45926..87572fcd438 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -86,12 +86,19 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel): col = layout.column(heading="Show") col.prop(arm, "show_names", text="Names") - col.prop(arm, "show_axes", text="Axes") col.prop(arm, "show_bone_custom_shapes", text="Shapes") col.prop(arm, "show_group_colors", text="Group Colors") + if ob: col.prop(ob, "show_in_front", text="In Front") + col = layout.column(align=False, heading="Axes") + row = col.row(align=True) + row.prop(arm, "show_axes", text="") + sub = row.row(align=True) + sub.active = arm.show_axes + sub.prop(arm, "axes_position", text="Position") + class DATA_MT_bone_group_context_menu(Menu): bl_label = "Bone Group Specials" diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index f3e116ca321..6452ad8465b 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -292,10 +292,15 @@ class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel): sub = col.column() sub.active = bool(pchan and pchan.custom_shape) sub.separator() - sub.prop(pchan, "custom_shape_scale", text="Scale") + + sub.prop(pchan, "custom_shape_scale_xyz", text="Scale") + sub.prop(pchan, "custom_shape_translation", text="Translation") + sub.prop(pchan, "custom_shape_rotation_euler", text="Rotation") + sub.prop_search(pchan, "custom_shape_transform", ob.pose, "bones", text="Override Transform") sub.prop(pchan, "use_custom_shape_bone_size") + sub.separator() sub.prop(bone, "show_wire", text="Wireframe") diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index 4bd2d66e257..85f672cd50f 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -267,6 +267,7 @@ class DATA_PT_pathanim(CurveButtonsPanelCurve, Panel): # these are for paths only col.separator() + col.prop(curve, "use_path_clamp") col.prop(curve, "use_path_follow") diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py index 69720a6c54b..e71ea2f31a4 100644 --- a/release/scripts/startup/bl_ui/properties_data_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py @@ -113,7 +113,8 @@ class GPENCIL_MT_layer_context_menu(Menu): layout.operator("gpencil.layer_merge", icon='SORT_ASC', text="Merge Down") layout.separator() - layout.menu("VIEW3D_MT_gpencil_copy_layer") + layout.operator("gpencil.layer_duplicate_object", text="Copy Layer to Selected").only_active=True + layout.operator("gpencil.layer_duplicate_object", text="Copy All Layers to Selected").only_active=False class DATA_PT_gpencil_layers(DataButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_data_pointcloud.py b/release/scripts/startup/bl_ui/properties_data_pointcloud.py index f0584c81c0c..3b7e7a42fd4 100644 --- a/release/scripts/startup/bl_ui/properties_data_pointcloud.py +++ b/release/scripts/startup/bl_ui/properties_data_pointcloud.py @@ -71,10 +71,10 @@ class POINTCLOUD_MT_add_attribute(Menu): layout = self.layout pointcloud = context.pointcloud - self.add_standard_attribute(layout, pointcloud, 'Radius', 'FLOAT', 'POINT') - self.add_standard_attribute(layout, pointcloud, 'Color', 'FLOAT_COLOR', 'POINT') - self.add_standard_attribute(layout, pointcloud, 'Particle ID', 'INT', 'POINT') - self.add_standard_attribute(layout, pointcloud, 'Velocity', 'FLOAT_VECTOR', 'POINT') + self.add_standard_attribute(layout, pointcloud, 'radius', 'FLOAT', 'POINT') + self.add_standard_attribute(layout, pointcloud, 'color', 'FLOAT_COLOR', 'POINT') + self.add_standard_attribute(layout, pointcloud, 'id', 'INT', 'POINT') + self.add_standard_attribute(layout, pointcloud, 'velocity', 'FLOAT_VECTOR', 'POINT') layout.separator() 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 c23cc838e51..0da0716e850 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -147,8 +147,7 @@ class GreasePencilDisplayPanel: if self.is_popover: row = layout.row(align=True) - row.prop(settings, "show_brush", text="") - row.label(text="Display Cursor") + row.prop(settings, "show_brush", text="Display Cursor") col = layout.column(align=True) col.active = settings.show_brush @@ -180,21 +179,6 @@ class GreasePencilBrushFalloff: bl_label = "Falloff" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - ts = context.tool_settings - settings = None - if context.mode == 'PAINT_GPENCIL': - settings = ts.gpencil_paint - if context.mode == 'SCULPT_GPENCIL': - settings = ts.gpencil_sculpt_paint - elif context.mode == 'WEIGHT_GPENCIL': - settings = ts.gpencil_weight_paint - elif context.mode == 'VERTEX_GPENCIL': - settings = ts.gpencil_vertex_paint - - return (settings and settings.brush and settings.brush.curve) - def draw(self, context): layout = self.layout ts = context.tool_settings @@ -408,7 +392,7 @@ class AnnotationDataPanel: bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): - if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}: + if context.space_data.type not in {'VIEW_3D', 'TOPBAR', 'SEQUENCE_EDITOR'}: self.layout.prop(context.space_data, "show_annotation", text="") def draw(self, context): @@ -822,6 +806,12 @@ class GreasePencilLayerMasksPanel: col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="") col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="") + col2.separator() + + sub = col2.column(align=True) + sub.operator("gpencil.layer_mask_move", icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.layer_mask_move", icon='TRIA_DOWN', text="").type = 'DOWN' + class GreasePencilLayerRelationsPanel: @@ -852,6 +842,10 @@ class GreasePencilLayerRelationsPanel: col = layout.row(align=True) col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer") + col = layout.row(align=True) + # Only enable this property when a view layer is selected. + col.enabled = bool(gpl.viewlayer_render) + col.prop(gpl, "use_viewlayer_masks") class GreasePencilLayerDisplayPanel: @@ -882,37 +876,43 @@ class GreasePencilFlipTintColors(Operator): bl_idname = "gpencil.tint_flip" bl_description = "Switch tint colors" - def execute(self, context): - try: - ts = context.tool_settings - settings = None - if context.mode == 'PAINT_GPENCIL': - settings = ts.gpencil_paint - if context.mode == 'SCULPT_GPENCIL': - settings = ts.gpencil_sculpt_paint - elif context.mode == 'WEIGHT_GPENCIL': - settings = ts.gpencil_weight_paint - elif context.mode == 'VERTEX_GPENCIL': - settings = ts.gpencil_vertex_paint - - brush = settings.brush - if brush is not None: - color = brush.color - secondary_color = brush.secondary_color + @classmethod + def poll(cls, context): + ts = context.tool_settings + settings = None + if context.mode == 'PAINT_GPENCIL': + settings = ts.gpencil_paint + if context.mode == 'SCULPT_GPENCIL': + settings = ts.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = ts.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = ts.gpencil_vertex_paint - orig_prim = color.hsv - orig_sec = secondary_color.hsv + return settings and settings.brush - color.hsv = orig_sec - secondary_color.hsv = orig_prim + def execute(self, context): + ts = context.tool_settings + settings = None + if context.mode == 'PAINT_GPENCIL': + settings = ts.gpencil_paint + if context.mode == 'SCULPT_GPENCIL': + settings = ts.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = ts.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = ts.gpencil_vertex_paint - return {'FINISHED'} + brush = settings.brush + color = brush.color + secondary_color = brush.secondary_color - except Exception as e: - utils_core.error_handlers(self, "gpencil.tint_flip", e, - "Flip Colors could not be completed") + orig_prim = color.hsv + orig_sec = secondary_color.hsv - return {'CANCELLED'} + color.hsv = orig_sec + secondary_color.hsv = orig_prim + return {'FINISHED'} classes = ( diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index d85078d4ec2..217ec248764 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -277,6 +277,7 @@ class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel): class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel): bl_label = "Line Art" bl_options = {'DEFAULT_CLOSED'} + bl_order = 10 @classmethod def poll(cls, context): @@ -285,19 +286,17 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True mat = context.material lineart = mat.lineart layout.prop(lineart, "use_transparency") - if lineart.use_transparency: - - layout.label(text="Transparency Masks:") - - row = layout.row(align=True) - for i in range(8): - row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True) + row = layout.row(align=True, heading="Masks") + row.active = lineart.use_transparency + for i in range(8): + row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True) classes = ( diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py index 6a5c000116f..9d099ff2231 100644 --- a/release/scripts/startup/bl_ui/properties_material_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py @@ -46,13 +46,19 @@ class GPENCIL_MT_material_context_menu(Menu): layout.separator() - layout.operator("object.material_slot_remove_unused") - layout.operator("gpencil.stroke_merge_material", text="Merge Similar") - - layout.separator() layout.operator("gpencil.material_to_vertex_color", text="Convert Materials to Vertex Color") layout.operator("gpencil.extract_palette_vertex", text="Extract Palette from Vertex Color") + layout.separator() + + layout.operator("gpencil.materials_copy_to_object", text="Copy Material to Selected").only_active = True + layout.operator("gpencil.materials_copy_to_object", text="Copy All Materials to Selected").only_active = False + + layout.separator() + + layout.operator("gpencil.stroke_merge_material", text="Merge Similar") + layout.operator("object.material_slot_remove_unused") + class GPENCIL_UL_matslots(UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index b74100aa570..4ea1ec26738 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -311,6 +311,7 @@ class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel): class OBJECT_PT_lineart(ObjectButtonsPanel, Panel): bl_label = "Line Art" bl_options = {'DEFAULT_CLOSED'} + bl_order = 10 @classmethod def poll(cls, context): @@ -328,7 +329,9 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel): row = layout.row(heading="Override Crease") row.prop(lineart, "use_crease_override", text="") - row.prop(lineart, "crease_threshold", slider=True, text="") + subrow = row.row() + subrow.active = lineart.use_crease_override + subrow.prop(lineart, "crease_threshold", slider=True, text="") class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 6989139d447..4bfd2fd32b0 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -356,7 +356,7 @@ class StrokePanel(BrushPanel): col.operator("paintcurve.draw") col.separator() - if brush.use_space: + if brush.use_space or brush.use_line or brush.use_curve: col.separator() row = col.row(align=True) col.prop(brush, "dash_ratio", text="Dash Ratio") @@ -1225,7 +1225,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row = layout.row(align=True) row.prop(gp_settings, "fill_factor") row = layout.row(align=True) - row.prop(gp_settings, "fill_leak", text="Leak Size") + row.prop(gp_settings, "dilate") row = layout.row(align=True) row.prop(brush, "size", text="Thickness") layout.use_property_split = use_property_split_prev @@ -1235,7 +1235,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(brush, "size", text="Radius") row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') - if gp_settings.use_pressure and context.area.type == 'PROPERTIES': + if gp_settings.use_pressure and not compact: col = layout.column() col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, use_negative_slope=True) @@ -1244,7 +1244,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(gp_settings, "pen_strength", slider=True) row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') - if gp_settings.use_strength_pressure and context.area.type == 'PROPERTIES': + if gp_settings.use_strength_pressure and not compact: col = layout.column() col.template_curve_mapping(gp_settings, "curve_strength", brush=True, use_negative_slope=True) diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index a9f040db9b5..ae15fda2c69 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -1803,9 +1803,10 @@ class PARTICLE_PT_force_fields_type1(ParticleButtonsPanel, Panel): part = particle_get_settings(context) - col = layout.column() - col.prop(part.force_field_1, "type", text="Type 1") - basic_force_field_settings_ui(self, part.force_field_1) + if part.force_field_1: + col = layout.column() + col.prop(part.force_field_1, "type", text="Type 1") + basic_force_field_settings_ui(self, part.force_field_1) class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel): @@ -1819,9 +1820,10 @@ class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel): part = particle_get_settings(context) - col = layout.column() - col.prop(part.force_field_2, "type", text="Type 2") - basic_force_field_settings_ui(self, part.force_field_2) + if part.force_field_2: + col = layout.column() + col.prop(part.force_field_2, "type", text="Type 2") + basic_force_field_settings_ui(self, part.force_field_2) class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel): @@ -1836,7 +1838,8 @@ class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel): part = particle_get_settings(context) - basic_force_field_falloff_ui(self, part.force_field_1) + if part.force_field_1: + basic_force_field_falloff_ui(self, part.force_field_1) class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel): @@ -1851,7 +1854,8 @@ class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel): part = particle_get_settings(context) - basic_force_field_falloff_ui(self, part.force_field_2) + if part.force_field_2: + basic_force_field_falloff_ui(self, part.force_field_2) class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index 82f43790d72..f13a808e324 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -38,13 +38,12 @@ class PhysicButtonsPanel: def physics_add(layout, md, name, type, typeicon, toggles): row = layout.row(align=True) if md: - row.context_pointer_set("modifier", md) row.operator( "object.modifier_remove", text=name, text_ctxt=i18n_contexts.default, icon='X', - ) + ).modifier = md.name if toggles: row.prop(md, "show_viewport", text="") row.prop(md, "show_render", text="") @@ -74,17 +73,13 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel): def draw(self, context): layout = self.layout - row = layout.row(align=True) - row.alignment = 'LEFT' - row.label(text="Enable physics for:") - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) obj = context.object col = flow.column() - if obj.field.type == 'NONE': + if not obj.field or obj.field.type == 'NONE': col.operator("object.forcefield_toggle", text="Force Field", icon='FORCE_FORCE') else: col.operator("object.forcefield_toggle", text="Force Field", icon='X') diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index b236b2ee7cf..1ad88744b16 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -44,6 +44,10 @@ class FILEBROWSER_HT_header(Header): layout.separator_spacer() + layout.prop(params, "import_type", text="") + + layout.separator_spacer() + # Uses prop_with_popover() as popover() only adds the triangle icon in headers. layout.prop_with_popover( params, diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index e7589709130..11eaf55a98b 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -246,11 +246,12 @@ class IMAGE_MT_image(Menu): layout.separator() layout.operator("image.pack", text="Pack") - if ima: + if ima and context.area.ui_type == 'IMAGE_EDITOR': layout.separator() layout.operator("palette.extract_from_image", text="Extract Palette") layout.operator("gpencil.image_to_grease_pencil", text="Generate Grease Pencil") + class IMAGE_MT_image_flip(Menu): bl_label = "Flip" diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index cd65980fc0d..3a97b104271 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -92,16 +92,15 @@ class INFO_MT_area(Menu): layout.separator() - layout.operator("screen.area_dupli", icon='WINDOW') - - layout.separator() - layout.operator("screen.screen_full_area") layout.operator( "screen.screen_full_area", - text="Toggle Fullscreen Area", - icon='FULLSCREEN_ENTER', - ).use_hide_panels = True + text="Toggle Fullscreen Area").use_hide_panels = True + layout.operator("screen.area_dupli") + + layout.separator() + + layout.operator("screen.area_close") class INFO_MT_context_menu(Menu): diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 11685b3e7e0..c668d3908b3 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -18,7 +18,6 @@ # <pep8 compliant> import bpy -import nodeitems_utils from bpy.types import Header, Menu, Panel from bpy.app.translations import pgettext_iface as iface_ from bpy.app.translations import contexts as i18n_contexts @@ -225,6 +224,8 @@ class NODE_MT_add(bpy.types.Menu): bl_translation_context = i18n_contexts.operator_default def draw(self, context): + import nodeitems_utils + layout = self.layout layout.operator_context = 'INVOKE_DEFAULT' @@ -644,8 +645,12 @@ class NODE_PT_quality(bpy.types.Panel): snode = context.space_data tree = snode.node_tree + prefs = bpy.context.preferences col = layout.column() + if prefs.experimental.use_full_frame_compositor: + col.prop(tree, "execution_mode") + col.prop(tree, "render_quality", text="Render") col.prop(tree, "edit_quality", text="Edit") col.prop(tree, "chunk_size") diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 5c5a78f3942..6eafa570f4c 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -51,13 +51,13 @@ class OUTLINER_HT_header(Header): row.prop(space, "use_sync_select", icon='UV_SYNC_SELECT', text="") row = layout.row(align=True) - if display_mode in {'SCENES', 'VIEW_LAYER'}: + if display_mode in {'SCENES', 'VIEW_LAYER', 'LIBRARY_OVERRIDES'}: row.popover( panel="OUTLINER_PT_filter", text="", icon='FILTER', ) - elif display_mode in {'LIBRARIES', 'ORPHAN_DATA'}: + if display_mode in {'LIBRARIES', 'LIBRARY_OVERRIDES', 'ORPHAN_DATA'}: row.prop(space, "use_filter_id_type", text="", icon='FILTER') sub = row.row(align=True) sub.active = space.use_filter_id_type @@ -162,13 +162,13 @@ class OUTLINER_MT_collection_view_layer(Menu): layout.operator("outliner.collection_exclude_set") layout.operator("outliner.collection_exclude_clear") + layout.operator("outliner.collection_holdout_set") + layout.operator("outliner.collection_holdout_clear") + if context.engine == 'CYCLES': layout.operator("outliner.collection_indirect_only_set") layout.operator("outliner.collection_indirect_only_clear") - layout.operator("outliner.collection_holdout_set") - layout.operator("outliner.collection_holdout_clear") - class OUTLINER_MT_collection_visibility(Menu): bl_label = "Visibility" @@ -341,19 +341,26 @@ class OUTLINER_PT_filter(Panel): col = layout.column(align=True) col.prop(space, "use_sort_alpha") - row = layout.row(align=True) - row.prop(space, "use_sync_select", text="Sync Selection") + if display_mode not in {'LIBRARY_OVERRIDES'}: + row = layout.row(align=True) + row.prop(space, "use_sync_select", text="Sync Selection") - row = layout.row(align=True) - row.prop(space, "show_mode_column", text="Show Mode Column") - layout.separator() + row = layout.row(align=True) + row.prop(space, "show_mode_column", text="Show Mode Column") + layout.separator() col = layout.column(align=True) col.label(text="Search") col.prop(space, "use_filter_complete", text="Exact Match") col.prop(space, "use_filter_case_sensitive", text="Case Sensitive") - if display_mode != 'VIEW_LAYER': + if display_mode in {'LIBRARY_OVERRIDES'} and bpy.data.libraries: + col.separator() + row = col.row() + row.label(icon='LIBRARY_DATA_OVERRIDE') + row.prop(space, "use_filter_lib_override_system", text="System Overrides") + + if display_mode not in {'VIEW_LAYER'}: return layout.separator() @@ -405,10 +412,6 @@ class OUTLINER_PT_filter(Panel): row = sub.row() row.label(icon='EMPTY_DATA') row.prop(space, "use_filter_object_empty", text="Empties") - row = sub.row() - if bpy.data.libraries: - row.label(icon='LIBRARY_DATA_OVERRIDE') - row.prop(space, "use_filter_lib_override", text="Library Overrides") if ( bpy.data.curves or @@ -425,6 +428,16 @@ class OUTLINER_PT_filter(Panel): row.label(icon='BLANK1') row.prop(space, "use_filter_object_others", text="Others") + if bpy.data.libraries: + col.separator() + row = col.row() + row.label(icon='LIBRARY_DATA_OVERRIDE') + row.prop(space, "use_filter_lib_override", text="Library Overrides") + row = col.row() + row.label(icon='LIBRARY_DATA_OVERRIDE') + row.prop(space, "use_filter_lib_override_system", text="System Overrides") + + classes = ( OUTLINER_HT_header, diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index f6a03b4769c..07d9b0ee1f8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1381,7 +1381,6 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): col = layout.column() col.prop(strip, "filepath", text="") col.prop(strip.colorspace_settings, "name", text="Color Space") - col.prop(strip, "mpeg_preseek") col.prop(strip, "stream_index") col.prop(strip, "use_deinterlace") @@ -1398,8 +1397,8 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): box.template_image_stereo_3d(strip.stereo_3d_format) # Resolution. - col = layout.column(align=True) - col = col.box() + col = layout.box() + col = col.column(align=True) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' split.label(text="Resolution") @@ -1409,6 +1408,14 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): split.label(text="%dx%d" % size, translate=False) else: split.label(text="None") + #FPS + if elem.orig_fps: + split = col.split(factor=0.5, align=False) + split.alignment = 'RIGHT' + split.label(text="FPS") + split.alignment = 'LEFT' + split.label(text="%.2f" % elem.orig_fps, translate=False) + class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): @@ -1453,7 +1460,7 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): if strip.scene_input == 'CAMERA': layout = layout.column(heading="Show") - layout.prop(strip, "use_grease_pencil", text="Grease Pencil") + layout.prop(strip, "use_annotations", text="Annotations") if scene: # Warning, this is not a good convention to follow. # Expose here because setting the alpha from the 'Render' menu is very inconvenient. @@ -1938,7 +1945,7 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): layout.prop(proxy, "use_overwrite") col = layout.column() - col.prop(proxy, "quality", text="Build JPEG Quality") + col.prop(proxy, "quality", text="Quality") if strip.type == 'MOVIE': col = layout.column() diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py index 188eddbcce3..13e435a7350 100644 --- a/release/scripts/startup/bl_ui/space_spreadsheet.py +++ b/release/scripts/startup/bl_ui/space_spreadsheet.py @@ -28,8 +28,17 @@ class SPREADSHEET_HT_header(bpy.types.Header): layout.template_header() - pinned_id = space.pinned_id - used_id = pinned_id if pinned_id else context.active_object + if len(space.context_path) == 0: + self.draw_without_context_path(layout) + return + root_context = space.context_path[0] + if root_context.type != 'OBJECT': + self.draw_without_context_path(layout) + return + obj = root_context.object + if obj is None: + self.draw_without_context_path(layout) + return layout.prop(space, "object_eval_state", text="") if space.object_eval_state != 'ORIGINAL': @@ -37,16 +46,61 @@ class SPREADSHEET_HT_header(bpy.types.Header): if space.geometry_component_type != 'INSTANCES': layout.prop(space, "attribute_domain", text="") - if used_id: - layout.label(text=used_id.name, icon='OBJECT_DATA') + context_path = space.context_path + if space.object_eval_state == 'ORIGINAL': + # Only show first context. + context_path = context_path[:1] + if space.display_context_path_collapsed: + self.draw_collapsed_context_path(context, layout, context_path) + else: + self.draw_full_context_path(context, layout, context_path) - layout.operator("spreadsheet.toggle_pin", text="", icon='PINNED' if pinned_id else 'UNPINNED', emboss=False) + pin_icon = 'PINNED' if space.is_pinned else 'UNPINNED' + layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False) layout.separator_spacer() - if isinstance(used_id, bpy.types.Object) and used_id.mode == 'EDIT': + if isinstance(obj, bpy.types.Object) and obj.mode == 'EDIT': layout.prop(space, "show_only_selected", text="Selected Only") + def draw_without_context_path(self, layout): + layout.label(text="No active context") + + def draw_full_context_path(self, context, layout, context_path): + space = context.space_data + row = layout.row() + for ctx in context_path[:-1]: + subrow = row.row(align=True) + self.draw_spreadsheet_context(subrow, ctx) + self.draw_spreadsheet_context_path_icon(subrow, space) + + self.draw_spreadsheet_context(row, context_path[-1]) + + def draw_collapsed_context_path(self, context, layout, context_path): + space = context.space_data + row = layout.row(align=True) + self.draw_spreadsheet_context(row, context_path[0]) + if len(context_path) == 1: + return + self.draw_spreadsheet_context_path_icon(row, space) + if len(context_path) > 2: + self.draw_spreadsheet_context_path_icon(row, space, icon='DOT') + self.draw_spreadsheet_context_path_icon(row, space) + self.draw_spreadsheet_context(row, context_path[-1]) + + def draw_spreadsheet_context(self, layout, ctx): + if ctx.type == 'OBJECT': + if ctx.object is None: + layout.label(text="<no object>", icon='OBJECT_DATA') + else: + layout.label(text=ctx.object.name, icon='OBJECT_DATA') + elif ctx.type == 'MODIFIER': + layout.label(text=ctx.modifier_name, icon='MODIFIER') + elif ctx.type == 'NODE': + layout.label(text=ctx.node_name, icon='NODE') + + def draw_spreadsheet_context_path_icon(self, layout, space, icon='RIGHTARROW_THIN'): + layout.prop(space, "display_context_path_collapsed", icon_only=True, emboss=False, icon=icon) classes = ( SPREADSHEET_HT_header, diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index c937882bd6e..93ab12e8462 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -284,7 +284,7 @@ class TEXT_MT_templates_py(Menu): def draw(self, _context): self.path_menu( - bpy.utils.script_paths("templates_py"), + bpy.utils.script_paths(subdir="templates_py"), "text.open", props_default={"internal": True}, filter_ext=lambda ext: (ext.lower() == ".py") @@ -296,7 +296,7 @@ class TEXT_MT_templates_osl(Menu): def draw(self, _context): self.path_menu( - bpy.utils.script_paths("templates_osl"), + bpy.utils.script_paths(subdir="templates_osl"), "text.open", props_default={"internal": True}, filter_ext=lambda ext: (ext.lower() == ".osl") diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 774e2938deb..30967e9746d 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -300,6 +300,8 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel): col.label(text="New Keyframe Type") col.prop(tool_settings, "keyframe_type", text="") + layout.prop(tool_settings, "use_keyframe_cycle_aware") + class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel): bl_label = "Auto Keyframing" @@ -327,8 +329,6 @@ class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel): if not prefs.edit.use_keyframe_insert_available: col.prop(tool_settings, "use_record_with_nla", text="Layered Recording") - col.prop(tool_settings, "use_keyframe_cycle_aware") - ################################### diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 12ec863327c..cde430c1e6f 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -218,7 +218,7 @@ class ToolSelectPanelHelper: assert(type(icon_name) is str) icon_value = _icon_cache.get(icon_name) if icon_value is None: - dirname = bpy.utils.system_resource('DATAFILES', "icons") + dirname = bpy.utils.system_resource('DATAFILES', path="icons") filename = os.path.join(dirname, icon_name + ".dat") try: icon_value = bpy.app.icons.new_triangles_from_file(filename) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 1e52142c85c..c55f637f8b2 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -164,7 +164,7 @@ class _defs_annotate: gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - if context.space_data.type == 'VIEW_3D': + if context.space_data.type in {'VIEW_3D', 'SEQUENCE_EDITOR'}: if region_type == 'TOOL_HEADER': sub = layout.split(align=True, factor=0.5) sub.ui_units_x = 6.5 @@ -206,14 +206,22 @@ class _defs_annotate: col = layout.row().column(align=True) col.prop(props, "arrowstyle_start", text="Style Start") col.prop(props, "arrowstyle_end", text="End") - elif tool.idname == "builtin.annotate" and region_type != 'TOOL_HEADER': - layout.separator() + elif tool.idname == "builtin.annotate": props = tool.operator_properties("gpencil.annotate") - layout.prop(props, "use_stabilizer", text="Stabilize Stroke") - col = layout.column(align=False) - col.active = props.use_stabilizer - col.prop(props, "stabilizer_radius", text="Radius", slider=True) - col.prop(props, "stabilizer_factor", text="Factor", slider=True) + if region_type == 'TOOL_HEADER': + row = layout.row() + row.prop(props, "use_stabilizer", text="Stabilize Stroke") + subrow = layout.row(align=False) + subrow.active = props.use_stabilizer + subrow.prop(props, "stabilizer_radius", text="Radius", slider=True) + subrow.prop(props, "stabilizer_factor", text="Factor", slider=True) + else: + layout.separator() + layout.prop(props, "use_stabilizer", text="Stabilize Stroke") + col = layout.column(align=False) + col.active = props.use_stabilizer + col.prop(props, "stabilizer_radius", text="Radius", slider=True) + col.prop(props, "stabilizer_factor", text="Factor", slider=True) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) def scribble(*, draw_settings): @@ -426,7 +434,7 @@ class _defs_view3d_select: from gpu_extras.presets import draw_circle_2d props = tool.operator_properties("view3d.select_circle") radius = props.radius - draw_circle_2d(xy, (1.0,) * 4, radius, 32) + draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) return dict( idname="builtin.select_circle", @@ -1805,7 +1813,7 @@ class _defs_image_uv_select: from gpu_extras.presets import draw_circle_2d props = tool.operator_properties("uv.select_circle") radius = props.radius - draw_circle_2d(xy, (1.0,) * 4, radius, 32) + draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) return dict( idname="builtin.select_circle", @@ -1850,7 +1858,7 @@ class _defs_image_uv_sculpt: if brush is None: return radius = brush.size - draw_circle_2d(xy, (1.0,) * 4, radius, 32) + draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) return generate_from_enum_ex( context, @@ -2142,7 +2150,7 @@ class _defs_gpencil_edit: from gpu_extras.presets import draw_circle_2d props = tool.operator_properties("gpencil.select_circle") radius = props.radius - draw_circle_2d(xy, (1.0,) * 4, radius, 32) + draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) return dict( idname="builtin.select_circle", @@ -2364,7 +2372,7 @@ class _defs_node_select: from gpu_extras.presets import draw_circle_2d props = tool.operator_properties("node.select_circle") radius = props.radius - draw_circle_2d(xy, (1.0,) * 4, radius, 32) + draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) return dict( idname="builtin.select_circle", diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index adab0b0c88a..5e68896a2a7 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -504,8 +504,6 @@ class TOPBAR_MT_file_external_data(Menu): icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT' layout.operator("file.autopack_toggle", icon=icon) - layout.separator() - pack_all = layout.row() pack_all.operator("file.pack_all") pack_all.active = not bpy.data.use_autopack @@ -516,8 +514,16 @@ class TOPBAR_MT_file_external_data(Menu): layout.separator() + layout.operator("file.pack_libraries") + layout.operator("file.unpack_libraries") + + layout.separator() + layout.operator("file.make_paths_relative") layout.operator("file.make_paths_absolute") + + layout.separator() + layout.operator("file.report_missing_files") layout.operator("file.find_missing_files") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 96695ff1be5..26ad22d3ac2 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -570,7 +570,7 @@ class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel): layout.prop(system, "audio_device", expand=False) sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False) - sub.active = system.audio_device not in {'NONE', 'Null'} + sub.active = system.audio_device not in {'NONE', 'None'} sub.prop(system, "audio_channels", text="Channels") sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer") sub.prop(system, "audio_sample_rate", text="Sample Rate") @@ -1815,7 +1815,7 @@ class USERPREF_PT_addons(AddOnPanel, Panel): addon_user_dirs = tuple( p for p in ( os.path.join(prefs.filepaths.script_directory, "addons"), - bpy.utils.user_resource('SCRIPTS', "addons"), + bpy.utils.user_resource('SCRIPTS', path="addons"), ) if p ) @@ -2241,9 +2241,9 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_sculpt_vertex_colors"}, "T71947"), - ({"property": "use_switch_object_operator"}, "T80402"), ({"property": "use_sculpt_tools_tilt"}, "T82877"), ({"property": "use_asset_browser"}, ("project/profile/124/", "Milestone 1")), + ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), ), ) @@ -2256,6 +2256,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): context, ( ({"property": "use_new_hair_type"}, "T68981"), ({"property": "use_new_point_cloud_type"}, "T75717"), + ({"property": "use_full_frame_compositor"}, "T88150"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index e68f006ccc1..fd56e86ea39 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -161,9 +161,9 @@ class VIEW3D_HT_tool_header(Header): elif mode_string in {'EDIT_MESH', 'PAINT_WEIGHT', 'SCULPT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}: # Mesh Modes, Use Mesh Symmetry row, sub = row_for_mirror() - sub.prop(context.object.data, "use_mirror_x", text="X", toggle=True) - sub.prop(context.object.data, "use_mirror_y", text="Y", toggle=True) - sub.prop(context.object.data, "use_mirror_z", text="Z", toggle=True) + sub.prop(context.object, "use_mesh_mirror_x", text="X", toggle=True) + sub.prop(context.object, "use_mesh_mirror_y", text="Y", toggle=True) + sub.prop(context.object, "use_mesh_mirror_z", text="Z", toggle=True) if mode_string == 'EDIT_MESH': tool_settings = context.tool_settings layout.prop(tool_settings, "use_mesh_automerge", text="") @@ -938,7 +938,8 @@ class VIEW3D_MT_transform_base: layout.operator("transform.bend", text="Bend") layout.operator("transform.push_pull", text="Push/Pull") - if context.mode != 'OBJECT': + if context.mode in {'EDIT_MESH', 'EDIT_ARMATURE', 'EDIT_SURFACE', 'EDIT_CURVE', + 'EDIT_LATTICE', 'EDIT_METABALL'}: layout.operator("transform.vertex_warp", text="Warp") layout.operator_context = 'EXEC_REGION_WIN' layout.operator("transform.vertex_random", text="Randomize").offset = 0.1 @@ -1366,7 +1367,7 @@ class VIEW3D_MT_select_object(Menu): layout.operator_menu_enum("object.select_by_type", "type", text="Select All by Type") layout.operator("object.select_camera", text="Select Active Camera") - layout.operator("object.select_mirror", text="Mirror Selection") + layout.operator("object.select_mirror") layout.operator("object.select_random", text="Select Random") layout.separator() @@ -1424,7 +1425,7 @@ class VIEW3D_MT_select_pose(Menu): layout.separator() - layout.operator("pose.select_mirror", text="Flip Active") + layout.operator("pose.select_mirror") layout.separator() @@ -1597,7 +1598,7 @@ class VIEW3D_MT_select_edit_mesh(Menu): layout.separator() layout.operator("mesh.select_axis", text="Side of Active") - layout.operator("mesh.select_mirror", text="Mirror Selection") + layout.operator("mesh.select_mirror") class VIEW3D_MT_select_edit_curve(Menu): @@ -1784,7 +1785,7 @@ class VIEW3D_MT_select_edit_armature(Menu): layout.separator() - layout.operator("armature.select_mirror", text="Mirror").extend = False + layout.operator("armature.select_mirror") layout.separator() @@ -2313,6 +2314,7 @@ class VIEW3D_MT_object_animation(Menu): layout.operator("nla.bake", text="Bake Action...") layout.operator("gpencil.bake_mesh_animation", text="Bake Mesh to Grease Pencil...") + layout.operator("gpencil.bake_grease_pencil_animation", text="Bake Object Transform to Grease Pencil...") class VIEW3D_MT_object_rigid_body(Menu): @@ -3038,6 +3040,11 @@ class VIEW3D_MT_sculpt(Menu): layout.operator("sculpt.optimize") + layout.separator() + + props = layout.operator("object.transfer_mode", text="Transfer Sculpt Mode") + props.use_eyedropper = True + class VIEW3D_MT_mask(Menu): bl_label = "Mask" @@ -3090,19 +3097,15 @@ class VIEW3D_MT_mask(Menu): layout.separator() - props = layout.operator("sculpt.mask_expand", text="Expand Mask by Topology") - props.use_normals = False - props.keep_previous_mask = False + props = layout.operator("sculpt.expand", text="Expand Mask by Topology") + props.target = 'MASK' + props.falloff_type = 'GEODESIC' props.invert = True - props.smooth_iterations = 2 - props.create_face_set = False - props = layout.operator("sculpt.mask_expand", text="Expand Mask by Curvature") - props.use_normals = True - props.keep_previous_mask = True + props = layout.operator("sculpt.expand", text="Expand Mask by Normals") + props.target = 'MASK' + props.falloff_type = 'NORMALS' props.invert = False - props.smooth_iterations = 0 - props.create_face_set = False layout.separator() @@ -3156,6 +3159,20 @@ class VIEW3D_MT_face_sets(Menu): layout.separator() + props = layout.operator("sculpt.expand", text="Expand Face Set by Topology") + props.target = 'FACE_SETS' + props.falloff_type = 'GEODESIC' + props.invert = False + props.use_modify_active = False + + props = layout.operator("sculpt.expand", text="Expand Active Face Set") + props.target = 'FACE_SETS' + props.falloff_type = 'BOUNDARY_FACE_SET' + props.invert = False + props.use_modify_active = True + + layout.separator() + op = layout.operator("mesh.face_set_extract", text='Extract Face Set') layout.separator() @@ -4940,28 +4957,6 @@ class VIEW3D_MT_assign_material(Menu): icon='LAYER_ACTIVE' if mat == mat_active else 'BLANK1').material = mat.name -class VIEW3D_MT_gpencil_copy_layer(Menu): - bl_label = "Copy Layer to Object" - - def draw(self, context): - layout = self.layout - view_layer = context.view_layer - obact = context.active_object - gpl = context.active_gpencil_layer - - done = False - if gpl is not None: - for ob in view_layer.objects: - if ob.type == 'GPENCIL' and ob != obact: - layout.operator("gpencil.layer_duplicate_object", text=ob.name).object = ob.name - done = True - - if done is False: - layout.label(text="No destination object", icon='ERROR') - else: - layout.label(text="No layer to copy", icon='ERROR') - - class VIEW3D_MT_edit_gpencil(Menu): bl_label = "Grease Pencil" @@ -5041,6 +5036,10 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.prop(settings, "use_scale_thickness", text="Scale Thickness") layout.separator() + layout.operator("gpencil.stroke_normalize", text="Normalize Thickness").mode = 'THICKNESS' + layout.operator("gpencil.stroke_normalize", text="Normalize Opacity").mode = 'OPACITY' + + layout.separator() layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform") @@ -6175,10 +6174,13 @@ class VIEW3D_PT_overlay_geometry(Panel): sub.prop(overlay, "wireframe_opacity", text="Opacity") row = col.row(align=True) - if context.mode not in { - 'EDIT_ARMATURE', 'POSE', 'OBJECT', - 'PAINT_GPENCIL', 'VERTEX_GPENCIL', 'WEIGHT_GPENCIL', 'SCULPT_GPENCIL', 'EDIT_GPENCIL', - }: + + # These properties should be always available in the UI for all modes + # other than Object. + # Even when the Fade Inactive Geometry overlay is not affecting the + # current active object depending on its mode, it will always affect + # the rest of the scene. + if context.mode != 'OBJECT': row.prop(overlay, "show_fade_inactive", text="") sub = row.row() sub.active = overlay.show_fade_inactive @@ -6997,7 +6999,7 @@ class VIEW3D_PT_context_properties(Panel): if member: # Draw with no edit button - rna_prop_ui.draw(self.layout, context, member, object, False) + rna_prop_ui.draw(self.layout, context, member, object, use_edit=False) # Grease Pencil Object - Multiframe falloff tools @@ -7629,7 +7631,6 @@ classes = ( VIEW3D_MT_weight_gpencil, VIEW3D_MT_gpencil_animation, VIEW3D_MT_gpencil_simplify, - VIEW3D_MT_gpencil_copy_layer, VIEW3D_MT_gpencil_autoweights, VIEW3D_MT_gpencil_edit_context_menu, VIEW3D_MT_edit_curve, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 72baa5331bf..89bb2005f53 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -123,13 +123,15 @@ class View3DPanel: # **************** standard tool clusters ****************** # Used by vertex & weight paint -def draw_vpaint_symmetry(layout, vpaint, mesh): +def draw_vpaint_symmetry(layout, vpaint, obj): col = layout.column() row = col.row(heading="Mirror", align=True) - row.prop(mesh, "use_mirror_x", text="X", toggle=True) - row.prop(mesh, "use_mirror_y", text="Y", toggle=True) - row.prop(mesh, "use_mirror_z", text="Z", toggle=True) + row.prop(obj, "use_mesh_mirror_x", text="X", toggle=True) + row.prop(obj, "use_mesh_mirror_y", text="Y", toggle=True) + row.prop(obj, "use_mesh_mirror_z", text="Z", toggle=True) + col = layout.column() + col.active = not obj.data.use_mirror_vertex_groups col.prop(vpaint, "radial_symmetry", text="Radial") @@ -622,9 +624,15 @@ class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel): @classmethod def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and settings.brush and - (context.sculpt_object or context.image_paint_object or context.vertex_paint_object)) + if ( + (settings := cls.paint_settings(context)) and + (brush := settings.brush) + ): + if context.sculpt_object or context.vertex_paint_object: + return True + elif context.image_paint_object: + return (brush.image_tool == 'DRAW') + return False def draw(self, context): layout = self.layout @@ -971,12 +979,12 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel): wpaint = tool_settings.weight_paint mesh = context.object.data - draw_vpaint_symmetry(layout, wpaint, mesh) + layout.prop(mesh, 'use_mirror_vertex_groups') - col = layout.column(align=True) - col.prop(mesh, 'use_mirror_vertex_group_x', text="Vertex Group X") - row = col.row() - row.active = mesh.use_mirror_vertex_group_x + draw_vpaint_symmetry(layout, wpaint, context.object) + + row = layout.row() + row.active = mesh.use_mirror_vertex_groups row.prop(mesh, "use_mirror_topology") @@ -1051,7 +1059,7 @@ class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel): tool_settings = context.tool_settings vpaint = tool_settings.vertex_paint - draw_vpaint_symmetry(layout, vpaint, context.object.data) + draw_vpaint_symmetry(layout, vpaint, context.object) class VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar(Panel): @@ -1314,6 +1322,14 @@ class VIEW3D_PT_tools_particlemode_options_display(View3DPanel, Panel): # Grease Pencil drawing brushes +def tool_use_brush(context): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + if tool and tool.has_datablock is False: + return False + + return True + class GreasePencilPaintPanel: bl_context = ".greasepencil_paint" @@ -1325,6 +1341,10 @@ class GreasePencilPaintPanel: if context.gpencil_data is None: return False + # Hide for tools not using bruhses + if tool_use_brush(context) is False: + return False + gpd = context.gpencil_data return bool(gpd.is_stroke_paint_mode) else: @@ -1448,7 +1468,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): 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') + row.prop( + gp_settings, + "show_fill_boundary", + icon='HIDE_OFF' if gp_settings.show_fill_boundary else 'HIDE_ON', + text="", + ) col.separator() row = col.row(align=True) @@ -1457,7 +1482,15 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): col.separator() row = col.row(align=True) row.prop(gp_settings, "extend_stroke_factor") - row.prop(gp_settings, "show_fill_extend", text="", icon='GRID') + row.prop( + gp_settings, + "show_fill_extend", + icon='HIDE_OFF' if gp_settings.show_fill_extend else 'HIDE_ON', + text="", + ) + + col.separator() + col.prop(gp_settings, "fill_leak", text="Leak Size") col.separator() col.prop(gp_settings, "fill_simplify_level", text="Simplify") @@ -1697,9 +1730,14 @@ class VIEW3D_PT_tools_grease_pencil_brush_paint_falloff(GreasePencilBrushFalloff if brush is None: return False - tool = brush.gpencil_tool + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + if tool and tool.idname != 'builtin_brush.Tint': + return False + + gptool = brush.gpencil_tool - return (settings and settings.brush and settings.brush.curve and tool == 'TINT') + return (settings and settings.brush and settings.brush.curve and gptool == 'TINT') # Grease Pencil stroke sculpting tools @@ -2018,6 +2056,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_mixcolor(View3DPanel, Panel): if context.region.type == 'TOOL_HEADER': return False + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + if tool and tool.idname in('builtin.cutter', 'builtin.eyedropper', 'builtin.interpolate'): + return False + if brush.gpencil_tool == 'TINT': return True @@ -2074,6 +2117,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel): if ob is None or brush is None: return False + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + if tool and tool.idname in('builtin.cutter', 'builtin.eyedropper', 'builtin.interpolate'): + return False + if brush.gpencil_tool == 'TINT': return True diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index 012febc7cc7..83151a3480c 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -44,6 +44,7 @@ ANIM_KS_LOCATION_ID = "Location" ANIM_KS_ROTATION_ID = "Rotation" ANIM_KS_SCALING_ID = "Scaling" ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale" +ANIM_KS_LOC_ROT_SCALE_CPROP_ID = "LocRotScaleCProp" ANIM_KS_AVAILABLE_ID = "Available" ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter" ANIM_KS_WHOLE_CHARACTER_SELECTED_ID = "WholeCharacterSelected" @@ -159,6 +160,22 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo): keyingsets_utils.RKS_GEN_scaling(self, context, ks, data) +# LocRotScaleCProp +class BUILTIN_KSI_LocRotScaleCProp(KeyingSetInfo): + """Key location/rotation/scale as well as custom properties""" + bl_idname = ANIM_KS_LOC_ROT_SCALE_CPROP_ID + bl_label = "Location, Rotation, Scale & Custom Properties" + + poll = keyingsets_utils.RKS_POLL_selected_items + iterator = keyingsets_utils.RKS_ITER_selected_item + + def generate(self, context, ks, data): + keyingsets_utils.RKS_GEN_location(self, context, ks, data) + keyingsets_utils.RKS_GEN_rotation(self, context, ks, data) + keyingsets_utils.RKS_GEN_scaling(self, context, ks, data) + keyingsets_utils.RKS_GEN_custom_props(self, context, ks, data) + + # RotScale class BUILTIN_KSI_RotScale(KeyingSetInfo): """Insert a keyframe on each of the rotation and scale channels""" @@ -350,7 +367,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo): bl_label = "Available" # poll - selected objects or selected object with animation data - def poll(ksi, context): + def poll(self, context): ob = context.active_object if ob: # TODO: this fails if one animation-less object is active, but many others are selected @@ -366,14 +383,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo): ############################### - -# All properties that are likely to get animated in a character rig -class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): - """Insert a keyframe for all properties that are likely to get animated in a character rig """ \ - """(useful when blocking out a shot)""" - bl_idname = ANIM_KS_WHOLE_CHARACTER_ID - bl_label = "Whole Character" - +class WholeCharacterMixin: # these prefixes should be avoided, as they are not really bones # that animators should be touching (or need to touch) badBonePrefixes = ( @@ -387,38 +397,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): ) # poll - pose-mode on active object only - def poll(ksi, context): + def poll(self, context): return ((context.active_object) and (context.active_object.pose) and (context.active_object.mode == 'POSE')) # iterator - all bones regardless of selection - def iterator(ksi, context, ks): + def iterator(self, context, ks): for bone in context.active_object.pose.bones: - if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes): - ksi.generate(context, ks, bone) + if not bone.name.startswith(self.badBonePrefixes): + self.generate(context, ks, bone) # generator - all unlocked bone transforms + custom properties - def generate(ksi, context, ks, bone): + def generate(self, context, ks, bone): # loc, rot, scale - only include unlocked ones if not bone.bone.use_connect: - ksi.doLoc(ks, bone) + self.doLoc(ks, bone) if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}: - ksi.doRot4d(ks, bone) + self.doRot4d(ks, bone) else: - ksi.doRot3d(ks, bone) - ksi.doScale(ks, bone) + self.doRot3d(ks, bone) + self.doScale(ks, bone) # bbone properties? - ksi.doBBone(context, ks, bone) + self.doBBone(context, ks, bone) # custom props? - ksi.doCustomProps(ks, bone) - + self.doCustomProps(ks, bone) # ---------------- # helper to add some bone's property to the Keying Set - def addProp(ksi, ks, bone, prop, index=-1, use_groups=True): + def addProp(self, ks, bone, prop, index=-1, use_groups=True): # add the property name to the base path id_path = bone.path_from_id() id_block = bone.id_data @@ -439,16 +448,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # ---------------- # location properties - def doLoc(ksi, ks, bone): + def doLoc(self, ks, bone): if bone.lock_location == (False, False, False): - ksi.addProp(ks, bone, "location") + self.addProp(ks, bone, "location") else: for i in range(3): if not bone.lock_location[i]: - ksi.addProp(ks, bone, "location", i) + self.addProp(ks, bone, "location", i) # rotation properties - def doRot4d(ksi, ks, bone): + def doRot4d(self, ks, bone): # rotation mode affects the property used if bone.rotation_mode == 'QUATERNION': prop = "rotation_quaternion" @@ -459,40 +468,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): if bone.lock_rotations_4d: # can check individually if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False): - ksi.addProp(ks, bone, prop) + self.addProp(ks, bone, prop) else: if bone.lock_rotation_w is False: - ksi.addProp(ks, bone, prop, 0) # w = 0 + self.addProp(ks, bone, prop, 0) # w = 0 for i in range(3): if not bone.lock_rotation[i]: - ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0 + self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0 elif True not in bone.lock_rotation: # if axis-angle rotations get locked as eulers, then it's too messy to allow anything # other than all open unless we keyframe the whole lot - ksi.addProp(ks, bone, prop) + self.addProp(ks, bone, prop) - def doRot3d(ksi, ks, bone): + def doRot3d(self, ks, bone): if bone.lock_rotation == (False, False, False): - ksi.addProp(ks, bone, "rotation_euler") + self.addProp(ks, bone, "rotation_euler") else: for i in range(3): if not bone.lock_rotation[i]: - ksi.addProp(ks, bone, "rotation_euler", i) + self.addProp(ks, bone, "rotation_euler", i) # scale properties - def doScale(ksi, ks, bone): + def doScale(self, ks, bone): if bone.lock_scale == (0, 0, 0): - ksi.addProp(ks, bone, "scale") + self.addProp(ks, bone, "scale") else: for i in range(3): if not bone.lock_scale[i]: - ksi.addProp(ks, bone, "scale", i) + self.addProp(ks, bone, "scale", i) # ---------------- # bendy bone properties - def doBBone(ksi, context, ks, pchan): + def doBBone(self, context, ks, pchan): bone = pchan.bone # This check is crude, but is the best we can do for now @@ -500,12 +509,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # (and the bone is a control bone). This may lead to some # false positives... if bone.bbone_segments > 1: - keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan) + keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan) # ---------------- # custom properties - def doCustomProps(ksi, ks, bone): + def doCustomProps(self, ks, bone): prop_type_compat = {bpy.types.BoolProperty, bpy.types.IntProperty, @@ -528,39 +537,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # be converted to an FCurve-compatible value, so we can't keyframe it anyway. continue if rna_property.rna_type in prop_type_compat: - ksi.addProp(ks, bone, prop_path) + self.addProp(ks, bone, prop_path) elif prop_rna.is_animatable: - ksi.addProp(ks, bone, prop) + self.addProp(ks, bone, prop) -# All properties that are likely to get animated in a character rig, only selected bones. + +class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo): + """Insert a keyframe for all properties that are likely to get animated in a character rig """ \ + """(useful when blocking out a shot)""" + bl_idname = ANIM_KS_WHOLE_CHARACTER_ID + bl_label = "Whole Character" -class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo): +class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo): """Insert a keyframe for all properties that are likely to get animated in a character rig """ \ """(only selected bones)""" bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID bl_label = "Whole Character (Selected Bones Only)" # iterator - all bones regardless of selection - def iterator(ksi, context, ks): + def iterator(self, context, ks): # Use either the selected bones, or all of them if none are selected. bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones for bone in bones: - if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes): + if bone.name.startswith(self.badBonePrefixes): continue - ksi.generate(context, ks, bone) - - # Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter. - poll = BUILTIN_KSI_WholeCharacter.poll - generate = BUILTIN_KSI_WholeCharacter.generate - addProp = BUILTIN_KSI_WholeCharacter.addProp - doLoc = BUILTIN_KSI_WholeCharacter.doLoc - doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d - doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d - doScale = BUILTIN_KSI_WholeCharacter.doScale - doBBone = BUILTIN_KSI_WholeCharacter.doBBone - doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps + self.generate(context, ks, bone) + ############################### @@ -578,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(ksi, context, ks, data): + def generate(self, context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -604,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(ksi, context, ks, data): + def generate(self, context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -638,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo): iterator = keyingsets_utils.RKS_ITER_selected_objects # generator - delta location channels only - def generate(ksi, context, ks, data): + def generate(self, context, ks, data): # get id-block and path info id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data) @@ -664,6 +668,7 @@ classes = ( BUILTIN_KSI_Scaling, BUILTIN_KSI_LocRot, BUILTIN_KSI_LocRotScale, + BUILTIN_KSI_LocRotScaleCProp, BUILTIN_KSI_LocScale, BUILTIN_KSI_RotScale, BUILTIN_KSI_DeltaLocation, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index febb31af188..5927123cdd8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -34,7 +34,7 @@ class SortedNodeCategory(NodeCategory): if isinstance(items, list): items = sorted(items, key=lambda item: item.label.lower()) - super().__init__(identifier, name, description, items) + super().__init__(identifier, name, description=description, items=items) class CompositorNodeCategory(SortedNodeCategory): @@ -119,8 +119,8 @@ def node_group_items(context): if group.name.startswith('.'): continue yield NodeItem(node_tree_group_type[group.bl_idname], - group.name, - {"node_tree": "bpy.data.node_groups[%r]" % group.name}) + label=group.name, + settings={"node_tree": "bpy.data.node_groups[%r]" % group.name}) # only show input/output nodes inside node groups @@ -368,6 +368,7 @@ compositor_node_categories = [ NodeItem("CompositorNodePixelate"), NodeItem("CompositorNodeSunBeams"), NodeItem("CompositorNodeDenoise"), + NodeItem("CompositorNodeAntiAliasing"), ]), CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[ NodeItem("CompositorNodeNormal"), @@ -471,37 +472,44 @@ texture_node_categories = [ ]), ] - -def not_implemented_node(idname): - NodeType = getattr(bpy.types, idname) - name = NodeType.bl_rna.name - label = "%s (mockup)" % name - return NodeItem(idname, label=label) - - geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ NodeItem("GeometryNodeAttributeRandomize"), NodeItem("GeometryNodeAttributeMath"), + NodeItem("GeometryNodeAttributeClamp"), NodeItem("GeometryNodeAttributeCompare"), NodeItem("GeometryNodeAttributeConvert"), + NodeItem("GeometryNodeAttributeCurveMap"), NodeItem("GeometryNodeAttributeFill"), NodeItem("GeometryNodeAttributeMix"), NodeItem("GeometryNodeAttributeProximity"), NodeItem("GeometryNodeAttributeColorRamp"), NodeItem("GeometryNodeAttributeVectorMath"), + NodeItem("GeometryNodeAttributeVectorRotate"), NodeItem("GeometryNodeAttributeSampleTexture"), NodeItem("GeometryNodeAttributeCombineXYZ"), NodeItem("GeometryNodeAttributeSeparateXYZ"), NodeItem("GeometryNodeAttributeRemove"), + NodeItem("GeometryNodeAttributeMapRange"), + NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ + NodeItem("ShaderNodeRGBCurve"), NodeItem("ShaderNodeValToRGB"), NodeItem("ShaderNodeSeparateRGB"), NodeItem("ShaderNodeCombineRGB"), ]), + GeometryNodeCategory("GEO_CURVE", "Curve", items=[ + NodeItem("GeometryNodeCurveToMesh"), + NodeItem("GeometryNodeCurveResample"), + NodeItem("GeometryNodeMeshToCurve"), + NodeItem("GeometryNodeCurveLength"), + ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ + NodeItem("GeometryNodeBoundBox"), + NodeItem("GeometryNodeConvexHull"), + NodeItem("GeometryNodeDeleteGeometry"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), ]), @@ -512,15 +520,30 @@ geometry_node_categories = [ NodeItem("ShaderNodeValue"), NodeItem("FunctionNodeInputString"), NodeItem("FunctionNodeInputVector"), + NodeItem("GeometryNodeInputMaterial"), NodeItem("GeometryNodeIsViewport"), ]), + GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ + NodeItem("GeometryNodeMaterialAssign"), + NodeItem("GeometryNodeSelectByMaterial"), + NodeItem("GeometryNodeMaterialReplace"), + ]), GeometryNodeCategory("GEO_MESH", "Mesh", items=[ NodeItem("GeometryNodeBoolean"), NodeItem("GeometryNodeTriangulate"), NodeItem("GeometryNodeEdgeSplit"), NodeItem("GeometryNodeSubdivisionSurface"), NodeItem("GeometryNodeSubdivide"), - + ]), + GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[ + NodeItem("GeometryNodeMeshCircle"), + NodeItem("GeometryNodeMeshCone"), + NodeItem("GeometryNodeMeshCube"), + NodeItem("GeometryNodeMeshCylinder"), + NodeItem("GeometryNodeMeshGrid"), + NodeItem("GeometryNodeMeshIcoSphere"), + NodeItem("GeometryNodeMeshLine"), + NodeItem("GeometryNodeMeshUVSphere"), ]), GeometryNodeCategory("GEO_POINT", "Point", items=[ NodeItem("GeometryNodePointDistribute"), @@ -531,33 +554,25 @@ geometry_node_categories = [ NodeItem("GeometryNodeRotatePoints"), NodeItem("GeometryNodeAlignRotationToVector"), ]), - GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ - NodeItem("GeometryNodePointsToVolume"), - NodeItem("GeometryNodeVolumeToMesh"), - ]), - GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[ - NodeItem("GeometryNodeMeshCube"), - NodeItem("GeometryNodeMeshCircle"), - NodeItem("GeometryNodeMeshUVSphere"), - NodeItem("GeometryNodeMeshIcoSphere"), - NodeItem("GeometryNodeMeshCylinder"), - NodeItem("GeometryNodeMeshCone"), - NodeItem("GeometryNodeMeshLine"), - NodeItem("GeometryNodeMeshPlane"), - ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("FunctionNodeBooleanMath"), NodeItem("FunctionNodeFloatCompare"), + NodeItem("GeometryNodeSwitch"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ + NodeItem("ShaderNodeVectorCurve"), NodeItem("ShaderNodeSeparateXYZ"), NodeItem("ShaderNodeCombineXYZ"), NodeItem("ShaderNodeVectorMath"), NodeItem("ShaderNodeVectorRotate"), ]), + GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ + NodeItem("GeometryNodePointsToVolume"), + NodeItem("GeometryNodeVolumeToMesh"), + ]), GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items), GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[ NodeItem("NodeFrame"), |