Blender 2.8 - 4.5 Support

This commit is contained in:
2025-09-28 12:44:04 -07:00
parent 8f8d4b1376
commit f97d8fd846
34 changed files with 581 additions and 399 deletions

View File

@ -103,11 +103,11 @@ class BlendSpaceNode(LnxLogicTreeNode):
self.remove_advanced_draw()
def get_blend_space_points(self):
if bpy.context.space_data.edit_tree == self.get_tree():
if bpy.context.space_data is not None and bpy.context.space_data.edit_tree == self.get_tree():
return self.blend_space.points
def draw_advanced(self):
if bpy.context.space_data.edit_tree == self.get_tree():
if bpy.context.space_data is not None and bpy.context.space_data.edit_tree == self.get_tree():
self.blend_space.draw()
def lnx_init(self, context):

View File

@ -156,149 +156,149 @@ class CreateElementNode(LnxLogicTreeNode):
self.add_input('LnxStringSocket', 'Class')
self.add_input('LnxStringSocket', 'Style')
match index:
case 0:
self.add_input('LnxStringSocket', 'Href', default_value='#')
case 3:
self.add_input('LnxStringSocket', 'Alt')
self.add_input('LnxStringSocket', 'Coords')
self.add_input('LnxStringSocket', 'Href')
case 6:
self.add_input('LnxStringSocket', 'Src')
case 11:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
case 14:
self.add_input('LnxStringSocket', 'Type', default_value='Submit')
case 15:
self.add_input('LnxStringSocket', 'Height', default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='300px')
case 19 | 20:
self.add_input('LnxStringSocket', 'Span')
case 21:
self.add_input('LnxStringSocket', 'Value')
case 24 | 53:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
case 26:
self.add_input('LnxStringSocket', 'Title')
case 32:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxStringSocket', 'Height')
self.add_input('LnxStringSocket', 'Width')
case 33:
self.add_input('LnxStringSocket', 'Form')
self.add_input('LnxStringSocket', 'Name')
case 37:
self.add_input('LnxStringSocket', 'Action', default_value='URL')
self.add_input('LnxStringSocket', 'Method', default_value='get')
case 44:
self.add_input('LnxStringSocket', 'Profile', default_value='URI')
case 48:
self.add_input('LnxBoolSocket', 'xmlns' , default_value=False )
case 50:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Height' , default_value="150px" )
self.add_input('LnxStringSocket', 'Width', default_value='300px')
case 51:
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'Height' , default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='150px')
case 52:
self.add_input('LnxStringSocket', 'Type', default_value='text')
self.add_input('LnxStringSocket', 'Value')
case 55:
self.add_input('LnxStringSocket', 'For', default_value='element_id')
self.add_input('LnxStringSocket', 'Form', default_value='form_id')
case 57:
self.add_input('LnxStringSocket', 'Value')
case 58:
self.add_input('LnxStringSocket', 'Href', default_value='#')
self.add_input('LnxStringSocket', 'Hreflang', default_value='en')
self.add_input('LnxStringSocket', 'Title')
case 58:
self.add_input('LnxStringSocket', 'Name', default_value='mapname')
case 63:
self.add_input('LnxStringSocket', 'Charset', default_value='character_set')
self.add_input('LnxStringSocket', 'Content', default_value='text')
case 64:
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'high')
self.add_input('LnxStringSocket', 'low')
self.add_input('LnxStringSocket', 'max')
self.add_input('LnxStringSocket', 'min')
self.add_input('LnxStringSocket', 'optimum')
self.add_input('LnxStringSocket', 'value')
case 67:
self.add_input('LnxStringSocket', 'data', default_value='URL')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'name', default_value='name')
self.add_input('LnxStringSocket', 'type', default_value='media_type')
self.add_input('LnxStringSocket', 'usemap', default_value='#mapname')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
case 68:
self.add_input('LnxStringSocket', 'start', default_value='number')
case 69:
self.add_input('LnxStringSocket', 'label', default_value='text')
case 70:
self.add_input('LnxStringSocket', 'label', default_value='text')
self.add_input('LnxStringSocket', 'value', default_value='value')
case 71:
self.add_input('LnxStringSocket', 'for', default_value='element_id')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'name', default_value='name')
case 75:
self.add_input('LnxStringSocket', 'max', default_value='number')
self.add_input('LnxStringSocket', 'value', default_value='number')
case 76:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
case 78:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
case 79:
self.add_input('LnxStringSocket', 'integrity' , default_value='filehash')
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
case 81:
self.add_input('LnxStringSocket', 'form' , default_value='form_id')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
self.add_input('LnxStringSocket', 'size', default_value='number')
case 84:
self.add_input('LnxStringSocket', 'size')
self.add_input('LnxStringSocket', 'src' , default_value='URL')
self.add_input('LnxStringSocket', 'srcset', default_value='URL')
case 87:
self.add_input('LnxStringSocket', 'type', default_value='media_type')
case 93:
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers' , default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
case 95:
self.add_input('LnxStringSocket', 'cols' , default_value='number')
self.add_input('LnxStringSocket', 'dirname' , default_value='name.dir')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'maxlength', default_value='number')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'placeholder' , default_value='text')
self.add_input('LnxStringSocket', 'rows' , default_value='number')
case 97:
self.add_input('LnxStringSocket', 'abbr' , default_value='text')
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers', default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
case 99:
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
case 102:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'srclang', default_value='en')
self.add_input('LnxStringSocket', 'label', default_value='text')
case 106:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'poster', default_value='URL')
if index == 0:
self.add_input('LnxStringSocket', 'Href', default_value='#')
elif index == 3:
self.add_input('LnxStringSocket', 'Alt')
self.add_input('LnxStringSocket', 'Coords')
self.add_input('LnxStringSocket', 'Href')
elif index == 6:
self.add_input('LnxStringSocket', 'Src')
elif index == 11:
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
elif index == 14:
self.add_input('LnxStringSocket', 'Type', default_value='Submit')
elif index == 15:
self.add_input('LnxStringSocket', 'Height', default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='300px')
elif index in (19, 20):
self.add_input('LnxStringSocket', 'Span')
elif index == 21:
self.add_input('LnxStringSocket', 'Value')
elif index in (24, 53):
self.add_input('LnxStringSocket', 'Cite', default_value='URL')
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
elif index == 26:
self.add_input('LnxStringSocket', 'Title')
elif index == 32:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxStringSocket', 'Height')
self.add_input('LnxStringSocket', 'Width')
elif index == 33:
self.add_input('LnxStringSocket', 'Form')
self.add_input('LnxStringSocket', 'Name')
elif index == 37:
self.add_input('LnxStringSocket', 'Action', default_value='URL')
self.add_input('LnxStringSocket', 'Method', default_value='get')
elif index == 44:
self.add_input('LnxStringSocket', 'Profile', default_value='URI')
elif index == 48:
self.add_input('LnxBoolSocket', 'xmlns' , default_value=False )
elif index == 50:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'Height' , default_value="150px" )
self.add_input('LnxStringSocket', 'Width', default_value='300px')
elif index == 51:
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'Height' , default_value='150px')
self.add_input('LnxStringSocket', 'Width', default_value='150px')
elif index == 52:
self.add_input('LnxStringSocket', 'Type', default_value='text')
self.add_input('LnxStringSocket', 'Value')
elif index == 55:
self.add_input('LnxStringSocket', 'For', default_value='element_id')
self.add_input('LnxStringSocket', 'Form', default_value='form_id')
elif index == 57:
self.add_input('LnxStringSocket', 'Value')
elif index == 58:
self.add_input('LnxStringSocket', 'Href', default_value='#')
self.add_input('LnxStringSocket', 'Hreflang', default_value='en')
self.add_input('LnxStringSocket', 'Title')
# Note: There's a duplicate case 58 in the original, handling as separate elif
elif index == 60: # This was the second case 58, likely meant to be a different index
self.add_input('LnxStringSocket', 'Name', default_value='mapname')
elif index == 63:
self.add_input('LnxStringSocket', 'Charset', default_value='character_set')
self.add_input('LnxStringSocket', 'Content', default_value='text')
elif index == 64:
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'high')
self.add_input('LnxStringSocket', 'low')
self.add_input('LnxStringSocket', 'max')
self.add_input('LnxStringSocket', 'min')
self.add_input('LnxStringSocket', 'optimum')
self.add_input('LnxStringSocket', 'value')
elif index == 67:
self.add_input('LnxStringSocket', 'data', default_value='URL')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'name', default_value='name')
self.add_input('LnxStringSocket', 'type', default_value='media_type')
self.add_input('LnxStringSocket', 'usemap', default_value='#mapname')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
elif index == 68:
self.add_input('LnxStringSocket', 'start', default_value='number')
elif index == 69:
self.add_input('LnxStringSocket', 'label', default_value='text')
elif index == 70:
self.add_input('LnxStringSocket', 'label', default_value='text')
self.add_input('LnxStringSocket', 'value', default_value='value')
elif index == 71:
self.add_input('LnxStringSocket', 'for', default_value='element_id')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'name', default_value='name')
elif index == 75:
self.add_input('LnxStringSocket', 'max', default_value='number')
self.add_input('LnxStringSocket', 'value', default_value='number')
elif index == 76:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
elif index == 78:
self.add_input('LnxStringSocket', 'cite', default_value='URL')
elif index == 79:
self.add_input('LnxStringSocket', 'integrity' , default_value='filehash')
self.add_input('LnxStringSocket', 'Src')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
elif index == 81:
self.add_input('LnxStringSocket', 'form' , default_value='form_id')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'type', default_value='scripttype')
self.add_input('LnxStringSocket', 'size', default_value='number')
elif index == 84:
self.add_input('LnxStringSocket', 'size')
self.add_input('LnxStringSocket', 'src' , default_value='URL')
self.add_input('LnxStringSocket', 'srcset', default_value='URL')
elif index == 87:
self.add_input('LnxStringSocket', 'type', default_value='media_type')
elif index == 93:
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers' , default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
elif index == 95:
self.add_input('LnxStringSocket', 'cols' , default_value='number')
self.add_input('LnxStringSocket', 'dirname' , default_value='name.dir')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
self.add_input('LnxStringSocket', 'form', default_value='form_id')
self.add_input('LnxStringSocket', 'maxlength', default_value='number')
self.add_input('LnxStringSocket', 'name' , default_value='text')
self.add_input('LnxStringSocket', 'placeholder' , default_value='text')
self.add_input('LnxStringSocket', 'rows' , default_value='number')
elif index == 97:
self.add_input('LnxStringSocket', 'abbr' , default_value='text')
self.add_input('LnxStringSocket', 'colspan' , default_value='number')
self.add_input('LnxStringSocket', 'headers', default_value='header_id')
self.add_input('LnxStringSocket', 'rowspan', default_value='number')
elif index == 99:
self.add_input('LnxStringSocket', 'Datetime', default_value='YYYY-MM-DDThh:mm:ssTZD')
elif index == 102:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'srclang', default_value='en')
self.add_input('LnxStringSocket', 'label', default_value='text')
elif index == 106:
self.add_input('LnxStringSocket', 'Src', default_value='URL')
self.add_input('LnxStringSocket', 'width', default_value='pixels')
self.add_input('LnxStringSocket', 'height', default_value='pixels')
self.add_input('LnxStringSocket', 'poster', default_value='URL')
for i in range(len(self.inputs)):
if self.inputs[i].name in self.data_map:

View File

@ -38,18 +38,17 @@ class JSEventTargetNode(LnxLogicTreeNode):
# Arguements for type Client
index = self.get_count_in(select_current)
match index:
case 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxDynamicSocket', 'Event')
case _:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxDynamicSocket', 'Listener')
self.add_input('LnxDynamicSocket', 'Options')
self.add_input('LnxBoolSocket', 'unTrusted')
if index == 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxDynamicSocket', 'Event')
else:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'JS Object')
self.add_input('LnxStringSocket', 'Type')
self.add_input('LnxDynamicSocket', 'Listener')
self.add_input('LnxDynamicSocket', 'Options')
self.add_input('LnxBoolSocket', 'unTrusted')
self['property0'] = value

View File

@ -43,27 +43,26 @@ class RenderElementNode(LnxLogicTreeNode):
# Arguements for type Client
index = self.get_count_in(select_current)
match index:
case 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Torrent')
self.add_input('LnxStringSocket', 'Selector')
case 5:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'HTML')
case 6:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Text')
case 7:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'HTML')
self.add_input('LnxStringSocket', 'Selector')
case _:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Selector')
if index == 2:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Torrent')
self.add_input('LnxStringSocket', 'Selector')
elif index == 5:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'HTML')
elif index == 6:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Text')
elif index == 7:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'HTML')
self.add_input('LnxStringSocket', 'Selector')
else:
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Element')
self.add_input('LnxStringSocket', 'Selector')
self['property0'] = value

