diff --git a/win_text_editor/lib/shared/components/tree_view.dart b/win_text_editor/lib/shared/components/tree_view.dart index 7351eeb..f48de5a 100644 --- a/win_text_editor/lib/shared/components/tree_view.dart +++ b/win_text_editor/lib/shared/components/tree_view.dart @@ -23,6 +23,8 @@ class TreeViewConfig { final Map icons; final bool showRefreshButton; final IconData refreshIcon; + final bool draggable; // 是否可拖拽 + final bool droppable; // 是否可作为拖放目标 const TreeViewConfig({ this.lazyLoad = false, @@ -34,6 +36,8 @@ class TreeViewConfig { this.icons = const {}, this.showRefreshButton = false, this.refreshIcon = Icons.refresh, + this.draggable = false, + this.droppable = false, }); } @@ -98,26 +102,75 @@ class _TreeViewState extends State { Widget build(BuildContext context) { return Stack( children: [ - ListView.builder( - controller: _effectiveController, - physics: const ClampingScrollPhysics(), - itemCount: _countVisibleNodes(widget.nodes), - itemBuilder: (context, index) { - final node = _getVisibleNode(widget.nodes, index); - final isSelected = _selectedIds.contains(node.id); + DragTarget( + onWillAcceptWithDetails: (data) { + // 检查是否允许拖放 + if (!widget.config.droppable) return false; + return true; + }, + onAcceptWithDetails: (draggedNode) { + // 处理拖拽放置逻辑 + // 你可以在这里实现节点移动或排序的逻辑 + + debugPrint('Dropped ${draggedNode.data.name}'); + }, + builder: (context, candidateData, rejectedData) { + return ListView.builder( + controller: _effectiveController, + physics: const ClampingScrollPhysics(), + itemCount: _countVisibleNodes(widget.nodes), + itemBuilder: (context, index) { + final node = _getVisibleNode(widget.nodes, index); + final isSelected = _selectedIds.contains(node.id); - return widget.nodeBuilder != null - ? widget.nodeBuilder!(context, node, isSelected, () => _handleNodeTap(node)) - : TreeNodeWidget( - key: ValueKey(node.id), - node: node, - config: widget.config, - isSelected: isSelected, - isChecked: _checkedIds.contains(node.id), - onTap: () => _handleNodeTap(node), - onDoubleTap: () => widget.onNodeDoubleTap?.call(node), - onCheckChanged: (value) => _handleNodeCheckChanged(node, value), - ); + return widget.nodeBuilder != null + ? widget.nodeBuilder!(context, node, isSelected, () => _handleNodeTap(node)) + : Draggable( + data: node, + feedback: Material( + child: Container( + width: 200, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(4), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Text(node.title), + ), + ), + childWhenDragging: Opacity( + opacity: 0.5, + child: TreeNodeWidget( + key: ValueKey(node.id), + node: node, + config: widget.config, + isSelected: isSelected, + isChecked: _checkedIds.contains(node.id), + onTap: () => _handleNodeTap(node), + onDoubleTap: () => widget.onNodeDoubleTap?.call(node), + onCheckChanged: (value) => _handleNodeCheckChanged(node, value), + ), + ), + child: TreeNodeWidget( + key: ValueKey(node.id), + node: node, + config: widget.config, + isSelected: isSelected, + isChecked: _checkedIds.contains(node.id), + onTap: () => _handleNodeTap(node), + onDoubleTap: () => widget.onNodeDoubleTap?.call(node), + onCheckChanged: (value) => _handleNodeCheckChanged(node, value), + ), + ); + }, + ); }, ),