diff options
Diffstat (limited to 'release/scripts/startup/bl_ui')
23 files changed, 981 insertions, 152 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index a986d4be993..e4fa10438e0 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -9,6 +9,8 @@ if "bpy" in locals(): del reload _modules = [ + "node_add_menu", + "node_add_menu_geometry", "properties_animviz", "properties_constraint", "properties_data_armature", diff --git a/release/scripts/startup/bl_ui/node_add_menu.py b/release/scripts/startup/bl_ui/node_add_menu.py new file mode 100644 index 00000000000..873dbd533a5 --- /dev/null +++ b/release/scripts/startup/bl_ui/node_add_menu.py @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +import bpy +from bpy.types import Menu +from bpy.app.translations import ( + pgettext_iface as iface_, + contexts as i18n_contexts, +) + + +def add_node_type(layout, node_type, *, label=None): + """Add a node type to a menu.""" + bl_rna = bpy.types.Node.bl_rna_get_subclass(node_type) + if not label: + label = bl_rna.name if bl_rna else iface_("Unknown") + translation_context = bl_rna.translation_context if bl_rna else i18n_contexts.default + props = layout.operator("node.add_node", text=label, text_ctxt=translation_context) + props.type = node_type + props.use_transform = True + return props + + +def draw_node_group_add_menu(context, layout): + """Add items to the layout used for interacting with node groups.""" + space_node = context.space_data + node_tree = space_node.edit_tree + all_node_groups = context.blend_data.node_groups + + layout.operator("node.group_make") + layout.operator("node.group_ungroup") + if node_tree in all_node_groups.values(): + layout.separator() + add_node_type(layout, "NodeGroupInput") + add_node_type(layout, "NodeGroupOutput") + + if node_tree: + from nodeitems_builtins import node_tree_group_type + + def contains_group(nodetree, group): + if nodetree == group: + return True + for node in nodetree.nodes: + if node.bl_idname in node_tree_group_type.values() and node.node_tree is not None: + if contains_group(node.node_tree, group): + return True + return False + + groups = [ + group for group in context.blend_data.node_groups + if (group.bl_idname == node_tree.bl_idname and + not contains_group(group, node_tree) and + not group.name.startswith('.')) + ] + if groups: + layout.separator() + for group in groups: + props = add_node_type(layout, node_tree_group_type[group.bl_idname], label=group.name) + ops = props.settings.add() + ops.name = "node_tree" + ops.value = "bpy.data.node_groups[%r]" % group.name + + +def draw_assets_for_catalog(layout, catalog_path): + layout.template_node_asset_menu_items(catalog_path=catalog_path) + + +def draw_root_assets(layout): + layout.menu_contents("NODE_MT_node_add_root_catalogs") + + +classes = ( +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/node_add_menu_geometry.py b/release/scripts/startup/bl_ui/node_add_menu_geometry.py new file mode 100644 index 00000000000..c076cd7395a --- /dev/null +++ b/release/scripts/startup/bl_ui/node_add_menu_geometry.py @@ -0,0 +1,470 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +import bpy +from bpy.types import Menu +from bl_ui import node_add_menu +from bpy.app.translations import pgettext_iface as iface_ + + +class NODE_MT_geometry_node_GEO_ATTRIBUTE(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_ATTRIBUTE" + bl_label = "Attribute" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeAttributeStatistic") + node_add_menu.add_node_type(layout, "GeometryNodeCaptureAttribute") + node_add_menu.add_node_type(layout, "GeometryNodeAttributeDomainSize") + node_add_menu.add_node_type(layout, "GeometryNodeRemoveAttribute") + node_add_menu.add_node_type(layout, "GeometryNodeStoreNamedAttribute") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_COLOR(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_COLOR" + bl_label = "Color" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "ShaderNodeValToRGB") + node_add_menu.add_node_type(layout, "FunctionNodeCombineColor") + props = node_add_menu.add_node_type(layout, "ShaderNodeMix", label=iface_("Mix Color")) + ops = props.settings.add() + ops.name = "data_type" + ops.value = "'RGBA'" + node_add_menu.add_node_type(layout, "ShaderNodeRGBCurve") + node_add_menu.add_node_type(layout, "FunctionNodeSeparateColor") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_CURVE(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_CURVE" + bl_label = "Curve" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeCurveLength") + node_add_menu.add_node_type(layout, "GeometryNodeCurveToMesh") + node_add_menu.add_node_type(layout, "GeometryNodeCurveToPoints") + node_add_menu.add_node_type(layout, "GeometryNodeDeformCurvesOnSurface") + node_add_menu.add_node_type(layout, "GeometryNodeFillCurve") + node_add_menu.add_node_type(layout, "GeometryNodeFilletCurve") + node_add_menu.add_node_type(layout, "GeometryNodeResampleCurve") + node_add_menu.add_node_type(layout, "GeometryNodeReverseCurve") + node_add_menu.add_node_type(layout, "GeometryNodeSampleCurve") + node_add_menu.add_node_type(layout, "GeometryNodeSubdivideCurve") + node_add_menu.add_node_type(layout, "GeometryNodeTrimCurve") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeInputCurveHandlePositions") + node_add_menu.add_node_type(layout, "GeometryNodeInputTangent") + node_add_menu.add_node_type(layout, "GeometryNodeInputCurveTilt") + node_add_menu.add_node_type(layout, "GeometryNodeCurveEndpointSelection") + node_add_menu.add_node_type(layout, "GeometryNodeCurveHandleTypeSelection") + node_add_menu.add_node_type(layout, "GeometryNodeInputSplineCyclic") + node_add_menu.add_node_type(layout, "GeometryNodeSplineLength") + node_add_menu.add_node_type(layout, "GeometryNodeSplineParameter") + node_add_menu.add_node_type(layout, "GeometryNodeInputSplineResolution") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeSetCurveNormal") + node_add_menu.add_node_type(layout, "GeometryNodeSetCurveRadius") + node_add_menu.add_node_type(layout, "GeometryNodeSetCurveTilt") + node_add_menu.add_node_type(layout, "GeometryNodeSetCurveHandlePositions") + node_add_menu.add_node_type(layout, "GeometryNodeCurveSetHandles") + node_add_menu.add_node_type(layout, "GeometryNodeSetSplineCyclic") + node_add_menu.add_node_type(layout, "GeometryNodeSetSplineResolution") + node_add_menu.add_node_type(layout, "GeometryNodeCurveSplineType") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE" + bl_label = "Curve Primitives" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeCurveArc") + node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveBezierSegment") + node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveCircle") + node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveLine") + node_add_menu.add_node_type(layout, "GeometryNodeCurveSpiral") + node_add_menu.add_node_type(layout, "GeometryNodeCurveQuadraticBezier") + node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveQuadrilateral") + node_add_menu.add_node_type(layout, "GeometryNodeCurveStar") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_curve_topology(Menu): + bl_idname = "NODE_MT_geometry_node_curve_topology" + bl_label = "Curve Topology" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeOffsetPointInCurve") + node_add_menu.add_node_type(layout, "GeometryNodeCurveOfPoint") + node_add_menu.add_node_type(layout, "GeometryNodePointsOfCurve") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_GEOMETRY(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_GEOMETRY" + bl_label = "Geometry" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeBoundBox") + node_add_menu.add_node_type(layout, "GeometryNodeConvexHull") + node_add_menu.add_node_type(layout, "GeometryNodeDeleteGeometry") + node_add_menu.add_node_type(layout, "GeometryNodeDuplicateElements") + node_add_menu.add_node_type(layout, "GeometryNodeProximity") + node_add_menu.add_node_type(layout, "GeometryNodeGeometryToInstance") + node_add_menu.add_node_type(layout, "GeometryNodeJoinGeometry") + node_add_menu.add_node_type(layout, "GeometryNodeMergeByDistance") + node_add_menu.add_node_type(layout, "GeometryNodeRaycast") + node_add_menu.add_node_type(layout, "GeometryNodeSampleIndex") + node_add_menu.add_node_type(layout, "GeometryNodeSampleNearest") + node_add_menu.add_node_type(layout, "GeometryNodeSeparateComponents") + node_add_menu.add_node_type(layout, "GeometryNodeSeparateGeometry") + node_add_menu.add_node_type(layout, "GeometryNodeTransform") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeSetID") + node_add_menu.add_node_type(layout, "GeometryNodeSetPosition") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_INPUT(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_INPUT" + bl_label = "Input" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "FunctionNodeInputBool") + node_add_menu.add_node_type(layout, "GeometryNodeCollectionInfo") + node_add_menu.add_node_type(layout, "FunctionNodeInputColor") + node_add_menu.add_node_type(layout, "FunctionNodeInputInt") + node_add_menu.add_node_type(layout, "GeometryNodeIsViewport") + node_add_menu.add_node_type(layout, "GeometryNodeInputMaterial") + node_add_menu.add_node_type(layout, "GeometryNodeObjectInfo") + node_add_menu.add_node_type(layout, "GeometryNodeSelfObject") + node_add_menu.add_node_type(layout, "FunctionNodeInputString") + node_add_menu.add_node_type(layout, "ShaderNodeValue") + node_add_menu.add_node_type(layout, "FunctionNodeInputVector") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeInputID") + node_add_menu.add_node_type(layout, "GeometryNodeInputIndex") + node_add_menu.add_node_type(layout, "GeometryNodeInputNamedAttribute") + node_add_menu.add_node_type(layout, "GeometryNodeInputNormal") + node_add_menu.add_node_type(layout, "GeometryNodeInputPosition") + node_add_menu.add_node_type(layout, "GeometryNodeInputRadius") + node_add_menu.add_node_type(layout, "GeometryNodeInputSceneTime") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_INSTANCE(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_INSTANCE" + bl_label = "Instances" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeInstanceOnPoints") + node_add_menu.add_node_type(layout, "GeometryNodeInstancesToPoints") + node_add_menu.add_node_type(layout, "GeometryNodeRealizeInstances") + node_add_menu.add_node_type(layout, "GeometryNodeRotateInstances") + node_add_menu.add_node_type(layout, "GeometryNodeScaleInstances") + node_add_menu.add_node_type(layout, "GeometryNodeTranslateInstances") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceRotation") + node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceScale") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_MATERIAL(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_MATERIAL" + bl_label = "Material" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeReplaceMaterial") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeInputMaterialIndex") + node_add_menu.add_node_type(layout, "GeometryNodeMaterialSelection") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeSetMaterial") + node_add_menu.add_node_type(layout, "GeometryNodeSetMaterialIndex") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_GEO_MESH(Menu): + bl_idname = "NODE_MT_geometry_node_GEO_MESH" + bl_label = "Mesh" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeDualMesh") + node_add_menu.add_node_type(layout, "GeometryNodeEdgePathsToCurves") + node_add_menu.add_node_type(layout, "GeometryNodeEdgePathsToSelection") + node_add_menu.add_node_type(layout, "GeometryNodeExtrudeMesh") + node_add_menu.add_node_type(layout, "GeometryNodeFlipFaces") + node_add_menu.add_node_type(layout, "GeometryNodeMeshBoolean") + node_add_menu.add_node_type(layout, "GeometryNodeMeshToCurve") + node_add_menu.add_node_type(layout, "GeometryNodeMeshToPoints") + node_add_menu.add_node_type(layout, "GeometryNodeMeshToVolume") + node_add_menu.add_node_type(layout, "GeometryNodeSampleNearestSurface") + node_add_menu.add_node_type(layout, "GeometryNodeSampleUVSurface") + node_add_menu.add_node_type(layout, "GeometryNodeScaleElements") + node_add_menu.add_node_type(layout, "GeometryNodeSplitEdges") + node_add_menu.add_node_type(layout, "GeometryNodeSubdivideMesh") + node_add_menu.add_node_type(layout, "GeometryNodeSubdivisionSurface") + node_add_menu.add_node_type(layout, "GeometryNodeTriangulate") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshEdgeAngle") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshEdgeNeighbors") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshEdgeVertices") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceArea") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceNeighbors") + node_add_menu.add_node_type(layout, "GeometryNodeMeshFaceSetBoundaries") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceIsPlanar") + node_add_menu.add_node_type(layout, "GeometryNodeInputShadeSmooth") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshIsland") + node_add_menu.add_node_type(layout, "GeometryNodeInputShortestEdgePaths") + node_add_menu.add_node_type(layout, "GeometryNodeInputMeshVertexNeighbors") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeSetShadeSmooth") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_PRIMITIVES_MESH(Menu): + bl_idname = "NODE_MT_category_PRIMITIVES_MESH" + bl_label = "Mesh Primitives" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeMeshCone") + node_add_menu.add_node_type(layout, "GeometryNodeMeshCube") + node_add_menu.add_node_type(layout, "GeometryNodeMeshCylinder") + node_add_menu.add_node_type(layout, "GeometryNodeMeshGrid") + node_add_menu.add_node_type(layout, "GeometryNodeMeshIcoSphere") + node_add_menu.add_node_type(layout, "GeometryNodeMeshCircle") + node_add_menu.add_node_type(layout, "GeometryNodeMeshLine") + node_add_menu.add_node_type(layout, "GeometryNodeMeshUVSphere") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_mesh_topology(Menu): + bl_idname = "NODE_MT_geometry_node_mesh_topology" + bl_label = "Mesh Topology" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeCornersOfFace"), + node_add_menu.add_node_type(layout, "GeometryNodeCornersOfVertex"), + node_add_menu.add_node_type(layout, "GeometryNodeEdgesOfCorner"), + node_add_menu.add_node_type(layout, "GeometryNodeEdgesOfVertex"), + node_add_menu.add_node_type(layout, "GeometryNodeFaceOfCorner"), + node_add_menu.add_node_type(layout, "GeometryNodeOffsetCornerInFace"), + node_add_menu.add_node_type(layout, "GeometryNodeVertexOfCorner"), + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_OUTPUT(Menu): + bl_idname = "NODE_MT_category_GEO_OUTPUT" + bl_label = "Output" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeViewer") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_POINT(Menu): + bl_idname = "NODE_MT_category_GEO_POINT" + bl_label = "Point" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsInVolume") + node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsOnFaces") + node_add_menu.add_node_type(layout, "GeometryNodePoints") + node_add_menu.add_node_type(layout, "GeometryNodePointsToVertices") + node_add_menu.add_node_type(layout, "GeometryNodePointsToVolume") + layout.separator() + node_add_menu.add_node_type(layout, "GeometryNodeSetPointRadius") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_TEXT(Menu): + bl_idname = "NODE_MT_category_GEO_TEXT" + bl_label = "Text" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeStringJoin") + node_add_menu.add_node_type(layout, "FunctionNodeReplaceString") + node_add_menu.add_node_type(layout, "FunctionNodeSliceString") + node_add_menu.add_node_type(layout, "FunctionNodeStringLength") + node_add_menu.add_node_type(layout, "GeometryNodeStringToCurves") + node_add_menu.add_node_type(layout, "FunctionNodeValueToString") + layout.separator() + node_add_menu.add_node_type(layout, "FunctionNodeInputSpecialCharacters") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_TEXTURE(Menu): + bl_idname = "NODE_MT_category_GEO_TEXTURE" + bl_label = "Texture" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "ShaderNodeTexBrick") + node_add_menu.add_node_type(layout, "ShaderNodeTexChecker") + node_add_menu.add_node_type(layout, "ShaderNodeTexGradient") + node_add_menu.add_node_type(layout, "GeometryNodeImageTexture") + node_add_menu.add_node_type(layout, "ShaderNodeTexMagic") + node_add_menu.add_node_type(layout, "ShaderNodeTexMusgrave") + node_add_menu.add_node_type(layout, "ShaderNodeTexNoise") + node_add_menu.add_node_type(layout, "ShaderNodeTexVoronoi") + node_add_menu.add_node_type(layout, "ShaderNodeTexWave") + node_add_menu.add_node_type(layout, "ShaderNodeTexWhiteNoise") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_UTILITIES(Menu): + bl_idname = "NODE_MT_category_GEO_UTILITIES" + bl_label = "Utilities" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeAccumulateField") + node_add_menu.add_node_type(layout, "FunctionNodeAlignEulerToVector") + node_add_menu.add_node_type(layout, "FunctionNodeBooleanMath") + node_add_menu.add_node_type(layout, "ShaderNodeClamp") + node_add_menu.add_node_type(layout, "FunctionNodeCompare") + node_add_menu.add_node_type(layout, "GeometryNodeFieldAtIndex") + node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve") + node_add_menu.add_node_type(layout, "FunctionNodeFloatToInt") + node_add_menu.add_node_type(layout, "GeometryNodeFieldOnDomain") + node_add_menu.add_node_type(layout, "ShaderNodeMapRange") + node_add_menu.add_node_type(layout, "ShaderNodeMath") + node_add_menu.add_node_type(layout, "ShaderNodeMix") + node_add_menu.add_node_type(layout, "FunctionNodeRandomValue") + node_add_menu.add_node_type(layout, "FunctionNodeRotateEuler") + node_add_menu.add_node_type(layout, "GeometryNodeSwitch") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_UV(Menu): + bl_idname = "NODE_MT_category_GEO_UV" + bl_label = "UV" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeUVPackIslands") + node_add_menu.add_node_type(layout, "GeometryNodeUVUnwrap") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_VECTOR(Menu): + bl_idname = "NODE_MT_category_GEO_VECTOR" + bl_label = "Vector" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "ShaderNodeCombineXYZ") + node_add_menu.add_node_type(layout, "ShaderNodeSeparateXYZ") + node_add_menu.add_node_type(layout, "ShaderNodeVectorCurve") + node_add_menu.add_node_type(layout, "ShaderNodeVectorMath") + node_add_menu.add_node_type(layout, "ShaderNodeVectorRotate") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_VOLUME(Menu): + bl_idname = "NODE_MT_category_GEO_VOLUME" + bl_label = "Volume" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "GeometryNodeVolumeCube") + node_add_menu.add_node_type(layout, "GeometryNodeVolumeToMesh") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_GROUP(Menu): + bl_idname = "NODE_MT_category_GEO_GROUP" + bl_label = "Group" + + def draw(self, context): + layout = self.layout + node_add_menu.draw_node_group_add_menu(context, layout) + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_category_GEO_LAYOUT(Menu): + bl_idname = "NODE_MT_category_GEO_LAYOUT" + bl_label = "Layout" + + def draw(self, _context): + layout = self.layout + node_add_menu.add_node_type(layout, "NodeFrame") + node_add_menu.add_node_type(layout, "NodeReroute") + node_add_menu.draw_assets_for_catalog(layout, self.bl_label) + + +class NODE_MT_geometry_node_add_all(Menu): + bl_idname = "NODE_MT_geometry_node_add_all" + bl_label = "" + + def draw(self, _context): + layout = self.layout + layout.menu("NODE_MT_geometry_node_GEO_ATTRIBUTE") + layout.menu("NODE_MT_geometry_node_GEO_COLOR") + layout.menu("NODE_MT_geometry_node_GEO_CURVE") + layout.menu("NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE") + layout.menu("NODE_MT_geometry_node_curve_topology") + layout.menu("NODE_MT_geometry_node_GEO_GEOMETRY") + layout.menu("NODE_MT_geometry_node_GEO_INPUT") + layout.menu("NODE_MT_geometry_node_GEO_INSTANCE") + layout.menu("NODE_MT_geometry_node_GEO_MATERIAL") + layout.menu("NODE_MT_geometry_node_GEO_MESH") + layout.menu("NODE_MT_category_PRIMITIVES_MESH") + layout.menu("NODE_MT_geometry_node_mesh_topology") + layout.menu("NODE_MT_category_GEO_OUTPUT") + layout.menu("NODE_MT_category_GEO_POINT") + layout.menu("NODE_MT_category_GEO_TEXT") + layout.menu("NODE_MT_category_GEO_TEXTURE") + layout.menu("NODE_MT_category_GEO_UTILITIES") + layout.menu("NODE_MT_category_GEO_UV") + layout.menu("NODE_MT_category_GEO_VECTOR") + layout.menu("NODE_MT_category_GEO_VOLUME") + layout.menu("NODE_MT_category_GEO_GROUP") + layout.menu("NODE_MT_category_GEO_LAYOUT") + node_add_menu.draw_root_assets(layout) + + +classes = ( + NODE_MT_geometry_node_add_all, + NODE_MT_geometry_node_GEO_ATTRIBUTE, + NODE_MT_geometry_node_GEO_COLOR, + NODE_MT_geometry_node_GEO_CURVE, + NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE, + NODE_MT_geometry_node_curve_topology, + NODE_MT_geometry_node_GEO_GEOMETRY, + NODE_MT_geometry_node_GEO_INPUT, + NODE_MT_geometry_node_GEO_INSTANCE, + NODE_MT_geometry_node_GEO_MATERIAL, + NODE_MT_geometry_node_GEO_MESH, + NODE_MT_category_PRIMITIVES_MESH, + NODE_MT_geometry_node_mesh_topology, + NODE_MT_category_GEO_OUTPUT, + NODE_MT_category_GEO_POINT, + NODE_MT_category_GEO_TEXT, + NODE_MT_category_GEO_TEXTURE, + NODE_MT_category_GEO_UTILITIES, + NODE_MT_category_GEO_UV, + NODE_MT_category_GEO_VECTOR, + NODE_MT_category_GEO_VOLUME, + NODE_MT_category_GEO_GROUP, + NODE_MT_category_GEO_LAYOUT, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_data_curves.py b/release/scripts/startup/bl_ui/properties_data_curves.py index ff0eabeb7d9..df80bdb4552 100644 --- a/release/scripts/startup/bl_ui/properties_data_curves.py +++ b/release/scripts/startup/bl_ui/properties_data_curves.py @@ -44,7 +44,13 @@ class DATA_PT_curves_surface(DataButtonsPanel, Panel): layout.use_property_split = True layout.prop(ob.data, "surface") - layout.prop(ob.data, "surface_uv_map", text="UV Map") + has_surface = ob.data.surface is not None + if has_surface: + layout.prop_search(ob.data, "surface_uv_map", ob.data.surface.data, "uv_layers", text="UV Map") + else: + row = layout.row() + row.prop(ob.data, "surface_uv_map", text="UV Map") + row.enabled = has_surface class CURVES_MT_add_attribute(Menu): diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index d878eea0cb9..a6b97fbdc85 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -503,11 +503,15 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel): else: col.operator("mesh.customdata_bevel_weight_vertex_add", icon='ADD') - col = layout.column(heading="Store") + if me.has_crease_edge: + col.operator("mesh.customdata_crease_edge_clear", icon='X') + else: + col.operator("mesh.customdata_crease_edge_add", icon='ADD') - col.enabled = obj is not None and obj.mode != 'EDIT' - col.prop(me, "use_customdata_vertex_crease", text="Vertex Crease") - col.prop(me, "use_customdata_edge_crease", text="Edge Crease") + if me.has_crease_vertex: + col.operator("mesh.customdata_crease_vertex_clear", icon='X') + else: + col.operator("mesh.customdata_crease_vertex_add", icon='ADD') class DATA_PT_custom_props_mesh(MeshButtonsPanel, PropertyPanel, Panel): @@ -582,7 +586,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): def draw_attribute_warnings(self, context, layout): ob = context.object - mesh = ob.data + mesh = context.mesh unique_names = set() colliding_names = [] @@ -591,8 +595,11 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): {"position": None, "shade_smooth": None, "normal": None, "crease": None}, mesh.attributes, mesh.uv_layers, - ob.vertex_groups, + None if ob is None else ob.vertex_groups, ): + if collection is None: + colliding_names.append("Cannot check for object vertex groups when pinning mesh") + continue for name in collection.keys(): unique_names_len = len(unique_names) unique_names.add(name) 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 38522a1bf84..83bb0f7dd8c 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -51,11 +51,6 @@ class GreasePencilSculptAdvancedPanel: tool = brush.gpencil_sculpt_tool gp_settings = brush.gpencil_settings - col = layout.column(heading="Auto-Masking", align=True) - col.prop(gp_settings, "use_automasking_stroke", text="Stroke") - col.prop(gp_settings, "use_automasking_layer", text="Layer") - col.prop(gp_settings, "use_automasking_material", text="Material") - if tool in {'SMOOTH', 'RANDOMIZE'}: col = layout.column(heading="Affect", align=True) col.prop(gp_settings, "use_edit_position", text="Position") diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py index bc7e8bb4347..61384f25afb 100644 --- a/release/scripts/startup/bl_ui/properties_output.py +++ b/release/scripts/startup/bl_ui/properties_output.py @@ -3,7 +3,10 @@ import bpy from bpy.types import Menu, Panel, UIList from bl_ui.utils import PresetPanel -from bpy.app.translations import pgettext_tip as tip_ +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_tip as tip_, +) class RENDER_PT_format_presets(PresetPanel, Panel): @@ -377,7 +380,14 @@ class RENDER_PT_encoding_video(RenderOutputButtonsPanel, Panel): layout = self.layout ffmpeg = context.scene.render.ffmpeg - needs_codec = ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG', 'MPEG4', 'WEBM'} + needs_codec = ffmpeg.format in { + 'AVI', + 'QUICKTIME', + 'MKV', + 'OGG', + 'MPEG4', + 'WEBM' + } if needs_codec: layout.prop(ffmpeg, "codec") @@ -388,7 +398,12 @@ class RENDER_PT_encoding_video(RenderOutputButtonsPanel, Panel): layout.prop(ffmpeg, "use_lossless_output") # Output quality - use_crf = needs_codec and ffmpeg.codec in {'H264', 'MPEG4', 'WEBM'} + use_crf = needs_codec and ffmpeg.codec in { + 'H264', + 'MPEG4', + 'WEBM', + 'AV1' + } if use_crf: layout.prop(ffmpeg, "constant_rate_factor") diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9b1cf11f6e7..0e49a506e73 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -928,29 +928,80 @@ def brush_settings_advanced(layout, context, brush, popover=False): use_frontface = False if mode == 'SCULPT': + sculpt = context.tool_settings.sculpt capabilities = brush.sculpt_capabilities use_accumulate = capabilities.has_accumulate use_frontface = True col = layout.column(heading="Auto-Masking", align=True) - # topology automasking + col = layout.column(align=True) col.prop(brush, "use_automasking_topology", text="Topology") - - # face masks automasking col.prop(brush, "use_automasking_face_sets", text="Face Sets") - # boundary edges/face sets automasking + layout.separator() + + col = layout.column(align=True) col.prop(brush, "use_automasking_boundary_edges", text="Mesh Boundary") col.prop(brush, "use_automasking_boundary_face_sets", text="Face Sets Boundary") - col.prop(brush, "automasking_boundary_edges_propagation_steps") + + if brush.use_automasking_boundary_edges or brush.use_automasking_boundary_face_sets: + col = layout.column() + col.use_property_split = False + split = col.split(factor=0.4) + col = split.column() + split.prop(brush, "automasking_boundary_edges_propagation_steps") + + layout.separator() + + col = layout.column(align=True) + row = col.row() + row.prop(brush, "use_automasking_cavity", text="Cavity") + + is_cavity_active = brush.use_automasking_cavity or brush.use_automasking_cavity_inverted + + if is_cavity_active: + row.operator("sculpt.mask_from_cavity", text="Create Mask") + + col.prop(brush, "use_automasking_cavity_inverted", text="Cavity (inverted)") + + if is_cavity_active: + col = layout.column(align=True) + col.prop(brush, "automasking_cavity_factor", text="Factor") + col.prop(brush, "automasking_cavity_blur_steps", text="Blur") + + col = layout.column() + col.prop(brush, "use_automasking_custom_cavity_curve", text="Custom Curve") + + if brush.use_automasking_custom_cavity_curve: + col.template_curve_mapping(brush, "automasking_cavity_curve") + + layout.separator() + + col = layout.column(align=True) + col.prop(brush, "use_automasking_view_normal", text="View Normal") + + if brush.use_automasking_view_normal: + col.prop(brush, "use_automasking_view_occlusion", text="Occlusion") + subcol = col.column(align=True) + subcol.active = not brush.use_automasking_view_occlusion + subcol.prop(sculpt, "automasking_view_normal_limit", text="Limit") + subcol.prop(sculpt, "automasking_view_normal_falloff", text="Falloff") + + col = layout.column() + col.prop(brush, "use_automasking_start_normal", text="Area Normal") + + if brush.use_automasking_start_normal: + col = layout.column(align=True) + col.prop(sculpt, "automasking_start_normal_limit", text="Limit") + col.prop(sculpt, "automasking_start_normal_falloff", text="Falloff") layout.separator() # sculpt plane settings if capabilities.has_sculpt_plane: layout.prop(brush, "sculpt_plane") - col = layout.column(heading="Use Original", align=True) + col = layout.column(heading="Original", align=True) col.prop(brush, "use_original_normal", text="Normal") col.prop(brush, "use_original_plane", text="Plane") layout.separator() diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 080c8ca5726..8567ddb9372 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -239,6 +239,7 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel): class PARTICLE_PT_emission(ParticleButtonsPanel, Panel): bl_label = "Emission" + bl_translation_context = i18n_contexts.id_particlesettings COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'} @classmethod @@ -1679,7 +1680,7 @@ class PARTICLE_PT_children_roughness(ParticleButtonsPanel, Panel): if part.use_roughness_curve: sub = col.column() sub.template_curve_mapping(part, "roughness_curve") - sub.prop(part, "roughness_1", text=iface_("Roughness", i18n_contexts.id_particlesettings)) + sub.prop(part, "roughness_1", text="Roughness", text_ctxt=i18n_contexts.id_particlesettings) sub.prop(part, "roughness_1_size", text="Size") else: sub = col.column(align=True) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index c337e8018e6..54b3a20f966 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -2,7 +2,10 @@ import bpy from bpy.types import Panel, Header, Menu, UIList -from bpy.app.translations import pgettext_iface as iface_ +from bpy.app.translations import ( + pgettext_iface as iface_, + contexts as i18n_contexts, +) from bl_ui.utils import PresetPanel from bl_ui.properties_grease_pencil_common import ( AnnotationDrawingToolsPanel, @@ -1751,6 +1754,7 @@ class CLIP_MT_marker_pie(Menu): class CLIP_MT_tracking_pie(Menu): # Tracking Operators bl_label = "Tracking" + bl_translation_context = i18n_contexts.id_movieclip @classmethod def poll(cls, context): diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index a2e691c2d9f..f95650ccc23 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -318,6 +318,7 @@ class DOPESHEET_MT_view(Menu): st = context.space_data layout.prop(st, "show_region_ui") + layout.prop(st, "show_region_hud") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 0b26f0b1203..fcbd7bb423d 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -402,7 +402,7 @@ class IMAGE_MT_uvs(Menu): layout.menu("IMAGE_MT_uvs_mirror") layout.menu("IMAGE_MT_uvs_snap") - layout.prop_menu_enum(uv, "pixel_snap_mode") + layout.prop_menu_enum(uv, "pixel_round_mode") layout.prop(uv, "lock_bounds") layout.separator() @@ -537,10 +537,12 @@ class IMAGE_MT_pivot_pie(Menu): layout = self.layout pie = layout.menu_pie() - pie.prop_enum(context.space_data, "pivot_point", value='CENTER') - pie.prop_enum(context.space_data, "pivot_point", value='CURSOR') - pie.prop_enum(context.space_data, "pivot_point", value='INDIVIDUAL_ORIGINS') - pie.prop_enum(context.space_data, "pivot_point", value='MEDIAN') + sima = context.space_data + + pie.prop_enum(sima, "pivot_point", value='CENTER') + pie.prop_enum(sima, "pivot_point", value='CURSOR') + pie.prop_enum(sima, "pivot_point", value='INDIVIDUAL_ORIGINS') + pie.prop_enum(sima, "pivot_point", value='MEDIAN') class IMAGE_MT_uvs_snap_pie(Menu): @@ -1526,25 +1528,23 @@ class IMAGE_PT_overlay_guides(Panel): layout.active = overlay.show_overlays row = layout.row() - row_el = row.column() - row_el.prop(overlay, "show_grid_background", text="Grid") + row.prop(overlay, "show_grid_background", text="Grid") if overlay.show_grid_background: - layout.use_property_split = True - col = layout.column(align=False, heading="Fixed Subdivisions") - col.use_property_decorate = False + sub = row.row() + sub.prop(uvedit, "show_grid_over_image", text="Over Image") + sub.active = sima.image is not None - row = col.row(align=True) - sub = row.row(align=True) - sub.prop(uvedit, "use_custom_grid", text="") - sub = sub.row(align=True) - sub.active = uvedit.use_custom_grid - sub.prop(uvedit, "custom_grid_subdivisions", text="") + layout.row().prop(uvedit, "grid_shape_source", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = False row = layout.row() - row.use_property_split = True - row.use_property_decorate = False - row.prop(uvedit, "tile_grid_shape", text="Tiles") + row.prop(uvedit, "custom_grid_subdivisions", text="Fixed Subdivisions") + row.active = uvedit.grid_shape_source == 'FIXED' + + layout.prop(uvedit, "tile_grid_shape", text="Tiles") class IMAGE_PT_overlay_uv_edit(Panel): diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index 5157a215f34..b43434d9988 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -88,6 +88,7 @@ class NLA_MT_view(Menu): st = context.space_data layout.prop(st, "show_region_ui") + layout.prop(st, "show_region_hud") layout.separator() layout.prop(st, "use_realtime_update") @@ -309,6 +310,11 @@ class NLA_MT_context_menu(Menu): layout.separator() + layout.operator("nla.meta_add") + layout.operator("nla.meta_remove") + + layout.separator() + layout.operator("nla.swap") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 118928ef9c6..593c6400529 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -219,10 +219,14 @@ class NODE_MT_add(bpy.types.Menu): import nodeitems_utils layout = self.layout - layout.operator_context = 'INVOKE_DEFAULT' - if nodeitems_utils.has_node_categories(context): + snode = context.space_data + if snode.tree_type == 'GeometryNodeTree': + props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM') + layout.separator() + layout.menu_contents("NODE_MT_geometry_node_add_all") + elif nodeitems_utils.has_node_categories(context): props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM') props.use_transform = True @@ -314,6 +318,7 @@ class NODE_MT_node(Menu): layout.operator("node.clipboard_copy", text="Copy") layout.operator("node.clipboard_paste", text="Paste") layout.operator("node.duplicate_move") + layout.operator("node.duplicate_move_linked") layout.operator("node.delete") layout.operator("node.delete_reconnect") diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 2b60158e3ae..8420d830257 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -54,7 +54,7 @@ class OUTLINER_HT_header(Header): icon='FILTER', ) - if display_mode in {'LIBRARIES' 'ORPHAN_DATA'}: + if display_mode in {'LIBRARIES', 'ORPHAN_DATA'}: row.prop(space, "use_filter_id_type", text="", icon='FILTER') sub = row.row(align=True) sub.active = space.use_filter_id_type diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index a99df1164a0..e703cf7f9c6 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -683,7 +683,7 @@ class SEQUENCER_MT_add(Menu): elif bpy_data_movieclips_len > 0: layout.operator_menu_enum("sequencer.movieclip_strip_add", "clip", text="Clip", icon='TRACKER') else: - layout.menu("SEQUENCER_MT_add_empty", text="Clip", icon='TRACKER') + layout.menu("SEQUENCER_MT_add_empty", text="Clip", text_ctxt=i18n_contexts.id_movieclip, icon='TRACKER') del bpy_data_movieclips_len bpy_data_masks_len = len(bpy.data.masks) diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py index 741ad544d67..846582b0142 100644 --- a/release/scripts/startup/bl_ui/space_spreadsheet.py +++ b/release/scripts/startup/bl_ui/space_spreadsheet.py @@ -11,34 +11,37 @@ class SPREADSHEET_HT_header(bpy.types.Header): space = context.space_data layout.template_header() + viewer_path = space.viewer_path.path - if len(space.context_path) == 0: - self.draw_without_context_path(layout) + if len(viewer_path) == 0: + self.draw_without_viewer_path(layout) return - root_context = space.context_path[0] - if root_context.type != 'OBJECT': - self.draw_without_context_path(layout) + root_context = viewer_path[0] + if root_context.type != 'ID': + self.draw_without_viewer_path(layout) return - obj = root_context.object + if not isinstance(root_context.id, bpy.types.Object): + self.draw_without_viewer_path(layout) + return + obj = root_context.id if obj is None: - self.draw_without_context_path(layout) + self.draw_without_viewer_path(layout) return layout.prop(space, "object_eval_state", text="") - 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) + viewer_path = viewer_path[:1] + if space.display_viewer_path_collapsed: + self.draw_collapsed_viewer_path(context, layout, viewer_path) else: - self.draw_full_context_path(context, layout, context_path) + self.draw_full_viewer_path(context, layout, viewer_path) pin_icon = 'PINNED' if space.is_pinned else 'UNPINNED' layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False) - if space.object_eval_state == 'VIEWER_NODE' and len(context_path) < 3: + if space.object_eval_state == 'VIEWER_NODE' and len(viewer_path) < 3: layout.label(text="No active viewer node", icon='INFO') layout.separator_spacer() @@ -49,50 +52,52 @@ class SPREADSHEET_HT_header(bpy.types.Header): sub.prop(space, "show_only_selected", text="") row.prop(space, "use_filter", toggle=True, icon='FILTER', icon_only=True) - def draw_without_context_path(self, layout): + def draw_without_viewer_path(self, layout): layout.label(text="No active context") - def draw_full_context_path(self, context, layout, context_path): + def draw_full_viewer_path(self, context, layout, viewer_path): space = context.space_data row = layout.row() - for ctx in context_path[:-1]: + for ctx in viewer_path[:-1]: subrow = row.row(align=True) self.draw_spreadsheet_context(subrow, ctx) - self.draw_spreadsheet_context_path_icon(subrow, space) + self.draw_spreadsheet_viewer_path_icon(subrow, space) - self.draw_spreadsheet_context(row, context_path[-1]) + self.draw_spreadsheet_context(row, viewer_path[-1]) - def draw_collapsed_context_path(self, context, layout, context_path): + def draw_collapsed_viewer_path(self, context, layout, viewer_path): space = context.space_data row = layout.row(align=True) - self.draw_spreadsheet_context(row, context_path[0]) - if len(context_path) == 1: + self.draw_spreadsheet_context(row, viewer_path[0]) + if len(viewer_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]) + self.draw_spreadsheet_viewer_path_icon(row, space) + if len(viewer_path) > 2: + self.draw_spreadsheet_viewer_path_icon(row, space, icon='DOT') + self.draw_spreadsheet_viewer_path_icon(row, space) + self.draw_spreadsheet_context(row, viewer_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') + if ctx.type == 'ID': + if ctx.id is not None and isinstance(ctx.id, bpy.types.Object): + layout.label(text=ctx.id.name, icon='OBJECT_DATA') else: - layout.label(text=ctx.object.name, icon='OBJECT_DATA') + layout.label(text="Invalid id") 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) + def draw_spreadsheet_viewer_path_icon(self, layout, space, icon='RIGHTARROW_THIN'): + layout.prop(space, "display_viewer_path_collapsed", icon_only=True, emboss=False, icon=icon) def selection_filter_available(self, space): - root_context = space.context_path[0] - if root_context.type != 'OBJECT': + root_context = space.viewer_path.path[0] + if root_context.type != 'ID': + return False + if not isinstance(root_context.id, bpy.types.Object): return False - obj = root_context.object + obj = root_context.id if obj is None: return False if obj.type == 'MESH': diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index d948ea09a74..3c04e71269d 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -120,6 +120,10 @@ class TIME_MT_view(Menu): scene = context.scene st = context.space_data + layout.prop(st, "show_region_hud") + + layout.separator() + layout.prop(st, "show_seconds") layout.prop(st, "show_locked_time") diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 39dfdd0eecb..81acc0837aa 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -232,7 +232,7 @@ class ToolSelectPanelHelper: def _icon_value_from_icon_handle(icon_name): import os if icon_name is not None: - assert(type(icon_name) is str) + assert type(icon_name) is str icon_value = _icon_cache.get(icon_name) if icon_value is None: dirname = bpy.utils.system_resource('DATAFILES', path="icons") diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index da089ea23b0..3320e1e24a7 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -2,7 +2,10 @@ import bpy from bpy.types import Header, Menu, Panel -from bpy.app.translations import pgettext_iface as iface_ +from bpy.app.translations import ( + pgettext_iface as iface_, + contexts as i18n_contexts, +) class TOPBAR_HT_upper_bar(Header): @@ -269,7 +272,7 @@ class TOPBAR_MT_file(Menu): layout = self.layout layout.operator_context = 'INVOKE_AREA' - layout.menu("TOPBAR_MT_file_new", text="New", icon='FILE_NEW') + layout.menu("TOPBAR_MT_file_new", text="New", text_ctxt=i18n_contexts.id_windowmanager, icon='FILE_NEW') layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER') layout.menu("TOPBAR_MT_file_open_recent") layout.operator("wm.revert_mainfile") @@ -408,9 +411,16 @@ class TOPBAR_MT_file_defaults(Menu): app_template, has_ext=False)) layout.operator("wm.save_homefile") - props = layout.operator("wm.read_factory_settings") if app_template: + display_name = bpy.path.display_name(iface_(app_template)) + props = layout.operator("wm.read_factory_settings", text="Load Factory Blender Settings") + props.app_template = app_template + props = layout.operator("wm.read_factory_settings", text="Load Factory %s Settings" % display_name) props.app_template = app_template + props.use_factory_startup_app_template_only = True + del display_name + else: + layout.operator("wm.read_factory_settings") # Include technical operators here which would otherwise have no way for users to access. @@ -724,7 +734,7 @@ class TOPBAR_MT_file_context_menu(Menu): layout = self.layout layout.operator_context = 'INVOKE_AREA' - layout.menu("TOPBAR_MT_file_new", text="New", icon='FILE_NEW') + layout.menu("TOPBAR_MT_file_new", text="New", text_ctxt=i18n_contexts.id_windowmanager, icon='FILE_NEW') layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER') layout.separator() diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 475ca8e9223..820a0e5ec60 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -109,7 +109,16 @@ class USERPREF_MT_save_load(Menu): sub_revert.operator("wm.read_userpref", text="Revert to Saved Preferences") layout.operator_context = 'INVOKE_AREA' - layout.operator("wm.read_factory_userpref", text="Load Factory Preferences") + + app_template = prefs.app_template + if app_template: + display_name = bpy.path.display_name(iface_(app_template)) + layout.operator("wm.read_factory_userpref", text="Load Factory Blender Preferences") + props = layout.operator("wm.read_factory_userpref", text="Load Factory %s Preferences" % display_name) + props.use_factory_startup_app_template_only = True + del display_name + else: + layout.operator("wm.read_factory_userpref", text="Load Factory Preferences") class USERPREF_PT_save_preferences(Panel): @@ -388,17 +397,18 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa # col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented. col.prop(edit, "use_duplicate_curves", text="Curves") col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") + col.prop(edit, "use_duplicate_lattice", text="Lattice") col = flow.column() - col.prop(edit, "use_duplicate_lattice", text="Lattice") col.prop(edit, "use_duplicate_light", text="Light") col.prop(edit, "use_duplicate_lightprobe", text="Light Probe") col.prop(edit, "use_duplicate_material", text="Material") col.prop(edit, "use_duplicate_mesh", text="Mesh") col.prop(edit, "use_duplicate_metaball", text="Metaball") + col.prop(edit, "use_duplicate_node_tree", text="Node Tree") + col.prop(edit, "use_duplicate_particle", text="Particle") col = flow.column() - col.prop(edit, "use_duplicate_particle", text="Particle") if hasattr(edit, "use_duplicate_pointcloud"): col.prop(edit, "use_duplicate_pointcloud", text="Point Cloud") col.prop(edit, "use_duplicate_speaker", text="Speaker") @@ -1533,6 +1543,23 @@ class USERPREF_PT_input_mouse(InputPanel, CenterAlignMixIn, Panel): flow.prop(inputs, "move_threshold") +class USERPREF_PT_input_touchpad(InputPanel, CenterAlignMixIn, Panel): + bl_label = "Touchpad" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + import sys + return sys.platform[:3] == "win" or sys.platform == "darwin" + + def draw_centered(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + col = layout.column() + col.prop(inputs, "use_multitouch_gestures") + + class USERPREF_PT_input_tablet(InputPanel, CenterAlignMixIn, Panel): bl_label = "Tablet" @@ -2410,6 +2437,7 @@ classes = ( USERPREF_PT_input_keyboard, USERPREF_PT_input_mouse, USERPREF_PT_input_tablet, + USERPREF_PT_input_touchpad, USERPREF_PT_input_ndof, USERPREF_PT_navigation_orbit, USERPREF_PT_navigation_zoom, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a687f3c937f..947f9056df7 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -108,7 +108,7 @@ class VIEW3D_HT_tool_header(Header): brush = context.tool_settings.gpencil_sculpt_paint.brush if brush: tool = brush.gpencil_sculpt_tool - if tool != 'CLONE': + if tool in {'SMOOTH', 'RANDOMIZE'}: layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover") layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': @@ -837,13 +837,23 @@ class VIEW3D_HT_header(Header): panel="VIEW3D_PT_gpencil_guide", text="Guides", ) - - layout.separator_spacer() + if object_mode == 'SCULPT_GPENCIL': + layout.popover( + panel="VIEW3D_PT_gpencil_sculpt_automasking", + text="", + icon="MOD_MASK" + ) + elif object_mode == 'SCULPT': + layout.popover( + panel="VIEW3D_PT_sculpt_automasking", + text="", + icon="MOD_MASK" + ) else: # Transform settings depending on tool header visibility VIEW3D_HT_header.draw_xform_template(layout, context) - layout.separator_spacer() + layout.separator_spacer() # Viewport Settings layout.popover( @@ -1216,6 +1226,7 @@ class VIEW3D_MT_view(Menu): layout.operator("view3d.view_all").center = False layout.operator("view3d.view_persportho", text="Perspective/Orthographic") layout.menu("VIEW3D_MT_view_local") + layout.prop(view, "show_viewer", text="Viewer Node") layout.separator() @@ -3011,6 +3022,7 @@ class VIEW3D_MT_brush_paint_modes(Menu): layout.prop(brush, "use_paint_vertex", text="Vertex Paint") layout.prop(brush, "use_paint_weight", text="Weight Paint") layout.prop(brush, "use_paint_image", text="Texture Paint") + layout.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves") class VIEW3D_MT_paint_vertex(Menu): @@ -3113,21 +3125,33 @@ class VIEW3D_MT_paint_weight_lock(Menu): op = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All") op.action, op.mask = 'LOCK', 'ALL' - op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All") - op.action, op.mask = 'UNLOCK', 'ALL' - op = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Selected") + + op = layout.operator("object.vertex_group_lock", text="Lock Selected") op.action, op.mask = 'LOCK', 'SELECTED' - op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock Selected") - op.action, op.mask = 'UNLOCK', 'SELECTED' - op = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Unselected") + + op = layout.operator("object.vertex_group_lock", text="Lock Unselected") op.action, op.mask = 'LOCK', 'UNSELECTED' - op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock Unselected") - op.action, op.mask = 'UNLOCK', 'UNSELECTED' + op = layout.operator("object.vertex_group_lock", text="Lock Only Selected") op.action, op.mask = 'LOCK', 'INVERT_UNSELECTED' + op = layout.operator("object.vertex_group_lock", text="Lock Only Unselected") op.action, op.mask = 'UNLOCK', 'INVERT_UNSELECTED' - op = layout.operator("object.vertex_group_lock", text="Invert Locks") + + layout.separator() + + op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All") + op.action, op.mask = 'UNLOCK', 'ALL' + + op = layout.operator("object.vertex_group_lock", text="Unlock Selected") + op.action, op.mask = 'UNLOCK', 'SELECTED' + + op = layout.operator("object.vertex_group_lock", text="Unlock Unselected") + op.action, op.mask = 'UNLOCK', 'UNSELECTED' + + layout.separator() + + op = layout.operator("object.vertex_group_lock", icon='ARROW_LEFTRIGHT', text="Invert Locks") op.action, op.mask = 'INVERT', 'ALL' @@ -3297,7 +3321,8 @@ class VIEW3D_MT_mask(Menu): layout.separator() - props = layout.operator("sculpt.dirty_mask", text='Dirty Mask') + props = layout.operator("sculpt.mask_from_cavity", text="Mask From Cavity") + props.use_automask_settings = False layout.separator() @@ -3354,8 +3379,7 @@ class VIEW3D_MT_face_sets(Menu): op = layout.operator("sculpt.face_set_change_visibility", text='Invert Visible Face Sets') op.mode = 'INVERT' - op = layout.operator("sculpt.face_set_change_visibility", text='Show All Face Sets') - op.mode = 'SHOW_ALL' + op = layout.operator("sculpt.reveal_all", text='Show All Face Sets') layout.separator() @@ -5493,6 +5517,26 @@ class VIEW3D_MT_sculpt_automasking_pie(Menu): pie.prop(sculpt, "use_automasking_face_sets", text="Face Sets") pie.prop(sculpt, "use_automasking_boundary_edges", text="Mesh Boundary") pie.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary") + pie.prop(sculpt, "use_automasking_cavity", text="Cavity") + pie.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (Inverted)") + pie.prop(sculpt, "use_automasking_start_normal", text="Area Normal") + pie.prop(sculpt, "use_automasking_view_normal", text="View Normal") + + +class VIEW3D_MT_sculpt_gpencil_automasking_pie(Menu): + bl_label = "Automasking" + + def draw(self, context): + layout = self.layout + pie = layout.menu_pie() + + tool_settings = context.tool_settings + + pie.prop(tool_settings.gpencil_sculpt, "use_automasking_stroke", text="Stroke") + pie.prop(tool_settings.gpencil_sculpt, "use_automasking_layer_stroke", text="Layer") + pie.prop(tool_settings.gpencil_sculpt, "use_automasking_material_stroke", text="Material") + pie.prop(tool_settings.gpencil_sculpt, "use_automasking_layer_active", text="Active Layer") + pie.prop(tool_settings.gpencil_sculpt, "use_automasking_material_active", text="Active Material") class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu): @@ -5512,8 +5556,7 @@ class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu): op = pie.operator("sculpt.face_set_change_visibility", text='Invert Visible') op.mode = 'INVERT' - op = pie.operator("sculpt.face_set_change_visibility", text='Show All') - op.mode = 'SHOW_ALL' + op = pie.operator("sculpt.reveal_all", text='Show All') class VIEW3D_MT_wpaint_vgroup_lock_pie(Menu): @@ -6347,6 +6390,13 @@ class VIEW3D_PT_overlay_geometry(Panel): sub.prop(overlay, "wireframe_opacity", text="Opacity") row = col.row(align=True) + row.active = view.show_viewer + row.prop(overlay, "show_viewer_attribute", text="") + subrow = row.row(align=True) + subrow.active = overlay.show_viewer_attribute + subrow.prop(overlay, "viewer_attribute_opacity", text="Viewer Node") + + row = col.row(align=True) # These properties should be always available in the UI for all modes # other than Object. @@ -7437,6 +7487,27 @@ def draw_gpencil_material_active(context, layout): row.prop(ma, "name", text="") +class VIEW3D_PT_gpencil_sculpt_automasking(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Auto-masking" + bl_ui_units_x = 10 + + def draw(self, context): + layout = self.layout + + tool_settings = context.scene.tool_settings + layout.label(text="Auto-masking") + + col = layout.column(align=True) + col.prop(tool_settings.gpencil_sculpt, "use_automasking_stroke", text="Stroke") + col.prop(tool_settings.gpencil_sculpt, "use_automasking_layer_stroke", text="Layer") + col.prop(tool_settings.gpencil_sculpt, "use_automasking_material_stroke", text="Material") + col.separator() + col.prop(tool_settings.gpencil_sculpt, "use_automasking_layer_active", text="Active Layer") + col.prop(tool_settings.gpencil_sculpt, "use_automasking_material_active", text="Active Material") + + class VIEW3D_PT_gpencil_sculpt_context_menu(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'WINDOW' @@ -7669,6 +7740,77 @@ class VIEW3D_PT_paint_weight_context_menu(Panel): ) +class VIEW3D_PT_sculpt_automasking(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Auto-Masking" + bl_ui_units_x = 10 + + def draw(self, context): + layout = self.layout + + tool_settings = context.tool_settings + sculpt = tool_settings.sculpt + layout.label(text="Auto-Masking") + + col = layout.column(align=True) + col.prop(sculpt, "use_automasking_topology", text="Topology") + col.prop(sculpt, "use_automasking_face_sets", text="Face Sets") + + col.separator() + + col = layout.column(align=True) + col.prop(sculpt, "use_automasking_boundary_edges", text="Mesh Boundary") + col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary") + + if sculpt.use_automasking_boundary_edges or sculpt.use_automasking_boundary_face_sets: + col.prop(sculpt.brush, "automasking_boundary_edges_propagation_steps") + + col.separator() + + col = layout.column(align=True) + row = col.row() + row.prop(sculpt, "use_automasking_cavity", text="Cavity") + + is_cavity_active = sculpt.use_automasking_cavity or sculpt.use_automasking_cavity_inverted + + if is_cavity_active: + row.operator("sculpt.mask_from_cavity", text="Create Mask") + + col.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (inverted)") + + if is_cavity_active: + col = layout.column(align=True) + col.prop(sculpt, "automasking_cavity_factor", text="Factor") + col.prop(sculpt, "automasking_cavity_blur_steps", text="Blur") + + col = layout.column() + col.prop(sculpt, "use_automasking_custom_cavity_curve", text="Custom Curve") + + if sculpt.use_automasking_custom_cavity_curve: + col.template_curve_mapping(sculpt, "automasking_cavity_curve") + + col.separator() + + col = layout.column(align=True) + col.prop(sculpt, "use_automasking_view_normal", text="View Normal") + + if sculpt.use_automasking_view_normal: + col.prop(sculpt, "use_automasking_view_occlusion", text="Occlusion") + subcol = col.column(align=True) + subcol.active = not sculpt.use_automasking_view_occlusion + subcol.prop(sculpt, "automasking_view_normal_limit", text="Limit") + subcol.prop(sculpt, "automasking_view_normal_falloff", text="Falloff") + + col = layout.column() + col.prop(sculpt, "use_automasking_start_normal", text="Area Normal") + + if sculpt.use_automasking_start_normal: + col = layout.column(align=True) + col.prop(sculpt, "automasking_start_normal_limit", text="Limit") + col.prop(sculpt, "automasking_start_normal_falloff", text="Falloff") + + class VIEW3D_PT_sculpt_context_menu(Panel): # Only for popover, these are dummy values. bl_space_type = 'VIEW_3D' @@ -8002,6 +8144,7 @@ classes = ( VIEW3D_MT_proportional_editing_falloff_pie, VIEW3D_MT_sculpt_mask_edit_pie, VIEW3D_MT_sculpt_automasking_pie, + VIEW3D_MT_sculpt_gpencil_automasking_pie, VIEW3D_MT_wpaint_vgroup_lock_pie, VIEW3D_MT_sculpt_face_sets_edit_pie, VIEW3D_MT_sculpt_curves, @@ -8016,6 +8159,7 @@ classes = ( VIEW3D_PT_annotation_onion, VIEW3D_PT_gpencil_multi_frame, VIEW3D_PT_gpencil_curve_edit, + VIEW3D_PT_gpencil_sculpt_automasking, VIEW3D_PT_quad_view, VIEW3D_PT_view3d_stereo, VIEW3D_PT_shading, @@ -8059,6 +8203,7 @@ classes = ( VIEW3D_PT_gpencil_sculpt_context_menu, VIEW3D_PT_gpencil_weight_context_menu, VIEW3D_PT_gpencil_draw_context_menu, + VIEW3D_PT_sculpt_automasking, VIEW3D_PT_sculpt_context_menu, TOPBAR_PT_gpencil_materials, TOPBAR_PT_gpencil_vertexcolor, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index d15be4a9d3f..111fb0d8bae 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -from bpy.types import Menu, Panel, UIList +from bpy.types import Menu, Panel, UIList, WindowManager from bl_ui.properties_grease_pencil_common import ( GreasePencilSculptAdvancedPanel, GreasePencilDisplayPanel, @@ -52,6 +52,8 @@ class VIEW3D_MT_brush_context_menu(Menu): elif context.sculpt_object: layout.prop_menu_enum(brush, "sculpt_tool") layout.operator("brush.reset") + elif context.tool_settings.curves_sculpt: + layout.prop_menu_enum(brush, "curves_sculpt_tool") class VIEW3D_MT_brush_gpencil_context_menu(Menu): @@ -81,22 +83,6 @@ class VIEW3D_MT_brush_gpencil_context_menu(Menu): layout.operator("gpencil.brush_reset_all") -class VIEW3D_MT_brush_context_menu_paint_modes(Menu): - bl_label = "Enabled Modes" - - def draw(self, context): - layout = self.layout - - settings = UnifiedPaintPanel.paint_settings(context) - brush = settings.brush - - layout.prop(brush, "use_paint_sculpt", text="Sculpt") - layout.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt") - layout.prop(brush, "use_paint_vertex", text="Vertex Paint") - layout.prop(brush, "use_paint_weight", text="Weight Paint") - layout.prop(brush, "use_paint_image", text="Texture Paint") - - class View3DPanel: bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -278,7 +264,7 @@ class TEXTURE_UL_texpaintslots(UIList): # mat = data if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.prop(item, "name", text="", emboss=False, icon_value=item.icon_value) + layout.label(text=item.name, icon_value=item.icon_value) elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="") @@ -964,14 +950,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "use_sculpt_delay_updates") col.prop(sculpt, "use_deform_only") - col.separator() - - col = layout.column(heading="Auto-Masking", align=True) - col.prop(sculpt, "use_automasking_topology", text="Topology") - col.prop(sculpt, "use_automasking_face_sets", text="Face Sets") - col.prop(sculpt, "use_automasking_boundary_edges", text="Mesh Boundary") - col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary") - class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) @@ -1606,19 +1584,6 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): row.prop(gp_settings, "fill_layer_mode", text="Layers") col.separator() - row = col.row(align=True) - row.prop(gp_settings, "extend_stroke_factor") - 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") if gp_settings.fill_draw_mode != 'STROKE': col = layout.column(align=False, heading="Ignore Transparent") @@ -1878,6 +1843,39 @@ class VIEW3D_PT_tools_grease_pencil_brush_paint_falloff(GreasePencilBrushFalloff return (settings and settings.brush and settings.brush.curve and gptool == 'TINT') +class VIEW3D_PT_tools_grease_pencil_brush_gap_closure(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_advanced' + bl_label = "Gap Closure" + bl_category = "Tool" + + @classmethod + def poll(cls, context): + brush = context.tool_settings.gpencil_paint.brush + return brush is not None and brush.gpencil_tool == 'FILL' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + tool_settings = context.tool_settings + brush = tool_settings.gpencil_paint.brush + gp_settings = brush.gpencil_settings + + col = layout.column() + + col.prop(gp_settings, "extend_stroke_factor", text="Size") + row = col.row(align=True) + row.prop(gp_settings, "fill_extend_mode", text="Mode") + row = col.row(align=True) + row.prop(gp_settings, "show_fill_extend", text="Visual Aids") + + if gp_settings.fill_extend_mode == 'EXTEND': + row = col.row(align=True) + row.prop(gp_settings, "use_collide_strokes") + + # Grease Pencil stroke sculpting tools class GreasePencilSculptPanel: bl_context = ".greasepencil_sculpt" @@ -1917,7 +1915,7 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePenc if brush is not None: col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - if(brush.use_custom_icon): + if (brush.use_custom_icon): layout.row().prop(brush, "icon_filepath", text="") @@ -1966,7 +1964,7 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_brush_advanced(GreasePencilSculptAdva return False tool = brush.gpencil_sculpt_tool - return tool != 'CLONE' + return tool in {'SMOOTH', 'RANDOMIZE'} class VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover(GreasePencilSculptAdvancedPanel, View3DPanel, Panel): @@ -1984,7 +1982,7 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_brush_popover(GreasePencilSculptAdvan return False tool = brush.gpencil_sculpt_tool - return tool != 'CLONE' + return tool in {'SMOOTH', 'RANDOMIZE'} # Grease Pencil weight painting tools @@ -2026,7 +2024,7 @@ class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, Grea if brush is not None: col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - if(brush.use_custom_icon): + if (brush.use_custom_icon): layout.row().prop(brush, "icon_filepath", text="") @@ -2101,7 +2099,7 @@ class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, Grea if brush is not None: col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") - if(brush.use_custom_icon): + if (brush.use_custom_icon): layout.row().prop(brush, "icon_filepath", text="") @@ -2361,7 +2359,6 @@ class VIEW3D_PT_gpencil_brush_presets(Panel, PresetPanel): classes = ( VIEW3D_MT_brush_context_menu, VIEW3D_MT_brush_gpencil_context_menu, - VIEW3D_MT_brush_context_menu_paint_modes, VIEW3D_PT_tools_object_options, VIEW3D_PT_tools_object_options_transform, VIEW3D_PT_tools_meshedit_options, @@ -2430,6 +2427,7 @@ classes = ( VIEW3D_PT_tools_grease_pencil_brush_post_processing, VIEW3D_PT_tools_grease_pencil_brush_random, VIEW3D_PT_tools_grease_pencil_brush_stabilizer, + VIEW3D_PT_tools_grease_pencil_brush_gap_closure, VIEW3D_PT_tools_grease_pencil_paint_appearance, VIEW3D_PT_tools_grease_pencil_sculpt_select, VIEW3D_PT_tools_grease_pencil_sculpt_settings, |