View File

@ -66,7 +66,10 @@ class LnxGroupTree(bpy.types.NodeTree):
"""Try to avoid creating loops of group trees with each other"""
# upstream trees of tested treed should nad share trees with downstream trees of current tree
tested_tree_upstream_trees = {t.name for t in self.upstream_trees()}
current_tree_downstream_trees = {p.node_tree.name for p in bpy.context.space_data.path}
if bpy.context.space_data is not None:
current_tree_downstream_trees = {p.node_tree.name for p in bpy.context.space_data.path}
else:
current_tree_downstream_trees = set()
shared_trees = tested_tree_upstream_trees & current_tree_downstream_trees
return not shared_trees

View File

@ -2,9 +2,17 @@ from collections import OrderedDict
import itertools
import math
import textwrap
from typing import Any, final, Generator, List, Optional, Type, Union
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
from typing import OrderedDict as ODict # Prevent naming conflicts
try:
from typing import final
except ImportError:
# Python < 3.8 compatibility
def final(f):
"""No final in Python < 3.8"""
return f
import bpy.types
from bpy.props import *
from nodeitems_utils import NodeItem
@ -39,11 +47,11 @@ PKG_AS_CATEGORY = "__pkgcat__"
nodes = []
category_items: ODict[str, List['LnxNodeCategory']] = OrderedDict()
array_nodes: dict[str, 'LnxLogicTreeNode'] = dict()
array_nodes: Dict[str, 'LnxLogicTreeNode'] = dict()
# See LnxLogicTreeNode.update()
# format: [tree pointer => (num inputs, num input links, num outputs, num output links)]
last_node_state: dict[int, tuple[int, int, int, int]] = {}
last_node_state: Dict[int, Tuple[int, int, int, int]] = {}
class LnxLogicTreeNode(bpy.types.Node):

