diff options
Diffstat (limited to 'release/scripts/startup/bl_operators')
-rw-r--r-- | release/scripts/startup/bl_operators/__init__.py | 1 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/geometry_nodes.py | 10 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/node.py | 36 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/presets.py | 4 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/userpref.py | 4 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/uvcalc_transform.py | 464 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/vertexpaint_dirt.py | 22 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/wm.py | 84 |
8 files changed, 536 insertions, 89 deletions
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index 14dc72336f6..de0b7798072 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -31,6 +31,7 @@ _modules = [ "userpref", "uvcalc_follow_active", "uvcalc_lightmap", + "uvcalc_transform", "vertexpaint_dirt", "view3d", "wm", diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index ea4d40bb778..2b92b87a97f 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -3,11 +3,13 @@ import bpy from bpy.types import Operator +from bpy.app.translations import pgettext_data as data_ + def geometry_node_group_empty_new(): - group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree') - group.inputs.new('NodeSocketGeometry', "Geometry") - group.outputs.new('NodeSocketGeometry', "Geometry") + group = bpy.data.node_groups.new(data_("Geometry Nodes"), 'GeometryNodeTree') + group.inputs.new('NodeSocketGeometry', data_("Geometry")) + group.outputs.new('NodeSocketGeometry', data_("Geometry")) input_node = group.nodes.new('NodeGroupInput') output_node = group.nodes.new('NodeGroupOutput') output_node.is_active_output = True @@ -45,7 +47,7 @@ class NewGeometryNodesModifier(Operator): return geometry_modifier_poll(context) def execute(self, context): - modifier = context.object.modifiers.new("GeometryNodes", "NODES") + modifier = context.object.modifiers.new(data_("GeometryNodes"), "NODES") if not modifier: return {'CANCELLED'} diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index bad12ff4621..ff9b5a06fb7 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -14,6 +14,8 @@ from bpy.props import ( StringProperty, ) +from bpy.app.translations import pgettext_tip as tip_ + class NodeSetting(PropertyGroup): value: StringProperty( @@ -134,7 +136,7 @@ class NodeAddOperator: nodetype = properties["type"] bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype) if bl_rna is not None: - return bl_rna.description + return tip_(bl_rna.description) else: return "" @@ -147,37 +149,6 @@ class NODE_OT_add_node(NodeAddOperator, Operator): bl_options = {'REGISTER', 'UNDO'} -# Add a node and link it to an existing socket -class NODE_OT_add_and_link_node(NodeAddOperator, Operator): - '''Add a node to the active tree and link to an existing socket''' - bl_idname = "node.add_and_link_node" - bl_label = "Add and Link Node" - bl_options = {'REGISTER', 'UNDO'} - - link_socket_index: IntProperty( - name="Link Socket Index", - description="Index of the socket to link", - ) - - def execute(self, context): - space = context.space_data - ntree = space.edit_tree - - node = self.create_node(context) - if not node: - return {'CANCELLED'} - - to_socket = getattr(context, "link_to_socket", None) - if to_socket: - ntree.links.new(node.outputs[self.link_socket_index], to_socket) - - from_socket = getattr(context, "link_from_socket", None) - if from_socket: - ntree.links.new(from_socket, node.inputs[self.link_socket_index]) - - return {'FINISHED'} - - class NODE_OT_add_search(NodeAddOperator, Operator): '''Add a node to the active tree''' bl_idname = "node.add_search" @@ -304,7 +275,6 @@ class NODE_OT_tree_path_parent(Operator): classes = ( NodeSetting, - NODE_OT_add_and_link_node, NODE_OT_add_node, NODE_OT_add_search, NODE_OT_collapse_hide_unused_toggle, diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index cde4348977f..6bfce948412 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -11,11 +11,13 @@ from bpy.props import ( StringProperty, ) +from bpy.app.translations import pgettext_data as data_ + # For preset popover menu WindowManager.preset_name = StringProperty( name="Preset Name", description="Name for new preset", - default="New Preset" + default=data_("New Preset") ) diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 54de9c28144..ce23024fed5 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -1116,6 +1116,10 @@ class PREFERENCES_OT_studiolight_show(Operator): bl_label = "" bl_options = {'INTERNAL'} + @classmethod + def poll(cls, _context): + return bpy.ops.screen.userpref_show.poll() + def execute(self, context): context.preferences.active_section = 'LIGHTS' bpy.ops.screen.userpref_show('INVOKE_DEFAULT') diff --git a/release/scripts/startup/bl_operators/uvcalc_transform.py b/release/scripts/startup/bl_operators/uvcalc_transform.py new file mode 100644 index 00000000000..d52096f5485 --- /dev/null +++ b/release/scripts/startup/bl_operators/uvcalc_transform.py @@ -0,0 +1,464 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +import math + +from bpy.types import Operator +from mathutils import Matrix, Vector + +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + FloatVectorProperty, + IntProperty, +) + + +# ------------------------------------------------------------------------------ +# Local Utility Functions + +def is_face_uv_selected(face, uv_layer, any_edge): + """ + Returns True if the face is UV selected. + + :arg face: the face to query. + :type face: :class:`BMFace` + :arg uv_layer: the UV layer to source UVs from. + :type bmesh: :class:`BMLayerItem` + :arg any_edge: use edge selection instead of vertex selection. + :type any_edge: bool + :return: True if the face is UV selected. + :rtype: bool + """ + + if not face.select: # Geometry selection + return False + + import bpy + if bpy.context.tool_settings.use_uv_select_sync: + # In sync selection mode, UV selection comes solely from geometry selection. + return True + + if any_edge: + for loop in face.loops: + if loop[uv_layer].select_edge: + return True + return False + + for loop in face.loops: + if not loop[uv_layer].select: + return False + return True + + +def is_island_uv_selected(island, uv_layer, any_edge): + """ + Returns True if the island is UV selected. + + :arg island: list of faces to query. + :type island: sequence of :class:`BMFace`. + :arg uv_layer: the UV layer to source UVs from. + :type bmesh: :class:`BMLayerItem` + :arg any_edge: use edge selection instead of vertex selection. + :type any_edge: bool + :return: list of lists containing polygon indices. + :rtype: bool + """ + for face in island: + if is_face_uv_selected(face, uv_layer, any_edge): + return True + return False + + +def island_uv_bounds(island, uv_layer): + """ + The UV bounds of UV island. + + :arg island: list of faces to query. + :type island: sequence of :class:`BMFace`. + :arg uv_layer: the UV layer to source UVs from. + :return: U-min, V-min, U-max, V-max. + :rtype: list + """ + minmax = [1e30, 1e30, -1e30, -1e30] + for face in island: + for loop in face.loops: + u, v = loop[uv_layer].uv + minmax[0] = min(minmax[0], u) + minmax[1] = min(minmax[1], v) + minmax[2] = max(minmax[2], u) + minmax[3] = max(minmax[3], v) + return minmax + + +def island_uv_bounds_center(island, uv_layer): + """ + The UV bounds center of UV island. + + :arg island: list of faces to query. + :type island: sequence of :class:`BMFace`. + :arg uv_layer: the UV layer to source UVs from. + :return: U, V center. + :rtype: tuple + """ + minmax = island_uv_bounds(island, uv_layer) + return ( + (minmax[0] + minmax[2]) / 2.0, + (minmax[1] + minmax[3]) / 2.0, + ) + + +# ------------------------------------------------------------------------------ +# Align UV Rotation Operator + +def find_rotation_auto(bm, uv_layer, faces): + sum_u = 0.0 + sum_v = 0.0 + for face in faces: + prev_uv = face.loops[-1][uv_layer].uv + for loop in face.loops: + uv = loop[uv_layer].uv + du = uv[0] - prev_uv[0] + dv = uv[1] - prev_uv[1] + edge_angle = math.atan2(dv, du) + edge_angle *= 4.0 # Wrap 4 times around the circle + sum_u += math.cos(edge_angle) + sum_v += math.sin(edge_angle) + prev_uv = uv + + # Compute angle. + return -math.atan2(sum_v, sum_u) / 4.0 + + +def find_rotation_edge(bm, uv_layer, faces): + sum_u = 0.0 + sum_v = 0.0 + for face in faces: + prev_uv = face.loops[-1][uv_layer].uv + prev_select = face.loops[-1][uv_layer].select_edge + for loop in face.loops: + uv = loop[uv_layer].uv + if prev_select: + du = uv[0] - prev_uv[0] + dv = uv[1] - prev_uv[1] + edge_angle = math.atan2(dv, du) + edge_angle *= 2.0 # Wrap 2 times around the circle + sum_u += math.cos(edge_angle) + sum_v += math.sin(edge_angle) + + prev_uv = uv + prev_select = loop[uv_layer].select_edge + + # Add 90 degrees to align along V co-ordinate. + # Twice, because we divide by two. + sum_u, sum_v = -sum_u, -sum_v + + # Compute angle. + return -math.atan2(sum_v, sum_u) / 2.0 + + +def find_rotation_geometry(bm, uv_layer, faces, method, axis): + sum_u_co = Vector((0.0, 0.0, 0.0)) + sum_v_co = Vector((0.0, 0.0, 0.0)) + for face in faces: + # Triangulate. + for fan in range(2, len(face.loops)): + delta_uv0 = face.loops[fan - 1][uv_layer].uv - face.loops[0][uv_layer].uv + delta_uv1 = face.loops[fan][uv_layer].uv - face.loops[0][uv_layer].uv + + mat = Matrix((delta_uv0, delta_uv1)) + mat.invert_safe() + + delta_co0 = face.loops[fan - 1].vert.co - face.loops[0].vert.co + delta_co1 = face.loops[fan].vert.co - face.loops[0].vert.co + w = delta_co0.cross(delta_co1).length + # U direction in geometry co-ordinates. + sum_u_co += (delta_co0 * mat[0][0] + delta_co1 * mat[0][1]) * w + # V direction in geometry co-ordinates. + sum_v_co += (delta_co0 * mat[1][0] + delta_co1 * mat[1][1]) * w + + if axis == 'X': + axis_index = 0 + elif axis == 'Y': + axis_index = 1 + elif axis == 'Z': + axis_index = 2 + + # Compute angle. + return math.atan2(sum_u_co[axis_index], sum_v_co[axis_index]) + + +def align_uv_rotation_island(bm, uv_layer, faces, method, axis): + angle = 0.0 + if method == 'AUTO': + angle = find_rotation_auto(bm, uv_layer, faces) + elif method == 'EDGE': + angle = find_rotation_edge(bm, uv_layer, faces) + elif method == 'GEOMETRY': + angle = find_rotation_geometry(bm, uv_layer, faces, method, axis) + + if angle == 0.0: + return False # No change. + + # Find bounding box center. + mid_u, mid_v = island_uv_bounds_center(faces, uv_layer) + + cos_angle = math.cos(angle) + sin_angle = math.sin(angle) + + delta_u = mid_u - cos_angle * mid_u + sin_angle * mid_v + delta_v = mid_v - sin_angle * mid_u - cos_angle * mid_v + + # Apply transform. + for face in faces: + for loop in face.loops: + pre_uv = loop[uv_layer].uv + u = cos_angle * pre_uv[0] - sin_angle * pre_uv[1] + delta_u + v = sin_angle * pre_uv[0] + cos_angle * pre_uv[1] + delta_v + loop[uv_layer].uv = u, v + + return True + + +def align_uv_rotation_bmesh(mesh, bm, method, axis): + import bpy_extras.bmesh_utils + + uv_layer = bm.loops.layers.uv.active + if not uv_layer: + return False + + islands = bpy_extras.bmesh_utils.bmesh_linked_uv_islands(bm, uv_layer) + changed = False + for island in islands: + if is_island_uv_selected(island, uv_layer, method == 'EDGE'): + if align_uv_rotation_island(bm, uv_layer, island, method, axis): + changed = True + return changed + + +def align_uv_rotation(context, method, axis): + import bmesh + ob_list = context.objects_in_mode_unique_data + for ob in ob_list: + bm = bmesh.from_edit_mesh(ob.data) + if bm.loops.layers.uv: + if align_uv_rotation_bmesh(ob.data, bm, method, axis): + bmesh.update_edit_mesh(ob.data) + + return {'FINISHED'} + + +class AlignUVRotation(Operator): + """Align uv island's rotation""" + bl_idname = "uv.align_rotation" + bl_label = "Align Rotation" + bl_options = {'REGISTER', 'UNDO'} + + method: EnumProperty( + name="Method", description="Method to calculate rotation angle", + items=( + ('AUTO', "Auto", "Align from all edges"), + ('EDGE', "Edge", "Only selected edges"), + ('GEOMETRY', "Geometry", "Align to Geometry axis"), + ), + ) + + axis: EnumProperty( + name="Axis", description="Axis to align to", + items=( + ('X', "X", "X axis"), + ('Y', "Y", "Y axis"), + ('Z', "Z", "Z axis"), + ), + ) + + def execute(self, context): + return align_uv_rotation(context, self.method, self.axis) + + def draw(self, _context): + layout = self.layout + layout.prop(self, "method") + if self.method == 'GEOMETRY': + layout.prop(self, "axis") + + @classmethod + def poll(cls, context): + return context.mode == 'EDIT_MESH' + + +# ------------------------------------------------------------------------------ +# Randomize UV Operator + +def get_random_transform(transform_params, entropy): + from random import uniform + from random import seed as random_seed + + (seed, loc, rot, scale, scale_even) = transform_params + + # First, seed the RNG. + random_seed(seed + entropy) + + # Next, call uniform a known number of times. + offset_u = uniform(0.0, 1.0) + offset_v = uniform(0.0, 1.0) + angle = uniform(0.0, 1.0) + scale_u = uniform(0.0, 1.0) + scale_v = uniform(0.0, 1.0) + + # Apply the transform_params. + if loc: + offset_u *= loc[0] + offset_v *= loc[1] + else: + offset_u = 0.0 + offset_v = 0.0 + + if rot: + angle *= rot + else: + angle = 0.0 + + if scale: + scale_u = scale_u * (2.0 * scale[0] - 2.0) + 2.0 - scale[0] + scale_v = scale_v * (2.0 * scale[1] - 2.0) + 2.0 - scale[1] + else: + scale_u = 1.0 + scale_v = 1.0 + + if scale_even: + scale_v = scale_u + + # Results in homogenous co-ordinates. + return [[scale_u * math.cos(angle), -scale_v * math.sin(angle), offset_u], + [scale_u * math.sin(angle), scale_v * math.cos(angle), offset_v]] + + +def randomize_uv_transform_island(bm, uv_layer, faces, transform_params): + # Ensure consistent random values for island, regardless of selection etc. + entropy = min(f.index for f in faces) + + transform = get_random_transform(transform_params, entropy) + + # Find bounding box center. + mid_u, mid_v = island_uv_bounds_center(faces, uv_layer) + + del_u = transform[0][2] + mid_u - transform[0][0] * mid_u - transform[0][1] * mid_v + del_v = transform[1][2] + mid_v - transform[1][0] * mid_u - transform[1][1] * mid_v + + # Apply transform. + for face in faces: + for loop in face.loops: + pre_uv = loop[uv_layer].uv + u = transform[0][0] * pre_uv[0] + transform[0][1] * pre_uv[1] + del_u + v = transform[1][0] * pre_uv[0] + transform[1][1] * pre_uv[1] + del_v + loop[uv_layer].uv = (u, v) + + +def randomize_uv_transform_bmesh(mesh, bm, transform_params): + import bpy_extras.bmesh_utils + uv_layer = bm.loops.layers.uv.verify() + islands = bpy_extras.bmesh_utils.bmesh_linked_uv_islands(bm, uv_layer) + for island in islands: + if is_island_uv_selected(island, uv_layer, False): + randomize_uv_transform_island(bm, uv_layer, island, transform_params) + + +def randomize_uv_transform(context, transform_params): + import bmesh + ob_list = context.objects_in_mode_unique_data + for ob in ob_list: + bm = bmesh.from_edit_mesh(ob.data) + if not bm.loops.layers.uv: + continue + + # Only needed to access the minimum face index of each island. + bm.faces.index_update() + randomize_uv_transform_bmesh(ob.data, bm, transform_params) + + for ob in ob_list: + bmesh.update_edit_mesh(ob.data) + + return {'FINISHED'} + + +class RandomizeUVTransform(Operator): + """Randomize uv island's location, rotation, and scale""" + bl_idname = "uv.randomize_uv_transform" + bl_label = "Randomize" + bl_options = {'REGISTER', 'UNDO'} + + random_seed: IntProperty( + name="Random Seed", + description="Seed value for the random generator", + min=0, + max=10000, + default=0, + ) + use_loc: BoolProperty( + name="Randomize Location", + description="Randomize the location values", + default=True, + ) + loc: FloatVectorProperty( + name="Location", + description=("Maximum distance the objects " + "can spread over each axis"), + min=-100.0, + max=100.0, + size=2, + subtype='TRANSLATION', + default=(0.0, 0.0), + ) + use_rot: BoolProperty( + name="Randomize Rotation", + description="Randomize the rotation value", + default=True, + ) + rot: FloatProperty( + name="Rotation", + description="Maximum rotation", + min=-2.0 * math.pi, + max=2.0 * math.pi, + subtype='ANGLE', + default=0.0, + ) + use_scale: BoolProperty( + name="Randomize Scale", + description="Randomize the scale values", + default=True, + ) + scale_even: BoolProperty( + name="Scale Even", + description="Use the same scale value for both axes", + default=False, + ) + + scale: FloatVectorProperty( + name="Scale", + description="Maximum scale randomization over each axis", + min=-100.0, + max=100.0, + default=(1.0, 1.0), + size=2, + ) + + @classmethod + def poll(cls, context): + return context.mode == 'EDIT_MESH' + + def execute(self, context): + seed = self.random_seed + + loc = [0.0, 0.0] if not self.use_loc else self.loc + rot = 0.0 if not self.use_rot else self.rot + scale = None if not self.use_scale else self.scale + scale_even = self.scale_even + + transformParams = [seed, loc, rot, scale, scale_even] + return randomize_uv_transform(context, transformParams) + + +classes = ( + AlignUVRotation, + RandomizeUVTransform, +) diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py index a3c0b73fc0e..616e37d37e7 100644 --- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py +++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py @@ -2,14 +2,10 @@ # Copyright Campbell Barton. -def get_vcolor_layer_data(me): - for lay in me.vertex_colors: - if lay.active: - return lay.data - - lay = me.vertex_colors.new() - lay.active = True - return lay.data +def ensure_active_color_attribute(me): + if me.attributes.active_color: + return me.attributes.active_color + return me.color_attributes.new("Color", 'BYTE_COLOR', 'CORNER') def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only, normalize): @@ -99,17 +95,21 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, else: tone_range = 1.0 / tone_range - active_col_layer = get_vcolor_layer_data(me) - if not active_col_layer: + active_color_attribute = ensure_active_color_attribute(me) + if not active_color_attribute: return {'CANCELLED'} + point_domain = active_color_attribute.domain == 'POINT' + + attribute_data = active_color_attribute.data + use_paint_mask = me.use_paint_mask for i, p in enumerate(me.polygons): if not use_paint_mask or p.select: for loop_index in p.loop_indices: loop = me.loops[loop_index] v = loop.vertex_index - col = active_col_layer[loop_index].color + col = attribute_data[v if point_domain else loop_index].color tone = vert_tone[v] tone = (tone - min_tone) * tone_range diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 50fc6bad720..cbb5a63b754 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -18,6 +18,7 @@ from bpy.props import ( FloatVectorProperty, ) from bpy.app.translations import pgettext_iface as iface_ +from bpy.app.translations import pgettext_tip as tip_ def _rna_path_prop_search_for_context_impl(context, edit_text, unique_attrs): @@ -1060,31 +1061,31 @@ class WM_OT_url_open_preset(Operator): # Allow dynamically extending. preset_items = [ # Dynamic URL's. - (('BUG', "Bug", - "Report a bug with pre-filled version information"), + (('BUG', iface_("Bug"), + tip_("Report a bug with pre-filled version information")), _url_from_bug), - (('BUG_ADDON', "Add-on Bug", - "Report a bug in an add-on"), + (('BUG_ADDON', iface_("Add-on Bug"), + tip_("Report a bug in an add-on")), _url_from_bug_addon), - (('RELEASE_NOTES', "Release Notes", - "Read about what's new in this version of Blender"), + (('RELEASE_NOTES', iface_("Release Notes"), + tip_("Read about what's new in this version of Blender")), _url_from_release_notes), - (('MANUAL', "User Manual", - "The reference manual for this version of Blender"), + (('MANUAL', iface_("User Manual"), + tip_("The reference manual for this version of Blender")), _url_from_manual), - (('API', "Python API Reference", - "The API reference manual for this version of Blender"), + (('API', iface_("Python API Reference"), + tip_("The API reference manual for this version of Blender")), _url_from_api), # Static URL's. - (('FUND', "Development Fund", - "The donation program to support maintenance and improvements"), + (('FUND', iface_("Development Fund"), + tip_("The donation program to support maintenance and improvements")), "https://fund.blender.org"), - (('BLENDER', "blender.org", - "Blender's official web-site"), + (('BLENDER', iface_("blender.org"), + tip_("Blender's official web-site")), "https://www.blender.org"), - (('CREDITS', "Credits", - "Lists committers to Blender's source code"), + (('CREDITS', iface_("Credits"), + tip_("Lists committers to Blender's source code")), "https://www.blender.org/about/credits/"), ] @@ -2482,8 +2483,8 @@ class BatchRenameAction(bpy.types.PropertyGroup): ) # Weak, add/remove as properties. - op_add: BoolProperty() - op_remove: BoolProperty() + op_add: BoolProperty(name="Add") + op_remove: BoolProperty(name="Remove") class WM_OT_batch_rename(Operator): @@ -2569,7 +2570,7 @@ class WM_OT_batch_rename(Operator): if only_selected else scene.sequence_editor.sequences_all, "name", - "Strip(s)", + iface_("Strip(s)"), ) elif space_type == 'NODE_EDITOR': data_type_test = 'NODE' @@ -2581,7 +2582,7 @@ class WM_OT_batch_rename(Operator): if only_selected else list(space.node_tree.nodes), "name", - "Node(s)", + iface_("Node(s)"), ) elif space_type == 'OUTLINER': data_type_test = 'COLLECTION' @@ -2593,7 +2594,7 @@ class WM_OT_batch_rename(Operator): if only_selected else scene.collection.children_recursive, "name", - "Collection(s)", + iface_("Collection(s)"), ) else: if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object): @@ -2606,7 +2607,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [pbone.bone for ob in context.objects_in_mode_unique_data for pbone in ob.pose.bones], "name", - "Bone(s)", + iface_("Bone(s)"), ) elif mode == 'EDIT_ARMATURE': data_type_test = 'BONE' @@ -2618,24 +2619,24 @@ class WM_OT_batch_rename(Operator): if only_selected else [ebone for ob in context.objects_in_mode_unique_data for ebone in ob.data.edit_bones], "name", - "Edit Bone(s)", + iface_("Edit Bone(s)"), ) if check_context: return 'OBJECT' object_data_type_attrs_map = { - 'MESH': ("meshes", "Mesh(es)", bpy.types.Mesh), - 'CURVE': ("curves", "Curve(s)", bpy.types.Curve), - 'META': ("metaballs", "Metaball(s)", bpy.types.MetaBall), - 'VOLUME': ("volumes", "Volume(s)", bpy.types.Volume), - 'GPENCIL': ("grease_pencils", "Grease Pencil(s)", bpy.types.GreasePencil), - 'ARMATURE': ("armatures", "Armature(s)", bpy.types.Armature), - 'LATTICE': ("lattices", "Lattice(s)", bpy.types.Lattice), - 'LIGHT': ("lights", "Light(s)", bpy.types.Light), - 'LIGHT_PROBE': ("light_probes", "Light Probe(s)", bpy.types.LightProbe), - 'CAMERA': ("cameras", "Camera(s)", bpy.types.Camera), - 'SPEAKER': ("speakers", "Speaker(s)", bpy.types.Speaker), + 'MESH': ("meshes", iface_("Mesh(es)"), bpy.types.Mesh), + 'CURVE': ("curves", iface_("Curve(s)"), bpy.types.Curve), + 'META': ("metaballs", iface_("Metaball(s)"), bpy.types.MetaBall), + 'VOLUME': ("volumes", iface_("Volume(s)"), bpy.types.Volume), + 'GPENCIL': ("grease_pencils", iface_("Grease Pencil(s)"), bpy.types.GreasePencil), + 'ARMATURE': ("armatures", iface_("Armature(s)"), bpy.types.Armature), + 'LATTICE': ("lattices", iface_("Lattice(s)"), bpy.types.Lattice), + 'LIGHT': ("lights", iface_("Light(s)"), bpy.types.Light), + 'LIGHT_PROBE': ("light_probes", iface_("Light Probe(s)"), bpy.types.LightProbe), + 'CAMERA': ("cameras", iface_("Camera(s)"), bpy.types.Camera), + 'SPEAKER': ("speakers", iface_("Speaker(s)"), bpy.types.Speaker), } # Finish with space types. @@ -2653,7 +2654,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [id for id in bpy.data.objects if id.library is None], "name", - "Object(s)", + iface_("Object(s)"), ) elif data_type == 'COLLECTION': data = ( @@ -2668,7 +2669,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [id for id in bpy.data.collections if id.library is None], "name", - "Collection(s)", + iface_("Collection(s)"), ) elif data_type == 'MATERIAL': data = ( @@ -2687,7 +2688,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [id for id in bpy.data.materials if id.library is None], "name", - "Material(s)", + iface_("Material(s)"), ) elif data_type in object_data_type_attrs_map.keys(): attr, descr, ty = object_data_type_attrs_map[data_type] @@ -2912,7 +2913,7 @@ class WM_OT_batch_rename(Operator): row.prop(action, "op_remove", text="", icon='REMOVE') row.prop(action, "op_add", text="", icon='ADD') - layout.label(text="Rename %d %s" % (len(self._data[0]), self._data[2])) + layout.label(text=iface_("Rename %d %s") % (len(self._data[0]), self._data[2])) def check(self, context): changed = False @@ -2973,7 +2974,7 @@ class WM_OT_batch_rename(Operator): change_len += 1 total_len += 1 - self.report({'INFO'}, "Renamed %d of %d %s" % (change_len, total_len, descr)) + self.report({'INFO'}, tip_("Renamed %d of %d %s") % (change_len, total_len, descr)) return {'FINISHED'} @@ -3151,7 +3152,10 @@ class WM_OT_drop_blend_file(Operator): bl_label = "Handle dropped .blend file" bl_options = {'INTERNAL'} - filepath: StringProperty() + filepath: StringProperty( + subtype='FILE_PATH', + options={'SKIP_SAVE'}, + ) def invoke(self, context, _event): context.window_manager.popup_menu(self.draw_menu, title=bpy.path.basename(self.filepath), icon='QUESTION') |