handleExpandPath method
Batch-expand an entire hierarchy path in a single layout cycle.
Applies all graph mutations (expandChild at each level, and optionally expandWire at the leaf) before serialising and computing layout. This avoids the N × (toJsGraph + ELK + setState) overhead incurred by expanding one level at a time.
pathSegments are instance names from top to bottom.
If targetWireName is non-null the last segment is treated as
the container of that wire; otherwise it is the target module.
Implementation
Future<SchematicLayoutResult?> handleExpandPath(
List<String> pathSegments, {
String? targetWireName,
}) async {
if (isToggling || pathSegments.isEmpty) {
return null;
}
final engine = _layoutEngine;
if (engine == null || schematicAdapter == null) {
return null;
}
setState(() {
isToggling = true;
togglingLabel = 'Expanding...';
pendingToggleNodeId = pathSegments.last;
});
// Let the spinner appear before heavy processing.
final completer = Completer<void>();
WidgetsBinding.instance.addPostFrameCallback((_) {
completer.complete();
});
await completer.future;
SchedulerBinding.instance.ensureVisualUpdate();
// Fetch connectivity only when a wire needs to be revealed at the
// leaf. Path-only expansion (finding a module) never needs wires.
if (targetWireName != null) {
// pathSegments are instance names (e.g. 'ch0') but
// ensureConnectivity expects a node address (e.g. '0.0').
// Resolve through the schematic graph.
final nodeId = schematicAdapter!.schematic.resolvePathToNodeId(
pathSegments,
);
if (nodeId != null) {
await ensureConnectivity(nodeId);
}
}
try {
final changed = schematicAdapter!.expandPath(
pathSegments,
targetWireName: targetWireName,
);
if (!changed) {
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) {
if (mounted) {
setState(() {
isToggling = false;
pendingToggleNodeId = null;
});
}
return null;
}
setState(() {
layout = newLayout;
isToggling = false;
pendingToggleNodeId = null;
focusPortId = null;
recentlyToggledNodeId = null;
});
return newLayout;
} on Exception {
if (mounted) {
setState(() {
isToggling = false;
pendingToggleNodeId = null;
});
}
return null;
}
}