From 6e1f58e9dcd07d92f72162be5345418b3168f1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Bel?= Date: Tue, 27 Sep 2022 11:48:56 +0200 Subject: [PATCH] Added onHover and onTapCancel callbacks --- example/pubspec.lock | 51 ++- lib/src/canvas_touch_detector.dart | 353 ++++++++++-------- lib/src/hover.dart | 11 + lib/src/shape_handler.dart | 86 ++++- lib/src/shapes/util.dart | 17 +- lib/src/touchy_canvas.dart | 82 +++- lib/src/types/types.dart | 2 + lib/touchable.dart | 1 + pubspec.lock | 49 ++- .../hittest_test/defer_to_child.dart | 41 +- test/shape_handler/hittest_test/opaque.dart | 41 +- .../hittest_test/translucent.dart | 41 +- 12 files changed, 469 insertions(+), 306 deletions(-) create mode 100644 lib/src/hover.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index aecadfe..b79be13 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -21,28 +21,21 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" cupertino_icons: dependency: "direct main" description: @@ -56,7 +49,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -73,21 +66,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.8.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.2" sky_engine: dependency: transitive description: flutter @@ -99,7 +99,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -120,41 +120,34 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.12" touchable: dependency: "direct main" description: path: ".." relative: true source: path - version: "1.0.0" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "1.0.1" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.2" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" diff --git a/lib/src/canvas_touch_detector.dart b/lib/src/canvas_touch_detector.dart index 03bd936..fa2dbb7 100644 --- a/lib/src/canvas_touch_detector.dart +++ b/lib/src/canvas_touch_detector.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:touchable/src/hover.dart'; +import 'package:touchable/src/shapes/shape.dart'; import 'package:touchable/src/types/types.dart'; ///[CanvasTouchDetector] widget detects the gestures on your [CustomPaint] widget. @@ -26,6 +28,7 @@ class _CanvasTouchDetectorState extends State { final StreamController touchController = StreamController.broadcast(); StreamSubscription? streamSubscription; + final PreviousTouchState previousTouchState = PreviousTouchState(); Future addStreamListener(Function(Gesture) callBack) async { await streamSubscription?.cancel(); @@ -34,165 +37,188 @@ class _CanvasTouchDetectorState extends State { @override Widget build(BuildContext context) { - return TouchDetectionController(touchController, addStreamListener, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - child: Builder( - builder: (context) { - return widget.builder(context); - }, - ), - onTapDown: !widget.gesturesToOverride.contains(GestureType.onTapDown) + return TouchDetectionController( + touchController, + addStreamListener, + previousTouchState: previousTouchState, + child: MouseRegion( + hitTestBehavior: HitTestBehavior.translucent, + onHover: !widget.gesturesToOverride.contains(GestureType.onHover) ? null : (tapDetail) { - touchController - .add(Gesture(GestureType.onTapDown, tapDetail)); + touchController.add(Gesture( + GestureType.onHover, + OnHoverDetail(tapDetail.localPosition, null), + )); }, - onTapUp: !widget.gesturesToOverride.contains(GestureType.onTapUp) - ? null - : (tapDetail) { - touchController.add(Gesture(GestureType.onTapUp, tapDetail)); - }, - onHorizontalDragDown: !widget.gesturesToOverride - .contains(GestureType.onHorizontalDragDown) - ? null - : (tapDetail) { - touchController.add( - Gesture(GestureType.onHorizontalDragDown, tapDetail)); - }, - onHorizontalDragStart: !widget.gesturesToOverride - .contains(GestureType.onHorizontalDragStart) - ? null - : (tapDetail) { - touchController.add( - Gesture(GestureType.onHorizontalDragStart, tapDetail)); - }, - onHorizontalDragUpdate: !widget.gesturesToOverride - .contains(GestureType.onHorizontalDragUpdate) - ? null - : (tapDetail) { - touchController.add( - Gesture(GestureType.onHorizontalDragUpdate, tapDetail)); - }, - onVerticalDragDown: !widget.gesturesToOverride - .contains(GestureType.onVerticalDragDown) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onVerticalDragDown, tapDetail)); - }, - onVerticalDragStart: !widget.gesturesToOverride - .contains(GestureType.onVerticalDragStart) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onVerticalDragStart, tapDetail)); - }, - onVerticalDragUpdate: !widget.gesturesToOverride - .contains(GestureType.onVerticalDragUpdate) - ? null - : (tapDetail) { - touchController.add( - Gesture(GestureType.onVerticalDragUpdate, tapDetail)); - }, - onLongPressStart: !widget.gesturesToOverride - .contains(GestureType.onLongPressStart) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onLongPressStart, tapDetail)); - }, - onLongPressEnd: - !widget.gesturesToOverride.contains(GestureType.onLongPressEnd) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onLongPressEnd, tapDetail)); - }, - onLongPressMoveUpdate: !widget.gesturesToOverride - .contains(GestureType.onLongPressMoveUpdate) - ? null - : (tapDetail) { - touchController.add( - Gesture(GestureType.onLongPressMoveUpdate, tapDetail)); - }, - onScaleStart: - !widget.gesturesToOverride.contains(GestureType.onScaleStart) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onScaleStart, tapDetail)); - }, - onScaleUpdate: - !widget.gesturesToOverride.contains(GestureType.onScaleUpdate) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onScaleUpdate, tapDetail)); - }, - onForcePressStart: !widget.gesturesToOverride - .contains(GestureType.onForcePressStart) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onForcePressStart, tapDetail)); - }, - onForcePressEnd: - !widget.gesturesToOverride.contains(GestureType.onForcePressEnd) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onForcePressEnd, tapDetail)); - }, - onForcePressPeak: !widget.gesturesToOverride - .contains(GestureType.onForcePressPeak) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onForcePressPeak, tapDetail)); - }, - onForcePressUpdate: !widget.gesturesToOverride - .contains(GestureType.onForcePressUpdate) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onForcePressUpdate, tapDetail)); - }, - onPanStart: - !widget.gesturesToOverride.contains(GestureType.onPanStart) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onPanStart, tapDetail)); - }, - onPanUpdate: - !widget.gesturesToOverride.contains(GestureType.onPanUpdate) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onPanUpdate, tapDetail)); - }, - onPanDown: !widget.gesturesToOverride.contains(GestureType.onPanDown) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onPanDown, tapDetail)); - }, - onSecondaryTapDown: !widget.gesturesToOverride - .contains(GestureType.onSecondaryTapDown) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onSecondaryTapDown, tapDetail)); - }, - onSecondaryTapUp: !widget.gesturesToOverride - .contains(GestureType.onSecondaryTapUp) - ? null - : (tapDetail) { - touchController - .add(Gesture(GestureType.onSecondaryTapUp, tapDetail)); - }, - )); + child: GestureDetector( + behavior: HitTestBehavior.translucent, + child: Builder( + builder: (context) { + return widget.builder(context); + }, + ), + onTapDown: + !widget.gesturesToOverride.contains(GestureType.onTapDown) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onTapDown, tapDetail)); + }, + onTapUp: !widget.gesturesToOverride.contains(GestureType.onTapUp) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onTapUp, tapDetail)); + }, + onTapCancel: !widget.gesturesToOverride + .contains(GestureType.onTapCancel) + ? null + : () { + touchController.add(Gesture(GestureType.onTapCancel, null)); + }, + onHorizontalDragDown: !widget.gesturesToOverride + .contains(GestureType.onHorizontalDragDown) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onHorizontalDragDown, tapDetail)); + }, + onHorizontalDragStart: !widget.gesturesToOverride + .contains(GestureType.onHorizontalDragStart) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onHorizontalDragStart, tapDetail)); + }, + onHorizontalDragUpdate: !widget.gesturesToOverride + .contains(GestureType.onHorizontalDragUpdate) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onHorizontalDragUpdate, tapDetail)); + }, + onVerticalDragDown: !widget.gesturesToOverride + .contains(GestureType.onVerticalDragDown) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onVerticalDragDown, tapDetail)); + }, + onVerticalDragStart: !widget.gesturesToOverride + .contains(GestureType.onVerticalDragStart) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onVerticalDragStart, tapDetail)); + }, + onVerticalDragUpdate: !widget.gesturesToOverride + .contains(GestureType.onVerticalDragUpdate) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onVerticalDragUpdate, tapDetail)); + }, + onLongPressStart: !widget.gesturesToOverride + .contains(GestureType.onLongPressStart) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onLongPressStart, tapDetail)); + }, + onLongPressEnd: !widget.gesturesToOverride + .contains(GestureType.onLongPressEnd) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onLongPressEnd, tapDetail)); + }, + onLongPressMoveUpdate: !widget.gesturesToOverride + .contains(GestureType.onLongPressMoveUpdate) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onLongPressMoveUpdate, tapDetail)); + }, + onScaleStart: + !widget.gesturesToOverride.contains(GestureType.onScaleStart) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onScaleStart, tapDetail)); + }, + onScaleUpdate: + !widget.gesturesToOverride.contains(GestureType.onScaleUpdate) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onScaleUpdate, tapDetail)); + }, + onForcePressStart: !widget.gesturesToOverride + .contains(GestureType.onForcePressStart) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onForcePressStart, tapDetail)); + }, + onForcePressEnd: !widget.gesturesToOverride + .contains(GestureType.onForcePressEnd) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onForcePressEnd, tapDetail)); + }, + onForcePressPeak: !widget.gesturesToOverride + .contains(GestureType.onForcePressPeak) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onForcePressPeak, tapDetail)); + }, + onForcePressUpdate: !widget.gesturesToOverride + .contains(GestureType.onForcePressUpdate) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onForcePressUpdate, tapDetail)); + }, + onPanStart: + !widget.gesturesToOverride.contains(GestureType.onPanStart) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onPanStart, tapDetail)); + }, + onPanUpdate: + !widget.gesturesToOverride.contains(GestureType.onPanUpdate) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onPanUpdate, tapDetail)); + }, + onPanDown: + !widget.gesturesToOverride.contains(GestureType.onPanDown) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onPanDown, tapDetail)); + }, + onSecondaryTapDown: !widget.gesturesToOverride + .contains(GestureType.onSecondaryTapDown) + ? null + : (tapDetail) { + touchController.add( + Gesture(GestureType.onSecondaryTapDown, tapDetail)); + }, + onSecondaryTapUp: !widget.gesturesToOverride + .contains(GestureType.onSecondaryTapUp) + ? null + : (tapDetail) { + touchController + .add(Gesture(GestureType.onSecondaryTapUp, tapDetail)); + }, + )), + ); } @override @@ -205,14 +231,18 @@ class _CanvasTouchDetectorState extends State { class TouchDetectionController extends InheritedWidget { final StreamController _controller; final Function addListener; + final PreviousTouchState previousTouchState; bool get hasListener => _controller.hasListener; StreamController get controller => _controller; - const TouchDetectionController(this._controller, this.addListener, - {required Widget child}) - : super(child: child); + const TouchDetectionController( + this._controller, + this.addListener, { + required Widget child, + required this.previousTouchState, + }) : super(child: child); static TouchDetectionController? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType(); @@ -222,3 +252,8 @@ class TouchDetectionController extends InheritedWidget { return false; } } + +class PreviousTouchState { + final List lastHoveredShapes = []; + final List lastTappedDownShapes = []; +} diff --git a/lib/src/hover.dart b/lib/src/hover.dart new file mode 100644 index 0000000..96d7b86 --- /dev/null +++ b/lib/src/hover.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class OnHoverDetail { + final Offset localPosition; + final bool? hovering; + + const OnHoverDetail(this.localPosition, this.hovering); +} + + +typedef GestureOnHover = void Function(OnHoverDetail details); \ No newline at end of file diff --git a/lib/src/shape_handler.dart b/lib/src/shape_handler.dart index ac6a8fd..2a0ae93 100644 --- a/lib/src/shape_handler.dart +++ b/lib/src/shape_handler.dart @@ -1,6 +1,8 @@ import 'dart:ui'; import 'package:flutter/cupertino.dart'; +import 'package:touchable/src/canvas_touch_detector.dart'; +import 'package:touchable/src/hover.dart'; import 'package:touchable/src/shapes/clip.dart'; import 'package:touchable/src/shapes/shape.dart'; import 'package:touchable/src/shapes/util.dart'; @@ -31,7 +33,10 @@ class ShapeHandler { /// /// Looking at above diagram , given the stack position 3 , this function returns all ClipShapes that are pushed before 3 into the clip stack. List _getClipShapesBelowPosition(int position) { - return clipItems.where((element) => element.position <= position).map((e) => e.clipShape).toList(); + return clipItems + .where((element) => element.position <= position) + .map((e) => e.clipShape) + .toList(); } ///returns [true] if point lies inside all the clipShapes @@ -42,16 +47,17 @@ class ShapeHandler { return true; } - Offset _getActualOffsetFromScrollController( - Offset touchPoint, ScrollController? controller, AxisDirection direction) { + Offset _getActualOffsetFromScrollController(Offset touchPoint, + ScrollController? controller, AxisDirection direction) { if (controller == null) { return touchPoint; } final scrollPosition = controller.position; - final actualScrollPixels = direction == AxisDirection.left || direction == AxisDirection.up - ? scrollPosition.maxScrollExtent - scrollPosition.pixels - : scrollPosition.pixels; + final actualScrollPixels = + direction == AxisDirection.left || direction == AxisDirection.up + ? scrollPosition.maxScrollExtent - scrollPosition.pixels + : scrollPosition.pixels; if (direction == AxisDirection.left || direction == AxisDirection.right) { return Offset(touchPoint.dx + actualScrollPixels, touchPoint.dy); @@ -68,7 +74,8 @@ class ShapeHandler { continue; } if (shape.isInside(point)) { - if (_isPointInsideClipShapes(_getClipShapesBelowPosition(i), point) == false) { + if (_isPointInsideClipShapes(_getClipShapesBelowPosition(i), point) == + false) { if (shape.hitTestBehavior == HitTestBehavior.opaque) { return selectedShapes; } @@ -84,21 +91,68 @@ class ShapeHandler { } Future handleGestureEvent( - Gesture gesture, { + Gesture gesture, + PreviousTouchState previousTouchState, { ScrollController? scrollController, AxisDirection direction = AxisDirection.down, }) async { - var touchPoint = _getActualOffsetFromScrollController( - TouchCanvasUtil.getPointFromGestureDetail(gesture.gestureDetail), scrollController, direction); - if (!_registeredGestures.contains(gesture.gestureType)) return; - - var touchedShapes = _getTouchedShapes(touchPoint); - if (touchedShapes.isEmpty) return; - for (var touchedShape in touchedShapes) { - if (touchedShape.registeredGestures.contains(gesture.gestureType)) { + if (_registeredGestures.contains(gesture.gestureType) && + gesture.gestureType == GestureType.onTapCancel) { + for (var touchedShape in previousTouchState.lastTappedDownShapes) { var callback = touchedShape.getCallbackFromGesture(gesture); callback(); } + previousTouchState.lastTappedDownShapes.clear(); + } else { + var touchPoint = _getActualOffsetFromScrollController( + TouchCanvasUtil.getPointFromGestureDetail(gesture.gestureDetail), + scrollController, + direction); + if (!_registeredGestures.contains(gesture.gestureType)) return; + + var touchedShapes = _getTouchedShapes(touchPoint); + if (touchedShapes.isEmpty && gesture.gestureType != GestureType.onHover) { + return; + } + if (gesture.gestureType == GestureType.onHover) { + for (var touchedShape in previousTouchState.lastHoveredShapes) { + if (!touchedShapes.contains(touchedShape)) { + Gesture newGesture = Gesture( + gesture.gestureType, + OnHoverDetail( + TouchCanvasUtil.getPointFromGestureDetail( + gesture.gestureDetail), + false)); + var callback = touchedShape.getCallbackFromGesture(newGesture); + callback(); + } + } + previousTouchState.lastHoveredShapes.clear(); + } + for (var touchedShape in touchedShapes) { + if (touchedShape.registeredGestures.contains(gesture.gestureType)) { + if (gesture.gestureType == GestureType.onTapDown) { + previousTouchState.lastTappedDownShapes.add(touchedShape); + } else if (gesture.gestureType == GestureType.onTapUp) { + previousTouchState.lastTappedDownShapes.clear(); + } + previousTouchState.lastHoveredShapes.add(touchedShape); + + if (gesture.gestureType == GestureType.onHover) { + Gesture newGesture = Gesture( + gesture.gestureType, + OnHoverDetail( + TouchCanvasUtil.getPointFromGestureDetail( + gesture.gestureDetail), + true)); + var callback = touchedShape.getCallbackFromGesture(newGesture); + callback(); + } else { + var callback = touchedShape.getCallbackFromGesture(gesture); + callback(); + } + } + } } } } diff --git a/lib/src/shapes/util.dart b/lib/src/shapes/util.dart index bcd96dc..22ca89b 100644 --- a/lib/src/shapes/util.dart +++ b/lib/src/shapes/util.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:touchable/src/hover.dart'; import 'package:touchable/src/types/types.dart'; class ShapeUtil { @@ -37,8 +38,11 @@ class TouchCanvasUtil { return (gestureDetail as ScaleUpdateDetails).localFocalPoint; case ForcePressDetails: return (gestureDetail as ForcePressDetails).localPosition; + case OnHoverDetail: + return (gestureDetail as OnHoverDetail).localPosition; default: - throw Exception("gestureDetail.runTimeType = ${gestureDetail.runtimeType} is not recognized ! "); + throw Exception( + "gestureDetail.runTimeType = ${gestureDetail.runtimeType} is not recognized ! "); } } @@ -57,6 +61,8 @@ class TouchCanvasUtil { required GestureDragDownCallback? onPanDown, required GestureTapDownCallback? onSecondaryTapDown, required GestureTapUpCallback? onSecondaryTapUp, + required GestureOnHover? onHover, + required GestureTapCancelCallback? onTapCancel, }) { var map = {}; if (onTapDown != null) { @@ -68,7 +74,8 @@ class TouchCanvasUtil { map.putIfAbsent(GestureType.onLongPressStart, () => onLongPressStart); } if (onLongPressMoveUpdate != null) { - map.putIfAbsent(GestureType.onLongPressMoveUpdate, () => onLongPressMoveUpdate); + map.putIfAbsent( + GestureType.onLongPressMoveUpdate, () => onLongPressMoveUpdate); } if (onLongPressEnd != null) { map.putIfAbsent(GestureType.onLongPressEnd, () => onLongPressEnd); @@ -103,6 +110,12 @@ class TouchCanvasUtil { if (onSecondaryTapUp != null) { map.putIfAbsent(GestureType.onSecondaryTapUp, () => onSecondaryTapUp); } + if (onHover != null) { + map.putIfAbsent(GestureType.onHover, () => onHover); + } + if (onHover != null) { + map.putIfAbsent(GestureType.onTapCancel, () => (_) => onTapCancel); + } return map; } diff --git a/lib/src/touchy_canvas.dart b/lib/src/touchy_canvas.dart index aa46844..3535ad2 100644 --- a/lib/src/touchy_canvas.dart +++ b/lib/src/touchy_canvas.dart @@ -1,10 +1,10 @@ -import 'dart:math'; import 'dart:typed_data'; import 'dart:ui'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' hide Image; import 'package:touchable/src/canvas_touch_detector.dart'; +import 'package:touchable/src/hover.dart'; import 'package:touchable/src/shape_handler.dart'; import 'package:touchable/src/shapes/arc.dart'; import 'package:touchable/src/shapes/circle.dart'; @@ -36,6 +36,7 @@ class TouchyCanvas { touchController?.addListener((event) { _shapeHandler.handleGestureEvent( event, + touchController.previousTouchState, scrollController: scrollController, direction: scrollDirection, ); @@ -52,7 +53,8 @@ class TouchyCanvas { _shapeHandler.addShape(ClipRRectShape(rrect)); } - void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}) { + void clipRect(Rect rect, + {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}) { _canvas.clipRect(rect, clipOp: clipOp, doAntiAlias: doAntiAlias); _shapeHandler.addShape(ClipRectShape(rect, clipOp: clipOp)); } @@ -76,6 +78,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawCircle(c, radius, paint); _shapeHandler.addShape(Circle( @@ -98,6 +102,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -121,6 +127,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawLine(p1, p2, paint); _shapeHandler.addShape(Line(p1, p2, @@ -141,6 +149,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -163,6 +173,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawOval(rect, paint); _shapeHandler.addShape(Oval(rect, @@ -183,6 +195,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -211,6 +225,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawPath(path, paint); _shapeHandler.addShape(PathShape(path, @@ -231,6 +247,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -254,6 +272,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawPoints(pointMode, points, paint); _shapeHandler.addShape(Point(pointMode, points, @@ -274,6 +294,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -296,6 +318,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawRRect(rrect, paint); _shapeHandler.addShape(RoundedRectangle(rrect, @@ -316,6 +340,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -339,6 +365,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawRawPoints(pointMode, points, paint); List offsetPoints = []; @@ -363,6 +391,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -385,6 +415,8 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawRect(rect, paint); _shapeHandler.addShape(Rectangle(rect, @@ -405,10 +437,13 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } - void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { + void drawShadow( + Path path, Color color, double elevation, bool transparentOccluder) { _canvas.drawShadow(path, color, elevation, transparentOccluder); // _shapeHandler.addShape(PathShape(path)); } @@ -433,9 +468,13 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawImage(image, p, paint); - _shapeHandler.addShape(Rectangle(Rect.fromLTWH(p.dx, p.dy, image.width.toDouble(), image.height.toDouble()), + _shapeHandler.addShape(Rectangle( + Rect.fromLTWH( + p.dx, p.dy, image.width.toDouble(), image.height.toDouble()), paint: paint, hitTestBehavior: hitTestBehavior, gestureMap: TouchCanvasUtil.getGestureCallbackMap( @@ -453,6 +492,8 @@ class TouchyCanvas { onPanDown: onPanDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, + onHover: onHover, + onTapCancel: onTapCancel, ))); } @@ -478,26 +519,31 @@ class TouchyCanvas { GestureDragDownCallback? onPanDown, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, + GestureOnHover? onHover, + GestureTapCancelCallback? onTapCancel, }) { _canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint); var arc = Arc(rect, startAngle, sweepAngle, useCenter, paint: paint, hitTestBehavior: hitTestBehavior, gestureMap: TouchCanvasUtil.getGestureCallbackMap( - onTapDown: onTapDown, - onTapUp: null, - onLongPressStart: null, - onLongPressEnd: null, - onLongPressMoveUpdate: null, - onForcePressStart: null, - onForcePressEnd: null, - onForcePressPeak: null, - onForcePressUpdate: null, - onPanStart: null, - onPanUpdate: null, - onPanDown: null, - onSecondaryTapDown: null, - onSecondaryTapUp: null)); + onTapDown: onTapDown, + onTapUp: null, + onLongPressStart: null, + onLongPressEnd: null, + onLongPressMoveUpdate: null, + onForcePressStart: null, + onForcePressEnd: null, + onForcePressPeak: null, + onForcePressUpdate: null, + onPanStart: null, + onPanUpdate: null, + onPanDown: null, + onSecondaryTapDown: null, + onSecondaryTapUp: null, + onHover: null, + onTapCancel: null, + )); _shapeHandler.addShape(arc); } diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index ff2476a..a8f1603 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -41,4 +41,6 @@ enum GestureType { onPanDown, onSecondaryTapDown, onSecondaryTapUp, + onHover, + onTapCancel, } diff --git a/lib/touchable.dart b/lib/touchable.dart index 3ae3239..1233100 100644 --- a/lib/touchable.dart +++ b/lib/touchable.dart @@ -31,3 +31,4 @@ library touchable; export 'src/canvas_touch_detector.dart'; export 'src/touchy_canvas.dart'; export 'src/types/types.dart'; +export 'src/hover.dart'; diff --git a/pubspec.lock b/pubspec.lock index ad6d1df..e15edce 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -21,35 +21,28 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -66,21 +59,28 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.8.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.2" sky_engine: dependency: transitive description: flutter @@ -92,7 +92,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -113,34 +113,27 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" + version: "0.4.12" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.2" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" diff --git a/test/shape_handler/hittest_test/defer_to_child.dart b/test/shape_handler/hittest_test/defer_to_child.dart index 1918a72..7ebf82b 100644 --- a/test/shape_handler/hittest_test/defer_to_child.dart +++ b/test/shape_handler/hittest_test/defer_to_child.dart @@ -21,22 +21,25 @@ void testDeferToChild() { Map getMap(Function function) { return TouchCanvasUtil.getGestureCallbackMap( - onTapDown: (detail) { - function(detail); - }, - onTapUp: null, - onLongPressStart: null, - onLongPressEnd: null, - onLongPressMoveUpdate: null, - onForcePressStart: null, - onForcePressEnd: null, - onForcePressPeak: null, - onForcePressUpdate: null, - onPanStart: null, - onPanUpdate: null, - onPanDown: null, - onSecondaryTapDown: null, - onSecondaryTapUp: null); + onTapDown: (detail) { + function(detail); + }, + onTapUp: null, + onLongPressStart: null, + onLongPressEnd: null, + onLongPressMoveUpdate: null, + onForcePressStart: null, + onForcePressEnd: null, + onForcePressPeak: null, + onForcePressUpdate: null, + onPanStart: null, + onPanUpdate: null, + onPanDown: null, + onSecondaryTapDown: null, + onSecondaryTapUp: null, + onHover: null, + onTapCancel: null, + ); } void addAllShapes() { @@ -132,8 +135,10 @@ void testDeferToChild() { addAllShapes(); void handleGesture(Offset position, List expected) { resultList = []; - shapeHandler.handleGestureEvent(Gesture( - GestureType.onTapDown, TapDownDetails(localPosition: position))); + shapeHandler.handleGestureEvent( + Gesture(GestureType.onTapDown, TapDownDetails(localPosition: position)), + PreviousTouchState(), + ); expect(resultList, expected); } diff --git a/test/shape_handler/hittest_test/opaque.dart b/test/shape_handler/hittest_test/opaque.dart index 90e58c7..3b54498 100644 --- a/test/shape_handler/hittest_test/opaque.dart +++ b/test/shape_handler/hittest_test/opaque.dart @@ -21,22 +21,25 @@ void testOpaque() { Map getMap(Function function) { return TouchCanvasUtil.getGestureCallbackMap( - onTapDown: (detail) { - function(detail); - }, - onTapUp: null, - onLongPressStart: null, - onLongPressEnd: null, - onLongPressMoveUpdate: null, - onForcePressStart: null, - onForcePressEnd: null, - onForcePressPeak: null, - onForcePressUpdate: null, - onPanStart: null, - onPanUpdate: null, - onPanDown: null, - onSecondaryTapDown: null, - onSecondaryTapUp: null); + onTapDown: (detail) { + function(detail); + }, + onTapUp: null, + onLongPressStart: null, + onLongPressEnd: null, + onLongPressMoveUpdate: null, + onForcePressStart: null, + onForcePressEnd: null, + onForcePressPeak: null, + onForcePressUpdate: null, + onPanStart: null, + onPanUpdate: null, + onPanDown: null, + onSecondaryTapDown: null, + onSecondaryTapUp: null, + onHover: null, + onTapCancel: null, + ); } void addAllShapes() { @@ -129,8 +132,10 @@ void testOpaque() { addAllShapes(); void handleGesture(Offset position, List expected) { resultList = []; - shapeHandler.handleGestureEvent(Gesture( - GestureType.onTapDown, TapDownDetails(localPosition: position))); + shapeHandler.handleGestureEvent( + Gesture(GestureType.onTapDown, TapDownDetails(localPosition: position)), + PreviousTouchState(), + ); expect(resultList, expected); } diff --git a/test/shape_handler/hittest_test/translucent.dart b/test/shape_handler/hittest_test/translucent.dart index c9016e8..76d3567 100644 --- a/test/shape_handler/hittest_test/translucent.dart +++ b/test/shape_handler/hittest_test/translucent.dart @@ -21,22 +21,25 @@ void testTranslucent() { Map getMap(Function function) { return TouchCanvasUtil.getGestureCallbackMap( - onTapDown: (detail) { - function(detail); - }, - onTapUp: null, - onLongPressStart: null, - onLongPressEnd: null, - onLongPressMoveUpdate: null, - onForcePressStart: null, - onForcePressEnd: null, - onForcePressPeak: null, - onForcePressUpdate: null, - onPanStart: null, - onPanUpdate: null, - onPanDown: null, - onSecondaryTapDown: null, - onSecondaryTapUp: null); + onTapDown: (detail) { + function(detail); + }, + onTapUp: null, + onLongPressStart: null, + onLongPressEnd: null, + onLongPressMoveUpdate: null, + onForcePressStart: null, + onForcePressEnd: null, + onForcePressPeak: null, + onForcePressUpdate: null, + onPanStart: null, + onPanUpdate: null, + onPanDown: null, + onSecondaryTapDown: null, + onSecondaryTapUp: null, + onHover: null, + onTapCancel: null, + ); } void addAllShapes() { @@ -131,8 +134,10 @@ void testTranslucent() { addAllShapes(); void handleGesture(Offset position, List expected) { resultList = []; - shapeHandler.handleGestureEvent(Gesture( - GestureType.onTapDown, TapDownDetails(localPosition: position))); + shapeHandler.handleGestureEvent( + Gesture(GestureType.onTapDown, TapDownDetails(localPosition: position)), + PreviousTouchState(), + ); expect(resultList, expected); }