Fix: Nodes: Inaccurate placement of framed nodes in swap operators

The swap operators use `Node.location` for location calculations. This expresses
a node's location relative to their parent frames.

This can lead to inaccurate location calculations if nodes have different parent
frames. This was partly addressed by using the custom `temporary_unframe`
context manager. However, that method is sensitive to the order of steps
executed by the operator, making it easy to accidentally let a few location bugs
go through.

This patch fixes the issue by using `Node.location_absolute` which gives the
node's absolute location. This also gets rid of the need for
`temporary_unframe`, and makes the location calculations more straightforward.

Pull Request: https://projects.blender.org/blender/blender/pulls/147424
This commit is contained in:
quackarooni
2025-10-07 12:33:30 +02:00
committed by Jacques Lucke
parent b24cc091f0
commit 1a6d932fbd

View File

@@ -43,24 +43,6 @@ switch_nodes = {
}
# A context manager for temporarily un-parenting nodes from their frames.
# This gets rid of issues with framed nodes using relative coordinates.
class temporary_unframe:
def __init__(self, nodes):
self.parent_dict = {}
for node in nodes:
if node.parent is not None:
self.parent_dict[node] = node.parent
node.parent = None
def __enter__(self):
return self
def __exit__(self, _type, _value, _traceback):
for node, parent in self.parent_dict.items():
node.parent = parent
def cast_value(source, target):
source_type = source.type
target_type = target.type
@@ -492,16 +474,14 @@ class NODE_OT_swap_node(NodeSwapOperator, Operator):
new_node = self.create_node(context, self.type)
self.apply_node_settings(new_node)
self.transfer_node_properties(old_node, new_node)
if self.visible_output:
for socket in new_node.outputs:
if socket.name != self.visible_output:
socket.hide = True
with temporary_unframe((old_node,)):
new_node.location = old_node.location
new_node.select = True
new_node.location_absolute = old_node.location_absolute
new_node.select = True
zone_pair = self.get_zone_pair(tree, old_node)
@@ -509,10 +489,9 @@ class NODE_OT_swap_node(NodeSwapOperator, Operator):
input_node, output_node = zone_pair
if input_node.select and output_node.select:
with temporary_unframe((input_node, output_node)):
new_node.location = (input_node.location + output_node.location) / 2
new_node.select = True
new_node.location_absolute = (input_node.location_absolute + output_node.location_absolute) / 2
self.transfer_node_properties(old_node, new_node)
self.transfer_input_values(input_node, new_node)
self.transfer_links(tree, input_node, new_node, is_input=True)
@@ -521,6 +500,8 @@ class NODE_OT_swap_node(NodeSwapOperator, Operator):
for node in zone_pair:
nodes_to_delete.add(node)
else:
self.transfer_node_properties(old_node, new_node)
if (old_node.bl_idname in switch_nodes) and (new_node.bl_idname in switch_nodes):
self.transfer_switch_data(old_node, new_node)
@@ -770,9 +751,8 @@ class NODE_OT_swap_zone(ZoneOperator, NodeSwapOperator, Operator):
if zone_pair is not None:
old_input_node, old_output_node = zone_pair
with temporary_unframe((old_input_node, old_output_node)):
input_node.location = old_input_node.location
output_node.location = old_output_node.location
input_node.location_absolute = old_input_node.location_absolute
output_node.location_absolute = old_output_node.location_absolute
self.transfer_node_properties(old_input_node, input_node)
self.transfer_node_properties(old_output_node, output_node)
@@ -789,12 +769,8 @@ class NODE_OT_swap_zone(ZoneOperator, NodeSwapOperator, Operator):
for node in zone_pair:
nodes_to_delete.add(node)
else:
with temporary_unframe((old_node,)):
input_node.location = old_node.location
output_node.location = old_node.location
input_node.location -= Vector(self.offset)
output_node.location += Vector(self.offset)
input_node.location_absolute = (old_node.location_absolute - Vector(self.offset))
output_node.location_absolute = (old_node.location_absolute + Vector(self.offset))
self.transfer_node_properties(old_node, input_node)
self.transfer_node_properties(old_node, output_node)