| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:collection'; |
| |
| import 'package:sky/base/hit_test.dart'; |
| import 'package:sky/rendering/sky_binding.dart'; |
| import 'package:sky/widgets/basic.dart'; |
| import 'package:sky/widgets/framework.dart'; |
| |
| typedef bool DragTargetWillAccept<T>(T data); |
| typedef void DragTargetAccept<T>(T data); |
| typedef Widget DragTargetBuilder<T>(List<T> candidateData, List<dynamic> rejectedData); |
| |
| class DragTarget<T> extends StatefulComponent { |
| DragTarget({ |
| Key key, |
| this.builder, |
| this.onWillAccept, |
| this.onAccept |
| }) : super(key: key); |
| |
| DragTargetBuilder<T> builder; |
| DragTargetWillAccept<T> onWillAccept; |
| DragTargetAccept<T> onAccept; |
| |
| final List<T> _candidateData = new List<T>(); |
| final List<dynamic> _rejectedData = new List<dynamic>(); |
| |
| void syncConstructorArguments(DragTarget source) { |
| builder = source.builder; |
| onWillAccept = source.onWillAccept; |
| onAccept = source.onAccept; |
| } |
| |
| bool didEnter(dynamic data) { |
| assert(!_candidateData.contains(data)); |
| assert(!_rejectedData.contains(data)); |
| if (data is T && (onWillAccept == null || onWillAccept(data))) { |
| setState(() { |
| _candidateData.add(data); |
| }); |
| return true; |
| } |
| _rejectedData.add(data); |
| return false; |
| } |
| |
| void didLeave(dynamic data) { |
| assert(_candidateData.contains(data) || _rejectedData.contains(data)); |
| setState(() { |
| _candidateData.remove(data); |
| _rejectedData.remove(data); |
| }); |
| } |
| |
| void didDrop(dynamic data) { |
| assert(_candidateData.contains(data)); |
| setState(() { |
| _candidateData.remove(data); |
| }); |
| if (onAccept != null) |
| onAccept(data); |
| } |
| |
| Widget build() { |
| return builder(new UnmodifiableListView<T>(_candidateData), |
| new UnmodifiableListView<dynamic>(_rejectedData)); |
| } |
| } |
| |
| class DragController { |
| DragController(this.data); |
| |
| final dynamic data; |
| |
| DragTarget _activeTarget; |
| bool _activeTargetWillAcceptDrop = false; |
| |
| DragTarget _getDragTarget(List<HitTestEntry> path) { |
| for (HitTestEntry entry in path.reversed) { |
| for (Widget widget in RenderObjectWrapper.getWidgetsForRenderObject(entry.target)) { |
| if (widget is DragTarget) |
| return widget; |
| } |
| } |
| return null; |
| } |
| |
| void update(Point globalPosition) { |
| HitTestResult result = SkyBinding.instance.hitTest(globalPosition); |
| DragTarget target = _getDragTarget(result.path); |
| if (target == _activeTarget) |
| return; |
| if (_activeTarget != null) |
| _activeTarget.didLeave(data); |
| _activeTarget = target; |
| _activeTargetWillAcceptDrop = _activeTarget != null && _activeTarget.didEnter(data); |
| } |
| |
| void cancel() { |
| if (_activeTarget != null) |
| _activeTarget.didLeave(data); |
| _activeTarget = null; |
| _activeTargetWillAcceptDrop = false; |
| } |
| |
| void drop() { |
| if (_activeTarget == null) |
| return; |
| if (_activeTargetWillAcceptDrop) |
| _activeTarget.didDrop(data); |
| else |
| _activeTarget.didLeave(data); |
| _activeTarget = null; |
| _activeTargetWillAcceptDrop = false; |
| } |
| } |