diff --git a/app_state.py b/app_state.py index 00e0be0..a98ebf9 100644 --- a/app_state.py +++ b/app_state.py @@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it. import configparser import os -VERSION = "0.0.0.123" +VERSION = "0.0.0.124" PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties") DEFAULT_GRID_SIZE = 10 DEFAULT_ADD_TIMESTAMP = True diff --git a/device_session.py b/device_session.py index 06fc96b..7788b9d 100644 --- a/device_session.py +++ b/device_session.py @@ -87,54 +87,39 @@ class DeviceSession: def _load_slot_registry(self, parts): """Builds slot_registry from kit manifests for the device's schema.""" registry = {} - base_slots = [] + own_slots = {} # "kit::Type" -> declared slots + bases = {} # "kit::Type" -> "kit::Base" or None for kit_id, part in enumerate(parts): kit_name = part.name checksum = part.checksum manifest = schema.find_manifest(kit_name, checksum) if manifest is None: continue - if kit_name == "sys": - base_slots = self._extract_component_base_slots(manifest) - self._register_kit_types(registry, kit_id, kit_name, manifest) - # Prepend inherited Component base slots (meta, etc.) to every type, - # then renumber IDs to match the wire slot index (positional in the + self._register_kit_types(registry, kit_id, kit_name, manifest, + own_slots, bases) + # Flatten each type's slots through its whole base chain (e.g. + # Component -> Service -> ontrolOx::Widget -> PushButtonImage), then + # renumber IDs to match the wire slot index (positional in the # flattened inherited + own list — see sedona Type.java resolveSlots). - if base_slots: - for key in registry: - existing_names = {s["name"] for s in registry[key]} - for bs in base_slots: - if bs["name"] not in existing_names: - registry[key].insert(0, dict(bs)) - for i, slot in enumerate(registry[key]): - slot["id"] = i + for key in registry: + qname = self.sox_client.type_name_map[key] + chain = [] + seen = set() + t = qname + while t and t not in seen and t in own_slots: + seen.add(t) + chain.append(t) + t = bases.get(t) + flat = [] + for t in reversed(chain): + flat.extend(dict(s) for s in own_slots[t]) + for i, slot in enumerate(flat): + slot["id"] = i + registry[key] = flat self.sox_client.slot_registry = registry - def _extract_component_base_slots(self, manifest): - """Extracts base slots from sys::Component that all types inherit.""" - for type_elem in manifest.iter("type"): - if type_elem.get("name") == "Component": - slots = [] - for slot_elem in type_elem.findall("slot"): - slot_name = slot_elem.get("name", "") - slot_id = int(slot_elem.get("id", "-1")) - slot_type = slot_elem.get("type", "int") - flags_str = slot_elem.get("flags", "") - slots.append({ - "name": slot_name, - "id": slot_id, - "type_name": slot_type, - "is_config": "c" in flags_str or "p" in flags_str, - "is_runtime": "r" in flags_str, - "is_action": "a" in flags_str, - "is_operator": "o" in flags_str, - "is_as_str": slot_type == "Str", - "is_prop": "a" not in flags_str, - }) - return slots - return [] - - def _register_kit_types(self, registry, kit_id, kit_name, manifest): + def _register_kit_types(self, registry, kit_id, kit_name, manifest, + own_slots, bases): root = manifest for type_elem in root.iter("type"): type_name = type_elem.get("name", "") @@ -165,6 +150,8 @@ class DeviceSession: }) registry[(kit_id, type_id_val)] = slots full_name = f"{kit_name}::{type_name}" + own_slots[full_name] = slots + bases[full_name] = type_elem.get("base") or None self.sox_client.type_name_map[(kit_id, type_id_val)] = full_name # update type_name on any cached component of this type for c in self.sox_client.cache: @@ -368,16 +355,13 @@ class DeviceSession: def _slot_name_by_id(self, kit_id, type_id_val, slot_id): # SOX links use absolute slot IDs across the full inheritance chain. - # final_components_registry has correctly resolved absolute IDs; - # the per-type slot_registry only stores local (per-type) IDs - # which are wrong for types with intermediate parents. type_name = self.sox_client.type_name_map.get((kit_id, type_id_val)) if type_name and type_name in app_state.final_components_registry: for s in app_state.final_components_registry[type_name]: if s["slot_id"] == slot_id: return s["name"] - # Fallback to slot_registry (local IDs — wrong for inherited types, - # but keeps things working when the schema registry is not yet loaded) + # Fallback to slot_registry (flattened through the base chain, so + # its IDs are absolute too) slots = self.sox_client.slot_registry.get((kit_id, type_id_val)) if slots: for s in slots: