handlePortExpandThrough method
Handle incremental port expansion with pass-through traversal.
Like handlePortExpand but continues through trivial gates (buffers,
inverters, slicers, concatenators) until non-trivial children or
external ports are reached. Triggered by Shift+clicking a port
marker in the schematic canvas.
Implementation
Future<SchematicLayoutResult?> handlePortExpandThrough(
String nodeId,
String portId,
) async {
if (isToggling) {
return null;
}
final engine = _layoutEngine;
if (engine == null || schematicAdapter == null) {
return null;
}
if (needsConnectivity(nodeId)) {
setState(() {
isToggling = true;
togglingLabel = 'Expanding...';
pendingToggleNodeId = nodeId;
});
final completer = Completer<void>();
WidgetsBinding.instance.addPostFrameCallback((_) {
completer.complete();
});
await completer.future;
SchedulerBinding.instance.ensureVisualUpdate();
await ensureConnectivity(nodeId);
} else {
isToggling = true;
pendingToggleNodeId = nodeId;
await ensureConnectivity(nodeId); // fast no-op
}
final preExpandSnapshot = schematicAdapter!.schematic.expansionSnapshot();
try {
final expanded = schematicAdapter!.expandPortThroughRecursive(
nodeId,
portId,
);
if (!expanded) {
if (mounted) {
setState(() {
isToggling = false;
pendingToggleNodeId = null;
});
}
return null;
}
final elkGraph = schematicAdapter!.schematic.toJsGraph();
final newLayout = await engine.computeLayoutFromElkGraph(
elkGraph,
sessionId: _sessionId,
);
if (newLayout.hasError || !mounted) {
schematicAdapter!.schematic.restoreExpansionSnapshot(preExpandSnapshot);
if (mounted) {
setState(() {
isToggling = false;
pendingToggleNodeId = null;
});
}
return null;
}
setState(() {
layout = newLayout;
isToggling = false;
pendingToggleNodeId = null;
focusPortId = portId;
recentlyToggledNodeId = null;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
focusPortId = null;
});
}
});
return newLayout;
} on Exception {
schematicAdapter!.schematic.restoreExpansionSnapshot(preExpandSnapshot);
if (mounted) {
setState(() {
isToggling = false;
pendingToggleNodeId = null;
});
}
return null;
}
}