diff --git a/example/lib/main.dart b/example/lib/main.dart index eafa53c..8adc4c8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -39,8 +39,9 @@ class _TestScreenState extends State { MindMapData( id: 'node1', title: 'πŸ“ λ…Έλ“œ1', + borderColor: Colors.green, children: [ - MindMapData(id: 'sub1', title: 'μ„œλΈŒ1'), + MindMapData(id: 'sub1', title: 'μ„œλΈŒ1', borderColor: Colors.purple), MindMapData(id: 'sub2', title: 'μ„œλΈŒ2'), ], ), @@ -94,6 +95,9 @@ class _TestScreenState extends State { 'πŸƒ 첫리프', () => _focusToNode(CameraFocus.firstLeaf, null), ), + // Forward/Backward focus buttons + _buildButton('⬅️ 이전', _focusPreviousNode), + _buildButton('λ‹€μŒ ➑️', _focusNextNode), ], ), ), @@ -171,7 +175,7 @@ class _TestScreenState extends State { levelSpacing: 120, nodeMargin: 15, ), - cameraFocus: CameraFocus.fitAll, + cameraFocus: currentFocus, focusNodeId: targetNodeId, focusAnimation: const Duration( milliseconds: 1000, @@ -256,4 +260,31 @@ class _TestScreenState extends State { return '🌳 μ „μ²΄νŠΈλ¦¬'; } } + + void _focusNextNode() { + final flatNodes = mindMapData.flatten(); + if (flatNodes.isEmpty) return; + int currentIdx = flatNodes.indexWhere((n) => n.id == targetNodeId); + int nextIdx = (currentIdx + 1) % flatNodes.length; + final nextNode = flatNodes[nextIdx]; + setState(() { + currentFocus = CameraFocus.custom; + targetNodeId = nextNode.id; + lastAction = 'λ‹€μŒ λ…Έλ“œλ‘œ 이동: ${nextNode.title}'; + }); + } + + void _focusPreviousNode() { + final flatNodes = mindMapData.flatten(); + if (flatNodes.isEmpty) return; + int currentIdx = flatNodes.indexWhere((n) => n.id == targetNodeId); + int prevIdx = (currentIdx - 1); + if (prevIdx < 0) prevIdx = flatNodes.length - 1; + final prevNode = flatNodes[prevIdx]; + setState(() { + currentFocus = CameraFocus.custom; + targetNodeId = prevNode.id; + lastAction = '이전 λ…Έλ“œλ‘œ 이동: ${prevNode.title}'; + }); + } } diff --git a/lib/src/models/mind_map_data.dart b/lib/src/models/mind_map_data.dart index 20985d6..ccb45bb 100644 --- a/lib/src/models/mind_map_data.dart +++ b/lib/src/models/mind_map_data.dart @@ -107,6 +107,15 @@ class MindMapData { @override String toString() { - return 'MindMapData(id: $id, title: $title, children: ${children.length})'; + return 'MindMapData(id: $id, title: $title, children: [36m${children.length} [0m)'; + } + + /// Returns a flat list of all nodes in pre-order traversal (self, then children) + List flatten() { + List result = [this]; + for (final child in children) { + result.addAll(child.flatten()); + } + return result; } } diff --git a/lib/src/widgets/mind_map_widget.dart b/lib/src/widgets/mind_map_widget.dart index 7c7ae90..0ef3a72 100644 --- a/lib/src/widgets/mind_map_widget.dart +++ b/lib/src/widgets/mind_map_widget.dart @@ -1,13 +1,14 @@ +import 'dart:math' as math; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'dart:math' as math; -import '../models/mind_map_data.dart'; -import '../models/mind_map_style.dart'; -import '../models/mind_map_node.dart'; +import '../enums/camera_focus.dart'; import '../enums/mind_map_layout.dart'; import '../enums/node_shape.dart'; -import '../enums/camera_focus.dart'; +import '../models/mind_map_data.dart'; +import '../models/mind_map_node.dart'; +import '../models/mind_map_style.dart'; // import '../enums/mind_map_type.dart'; import '../painters/mind_map_painter.dart'; import '../painters/node_painter.dart'; @@ -1647,6 +1648,8 @@ class _MindMapWidgetState extends State /// κ°œλ³„ λ…Έλ“œ μœ„μ ― λΉŒλ“œ / Build individual node widget Widget _buildNodeWidget(MindMapNode node) { final isSelected = _selectedNodeId == node.id; + final isFocused = + widget.focusNodeId != null && widget.focusNodeId == node.id; final actualSize = widget.style.getActualNodeSize( node.title, @@ -1746,8 +1749,11 @@ class _MindMapWidgetState extends State final nodeColor = node.color; final textColor = node.textColor ?? widget.style.defaultTextStyle.color; final borderColor = - node.borderColor ?? - (isSelected ? widget.style.selectionBorderColor : Colors.white); + (isFocused || isSelected) + ? widget.style.selectionBorderColor + : (node.borderColor ?? Colors.white); + final borderWidth = + (isFocused || isSelected) ? widget.style.selectionBorderWidth : 2.0; return Positioned( key: ValueKey('positioned_${node.id}'), @@ -1785,8 +1791,7 @@ class _MindMapWidgetState extends State shape: widget.style.nodeShape, fillColor: nodeColor, borderColor: borderColor, - borderWidth: - isSelected ? widget.style.selectionBorderWidth : 2.0, + borderWidth: borderWidth, shadowEnabled: widget.style.enableNodeShadow, shadowColor: widget.style.nodeShadowColor, shadowBlurRadius: widget.style.nodeShadowBlurRadius,