| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | import bpy | 
					
						
							|  |  |  | from bpy.props import * | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import lnx.log | 
					
						
							|  |  |  | import lnx.make_state | 
					
						
							|  |  |  | import lnx.node_utils | 
					
						
							|  |  |  | import lnx.props_traits_props | 
					
						
							|  |  |  | import lnx.utils | 
					
						
							|  |  |  | import lnx.logicnode.lnx_nodes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if lnx.is_reload(__name__): | 
					
						
							|  |  |  |     lnx.log = lnx.reload_module(lnx.log) | 
					
						
							|  |  |  |     lnx.make_state = lnx.reload_module(lnx.make_state) | 
					
						
							|  |  |  |     lnx.node_utils = lnx.reload_module(lnx.node_utils) | 
					
						
							|  |  |  |     lnx.props_traits_props = lnx.reload_module(lnx.props_traits_props) | 
					
						
							|  |  |  |     lnx.utils = lnx.reload_module(lnx.utils) | 
					
						
							|  |  |  |     lnx.logicnode.lnx_nodes = lnx.reload_module(lnx.logicnode.lnx_nodes) | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     lnx.enable_reload(__name__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_PT_Variables(bpy.types.Panel): | 
					
						
							|  |  |  |     bl_label = 'Tree Variables' | 
					
						
							|  |  |  |     bl_idname = 'LNX_PT_Variables' | 
					
						
							|  |  |  |     bl_space_type = 'NODE_EDITOR' | 
					
						
							|  |  |  |     bl_region_type = 'UI' | 
					
						
							|  |  |  |     bl_category = 'Leenkx' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         return context.space_data.tree_type == 'LnxLogicTreeType' and context.space_data.edit_tree | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def draw(self, context): | 
					
						
							|  |  |  |         layout = self.layout | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         node = context.active_node | 
					
						
							|  |  |  |         if node is None or node.lnx_logic_id == '': | 
					
						
							|  |  |  |             layout.operator('lnx.variable_promote_node', icon='PLUS') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             layout.operator('lnx.variable_node_make_local', icon='TRIA_DOWN_BAR') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         row = layout.row(align=True) | 
					
						
							|  |  |  |         col = row.column(align=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         num_prop_rows = max(len(tree.lnx_treevariableslist), 6) | 
					
						
							|  |  |  |         col.template_list('LNX_UL_TreeVarList', '', tree, 'lnx_treevariableslist', tree, 'lnx_treevariableslist_index', rows=num_prop_rows) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         col.operator('lnx.variable_assign_to_node', icon='NODE') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if len(tree.lnx_treevariableslist) > 0: | 
					
						
							|  |  |  |             selected_item = tree.lnx_treevariableslist[tree.lnx_treevariableslist_index] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             col.separator() | 
					
						
							|  |  |  |             sub_row = col.row(align=True) | 
					
						
							|  |  |  |             sub_row.alignment = 'EXPAND' | 
					
						
							|  |  |  |             op = sub_row.operator('lnx.add_var_node') | 
					
						
							|  |  |  |             op.node_id = selected_item.name | 
					
						
							|  |  |  |             op.node_type = selected_item.node_type | 
					
						
							|  |  |  |             op = sub_row.operator('lnx.add_setvar_node') | 
					
						
							|  |  |  |             op.node_id = selected_item.name | 
					
						
							|  |  |  |             op.node_type = selected_item.node_type | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         col = row.column(align=True) | 
					
						
							|  |  |  |         col.enabled = len(tree.lnx_treevariableslist) > 1 | 
					
						
							|  |  |  |         col.operator('lnx_treevariableslist.move_item', icon='TRIA_UP', text='').direction = 'UP' | 
					
						
							|  |  |  |         col.operator('lnx_treevariableslist.move_item', icon='TRIA_DOWN', text='').direction = 'DOWN' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if len(tree.lnx_treevariableslist) > 0: | 
					
						
							|  |  |  |             selected_item = tree.lnx_treevariableslist[tree.lnx_treevariableslist_index] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             box = layout.box() | 
					
						
							|  |  |  |             box.label(text='Selected Variable:') | 
					
						
							|  |  |  |             master_node = lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin.get_master_node(tree, selected_item.name) | 
					
						
							|  |  |  |             master_node.draw_content(context, box) | 
					
						
							|  |  |  |             for inp in master_node.inputs: | 
					
						
							|  |  |  |                 if hasattr(inp, 'draw'): | 
					
						
							|  |  |  |                     inp.draw(context, box, master_node, inp.label if inp.label is not None else inp.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_OT_TreeVariablePromoteNode(bpy.types.Operator): | 
					
						
							|  |  |  |     bl_idname = 'lnx.variable_promote_node' | 
					
						
							|  |  |  |     bl_label = 'New Var From Node' | 
					
						
							|  |  |  |     bl_description = 'Create a tree variable from the active node and promote it to a tree variable node' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var_name: StringProperty( | 
					
						
							|  |  |  |         name='Name', | 
					
						
							|  |  |  |         description='Name of the new tree variable', | 
					
						
							|  |  |  |         default='Untitled' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         if not lnx.logicnode.lnx_nodes.is_logic_node_edit_context(context): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         if tree is None: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node = context.active_node | 
					
						
							|  |  |  |         if node is None or not node.bl_idname.startswith('LN'): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not isinstance(node, lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return node.lnx_logic_id == '' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def invoke(self, context, event): | 
					
						
							|  |  |  |         wm = context.window_manager | 
					
						
							|  |  |  |         return wm.invoke_props_dialog(self, width=400) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         node: lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin = context.active_node | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var_type = node.bl_idname | 
					
						
							|  |  |  |         var_item = LNX_PG_TreeVarListItem.create_new(tree, self.var_name, var_type) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node.is_master_node = True | 
					
						
							|  |  |  |         node.lnx_logic_id = var_item.name | 
					
						
							|  |  |  |         node.use_custom_color = True | 
					
						
							|  |  |  |         node.color = var_item.color | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lnx.make_state.redraw_ui = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def draw(self, context): | 
					
						
							|  |  |  |         layout = self.layout | 
					
						
							|  |  |  |         row = layout.row() | 
					
						
							|  |  |  |         row.scale_y = 1.3 | 
					
						
							|  |  |  |         row.activate_init = True | 
					
						
							|  |  |  |         row.prop(self, 'var_name') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_OT_TreeVariableMakeLocalNode(bpy.types.Operator): | 
					
						
							|  |  |  |     bl_idname = 'lnx.variable_node_make_local' | 
					
						
							|  |  |  |     bl_label = 'Make Node Local' | 
					
						
							|  |  |  |     bl_description = ( | 
					
						
							|  |  |  |         'Remove the reference to the tree variable from the active node. ' | 
					
						
							|  |  |  |         'If the active node is the only node that links to the selected ' | 
					
						
							|  |  |  |         'tree variable, the tree variable is removed' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         if not lnx.logicnode.lnx_nodes.is_logic_node_edit_context(context): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         if tree is None: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node = context.active_node | 
					
						
							|  |  |  |         if node is None or not node.bl_idname.startswith('LN'): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not isinstance(node, lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return node.lnx_logic_id != '' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         node: lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin = context.active_node | 
					
						
							|  |  |  |         node.make_local() | 
					
						
							|  |  |  |         node.color = [0.608, 0.608, 0.608]  # default color | 
					
						
							|  |  |  |         node.use_custom_color = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_OT_TreeVariableVariableAssignToNode(bpy.types.Operator): | 
					
						
							|  |  |  |     bl_idname = 'lnx.variable_assign_to_node' | 
					
						
							|  |  |  |     bl_label = 'Assign To Node' | 
					
						
							|  |  |  |     bl_description = ( | 
					
						
							|  |  |  |             'Assign the selected tree variable to the active variable node. ' | 
					
						
							|  |  |  |             'The variable node must have the same type as the variable' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         if not lnx.logicnode.lnx_nodes.is_logic_node_edit_context(context): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         if tree is None or len(tree.lnx_treevariableslist) == 0: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node = context.active_node | 
					
						
							|  |  |  |         if node is None or not node.bl_idname.startswith('LN'): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not isinstance(node, lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Only assign variables to nodes of the correct type | 
					
						
							|  |  |  |         if node.bl_idname != tree.lnx_treevariableslist[tree.lnx_treevariableslist_index].node_type: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         node: lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin = context.active_node | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var_item = tree.lnx_treevariableslist[tree.lnx_treevariableslist_index] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Make node local first to ensure the old tree variable (if | 
					
						
							|  |  |  |         # linked) is notified that the node is no longer linked | 
					
						
							|  |  |  |         if node.lnx_logic_id != var_item.name: | 
					
						
							|  |  |  |             node.make_local() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node.lnx_logic_id = var_item.name | 
					
						
							|  |  |  |         node.use_custom_color = True | 
					
						
							|  |  |  |         node.color = var_item.color | 
					
						
							|  |  |  |         lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin.synchronize(tree, node.lnx_logic_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_OT_TreeVariableListMoveItem(bpy.types.Operator): | 
					
						
							|  |  |  |     bl_idname = 'lnx_treevariableslist.move_item' | 
					
						
							|  |  |  |     bl_label = 'Move' | 
					
						
							|  |  |  |     bl_description = 'Move an item in the list' | 
					
						
							|  |  |  |     bl_options = {'UNDO', 'INTERNAL'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     direction: EnumProperty( | 
					
						
							|  |  |  |         items=( | 
					
						
							|  |  |  |             ('UP', 'Up', ''), | 
					
						
							|  |  |  |             ('DOWN', 'Down', '') | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         index = tree.lnx_treevariableslist_index | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         max_index = len(tree.lnx_treevariableslist) - 1 | 
					
						
							|  |  |  |         new_index = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.direction == 'UP': | 
					
						
							|  |  |  |             new_index = index - 1 | 
					
						
							|  |  |  |         elif self.direction == 'DOWN': | 
					
						
							|  |  |  |             new_index = index + 1 | 
					
						
							|  |  |  |         new_index = max(0, min(new_index, max_index)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tree.lnx_treevariableslist.move(index, new_index) | 
					
						
							|  |  |  |         tree.lnx_treevariableslist_index = new_index | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return{'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_OT_AddVarGetterNode(bpy.types.Operator): | 
					
						
							|  |  |  |     """Add a node to get the value of the selected tree variable""" | 
					
						
							|  |  |  |     bl_idname = 'lnx.add_var_node' | 
					
						
							|  |  |  |     bl_label = 'Add Getter' | 
					
						
							|  |  |  |     bl_options = {'GRAB_CURSOR', 'BLOCKING'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     node_id: StringProperty() | 
					
						
							|  |  |  |     node_type: StringProperty() | 
					
						
							|  |  |  |     getter_node_ref = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         if not lnx.logicnode.lnx_nodes.is_logic_node_edit_context(context): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         return tree is not None and len(tree.lnx_treevariableslist) > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def invoke(self, context, event): | 
					
						
							|  |  |  |         context.window_manager.modal_handler_add(self) | 
					
						
							|  |  |  |         self.execute(context) | 
					
						
							|  |  |  |         return {'RUNNING_MODAL'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def modal(self, context, event): | 
					
						
							|  |  |  |         if event.type == 'MOUSEMOVE': | 
					
						
							|  |  |  |             self.getter_node_ref.location = context.space_data.cursor_location | 
					
						
							|  |  |  |         elif event.type == 'LEFTMOUSE':  # Confirm | 
					
						
							|  |  |  |             return {'FINISHED'} | 
					
						
							|  |  |  |         return {'RUNNING_MODAL'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         self.getter_node_ref = self.create_getter_node(context, self.node_type, self.node_id) | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def create_getter_node(context, node_type: str, node_id: str) -> lnx.logicnode.lnx_nodes.LnxLogicTreeNode: | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         nodes = context.space_data.path[-1].node_tree.nodes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node = nodes.new(node_type) | 
					
						
							|  |  |  |         node.location = context.space_data.cursor_location | 
					
						
							|  |  |  |         node.lnx_logic_id = node_id | 
					
						
							|  |  |  |         node.use_custom_color = True | 
					
						
							|  |  |  |         node.color = tree.lnx_treevariableslist[tree.lnx_treevariableslist_index].color | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin.synchronize(tree, node.lnx_logic_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return node | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_OT_AddVarSetterNode(bpy.types.Operator): | 
					
						
							|  |  |  |     """Add a node to set the value of the selected tree variable""" | 
					
						
							|  |  |  |     bl_idname = 'lnx.add_setvar_node' | 
					
						
							|  |  |  |     bl_label = 'Add Setter' | 
					
						
							|  |  |  |     bl_options = {'GRAB_CURSOR', 'BLOCKING'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     node_id: StringProperty() | 
					
						
							|  |  |  |     node_type: StringProperty() | 
					
						
							|  |  |  |     getter_node_ref = None | 
					
						
							|  |  |  |     setter_node_ref = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							|  |  |  |         if not lnx.logicnode.lnx_nodes.is_logic_node_edit_context(context): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         tree: bpy.types.NodeTree = context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         return tree is not None and len(tree.lnx_treevariableslist) > 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def invoke(self, context, event): | 
					
						
							|  |  |  |         context.window_manager.modal_handler_add(self) | 
					
						
							|  |  |  |         self.execute(context) | 
					
						
							|  |  |  |         return {'RUNNING_MODAL'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def modal(self, context, event): | 
					
						
							|  |  |  |         if event.type == 'MOUSEMOVE': | 
					
						
							|  |  |  |             self.setter_node_ref.location = context.space_data.cursor_location | 
					
						
							|  |  |  |             self.getter_node_ref.location[0] = context.space_data.cursor_location[0] | 
					
						
							|  |  |  |             self.getter_node_ref.location[1] = context.space_data.cursor_location[1] - self.setter_node_ref.height - 17 | 
					
						
							|  |  |  |         elif event.type == 'LEFTMOUSE':  # Confirm | 
					
						
							|  |  |  |             return {'FINISHED'} | 
					
						
							|  |  |  |         return {'RUNNING_MODAL'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         nodes = context.space_data.path[-1].node_tree.nodes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         node = LNX_OT_AddVarGetterNode.create_getter_node(context, self.node_type, self.node_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setter_node = nodes.new('LNSetVariableNode') | 
					
						
							|  |  |  |         setter_node.location = context.space_data.cursor_location | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         links = context.space_data.path[-1].node_tree.links | 
					
						
							|  |  |  |         links.new(node.outputs[0], setter_node.inputs[1]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.getter_node_ref = node | 
					
						
							|  |  |  |         self.setter_node_ref = setter_node | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_PG_TreeVarListItem(bpy.types.PropertyGroup): | 
					
						
							|  |  |  |     def _set_name(self, value: str): | 
					
						
							|  |  |  |         old_name = self._get_name() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 12:44:04 -07:00
										 |  |  |         if bpy.context.space_data is not None: | 
					
						
							|  |  |  |             tree = bpy.context.space_data.path[-1].node_tree | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return  # No valid context | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  |         lst = tree.lnx_treevariableslist | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if value == '': | 
					
						
							|  |  |  |             # Don't allow empty variable names | 
					
						
							|  |  |  |             new_name = old_name | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             new_name = lnx.utils.unique_name_in_lists(item_lists=[lst], name_attr='name', wanted_name=value, ignore_item=self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self['_name'] = new_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for node in tree.nodes: | 
					
						
							|  |  |  |             if node.lnx_logic_id == old_name: | 
					
						
							|  |  |  |                 node.lnx_logic_id = new_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _get_name(self) -> str: | 
					
						
							|  |  |  |         return self.get('_name', 'Untitled') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _update_color(self, context): | 
					
						
							|  |  |  |         space = context.space_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Can be None if color is set before tree is initialized (upon | 
					
						
							|  |  |  |         # updating old files to newer SDK for example) | 
					
						
							|  |  |  |         if space is not None: | 
					
						
							|  |  |  |             for node in space.path[-1].node_tree.nodes: | 
					
						
							|  |  |  |                 if node.lnx_logic_id == self.name: | 
					
						
							|  |  |  |                     node.use_custom_color = True | 
					
						
							|  |  |  |                     node.color = self.color | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     name: StringProperty( | 
					
						
							|  |  |  |         name='Name', | 
					
						
							|  |  |  |         description='The name of this variable', | 
					
						
							|  |  |  |         default='Untitled', | 
					
						
							|  |  |  |         get=_get_name, | 
					
						
							|  |  |  |         set=_set_name | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     node_type: StringProperty( | 
					
						
							|  |  |  |         name='Type', | 
					
						
							|  |  |  |         description='The type of this variable/the bl_idname of the node\'s that may use this variable', | 
					
						
							|  |  |  |         default='LNIntegerNode' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     color: FloatVectorProperty( | 
					
						
							|  |  |  |         name='Color', | 
					
						
							|  |  |  |         description='The color of the nodes that link to this tree variable', | 
					
						
							|  |  |  |         subtype='COLOR', | 
					
						
							|  |  |  |         default=[1.0, 1.0, 1.0], | 
					
						
							|  |  |  |         update=_update_color, | 
					
						
							|  |  |  |         size=3, | 
					
						
							|  |  |  |         min=0, | 
					
						
							|  |  |  |         max=1 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def create_new(cls, tree: bpy.types.NodeTree, item_name: str, item_type: str) -> 'LNX_PG_TreeVarListItem': | 
					
						
							|  |  |  |         lst = tree.lnx_treevariableslist | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var_item: LNX_PG_TreeVarListItem = lst.add() | 
					
						
							|  |  |  |         var_item['_name'] = lnx.utils.unique_name_in_lists( | 
					
						
							|  |  |  |             item_lists=[lst], name_attr='name', wanted_name=item_name, ignore_item=var_item | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         var_item.node_type = item_type | 
					
						
							|  |  |  |         var_item.color = lnx.utils.get_random_color_rgb() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tree.lnx_treevariableslist_index = len(lst) - 1 | 
					
						
							|  |  |  |         lnx.make_state.redraw_ui = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return var_item | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LNX_UL_TreeVarList(bpy.types.UIList): | 
					
						
							|  |  |  |     def draw_item(self, context, layout, data, item: LNX_PG_TreeVarListItem, icon, active_data, active_propname, index): | 
					
						
							|  |  |  |         node_type = lnx.utils.type_name_to_type(item.node_type).bl_label | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         row = layout.row(align=True) | 
					
						
							|  |  |  |         _row = row.row() | 
					
						
							|  |  |  |         _row.ui_units_x = 1.0 | 
					
						
							|  |  |  |         _row.prop(item, 'color', text='') | 
					
						
							|  |  |  |         row.prop(item, 'name', text='', emboss=False) | 
					
						
							|  |  |  |         row.label(text=node_type) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def on_update_node_logic_id(node: lnx.logicnode.lnx_nodes.LnxLogicTreeNode, context): | 
					
						
							|  |  |  |     node.on_logic_id_change() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def node_compat_sdk2203(): | 
					
						
							|  |  |  |     """Replace old lnx_logic_id system with tree variable system.""" | 
					
						
							|  |  |  |     for tree in bpy.data.node_groups: | 
					
						
							|  |  |  |         if tree.bl_idname == 'LnxLogicTreeType': | 
					
						
							|  |  |  |             # All tree variable nodes | 
					
						
							|  |  |  |             tv_nodes: dict[str, list[lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin]] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # The type of the tree variable. If two types are found for | 
					
						
							|  |  |  |             # a logic ID and one is dynamic, assume it's a getter node. | 
					
						
							|  |  |  |             # Otherwise show a warning upon conflict, it was undefined | 
					
						
							|  |  |  |             # behaviour before anyway. | 
					
						
							|  |  |  |             tv_types: dict[str, str] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # First pass: find all tree variable nodes and decide the | 
					
						
							|  |  |  |             # variable type in case of conflicts | 
					
						
							|  |  |  |             node: lnx.logicnode.lnx_nodes.LnxLogicTreeNode | 
					
						
							|  |  |  |             for node in list(tree.nodes): | 
					
						
							|  |  |  |                 if node.lnx_logic_id != '': | 
					
						
							|  |  |  |                     if not isinstance(node, lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin): | 
					
						
							|  |  |  |                         lnx.log.warn( | 
					
						
							|  |  |  |                             'While updating the file to the current SDK' | 
					
						
							|  |  |  |                             f' version, the node {node.name} in tree' | 
					
						
							|  |  |  |                             f' {tree.name} is no variable node but had' | 
					
						
							|  |  |  |                             ' a logic ID. The logic ID was reset to' | 
					
						
							|  |  |  |                             ' prevent undefined behaviour.' | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                         node.lnx_logic_id = '' | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if node.lnx_logic_id in tv_nodes: | 
					
						
							|  |  |  |                         tv_nodes[node.lnx_logic_id].append(node) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         # Check for getter nodes and type conflicts | 
					
						
							|  |  |  |                         cur_type = tv_types[node.lnx_logic_id] | 
					
						
							|  |  |  |                         if cur_type == 'LNDynamicNode': | 
					
						
							|  |  |  |                             tv_types[node.lnx_logic_id] = node.bl_idname | 
					
						
							|  |  |  |                         elif cur_type != node.bl_idname and node.bl_idname != 'LNDynamicNode': | 
					
						
							|  |  |  |                             lnx.log.warn( | 
					
						
							|  |  |  |                                 'Found nodes of different types with the' | 
					
						
							|  |  |  |                                 ' same logic ID while updating the file' | 
					
						
							|  |  |  |                                 ' to the current SDK version (undefined' | 
					
						
							|  |  |  |                                 ' behaviour).\n' | 
					
						
							|  |  |  |                                 f'\tConflicting types: {cur_type}, {node.bl_idname}\n' | 
					
						
							|  |  |  |                                 f'\tLogic ID: {node.lnx_logic_id}\n' | 
					
						
							|  |  |  |                                 f'\tNew type for both nodes: {cur_type}' | 
					
						
							|  |  |  |                             ) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         tv_nodes[node.lnx_logic_id] = [node] | 
					
						
							|  |  |  |                         tv_types[node.lnx_logic_id] = node.bl_idname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Second pass: add the tree variable and convert all found | 
					
						
							|  |  |  |             # tree var nodes to the correct type | 
					
						
							|  |  |  |             for logic_id in tv_nodes.keys(): | 
					
						
							|  |  |  |                 var_type = tv_types[logic_id] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var_item = LNX_PG_TreeVarListItem.create_new(tree, logic_id, var_type) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for node in tv_nodes[logic_id]: | 
					
						
							|  |  |  |                     if node.bl_idname != var_type: | 
					
						
							|  |  |  |                         newnode = tree.nodes.new(var_type) | 
					
						
							|  |  |  |                         lnx.node_utils.copy_basic_node_props(from_node=node, to_node=newnode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         # Connect outputs as good as possible | 
					
						
							|  |  |  |                         for i in range(min(len(node.outputs), len(newnode.outputs))): | 
					
						
							|  |  |  |                             for out in node.outputs: | 
					
						
							|  |  |  |                                 for link in out.links: | 
					
						
							|  |  |  |                                     tree.links.new(newnode.outputs[i], link.to_socket) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         tree.nodes.remove(node) | 
					
						
							|  |  |  |                         node = newnode | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # Hide sockets | 
					
						
							|  |  |  |                     node.on_logic_id_change() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     node.use_custom_color = True | 
					
						
							|  |  |  |                     node.color = var_item.color | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin.choose_new_master_node(tree, logic_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def node_compat_sdk2209(): | 
					
						
							|  |  |  |     # See https://github.com/leenkx3d/leenkx/pull/2538 | 
					
						
							|  |  |  |     for tree in bpy.data.node_groups: | 
					
						
							|  |  |  |         if tree.bl_idname == "LnxLogicTreeType": | 
					
						
							|  |  |  |             for item in tree.lnx_treevariableslist: | 
					
						
							|  |  |  |                 lnx.logicnode.lnx_nodes.LnxLogicVariableNodeMixin.synchronize(tree, item.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __REG_CLASSES = ( | 
					
						
							|  |  |  |     LNX_PT_Variables, | 
					
						
							|  |  |  |     LNX_OT_TreeVariableListMoveItem, | 
					
						
							|  |  |  |     LNX_OT_TreeVariableMakeLocalNode, | 
					
						
							|  |  |  |     LNX_OT_TreeVariableVariableAssignToNode, | 
					
						
							|  |  |  |     LNX_OT_TreeVariablePromoteNode, | 
					
						
							|  |  |  |     LNX_OT_AddVarGetterNode, | 
					
						
							|  |  |  |     LNX_OT_AddVarSetterNode, | 
					
						
							|  |  |  |     LNX_UL_TreeVarList, | 
					
						
							|  |  |  |     LNX_PG_TreeVarListItem, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | __reg_classes, __unreg_classes = bpy.utils.register_classes_factory(__REG_CLASSES) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def register(): | 
					
						
							|  |  |  |     __reg_classes() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bpy.types.Node.lnx_logic_id = StringProperty( | 
					
						
							|  |  |  |         name='ID', | 
					
						
							|  |  |  |         description='Nodes with equal identifier share data', | 
					
						
							|  |  |  |         default='', | 
					
						
							|  |  |  |         update=on_update_node_logic_id | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bpy.types.NodeTree.lnx_treevariableslist = CollectionProperty(type=LNX_PG_TreeVarListItem) | 
					
						
							|  |  |  |     bpy.types.NodeTree.lnx_treevariableslist_index = IntProperty(name='Index for lnx_variableslist', default=0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def unregister(): | 
					
						
							|  |  |  |     __unreg_classes() |