blob: d7d6d5d1c3c67f88bc98ddde492ae52a9107ab9f [file] [log] [blame]
// 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;
}
}