1
0

11 Commit-ok 5ce1e6fb59 ... 98705ef12d

Szerző SHA1 Üzenet Dátum
  Joseph Brandenburg 98705ef12d interface update causes tree update 6 napja
  Joseph Brandenburg 63a16dfd24 overlay variable for schema length 6 napja
  Joseph Brandenburg 0a6f922539 fix widget import in 5.2 1 hete
  Joseph Brandenburg 25ddc7b01a fix CTD from insert_link callback 1 hete
  Joseph Brandenburg 45f0fae36d fix bone display options versioning 1 hete
  Joseph Brandenburg dede5c4124 fix bbone display on bones 1 hete
  Joseph Brandenburg 88f0902c72 update tree when changing overlay 1 hete
  Joseph Brandenburg 6f802c1e4e meta-rig input use overlay 1 hete
  Joseph Brandenburg 0b9aee3cf0 move overlay stuff to new file 1 hete
  Joseph Brandenburg 321bdef59f overlay variables for float types 1 hete
  Joseph Brandenburg 148500fa6d variables in collection socket 1 hete

+ 32 - 12
base_definitions.py

@@ -10,7 +10,7 @@ from .utilities.utilities import (prRed, prGreen, prPurple, prWhite,
                               wrapRed, wrapGreen, wrapPurple, wrapWhite,
                               wrapOrange,)
 
-from .utilities.utilities import get_socket_maps, relink_socket_map, do_relink
+from .utilities.utilities import get_socket_maps, get_socket_overlay_info, do_relink
 from .mantis_dataclasses import MantisSocketTemplate
 
 MANTIS_VERSION_MAJOR=0
@@ -112,8 +112,8 @@ def update_reroute_cache_item_source_recursively(reroute, tree_cache, source):
                 )
             tree_cache[reroute.name] = cache_object
         cache_object.source = source
-        if reroute.outputs[0].is_linked: # reroutes are always single-output
-            for l in reroute.outputs[0].links: # with potentially many links
+        if rr_links := reroute.outputs[0].links: # reroutes are always single-output
+            for l in rr_links:                   # with potentially many links
                 if l.to_node.bl_idname == 'NodeReroute':
                     reroutes.add(l.to_node)
 
@@ -557,6 +557,7 @@ def node_group_update(node, force = False):
            (node.id_data.is_executing == True) or \
            (node.id_data.is_exporting == True):
             return
+
     # note: if (node.id_data.is_exporting == True) I need to be able to update so I can make links.
     if node.node_tree is None:
         node.inputs.clear(); node.outputs.clear()
@@ -578,10 +579,6 @@ def node_group_update(node, force = False):
         for i, socket in enumerate(collection):
             map[socket.identifier]=i
 
-    if node.node_tree is None:
-        node.inputs.clear(); node.outputs.clear()
-        node.id_data.do_live_update = toggle_update
-        return
     found_in, found_out = [], []
     update_input, update_output = False, False
     for item in node.node_tree.interface.items_tree:
@@ -636,6 +633,10 @@ def node_group_update(node, force = False):
     if not (update_input or update_output):
         node.id_data.do_live_update = toggle_update
         return
+    
+    # At this point, we know we're gonna change things so let's do this
+    # remember what has Overlay turned on and what the value is
+    overlay_state = get_socket_overlay_info(node) 
 
     if update_input or update_output and (socket_maps is not None):
         if update_input :
@@ -677,6 +678,7 @@ def node_group_update(node, force = False):
             socket_collection = node.inputs if is_input else node.outputs
             counter = input_index if is_input else output_index
             reorder_collection = reorder_me_input if is_input else reorder_me_output
+            socket = None
             if socket_map:
                 if item.identifier in socket_map.keys():
                     socket = relink_socket_map_add_socket(node, socket_collection, item, item.in_out)
@@ -733,6 +735,11 @@ def node_group_update(node, force = False):
                     to_index+=1
                 collection.move(i, to_index)
 
+        # restore the overlay state, whether it is using Overlay or not and the overlay text
+        for socket in node.inputs:
+            if socket and (overlay_text := overlay_state.get(socket.identifier)):
+                socket.use_overlay=True; socket.overlay_text = overlay_text
+        
         # at this point there is no wildcard socket
         if socket_map_in and '__extend__' in socket_map_in.keys():
             do_relink(node, None, socket_map_in, in_out='INPUT', parent_name='Constant' )
@@ -800,6 +807,9 @@ class MantisNodeGroup(Node, MantisUINode):
 class GraphError(Exception):
     pass
 
+class OverlayApplyError(Exception):
+    pass
+
 class BlenderVersionError(Exception):
     pass
 
@@ -1127,16 +1137,24 @@ class MantisNode:
         return change_handled
 
     def ui_modify_socket(self, ui_socket, socket_name=None) -> bool:
+        from .execution.overlays import ( node_scan_overlays, get_socket_idname_data_type,
+                                          parser_handle_text, handle_overlay_detect )
         """ Handle changes in the node's UI. Updates the rig if possible."""
         # Always update the node's data
         change_handled=False
         if socket_name is None: socket_name = ui_socket.name
         value = ui_socket.default_value
-        # apply overlay variables here so this updates predictably
+        if hasattr(ui_socket, 'use_overlay') and ui_socket.use_overlay:
+            value = ui_socket.overlay_text
+            if get_socket_idname_data_type(ui_socket.bl_idname) != str:
+                value = parser_handle_text(value)
+        # apply overlay variables here so this updates predictably            
         if (isinstance(value, str) and
                 (self.signature, ui_socket.identifier) in self.mContext.tagged_for_overlay):
-            from .execution.readtree import node_scan_overlays
-            value = node_scan_overlays(self, ui_socket.identifier)
+            try:
+                value = node_scan_overlays(self, ui_socket.identifier)
+            except OverlayApplyError:
+                return False # failed to apply the overlay and handle the change
         # TODO: I need to make Node Groups apply overlays downstream, but since
         #       they don't update live anyway, no need just yet
         if socket_name == 'Enable': value = not value
@@ -1184,7 +1202,7 @@ class MantisNode:
 
     def fill_parameters(self, ui_node=None)  -> None:
         from mathutils import Matrix
-        from .utilities.utilities import handle_overlay_detect
+        from .execution.overlays import handle_overlay_detect, get_socket_idname_data_type, parser_handle_text
         if not ui_node:
             if ( (self.signature[0] in FILL_PARAMETERS_SKIP_ROOTS) or
                 (self.signature[-1] in FILL_PARAMETERS_SKIP_NAMES) ): # I think this is harmless
@@ -1209,7 +1227,9 @@ class MantisNode:
                                                     tuple(v[12:16]) ))
                 elif (value := node_socket.default_value) is not None:
                     if hasattr(node_socket, 'use_overlay') and node_socket.use_overlay:
-                        value = "$"+node_socket.overlay_text
+                        value = node_socket.overlay_text
+                        if get_socket_idname_data_type(node_socket.bl_idname) != str:
+                            value = parser_handle_text(value)
                         # TODO: the overlay system should use a math parser for numerical types
                         #       that treats strings as variable names
                         # but for now I am just gonna hamfist it like this lol

+ 233 - 0
execution/overlays.py

@@ -0,0 +1,233 @@
+
+from ..utilities.utilities import (prRed, prGreen, prPurple, prWhite,
+                              prOrange,
+                              wrapRed, wrapGreen, wrapPurple, wrapWhite,
+                              wrapOrange,)
+
+from ..base_definitions import OverlayApplyError
+
+# For now, this is not yet implemented. lol
+def parser_handle_text(text):
+    return '$' + text
+
+def get_socket_idname_data_type(socket_idname):
+    data_type = str
+    match socket_idname:
+        case "IntSocket" | "UnsignedIntSocket":
+            data_type = int
+        case "FloatSocket" | "FloatPositiveSocket" | "FloatFactorSocket" | "FloatAngleSocket":
+            data_type = float
+    return (data_type)
+
+# If a $ character is in a value, it is an overlay request
+# in the root tree, supply it immediately, otherwise tag this node
+# once we finish pulling data from the tree we handle the overlay.
+def handle_overlay_detect(node, socket_id, socket_idname, value) -> str:
+    mContext = node.mContext
+    if node.signature[0] is None and len(node.signature) == 2:
+        value = fill_root_tree_overlays(mContext, socket_idname, value)
+    else: # since it is not root, we have to tag it.
+        mContext.tagged_for_overlay.append((node.signature, socket_id))
+    return value
+
+# The root tree doesn't have any interfaces to look up so we can fill it immediately
+def fill_root_tree_overlays(mContext, socket_idname, value):
+    root_tree = mContext.base_tree
+    root_items_tree = root_tree.interface.items_tree
+    socket_data_type = get_socket_idname_data_type(socket_idname)
+
+    for key, interface_item in root_items_tree.items():
+        if interface_item.item_type != "SOCKET": continue
+        if interface_item.in_out != "INPUT": continue
+        # then what the interface provides is determined by its idname in turn
+        match interface_item.bl_socket_idname:
+            case "IntSocket" | "BooleanSocket":
+                if socket_data_type in {int, float} and '$'+key == value:
+                    # TODO this is the wrong way to do this, replace this with parser later
+                    value = socket_data_type(interface_item.default_int)
+            case "FloatSocket":
+                if socket_data_type in {int, float} and '$'+key == value:
+                    value = socket_data_type(interface_item.default_float)
+            case "StringSocket" | 'BoneCollectionSocket':
+                if socket_data_type == str:
+                    value = socket_data_type(value.replace("$"+key, interface_item.default_string))
+    return value
+
+STRING_PADDING = 4
+overlay_valid_socket_types = [
+    "FloatSocket", "FloatPositiveSocket", "FloatFactorSocket",
+    "FloatAngleSocket", "UnsignedIntSocket", "IntSocket", "StringSocket",
+]
+# Do not use these input names as overlay keys:
+forbidden_key_names = ["",]
+forbidden_in_key_name = ['$',]
+def node_scan_overlays(node, socket_id,):
+    from ..misc_nodes import SimpleInputNode
+    from ..internal_nodes import AutoGenNode
+    from ..utilities.utilities import get_ui_node
+    from ..utilities.node_common import GraphError
+    interface_nodes = node.mContext.interface_nodes
+    if not isinstance(node, AutoGenNode):
+        ui_node = get_ui_node(node.ui_signature, node.base_tree)
+        # we'll assume that the overlay tag system checked if this is linked.
+        # TODO this is probably redundant with the check above this
+        # First let's check if it is a simple input node, by checking outputs that are marked "input"
+        for socket in ui_node.outputs:
+            # weird edge case for simple input nodes with unnamed sockets
+            # the socket.identifier is not used by Mantis but it defaults to 'socket'\
+            if socket.identifier == socket_id and socket.input == True: break
+        else:
+            for socket in ui_node.inputs:
+                if socket.identifier == socket_id: break
+            else:
+                raise KeyError(f"Socket not found by ID {socket_id} in node {node.ui_signature}\n"
+                            f"Candidates: {[socket.identifier for socket in ui_node.inputs]}")
+        value = socket.default_value # get the ACTUAL UI value
+        if hasattr(socket, 'overlay_text'):
+            value = socket.overlay_text
+            if get_socket_idname_data_type(socket.bl_idname) != str:
+                value = parser_handle_text(value)
+    else: # since it doesn't have a UI node
+        value = node.parameters[socket_id]
+    
+    overlay_data_type = str
+    if isinstance(node, (AutoGenNode) ):
+        overlay_data_type = type(node.parameters[list(node.outputs.values())[0].name])
+    else: # we have the UI socket
+        overlay_data_type = get_socket_idname_data_type(socket.bl_idname)
+    
+    if not isinstance(value, str): return
+    # First step: get the interface nodes and the overlay keys
+    # this should only check interfaces that are ancestors of this node
+    my_interfaces = []
+    for index in range(len(node.signature)-1):
+        if index == 0: continue # root
+        # we add one here because the node inside the schema references the schema's interface...
+        current_schema_interface_index = node.generated_at_schema_index[index+1]
+        if current_schema_interface_index < 0: # this indicates that it is not in a schema
+            interface = interface_nodes.get(
+                interface_signature := (None, *node.signature[1:index+1], "InputInterface"))
+        else: # First we get the interface that enters the schema (index 0)
+            interface = interface_nodes.get(
+                interface_signature := (None, *node.signature[1:index+1],
+                "InputInterface."+str( 0 ).zfill(4)))
+            if interface and interface not in my_interfaces: my_interfaces.append(interface)
+            if current_schema_interface_index == 0: continue # just did it
+            # we need to get the schema of the current index
+            interface = interface_nodes.get(
+                interface_signature := (None, *node.signature[1:index+1],
+                "InputInterface."+str(current_schema_interface_index).zfill(4)))
+        if interface:
+            my_interfaces.append( interface )
+    # the my_interfaces list is sorted in REVERSE order of importance
+    # Next, get all the overlay providers.
+    name_to_id_map = {}
+    name_to_type_map = {} # Just for printing reasonable error messages
+    overlay_keys = set()
+    root_overlay_values = {}
+    # First, we'll get the base tree's default inputs
+    for interface_item in node.mContext.base_tree.interface.items_tree:
+        if interface_item.item_type != "SOCKET": continue
+        if interface_item.in_out != "INPUT": continue
+        base_value=None
+        match interface_item.bl_socket_idname:
+            case "IntSocket" | "BooleanSocket":
+                # TODO parser for number types
+                base_value = interface_item.default_int
+            case "FloatSocket":
+                base_value = interface_item.default_float
+            case "StringSocket" | "BoneCollectionSocket":
+                base_value = interface_item.default_string
+        if base_value:
+            root_overlay_values[interface_item.name] = base_value
+        overlay_keys.add(interface_item.name)
+        name_to_id_map[interface_item.name] = None # don't worry about it
+        name_to_type_map[interface_item.name] = interface_item.bl_socket_idname
+    # BUG? should this be my_interfaces ? why bother with interfaces this may not be a part of?
+    for interface_node in interface_nodes.values():
+        # problem: the interface node stores socket identifiers as keys
+        # so in reality we need to get the group UI node and ask for its input names
+        # if there is a duplicate name, too bad!
+        group_ui_node = get_ui_node(interface_node.ui_signature[:-1], node.base_tree)
+        for interface_item in group_ui_node.node_tree.interface.items_tree:
+            if interface_item.item_type != 'SOCKET': continue
+            if interface_item.in_out != 'INPUT': continue
+            if interface_item.name in forbidden_key_names: continue
+            for substr in forbidden_in_key_name:
+                if substr in interface_item.name:
+                    break
+            else: # if we do not find a bad substr
+                name_to_id_map[interface_item.name]=interface_item.identifier
+                name_to_type_map[interface_item.name] = interface_item.bl_socket_idname
+                overlay_keys.add(interface_item.name)
+        # For schema interfaces, we also need to get the length and index.
+        if 'Index' in interface_node.outputs:
+            overlay_keys.add('Index'); overlay_keys.add('Length')
+    if 'Index' in overlay_keys:
+        name_to_id_map['Index']='Index'
+        name_to_type_map['Index']= "IntSocket"
+        name_to_id_map['Length']='Length'
+        name_to_type_map['Length']= "IntSocket"
+    # sort by reverse length so we find "CatsAndFrogs" before "Cats"
+    overlay_keys = sorted(overlay_keys, key= lambda a :-1*len(a))
+    # First I need to break it into substrings based on quotation marks
+    from ..utilities.utilities import split_quoted_substrings_to_strings
+    quoted_substrings = split_quoted_substrings_to_strings(value)
+    overlay_tries = []
+    for quoted_substring in  quoted_substrings:
+        overlay_tries.extend(quoted_substring.split("$"))
+    if overlay_data_type == str:
+        result = overlay_tries[0] # the 0th element is always before the split character
+    else: # TODO HACK later use the parser feature
+        result = 0
+    # the reason for the difference between overlay_try and key is because
+    #  we don't actually know if the $text part has a key in it
+    for overlay_try in overlay_tries[1:]:
+        if not overlay_try: continue # some of these are empty
+        overlay_resolved_value = "$" + overlay_try
+        for key in overlay_keys:
+            if key in overlay_try:
+                overlay_resolved_value = "$" + key
+                for interface in reversed(my_interfaces): # since this is reversed when we build it
+                    actual_key = name_to_id_map.get(key)
+                    socket = interface.inputs.get(actual_key)
+                    if key in ['Index', 'Length']:
+                        socket = interface.outputs.get(actual_key)
+                    if socket:
+                        overlay_value = None
+                        try:
+                            overlay_value = interface.evaluate_input(actual_key)
+                        except KeyError:
+                            if (socket_type := name_to_type_map.get(key) or 'NOT FOUND') in overlay_valid_socket_types:
+                                raise GraphError(f"Error: Variable {key} is not supported because it has "
+                                                 f"an invalid input in the node group: {interface.signature[:-1]}.")
+                            raise GraphError( f"Error: Variable {key} is not supported because it is "
+                                              f"type {socket_type}" )
+                            # NOTE that the above can also be caused by any BUG occuring in node group update
+                            # because it causes the interface to be invalid for the UI node that spawns the interface node.
+                        if overlay_data_type == str:
+                            if isinstance(overlay_value, (float, int)):
+                                overlay_value = str(overlay_value).zfill(STRING_PADDING)
+                            assert (isinstance(overlay_value, str)), f"Error: Variable {key} is not supported because it is type {type(overlay_value)}"
+                            overlay_resolved_value = overlay_try.replace(key, overlay_value)
+                        else:
+                            overlay_resolved_value = overlay_value
+                        break
+                else: # we'll try the base tree
+                    if base_tree_result := root_overlay_values.get(key):
+                        if overlay_data_type == str and not isinstance(base_tree_result, str):
+                            base_tree_result = str(base_tree_result).zfill(STRING_PADDING)
+                        overlay_resolved_value = base_tree_result
+                    else:
+                        prOrange(f"WARN: no overlay value applied for {node.ui_signature} for key {key}")
+                break # we found the key and are done here. One found Key per overlay-try
+        # this is where we can catch the errors if the type of the resolved value was wrong
+        if type(overlay_resolved_value) != overlay_data_type:
+            raise OverlayApplyError( f"Could not apply overlay variable to {node}::{socket_id}.")
+        result += overlay_resolved_value
+    # Simple Input sockets with unnamed sockets assign this ID automatically
+    # But Mantis uses the name instead. Usually the name and ID are the same.
+    if socket_id == 'socket' and ui_node.outputs[0].name == "":
+        socket_id = ''
+    node.parameters[socket_id] = result
+    return result # because sometimes I need to get the result, too

