// 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; } }