diff --git a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart index 70b86074..5fb648e3 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart @@ -336,6 +336,7 @@ class _CommunityStructureAreaState extends State { } spaces.add(newSpace); _updateNodePosition(newSpace, newSpace.position); + realignTree(); }); }, ); @@ -450,7 +451,6 @@ class _CommunityStructureAreaState extends State { void _saveSpaces() { if (widget.selectedCommunity == null) { - debugPrint("No community selected for saving spaces."); return; } @@ -530,34 +530,82 @@ class _CommunityStructureAreaState extends State { } Offset getBalancedChildPosition(SpaceModel parent) { - int totalSiblings = parent.children.length + 1; - double totalWidth = (totalSiblings - 1) * 250; // Horizontal spacing - double startX = parent.position.dx - (totalWidth / 2); + const double nodeWidth = 200; + const double verticalGap = 180; - Offset position = Offset(startX + (parent.children.length * 250), parent.position.dy + 180); + if (parent.children.isEmpty) { + // First child → exactly center + return Offset(parent.position.dx, parent.position.dy + verticalGap); + } else { + // More children → arrange them spaced horizontally + double totalWidth = (parent.children.length) * (nodeWidth + 60); + double startX = parent.position.dx - (totalWidth / 2); - // Check for overlaps & adjust - while (spaces.any((s) => (s.position - position).distance < 250)) { - position = Offset(position.dx + 250, position.dy); + double childX = startX + (parent.children.length * (nodeWidth + 60)); + return Offset(childX, parent.position.dy + verticalGap); } - - return position; } void realignTree() { - void updatePositions(SpaceModel node, double x, double y) { - node.position = Offset(x, y); + const double nodeWidth = 200; + const double nodeHeight = 100; + const double horizontalGap = 60; + const double verticalGap = 180; - int numChildren = node.children.length; - double childStartX = x - ((numChildren - 1) * 250) / 2; + double canvasRightEdge = 1000; + double canvasBottomEdge = 1000; - for (int i = 0; i < numChildren; i++) { - updatePositions(node.children[i], childStartX + (i * 250), y + 180); + double _calculateSubtreeWidth(SpaceModel node) { + if (node.children.isEmpty) return nodeWidth; + double totalWidth = 0; + for (var child in node.children) { + totalWidth += _calculateSubtreeWidth(child) + horizontalGap; + } + return totalWidth - horizontalGap; + } + + void _layoutTree(SpaceModel node, double startX, double y) { + double subtreeWidth = _calculateSubtreeWidth(node); + double centerX = startX + subtreeWidth / 2 - nodeWidth / 2; + node.position = Offset(centerX, y); + + canvasRightEdge = + centerX + nodeWidth > canvasRightEdge ? centerX + nodeWidth : canvasRightEdge; + canvasBottomEdge = y + nodeHeight > canvasBottomEdge ? y + nodeHeight : canvasBottomEdge; + + if (node.children.length == 1) { + final child = node.children.first; + double parentCenterX = node.position.dx + nodeWidth / 2; + double childX = parentCenterX - nodeWidth / 2; + double childY = y + verticalGap; + + child.position = Offset(childX, childY); + _layoutTree(child, childX, childY); + } else { + double childX = startX; + for (var child in node.children) { + double childWidth = _calculateSubtreeWidth(child); + _layoutTree(child, childX, y + verticalGap); + childX += childWidth + horizontalGap; + } } } - if (spaces.isNotEmpty) { - updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy); + final SpaceModel? root = spaces.firstWhere( + (s) => s.parent == null, + orElse: () => spaces.first, + ); + + if (root != null) { + double totalTreeWidth = _calculateSubtreeWidth(root); + double startingX = (canvasWidth / 2) - (totalTreeWidth / 2); + + _layoutTree(root, startingX, 100); + + setState(() { + canvasWidth = canvasRightEdge + 200; + canvasHeight = canvasBottomEdge + 200; + }); } } @@ -650,38 +698,87 @@ class _CommunityStructureAreaState extends State { /// **Find a new position ensuring no overlap** Offset getBalancedChildPosition(SpaceModel parent) { - int totalSiblings = parent.children.length + 1; - double totalWidth = (totalSiblings - 1) * horizontalGap; - double startX = parent.position.dx - (totalWidth / 2); - Offset position = Offset( - startX + (parent.children.length * horizontalGap), parent.position.dy + verticalGap); + const double nodeWidth = 200; + const double horizontalGap = 60; + const double verticalGap = 180; - // **Check for overlaps & adjust** - while (spaces.any((s) => (s.position - position).distance < horizontalGap)) { - position = Offset(position.dx + horizontalGap, position.dy); - } + int numSiblings = parent.children.length; + double totalWidth = numSiblings * (nodeWidth + horizontalGap); - print("🔹 New position for ${parent.name}: (${position.dx}, ${position.dy})"); - return position; + // Calculate position directly beneath the parent, spaced horizontally + double x = parent.position.dx - (totalWidth / 2) + numSiblings * (nodeWidth + horizontalGap); + double y = parent.position.dy + verticalGap; + + return Offset(x, y); } /// **Realign the entire tree after duplication** void realignTree() { - void updatePositions(SpaceModel node, double x, double y) { - node.position = Offset(x, y); - print("✅ Adjusted ${node.name} to (${x}, ${y})"); + const double nodeWidth = 200; + const double nodeHeight = 100; + const double horizontalGap = 60; + const double verticalGap = 180; - int numChildren = node.children.length; - double childStartX = x - ((numChildren - 1) * horizontalGap) / 2; + double canvasRightEdge = 1000; + double canvasBottomEdge = 1000; - for (int i = 0; i < numChildren; i++) { - updatePositions(node.children[i], childStartX + (i * horizontalGap), y + verticalGap); + // Step 1: Calculate the width of any subtree + double _calculateSubtreeWidth(SpaceModel node) { + if (node.children.isEmpty) return nodeWidth; + double totalWidth = 0; + for (var child in node.children) { + totalWidth += _calculateSubtreeWidth(child) + horizontalGap; + } + return totalWidth - horizontalGap; + } + + void _layoutTree(SpaceModel node, double startX, double y) { + double subtreeWidth = _calculateSubtreeWidth(node); + double centerX = startX + subtreeWidth / 2 - nodeWidth / 2; + node.position = Offset(centerX, y); + + canvasRightEdge = + centerX + nodeWidth > canvasRightEdge ? centerX + nodeWidth : canvasRightEdge; + canvasBottomEdge = y + nodeHeight > canvasBottomEdge ? y + nodeHeight : canvasBottomEdge; + + if (node.children.length == 1) { + final child = node.children.first; + + double parentCenterX = node.position.dx + nodeWidth / 2; + double childX = parentCenterX - nodeWidth / 2; + double childY = y + verticalGap; + + child.position = Offset(childX, childY); + + _layoutTree(child, childX, childY); + } else { + double childX = startX; + for (var child in node.children) { + double childWidth = _calculateSubtreeWidth(child); + _layoutTree(child, childX, y + verticalGap); + childX += childWidth + horizontalGap; + } } } - if (spaces.isNotEmpty) { - print("🔄 Realigning tree..."); - updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy); + // Step 3: Start centered + final SpaceModel? root = spaces.firstWhere( + (s) => s.parent == null, + orElse: () => spaces.first, + ); + + if (root != null) { + // Calculate total width of full tree + double totalTreeWidth = _calculateSubtreeWidth(root); + + double startingX = (canvasWidth / 2) - (totalTreeWidth / 2); + + _layoutTree(root, startingX, 100); + + setState(() { + canvasWidth = canvasRightEdge + 200; // give breathing space + canvasHeight = canvasBottomEdge + 200; + }); } } @@ -692,8 +789,6 @@ class _CommunityStructureAreaState extends State { : getBalancedChildPosition(duplicatedParent); final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces); - print( - "🟡 Duplicating ${original.name} → ${duplicatedName} at (${newPosition.dx}, ${newPosition.dy})"); final duplicated = SpaceModel( name: duplicatedName, @@ -722,7 +817,6 @@ class _CommunityStructureAreaState extends State { duplicated.incomingConnection = newConnection; duplicatedParent.addOutgoingConnection(newConnection); duplicatedParent.children.add(duplicated); - print("🔗 Created connection: ${duplicatedParent.name} → ${duplicated.name}"); } // **Recalculate the whole tree to avoid overlaps** @@ -739,19 +833,14 @@ class _CommunityStructureAreaState extends State { /// **Handle root duplication** if (space.parent == null) { - print("🟠 Duplicating root node: ${space.name}"); SpaceModel duplicatedRoot = duplicateRecursive(space, null); setState(() { spaces.add(duplicatedRoot); realignTree(); }); - - print("✅ Root duplication successful: ${duplicatedRoot.name}"); } else { duplicateRecursive(space, space.parent); } - - print("🟢 Finished duplication process for: ${space.name}"); } }