update ThermionListenerWidget to use newer InputHandler interface

This commit is contained in:
Nick Fisher
2025-05-09 11:24:28 +08:00
parent ebab1f528d
commit 602e220024

View File

@@ -3,10 +3,10 @@ import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart' hide KeyEvent;
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart' hide KeyEvent;
import 'package:thermion_dart/thermion_dart.dart' as t;
import 'package:thermion_flutter/src/widgets/src/pixel_ratio_aware.dart'; import 'package:thermion_flutter/src/widgets/src/pixel_ratio_aware.dart';
import 'package:vector_math/vector_math_64.dart';
extension OffsetExtension on Offset { extension OffsetExtension on Offset {
Vector2 toVector2() { Vector2 toVector2() {
@@ -15,17 +15,18 @@ extension OffsetExtension on Offset {
} }
/// ///
/// Captures swipe/pointer events and forwards to the provided [InputHandler]. /// Forwards cross-platform touch/mouse events to an
/// [InputHandler].
/// ///
class ThermionListenerWidget extends StatefulWidget { class ThermionListenerWidget extends StatefulWidget {
///
/// The content to display below the gesture detector/listener widget. /// The content to display below the gesture detector/listener widget.
/// This will usually be a ThermionWidget (so you can navigate by directly interacting with the viewport), but this is not necessary. /// This will usually be a ThermionWidget (so you can navigate by directly
/// It is equally possible to render the viewport/gesture controls elsewhere in the widget hierarchy. The only requirement is that they share the same [FilamentViewer]. /// interacting with the viewport), but this is not necessary. It is equally
/// /// possible to render the viewport/gesture controls elsewhere in the widget
/// hierarchy.
final Widget? child; final Widget? child;
/// /// A focus node for input events.
/// ///
/// ///
final FocusNode? focusNode; final FocusNode? focusNode;
@@ -35,6 +36,9 @@ class ThermionListenerWidget extends StatefulWidget {
/// ///
final InputHandler inputHandler; final InputHandler inputHandler;
///
///
///
const ThermionListenerWidget({ const ThermionListenerWidget({
Key? key, Key? key,
required this.inputHandler, required this.inputHandler,
@@ -71,9 +75,9 @@ class _ThermionListenerWidgetState extends State<ThermionListenerWidget> {
} }
if (event is KeyDownEvent || event is KeyRepeatEvent) { if (event is KeyDownEvent || event is KeyRepeatEvent) {
widget.inputHandler.keyDown(key!); widget.inputHandler.handle(t.KeyEvent(KeyEventType.down, key));
} else if (event is KeyUpEvent) { } else if (event is KeyUpEvent) {
widget.inputHandler.keyUp(key!); widget.inputHandler.handle(t.KeyEvent(KeyEventType.up, key));
return true; return true;
} }
return false; return false;
@@ -85,38 +89,64 @@ class _ThermionListenerWidgetState extends State<ThermionListenerWidget> {
HardwareKeyboard.instance.removeHandler(_handleKeyEvent); HardwareKeyboard.instance.removeHandler(_handleKeyEvent);
} }
t.MouseButton? _mouseButtonFromEvent(PointerEvent event) {
t.MouseButton? button;
if (event.buttons & kMiddleMouseButton != 0) {
button = MouseButton.middle;
} else if (event.buttons & kPrimaryMouseButton != 0) {
button = MouseButton.left;
} else if (event.buttons & kSecondaryMouseButton != 0) {
button = MouseButton.right;
}
return button;
}
Widget _desktop(double pixelRatio) { Widget _desktop(double pixelRatio) {
return Focus( return Focus(
focusNode: widget.focusNode, focusNode: widget.focusNode,
child: Listener( child: Listener(
onPointerHover: (event) { onPointerHover: (event) {
widget.inputHandler.onPointerHover( widget.inputHandler.handle(MouseEvent(
MouseEventType.hover,
_mouseButtonFromEvent(event),
event.localPosition.toVector2() * pixelRatio, event.localPosition.toVector2() * pixelRatio,
event.delta.toVector2() * pixelRatio); event.delta.toVector2() * pixelRatio));
}, },
onPointerSignal: (PointerSignalEvent pointerSignal) { onPointerSignal: (PointerSignalEvent pointerSignal) {
if (pointerSignal is PointerScrollEvent) { if (pointerSignal is PointerScrollEvent) {
widget.inputHandler.onPointerScroll( widget.inputHandler.handle(ScrollEvent(
pointerSignal.localPosition.toVector2() * pixelRatio, localPosition:
pointerSignal.scrollDelta.dy * pixelRatio); pointerSignal.localPosition.toVector2() * pixelRatio,
delta: pointerSignal.scrollDelta.dy * pixelRatio));
} }
}, },
onPointerPanZoomStart: (pzs) { onPointerPanZoomStart: (pzs) {
throw Exception("TODO - is this a pinch zoom on laptop trackpad?"); throw Exception("TODO - is this a pinch zoom on laptop trackpad?");
}, },
onPointerDown: (d) { onPointerDown: (event) {
widget.focusNode?.requestFocus(); widget.focusNode?.requestFocus();
widget.inputHandler.onPointerDown(
d.localPosition.toVector2() * pixelRatio, widget.inputHandler.handle(MouseEvent(
d.buttons & kMiddleMouseButton != 0); MouseEventType.buttonDown,
_mouseButtonFromEvent(event),
event.localPosition.toVector2() * pixelRatio,
event.delta.toVector2() * pixelRatio));
},
onPointerMove: (PointerMoveEvent event) {
widget.inputHandler.handle(MouseEvent(
MouseEventType.move,
_mouseButtonFromEvent(event),
event.localPosition.toVector2() * pixelRatio,
event.delta.toVector2() * pixelRatio));
},
onPointerUp: (event) {
widget.inputHandler.handle(MouseEvent(
MouseEventType.buttonUp,
_mouseButtonFromEvent(event),
event.localPosition.toVector2() * pixelRatio,
event.delta.toVector2() * pixelRatio));
}, },
onPointerMove: (PointerMoveEvent d) => widget.inputHandler
.onPointerMove(
d.localPosition.toVector2() * pixelRatio,
d.delta.toVector2() * pixelRatio,
d.buttons & kMiddleMouseButton != 0),
onPointerUp: (d) => widget.inputHandler
.onPointerUp(d.buttons & kMiddleMouseButton != 0),
child: widget.child, child: widget.child,
)); ));
} }
@@ -131,16 +161,8 @@ class _ThermionListenerWidgetState extends State<ThermionListenerWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PixelRatioAware(builder: (ctx, pixelRatio) { return PixelRatioAware(builder: (ctx, pixelRatio) {
return FutureBuilder( return SizedBox.expand(
initialData: 1.0, child: isDesktop ? _desktop(pixelRatio) : _mobile(pixelRatio));
future: widget.inputHandler.initialized,
builder: (_, initialized) {
if (initialized.data != true) {
return Container();
}
return SizedBox.expand(
child: isDesktop ? _desktop(pixelRatio) : _mobile(pixelRatio));
});
}); });
} }
} }
@@ -162,43 +184,52 @@ class _MobileListenerWidget extends StatefulWidget {
} }
class _MobileListenerWidgetState extends State<_MobileListenerWidget> { class _MobileListenerWidgetState extends State<_MobileListenerWidget> {
bool isPan = true;
@override
void initState() {
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTapDown: (details) => widget.inputHandler.onPointerDown( // onPanDown: (event) {
details.localPosition.toVector2() * widget.pixelRatio, false), // print("PAN DOWN");
// },
// onTapMove: (event) {
// print("TAP MOVE");
// },
onTapDown: (details) {
widget.inputHandler.handle(TouchEvent(TouchEventType.tap,
details.localPosition.toVector2() * widget.pixelRatio, null));
},
onDoubleTap: () { onDoubleTap: () {
widget.inputHandler.setActionForType(InputType.SCALE1, widget.inputHandler
isPan ? InputAction.TRANSLATE : InputAction.ROTATE); .handle(TouchEvent(TouchEventType.doubleTap, null, null));
}, },
onScaleStart: (details) async { onScaleStart: (event) async {
await widget.inputHandler.onScaleStart( widget.inputHandler.handle(ScaleStartEvent(
details.localFocalPoint.toVector2() * widget.pixelRatio, numPointers: event.pointerCount,
details.pointerCount, localFocalPoint: (
details.sourceTimeStamp); event.focalPoint.dx * widget.pixelRatio,
event.focalPoint.dy * widget.pixelRatio
)));
}, },
onScaleUpdate: (ScaleUpdateDetails details) async { onScaleUpdate: (ScaleUpdateDetails event) async {
await widget.inputHandler.onScaleUpdate( widget.inputHandler.handle(ScaleUpdateEvent(
details.localFocalPoint.toVector2() * widget.pixelRatio, numPointers: event.pointerCount,
details.focalPointDelta.toVector2() * widget.pixelRatio, localFocalPoint: (
details.horizontalScale, event.focalPoint.dx * widget.pixelRatio,
details.verticalScale, event.focalPoint.dy * widget.pixelRatio
details.scale, ),
details.pointerCount, localFocalPointDelta: (
details.rotation, event.focalPointDelta.dx * widget.pixelRatio,
details.sourceTimeStamp); event.focalPointDelta.dy * widget.pixelRatio
),
rotation: event.rotation,
horizontalScale: event.horizontalScale,
verticalScale: event.verticalScale,
scale: event.scale,
));
}, },
onScaleEnd: (details) async { onScaleEnd: (details) async {
await widget.inputHandler widget.inputHandler
.onScaleEnd(details.pointerCount, details.scaleVelocity); .handle(ScaleEndEvent(numPointers: details.pointerCount));
}, },
child: widget.child); child: widget.child);
} }