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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/scripts/startup/bl_operators')
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/geometry_nodes.py10
-rw-r--r--release/scripts/startup/bl_operators/node.py36
-rw-r--r--release/scripts/startup/bl_operators/presets.py4
-rw-r--r--release/scripts/startup/bl_operators/userpref.py4
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_transform.py464
-rw-r--r--release/scripts/startup/bl_operators/vertexpaint_dirt.py22
-rw-r--r--release/scripts/startup/bl_operators/wm.py84
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')