+ 1 - 178
execution/readtree.py

@@ -1,5 +1,6 @@
 from ..utilities.utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
                         wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
+from .overlays import node_scan_overlays
 
 # FIXME I don't think these signatures are unique.
 # TODO this is a really silly and bad and also really dumb way to do this
@@ -168,184 +169,6 @@ def data_from_tree(base_tree, tree_path, mContext, dummy_nodes, all_mantis_nodes
             data_from_tree(base_tree, tree_path+[mantis_node.ui_node], mContext, dummy_nodes, all_mantis_nodes, all_schema, in_schema)
     return dummy_nodes, all_mantis_nodes, all_schema
 
-STRING_PADDING = 4
-overlay_valid_socket_types = [
-    "FloatSocket", "FloatPositiveSocket", "FloatFactorSocket",
-    "FloatAngleSocket", "UnsignedIntSocket", "IntSocket", "StringSocket",
-]
-# Do not use these input names as overlay keys:
-forbidden_key_names = ["",]
-forbidden_in_key_name = ['$',]
-def node_scan_overlays(node, socket_id,):
-    from ..misc_nodes import SimpleInputNode
-    from ..internal_nodes import AutoGenNode
-    from ..utilities.utilities import get_ui_node
-    from ..utilities.node_common import GraphError
-    interface_nodes = node.mContext.interface_nodes
-    if not isinstance(node, AutoGenNode):
-        ui_node = get_ui_node(node.ui_signature, node.base_tree)
-        # we'll assume that the overlay tag system checked if this is linked.
-        # TODO this is probably redundant with the check above this
-        # First let's check if it is a simple input node, by checking outputs that are marked "input"
-        for socket in ui_node.outputs:
-            # weird edge case for simple input nodes with unnamed sockets
-            # the socket.identifier is not used by Mantis but it defaults to 'socket'\
-            if socket.identifier == socket_id and socket.input == True: break
-        else:
-            for socket in ui_node.inputs:
-                if socket.identifier == socket_id: break
-            else:
-                raise KeyError(f"Socket not found by ID {socket_id} in node {node.ui_signature}\n"
-                            f"Candidates: {[socket.identifier for socket in ui_node.inputs]}")
-        value = socket.default_value # get the ACTUAL UI value
-        if hasattr(socket, 'overlay_text'): # TODO this is all wrong but for now
-            value = "$"+socket.overlay_text # I am not implementing the Parser feature
-    else: # since it doesn't have a UI node
-        value = node.parameters[socket_id]
-    
-    overlay_data_type = str
-    if isinstance(node, (AutoGenNode) ):
-        overlay_data_type = type(node.parameters[list(node.outputs.values())[0].name])
-    else: # we have the UI socket
-        match socket.bl_idname:
-            case "IntSocket" | "UnsignedIntSocket":
-                overlay_data_type = int
-            case "FloatSocket" | "FloatPositiveSocket" | "FloatFactorSocket" | "FloatAngleSocket":
-                overlay_data_type = float
-    
-    if not isinstance(value, str): return
-    # First step: get the interface nodes and the overlay keys
-    # this should only check interfaces that are ancestors of this node
-    my_interfaces = []
-    for index in range(len(node.signature)-1):
-        if index == 0: continue # root
-        # we add one here because the node inside the schema references the schema's interface...
-        current_schema_interface_index = node.generated_at_schema_index[index+1]
-        if current_schema_interface_index < 0: # this indicates that it is not in a schema
-            interface = interface_nodes.get(
-                interface_signature := (None, *node.signature[1:index+1], "InputInterface"))
-        else: # First we get the interface that enters the schema (index 0)
-            interface = interface_nodes.get(
-                interface_signature := (None, *node.signature[1:index+1],
-                "InputInterface."+str( 0 ).zfill(4)))
-            if interface and interface not in my_interfaces: my_interfaces.append(interface)
-            if current_schema_interface_index == 0: continue # just did it
-            # we need to get the schema of the current index
-            interface = interface_nodes.get(
-                interface_signature := (None, *node.signature[1:index+1],
-                "InputInterface."+str(current_schema_interface_index).zfill(4)))
-        if interface:
-            my_interfaces.append( interface )
-    # the my_interfaces list is sorted in REVERSE order of importance
-    # Next, get all the overlay providers.
-    name_to_id_map = {}
-    name_to_type_map = {} # Just for printing reasonable error messages
-    overlay_keys = set()
-    root_overlay_values = {}
-    # First, we'll get the base tree's default inputs
-    for interface_item in node.mContext.base_tree.interface.items_tree:
-        if interface_item.item_type != "SOCKET": continue
-        if interface_item.in_out != "INPUT": continue
-        base_value=None
-        match interface_item.bl_socket_idname:
-            case "IntSocket" | "BooleanSocket":
-                # TODO parser for number types
-                base_value = interface_item.default_int
-            case "FloatSocket":
-                base_value = interface_item.default_float
-            case "StringSocket":
-                base_value = interface_item.default_string
-        if base_value:
-            root_overlay_values[interface_item.name] = base_value
-        overlay_keys.add(interface_item.name)
-        name_to_id_map[interface_item.name] = None # don't worry about it
-        name_to_type_map[interface_item.name] = interface_item.bl_socket_idname
-    # BUG? should this be my_interfaces ? why bother with interfaces this may not be a part of?
-    for interface_node in interface_nodes.values():
-        # problem: the interface node stores socket identifiers as keys
-        # so in reality we need to get the group UI node and ask for its input names
-        # if there is a duplicate name, too bad!
-        group_ui_node = get_ui_node(interface_node.ui_signature[:-1], node.base_tree)
-        for interface_item in group_ui_node.node_tree.interface.items_tree:
-            if interface_item.item_type != 'SOCKET': continue
-            if interface_item.in_out != 'INPUT': continue
-            if interface_item.name in forbidden_key_names: continue
-            for substr in forbidden_in_key_name:
-                if substr in interface_item.name:
-                    break
-            else: # if we do not find a bad substr
-                name_to_id_map[interface_item.name]=interface_item.identifier
-                name_to_type_map[interface_item.name] = interface_item.bl_socket_idname
-                overlay_keys.add(interface_item.name)
-        # For schema interfaces, we also need to get the length and index.
-        if 'Index' in interface.outputs:
-            overlay_keys.add('Index'); overlay_keys.add('Length')
-    if 'Index' in overlay_keys:
-        name_to_id_map['Index']='Index'
-        name_to_type_map['Index']= "IntSocket"
-        name_to_id_map['Length']='Length'
-        name_to_type_map['Length']= "IntSocket"
-    # sort by reverse length so we find "CatsAndFrogs" before "Cats"
-    overlay_keys = sorted(overlay_keys, key= lambda a :-1*len(a))
-    # First I need to break it into substrings based on quotation marks
-    from ..utilities.utilities import split_quoted_substrings_to_strings
-    quoted_substrings = split_quoted_substrings_to_strings(value)
-    overlay_tries = []
-    for quoted_substring in  quoted_substrings:
-        overlay_tries.extend(quoted_substring.split("$"))
-    if overlay_data_type == str:
-        result = overlay_tries[0] # the 0th element is always before the split character
-    else: # TODO HACK later use the parser feature
-        result = 0
-    # the reason for the difference between overlay_try and key is because
-    #  we don't actually know if the $text part has a key in it
-    for overlay_try in overlay_tries[1:]:
-        if not overlay_try: continue # some of these are empty
-        overlay_resolved_value = "$" + overlay_try
-        for key in overlay_keys:
-            if key in overlay_try:
-                overlay_resolved_value = "$" + key
-                for interface in reversed(my_interfaces): # since this is reversed when we build it
-                    actual_key = name_to_id_map.get(key)
-                    socket = interface.inputs.get(actual_key)
-                    if key in ['Index', 'Length']:
-                        socket = interface.outputs.get(actual_key)
-                    if socket:
-                        overlay_value = None
-                        try:
-                            overlay_value = interface.evaluate_input(actual_key)
-                        except KeyError:
-                            if (socket_type := name_to_type_map.get(key) or 'NOT FOUND') in overlay_valid_socket_types:
-                                raise GraphError(f"Error: Variable {key} is not supported because it has "
-                                                 f"an invalid input in the node group: {interface.signature[:-1]}.")
-                            raise GraphError( f"Error: Variable {key} is not supported because it is "
-                                              f"type {socket_type}" )
-                            # NOTE that the above can also be caused by any BUG occuring in node group update
-                            # because it causes the interface to be invalid for the UI node that spawns the interface node.
-                        if overlay_data_type == str:
-                            if isinstance(overlay_value, (float, int)):
-                                overlay_value = str(overlay_value).zfill(STRING_PADDING)
-                            assert (isinstance(overlay_value, str)), f"Error: Variable {key} is not supported because it is type {type(overlay_value)}"
-                            overlay_resolved_value = overlay_try.replace(key, overlay_value)
-                        else:
-                            overlay_resolved_value = overlay_value
-                        break
-                else: # we'll try the base tree
-                    if base_tree_result := root_overlay_values.get(key):
-                        if overlay_data_type == str and not isinstance(base_tree_result, str):
-                            base_tree_result = str(base_tree_result).zfill(STRING_PADDING)
-                        overlay_resolved_value = base_tree_result
-                    else:
-                        prOrange(f"WARN: no overlay value applied for {node.ui_signature} for key {key}")
-                break # we found the key and are done here. One found Key per overlay-try
-        result += overlay_resolved_value
-    # Simple Input sockets with unnamed sockets assign this ID automatically
-    # But Mantis uses the name instead. Usually the name and ID are the same.
-    if socket_id == 'socket' and ui_node.outputs[0].name == "":
-        socket_id = ''
-    node.parameters[socket_id] = result
-    return result # because sometimes I need to get the result, too
-
 from ..utilities.utilities import check_and_add_root, init_connections, init_dependencies, init_schema_dependencies
 def solve_schema_to_tree(mantis_node, all_mantis_nodes, roots=[], error_popups=False):
     from ..utilities.utilities import get_ui_node

+ 5 - 0
execution/schema_solve.py

@@ -58,6 +58,11 @@ class SchemaSolver:
             if (other := self.node.inputs['Schema Length'].links[0].from_node).prepared == False:
                 raise RuntimeError(f"Schema Length cannot be determined for {self.node} because {other} is not prepared.")
         self.solve_length = self.node.evaluate_input("Schema Length")
+        # The user may be using an overlay variable to set the schema length
+        from .overlays import node_scan_overlays
+        if isinstance(self.solve_length, str):
+            self.solve_length = node_scan_overlays(self.node, 'Schema Length')
+        if self.solve_length == 0: return
         # I'm making this a property of the solver because the solver's data is modified as it solves each iteration
         self.index = 0
         self.interfaces = []

+ 9 - 3
misc_nodes.py

@@ -370,12 +370,14 @@ class SimpleInputNode(MantisMiscNode):
         self.prepared, self.executed = True, True
 
     def fill_parameters(self, ui_node=None):
-        from .utilities.utilities import handle_overlay_detect
+        from .execution.overlays import handle_overlay_detect, get_socket_idname_data_type, parser_handle_text
         ui_outputs = ui_node.outputs
         node_socket = ui_outputs[0] # this is a simple input node.
         if (value := node_socket.default_value) is not None:
             if hasattr(node_socket, 'use_overlay') and node_socket.use_overlay:
-                value = '$' + node_socket.overlay_text
+                value = node_socket.overlay_text
+                if get_socket_idname_data_type(node_socket.bl_idname) != str:
+                    value = parser_handle_text(value)
             if isinstance(value, str) and "$" in value:
                 value = handle_overlay_detect(self, node_socket.identifier, node_socket.bl_idname, value)
             self.parameters[node_socket.name] = value
@@ -1204,7 +1206,11 @@ class InputWidget(MantisMiscNode):
                 from .utilities.geometry_node_graphgen import gen_scale_object_data_modifier
                 ng = gen_scale_object_data_modifier()
             scale_modifier.node_group = ng
-            scale_modifier['Socket_2']=scaling
+            if bpy_version < (5, 2, 0):
+                scale_modifier['Socket_2']=scaling
+            else:
+                scale_modifier.properties.inputs.Socket_2.value = scaling
+                
         # now we'll check for the mirrors.
         if any(axes_flipped) and do_mirror:
             if (flip_modifier := self.bObject.modifiers.get("Simple Flip")) is None:

+ 2 - 1
misc_nodes_ui.py

@@ -368,10 +368,11 @@ class UtilityMetaRigNode(Node, MantisMiscNode):
     
     def display_update(self, parsed_tree, context):
         mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
+        using_overlay = self.inputs['Meta-Armature'].use_overlay
         if mantis_node:
             self.armature= mantis_node.evaluate_input("Meta-Armature")
             self.pose_bone= mantis_node.evaluate_input("Meta-Bone")
-        if not self.armature:
+        if not self.armature and not using_overlay:
             self.inputs["Meta-Bone"].hide=True
         else:
             self.inputs["Meta-Bone"].hide=False

+ 158 - 61
socket_definitions.py

@@ -44,29 +44,62 @@ class MantisSocket(parent_classes):
         return self.color
     @classmethod
     def draw_color_simple(self):
-        return self.color_simple    
+        return self.color_simple
+
+def socket_update_get_root_tree(self, context):
+    context = bpy.context
+    if not context.space_data:
+        return None
+    if not hasattr(context.space_data, "path"):
+        return None
+    try:
+        root_tree = context.space_data.path[0].node_tree
+    except IndexError: # not in the UI, for example, in a script instead.
+        return None
+    if root_tree is None:
+        return None
+    if root_tree.is_executing or root_tree.is_exporting or not root_tree.do_live_update:
+        return None
+    return root_tree
+
+def overlay_toggle_update(self, context):
+    root_tree = socket_update_get_root_tree(self, context)
+    if root_tree is None: return
+    parsed_tree = self.node.id_data.parsed_tree
+    if hasattr(self.node, "display_update"):
+        self.node.display_update(parsed_tree=parsed_tree, context=context)
+    root_tree.update_tree(context, force = True)
+    # and we need to mark the node as un-prepared.
+    # For now I am not gonna try to resolve the overlay value from the UI.
+    
+def overlay_text_update(self, context):
+    root_tree = socket_update_get_root_tree(self, context)
+    if root_tree is None: return
+    # have to check each one. see default_update NOTE
+    handled = True
+    for mantis_node in root_tree.parsed_tree.values():
+        handled = try_update_mantis_node_for_socket(mantis_node, root_tree, self) and handled
+    if not handled:
+        root_tree.update_tree(context, force = True)
 
 class MantisSocketOverlayEnabled(MantisSocket):
     # this class just needs a couple properties
-    use_overlay: bpy.props.BoolProperty( default=False )
-    overlay_text: bpy.props.StringProperty( default="", )
+    use_overlay: bpy.props.BoolProperty( default=False, update=overlay_toggle_update)
+    overlay_text: bpy.props.StringProperty( default="", update=overlay_text_update)
     def draw(self, context, layout, node, text):
         is_simple_out = ((hasattr(self, "default_value")) and hasattr(self, "input") and getattr(self, "input"))
         if self.is_linked and not is_simple_out:
             layout.label(text=getattr(self, "display_text", "") )
         elif hasattr(self, "default_value"):
-            has_overlay = hasattr(self, "overlay_text") and hasattr(self, "use_overlay")
-            row = layout.row(align=True) if has_overlay else layout
+            row = layout.row(align=True)
             # We'll show the property or the overlay field here
-            prop_field = "overlay_text" if (has_overlay and self.use_overlay) else "default_value"
+            prop_field = "overlay_text" if self.use_overlay else "default_value"
             row.prop(self, prop_field,text=self.name)
             # and the button to choose whether or not to use the overlay field
-            if has_overlay: row.prop(self, "use_overlay", text="", icon='CONSOLE', toggle=True)
+            row.prop(self, "use_overlay", text="", icon='CONSOLE', toggle=True)
         else:
             layout.label(text=self.name)
 
-
-
 from .utilities.utilities import (prRed, prGreen, prPurple, prWhite,
                               prOrange,
                               wrapRed, wrapGreen, wrapPurple, wrapWhite,
@@ -357,12 +390,73 @@ enum_default_xForm_values =(
 from bpy.types import NodeTreeInterfaceSocket
 
 def interface_socket_update(self, context):
-    # we're just gonna do this the dumb way for now and invalidate the tree
-    # BUG HACK TODO actually I am gonna do this stuff later
-    # later, I can do this based on the connections in the tree
-    # and the socket updater can use the same code for group interface modifications
-    # TODO do this stuff because the tree will be a lot snappier
-    pass
+    from .execution.overlays import fill_root_tree_overlays, parser_handle_text
+    from itertools import chain
+    # so what I need to do here is:
+    #  - if this is the root tree, and the interface has connections, invalidate them
+    #  - or if the root tree node is using an overlay, update it directly
+    #  - if there are any overlay-tagged, invalidate them
+    root_tree = socket_update_get_root_tree(self, context)
+    parsed_tree = root_tree.parsed_tree
+    if len(parsed_tree) == 0: return # nothing to do here
+    # get the mContext
+    # HACK HACK HACK this is so ugly
+    keys = list(parsed_tree.keys())
+    mContext = None; i=0
+    while mContext is None:
+        mantis_node = parsed_tree[ keys[i] ]
+        mContext = mantis_node.mContext; i+=1
+        if i > len(keys): return # no mContext found
+        # should exit immediately, the first node probably has it
+    if self.id_data.name == root_tree.name:
+        # checc for node group inputs and recursively reset execution for their children
+        for node in root_tree.nodes:
+            if node.bl_idname in {'NodeGroupInput'}:
+                for o in node.outputs:
+                    for l in o.links:
+                        # We're in the root tree, that makes this easier
+                        if mantis_node := parsed_tree.get( (None, l.to_node.name) ):
+                            prPurple (mantis_node)
+                            mantis_node.reset_execution_recursive()
+            else: # The overlays in the root tree get set directly, let's handle that here, also
+                for socket in chain(node.inputs, node.outputs):   
+                    
+                    value = None
+                    if hasattr(socket, 'default_value'):
+                        value = socket.default_value
+                        if hasattr(socket, 'use_overlay') and socket.use_overlay:
+                            value = parser_handle_text(socket.overlay_text)
+                    if isinstance(value, str) and '$' in value:
+                        if socket.name  == 'Schema Length': # this one has to force a new parse
+                            root_tree.update_tree(force=True) # because it modifies tree topology
+                            return
+                        value = fill_root_tree_overlays(mContext, socket.bl_idname, value)
+
+    # Now update the overlay-tagged nodes.
+    for mantis_node_signature, socket_id in mContext.tagged_for_overlay:
+        if socket_id == 'Schema Length': break # break to force update
+        # We always need to parse again after changing schema length
+        mantis_node = parsed_tree.get(mantis_node_signature)
+        if mantis_node:
+            from .utilities.utilities import get_ui_node
+            if mantis_node.ui_signature is None: break # can't update it
+            ui_node = get_ui_node(mantis_node.ui_signature, root_tree)
+            if ui_node:
+                if socket_id == 'socket': # this is an anonymous output in a simple-input node
+                    ui_socket = ui_node.outputs[0]
+                else:
+                    if (ui_socket := ui_node.inputs.get(socket_id)) is None:
+                        for socket in ui_node.inputs:
+                            if socket_id == socket.identifier: break
+                        else: # Failed to update it, just update the tree.
+                            root_tree.update_tree(force=True); return
+                # Now we have the ui socket
+                mantis_node.ui_modify_socket(ui_socket, ui_socket.name)
+    else:
+        return
+    # if we break from the for loop above, we failed to update the socket and have to update the tree
+    root_tree.update_tree(force=True)
+    # TODO: I can update these values instead of resetting them
 
 
 interface_default_value_description="The default value of the socket when it is not connected."
@@ -494,19 +588,10 @@ def socket_update(mantis_node, ui_socket, socket_name=None):
     return node_updated
 
 def default_update(ui_socket, context, do_execute=True):
-    context = bpy.context
-    if not context.space_data:
-        return
-    if not hasattr(context.space_data, "path"):
-        return
-    try:
-        node_tree = context.space_data.path[0].node_tree
-    except IndexError: # not in the UI, for example, in a script instead.
-        return
-    if node_tree is None:
-        return
-    if node_tree.is_executing or node_tree.is_exporting or not node_tree.do_live_update:
-        return
+    if not context:
+        context = bpy.context
+    root_tree = socket_update_get_root_tree(ui_socket, context)
+    if root_tree is None: return
     # if it is a Schema Node, it will fail the checks below -- but we need it to update the tree.
     from .base_definitions import SchemaUINode
     if isinstance(ui_socket.node, SchemaUINode):
@@ -518,9 +603,9 @@ def default_update(ui_socket, context, do_execute=True):
         updating = ui_socket.node.is_updating
         ui_socket.node.is_updating=True
         try:
-            node_tree.update_tree(context, force = True)
+            root_tree.update_tree(context, force = True)
             prPurple(f"Executing tree after socket change: {ui_socket.node.name}:{ui_socket.name}")
-            node_tree.execute_tree(context)
+            root_tree.execute_tree(context)
         finally:
             ui_socket.node.is_updating=updating
         return
@@ -538,38 +623,44 @@ def default_update(ui_socket, context, do_execute=True):
     mantis_updated=True
     if (ui_socket.node.bl_idname in ["MantisNodeGroup", "MantisSchemaGroup"]):
         mantis_updated=False # this kind of socket can't be updated here (yet)
-        node_tree.update_tree(context, force=True)
+        root_tree.update_tree(context, force=True)
     elif ui_socket.node.bl_idname in array_output_types:
         mantis_updated=False
-        node_tree.update_tree(context, force=True)
+        root_tree.update_tree(context, force=True)
     elif hasattr(ui_socket, 'default_value'):
-        # we may not have to regenerate the tree; try and update the socket
-        from .utilities.utilities import tree_from_mantis_node
-        for mantis_node in node_tree.parsed_tree.values():
-            # check to see if the mantis node is in the same ui-tree as this ui_socket
-            if mantis_node.ui_signature is None: continue # autogenerated nodes
-            if mantis_node.ui_signature[-1] == ui_socket.node.name and \
-                        tree_from_mantis_node(mantis_node.ui_signature, node_tree) == ui_socket.node.id_data:
-                node_updated = True
-                from .misc_nodes import SimpleInputNode
-                if isinstance(mantis_node, SimpleInputNode):
-                    node_updated = socket_update(mantis_node, ui_socket)
-                    for l in mantis_node.outputs[ui_socket.name].links:
-                        node_updated = node_updated and socket_update(l.to_node, ui_socket, l.to_socket)
-                else:
-                    node_updated = socket_update(mantis_node, ui_socket)
-                # execute the tree if even one node didn't update
-                mantis_updated = node_updated and mantis_updated
+        # NOTE: there may be many mantis nodes for each UI node... so we need to try all of them.
+        for mantis_node in root_tree.parsed_tree.values():
+            # we may not have to regenerate the tree; try and update the socket
+            # the 'and' here ensures we trigger an update if even one of the mantis nodes fails
+            mantis_updated = try_update_mantis_node_for_socket(mantis_node, root_tree, ui_socket) and mantis_updated
         # we want to force it if we have made an unhandled change inside of a schema.
-        node_tree.update_tree(context, force = (mantis_updated == False))
-    node_tree.display_update(context)
+        root_tree.update_tree(context, force = (mantis_updated == False))
+    root_tree.display_update(context)
     if mantis_updated==False:
         try:
             prPurple(f"Executing tree after socket change: {ui_socket.node.name}:{ui_socket.name}")
-            node_tree.execute_tree(context)
+            root_tree.execute_tree(context)
         except Exception as e:
             prRed("Automatic Tree Execution failed because of %s" % e)
 
+def try_update_mantis_node_for_socket(mantis_node, node_tree, ui_socket):
+    # we may not have to regenerate the tree; try and update the socket
+    from .utilities.utilities import tree_from_mantis_node
+    # check to see if the mantis node is in the same ui-tree as this ui_socket
+    if mantis_node.ui_signature is None: return False# autogenerated nodes
+    if mantis_node.ui_signature[-1] == ui_socket.node.name and \
+                tree_from_mantis_node(mantis_node.ui_signature, node_tree) == ui_socket.node.id_data:
+        node_updated = True
+        from .misc_nodes import SimpleInputNode
+        if isinstance(mantis_node, SimpleInputNode):
+            node_updated = socket_update(mantis_node, ui_socket)
+            for l in mantis_node.outputs[ui_socket.name].links:
+                node_updated = node_updated and socket_update(l.to_node, ui_socket, l.to_socket)
+        else:
+            node_updated = socket_update(mantis_node, ui_socket)
+        # execute the tree if even one node didn't update
+        return node_updated
+    return False
 
 def update_socket(self, context,):
     default_update(self,context)
@@ -783,9 +874,6 @@ class GenericRotationSocket(MantisSocket):
     color = (0.0,0.0,0.0,0.0)
     input : bpy.props.BoolProperty(default =False,)
 
-
-
-
 ###############################
 
 class EnableSocket(MantisSocket):
@@ -1300,7 +1388,7 @@ def poll_is_armature(self, obj):
 # def poll_is_armature(self, obj):
     # return obj.type == "ARMATURE"
 
-class EnumMetaRigSocket(MantisSocket):
+class EnumMetaRigSocket(MantisSocketOverlayEnabled):
     '''Custom node socket type'''
     bl_idname = 'EnumMetaRigSocket'
     bl_label = "Meta Rig"
@@ -1325,7 +1413,16 @@ class EnumMetaRigSocket(MantisSocket):
         if self.is_output:
             layout.label(text=self.name)
         elif not (self.is_linked):
-            layout.prop_search(data=self, property="search_prop", search_data=bpy.data, search_property="objects", text="", icon="OUTLINER_OB_ARMATURE", results_are_suggestions=True)
+            row = layout.row(align=True)
+            # We'll show the property or the overlay field here
+            if self.use_overlay:
+                row.prop(self, "overlay_text",text=self.name)
+            else:
+                row.prop_search(data=self, property="search_prop", search_data=bpy.data,
+                                   search_property="objects", text="", icon="OUTLINER_OB_ARMATURE",
+                                   results_are_suggestions=True)
+            # and the button to choose whether or not to use the overlay field
+            row.prop(self, "use_overlay", text="", icon='CONSOLE', toggle=True)
         elif hasattr(self.node, "armature"):
             layout.label(text=self.node.armature)
             # TODO: we should actually use the parsed tree to query this info directly, since this socket may belong to a node group in/out
@@ -2492,7 +2589,7 @@ class EnumDriverType(MantisSocket):
 # enum for interpolation type
 # eventually gonna make it to the fancy stuff
 
-class FloatSocket(MantisSocket):
+class FloatSocket(MantisSocketOverlayEnabled):
     """Float Input socket"""
     bl_idname = 'FloatSocket'
     bl_label = "Float"
@@ -2502,7 +2599,7 @@ class FloatSocket(MantisSocket):
     color : bpy.props.FloatVectorProperty(default=cFloat, size=4)
     input : bpy.props.BoolProperty(default =False,)
 
-class FloatPositiveSocket(MantisSocket):
+class FloatPositiveSocket(MantisSocketOverlayEnabled):
     """Float Input socket"""
     bl_idname = 'FloatPositiveSocket'
     bl_label = "Float (Positive)"
@@ -2512,7 +2609,7 @@ class FloatPositiveSocket(MantisSocket):
     color : bpy.props.FloatVectorProperty(default=cFloat, size=4)
     input : bpy.props.BoolProperty(default =False,)
 
-class FloatFactorSocket(MantisSocket):
+class FloatFactorSocket(MantisSocketOverlayEnabled):
     '''xFrom Input Output'''
     bl_idname = 'FloatFactorSocket'
     bl_label = "Float (Factor)"
@@ -2527,7 +2624,7 @@ class FloatFactorSocket(MantisSocket):
     color : bpy.props.FloatVectorProperty(default=cFloat, size=4)
     input : bpy.props.BoolProperty(default =False,)
 
-class FloatAngleSocket(MantisSocket):
+class FloatAngleSocket(MantisSocketOverlayEnabled):
     '''xFrom Input Output'''
     bl_idname = 'FloatAngleSocket'
     bl_label = "Float (Angle)"

+ 1 - 1
utilities/i_o.py

@@ -47,7 +47,7 @@ prop_ignore = [ "__dict__", "__doc__", "__module__", "__weakref__",# "name",
                 # node stuff
                 "mantis_node_class_name", "color", "height", "initialized", "select",
                 "show_options", "show_preview", "show_texture", "use_custom_color",
-                "warning_propagation",
+                "warning_propagation", "panel_states",
                 # these are in Bone
                 "socket_count", "display_bb_settings", "display_def_settings",
                 "display_ik_settings", "display_vp_settings",

+ 13 - 40
utilities/utilities.py

@@ -131,6 +131,15 @@ def set_bone_selection(pose_bone, selection_state):
 # groups and changing sockets -- this is used extensively by Schema.
 ##################################################################################################
 
+# store the overlay state of the node's sockets to ensure they don't reset.
+# The socket will only be in the overlay map if it is set to use overlay
+def get_socket_overlay_info(node):
+    overlay_state = {} # we only need the inputs for this
+    for socket in node.inputs:
+        if hasattr(socket, 'use_overlay') and socket.use_overlay:
+            overlay_state[socket.identifier] = socket.overlay_text
+    return overlay_state
+
 # this one returns None if there is an error.
 def get_socket_maps(node, force=False):
     maps = [{}, {}]
@@ -519,7 +528,10 @@ def import_widget_obj(path,):
             from .geometry_node_graphgen import gen_import_obj_node_group
             ng = gen_import_obj_node_group()
         import_modifier.node_group = ng
-        import_modifier["Socket_0"]=path
+        if bpy_version < (5, 2, 0):
+            import_modifier["Socket_0"]=path
+        else:
+            import_modifier.properties.inputs.Socket_0.value = path
         return ob
 
 def import_object_from_file(path):
@@ -961,45 +973,6 @@ def SugiyamaGraph(tree, iterations):
 def project_point_to_plane(point, origin, normal):
     return point - normal.dot(point- origin)*normal
 
-# If a $ character is in a value, it is an overlay request
-# in the root tree, supply it immediately, otherwise tag this node
-# once we finish pulling data from the tree we handle the overlay.
-def handle_overlay_detect(node, socket_id, socket_idname, value) -> str:
-    mContext = node.mContext
-    root_tree = mContext.base_tree
-    root_items_tree = root_tree.interface.items_tree
-    # detect if it is in the root tree, if so, handle immediately
-    out_value = value
-    # what this recieves is determined by its idname
-    socket_type = str
-    match socket_idname:
-        case "IntSocket" | "UnsignedIntSocket":
-            socket_type = int
-        case "FloatSocket" | "FloatPositiveSocket" | "FloatFactorSocket" | "FloatAngleSocket":
-            socket_type = float
-    # ONLY for root node trees, we can just directly fill this.
-    if node.signature[0] is None and len(node.signature) == 2:
-        for key, interface_item in root_items_tree.items():
-            if interface_item.item_type != "SOCKET": continue
-            if interface_item.in_out != "INPUT": continue
-            # then what the interface provides is determined by its idname in turn
-            # NOTE I'm not 100% sure checking the socket_type is the right thing to do here
-            match interface_item.bl_socket_idname:
-                case "IntSocket" | "BooleanSocket":
-                    if socket_type in {int, float} and '$'+key == out_value:
-                        # TODO this is the wrong way to do this, replace this with parser later
-                        out_value = socket_type(interface_item.default_int)
-                case "FloatSocket":
-                    if socket_type in {int, float} and '$'+key == out_value:
-                        out_value = socket_type(interface_item.default_float)
-                case "StringSocket":
-                    if socket_type == str:
-                        out_value = socket_type(out_value.replace("$"+key, interface_item.default_string))
-
-    else: # since it is not root, we have to tag it.
-        mContext.tagged_for_overlay.append((node.signature, socket_id))
-    return out_value
-
 ####################################
 # CURVE STUFF
 ####################################

+ 10 - 12
versioning.py

@@ -474,23 +474,17 @@ def add_new_display_features_v0_13_5(*args, **kwargs):
     current_major_version = tree.mantis_version[0]
     current_minor_version = tree.mantis_version[1]
     current_sub_version = tree.mantis_version[2]
-    print(tree.mantis_version[:])
     if  current_major_version > 0:return# major version must be 0
     if current_minor_version > 13: return# minor version must be 12 or less
     if current_minor_version == 13 and current_sub_version >= 5: return# sub version must be 4 or less
     prPurple(f"Adding new Display Features to {tree.name}")
-    live_updates = tree.do_live_update
-    tree.do_live_update = False
-    live_updates = tree.do_live_update
-    bones = []
-    armatures = []
+    live_updates = tree.do_live_update; tree.do_live_update = False
+    
+    bones = []; armatures = []
     for node in tree.nodes:
-        if hasattr(node, 'is_updating'):
-            node.is_updating = True
-        if node.bl_idname == 'xFormBoneNode':
-            bones.append(node)
-        if node.bl_idname == 'xFormArmatureNode':
-            armatures.append(node)
+        if hasattr(node, 'is_updating'): node.is_updating = True
+        if node.bl_idname == 'xFormBoneNode': bones.append(node)
+        if node.bl_idname == 'xFormArmatureNode': armatures.append(node)
     try:
         for bone_node in bones:
             # we'll just try this...
@@ -499,6 +493,8 @@ def add_new_display_features_v0_13_5(*args, **kwargs):
                                                  UseTransformAtCustomShape,
                                                  UseTransformAroundCustomShape,
                                                  xFormBoneSockets)
+            if bone_node.inputs.get(BoneDisplayType.name):
+                continue # Already fixed
             input_templates = ( BoneDisplayType,
                                 CustomObjectWireWidth,
                                 UseTransformAroundCustomShape,
@@ -519,6 +515,8 @@ def add_new_display_features_v0_13_5(*args, **kwargs):
             # we'll just try this...
             from .xForm_socket_templates import (ArmatureDisplayType,
                                                  xFormArmatureSockets)
+            if bone_node.inputs.get(ArmatureDisplayType.name):
+                continue # Already fixed
             input_templates = ( ArmatureDisplayType, )
             armature_node.init_sockets(input_templates)
             armature_node.socket_templates = xFormArmatureSockets

+ 3 - 2
xForm_nodes_ui.py

@@ -161,8 +161,9 @@ class xFormBoneNode(Node, xFormNode):
                     self.display_bb_settings = segs > 1
                 except TypeError:
                     self.display_bb_settings = True # it is probably a driver
-            else:
-                self.display_bb_settings = self.inputs['BBone Segments'].default_value > 1
+            else: # Show the bbone settings if there could be a BBone.
+                self.display_bb_settings = ( (self.inputs['BBone Segments'].default_value > 1) \
+                                             or (self.inputs['BBone Segments'].use_overlay) )
             if self.inputs['Name'].is_linked and mantis_node_ready:
                 self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
 

+ 1 - 2
xForm_socket_templates.py

@@ -117,7 +117,6 @@ need_different_defaults = [
     ("bbone_easeout", 1, 0),
 ]
 
-
 # VERSIONING for Bone
 bone_hide_prop_group='bone'
 if bpy_version >= (5,0,0):
@@ -126,7 +125,7 @@ bone_hide_template = replace(HideTemplate, name='Hide', category='always_show',
                             blender_property='hide', default_value=False,
                             prop_owner_group = bone_hide_prop_group)
 
-bbone_template = SockTemplate( name="<BBone Template>",
+bbone_template = SockTemplate( name="<BBone Template>", category = 'bbone',
         default_value = 0.0, blender_property=None, is_input=True,
         bl_idname='FloatSocket', prop_owner_group='bone',)