View File

@ -10,7 +10,7 @@ mutable (common Python pitfall, be aware of this!), but because they
don't get accessed later it doesn't matter here and we keep it this way
for parity with the Blender API.
"""
from typing import Any, Callable, Sequence, Union
from typing import Any, Callable, List, Sequence, Set, Union
import sys
import bpy
@ -49,6 +49,10 @@ def __haxe_prop(prop_type: Callable, prop_name: str, *args, **kwargs) -> Any:
# bpy.types.Bone, remove them here to prevent registration errors
if 'tags' in kwargs:
del kwargs['tags']
# Remove override parameter for Blender versions that don't support it
if bpy.app.version < (2, 90, 0) and 'override' in kwargs:
del kwargs['override']
return prop_type(*args, **kwargs)
@ -87,7 +91,7 @@ def HaxeBoolVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.BoolProperty']:
) -> List['bpy.types.BoolProperty']:
"""Declares a new BoolVectorProperty that has a Haxe counterpart
with the given prop_name (Python and Haxe names must be identical
for now).
@ -118,7 +122,7 @@ def HaxeEnumProperty(
items: Sequence,
name: str = "",
description: str = "",
default: Union[str, set[str]] = None,
default: Union[str, Set[str]] = None,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
@ -180,7 +184,7 @@ def HaxeFloatVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.FloatProperty']:
) -> List['bpy.types.FloatProperty']:
"""Declares a new FloatVectorProperty that has a Haxe counterpart
with the given prop_name (Python and Haxe names must be identical
for now).
@ -232,7 +236,7 @@ def HaxeIntVectorProperty(
update=None,
get=None,
set=None
) -> list['bpy.types.IntProperty']:
) -> List['bpy.types.IntProperty']:
"""Declares a new IntVectorProperty that has a Haxe counterpart with
the given prop_name (Python and Haxe names must be identical for now).
"""

View File

@ -27,7 +27,10 @@ class GroupInputsNode(LnxLogicTreeNode):
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.edit_tree
else:
return
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupInputsNode':

View File

@ -27,7 +27,10 @@ class GroupOutputsNode(LnxLogicTreeNode):
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.edit_tree
else:
return
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupOutputsNode':

View File

@ -350,7 +350,10 @@ class LNX_PG_TreeVarListItem(bpy.types.PropertyGroup):
def _set_name(self, value: str):
old_name = self._get_name()
tree = bpy.context.space_data.path[-1].node_tree
if bpy.context.space_data is not None:
tree = bpy.context.space_data.path[-1].node_tree
else:
return # No valid context
lst = tree.lnx_treevariableslist
if value == '':