Skip to content

Commit 58247cb

Browse files
authored
fix: Recognize every input callbacks mixin when hit testing (#3994)
Follow up from #3986 (comment) `FlameGame.containsEventHandlerAt` backs `GameRenderBox.hitTestSelf`, which decides (for now) whether the game takes part in the hit test when the `GameWidget` uses a `HitTestBehavior` other than `opaque`. It enumerated some callbacks mixins and was missing five others: `LongPressCallbacks`, `TertiaryTapCallbacks`, `ScrollCallbacks`, `PointerMoveCallbacks` and `HoverCallbacks`. Before, components using any of those would silently received no events under `deferToChild` or `translucent`. Rather than extending the list and the problem, this adds two marker interfaces: - `InputCallbacks`: anchors the whole family, much like `Event` does for the event classes. - `PointerInputCallbacks implements InputCallbacks`: the positional/pointer subset, i.e. the mixins whose events carry a position and can therefore take part in hit testing. This serves this and other future purposes, and is public API for users to access and write generic code on top. I will still explore (after this) following the direction I proposed in #3982: `FlameGame` stops overriding `containsEventHandlerAt` altogether - the game reports a hit anywhere within its bounds, and per-component transparency becomes an explicit opt-in user override written against `PointerInputCallbacks` using this interface: ```dart @OverRide bool containsEventHandlerAt(Vector2 position) => componentsAtPoint(position).any((c) => c is PointerInputCallbacks); ``` Regardless, this is a great shape to have the callbacks in, so I am extracting this first.
1 parent 9081a0f commit 58247cb

15 files changed

Lines changed: 155 additions & 17 deletions

doc/flame/game_widget.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ instead.
7676
```{note}
7777
When using `deferToChild` or `translucent`, `FlameGame` determines whether a
7878
position has an interactive component by traversing the component tree via
79-
`componentsAtPoint`. Games that directly extend the low-level `Game` class
80-
report a hit on their entire surface by default; override
81-
`containsEventHandlerAt` to customize this.
79+
`componentsAtPoint`, and treating any component that implements
80+
`PointerInputCallbacks` as interactive. Games that directly extend the
81+
low-level `Game` class report a hit on their entire surface by default;
82+
override `containsEventHandlerAt` to customize this.
8283
```

packages/flame/lib/events.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
export 'src/events/callbacks/double_tap_callbacks.dart' show DoubleTapCallbacks;
22
export 'src/events/callbacks/drag_callbacks.dart' show DragCallbacks;
33
export 'src/events/callbacks/hover_callbacks.dart' show HoverCallbacks;
4+
export 'src/events/callbacks/input_callbacks.dart' show InputCallbacks;
45
export 'src/events/callbacks/long_press_callbacks.dart' show LongPressCallbacks;
6+
export 'src/events/callbacks/pointer_input_callbacks.dart'
7+
show PointerInputCallbacks;
58
export 'src/events/callbacks/pointer_move_callbacks.dart'
69
show PointerMoveCallbacks;
710
export 'src/events/callbacks/scale_callbacks.dart' show ScaleCallbacks;

packages/flame/lib/src/events/callbacks/double_tap_callbacks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import 'package:flame/events.dart';
1212
/// far away from each other, only one callback will be fired (or none).
1313
///
1414
/// This callback uses [DoubleTapDispatcher] to route events.
15-
mixin DoubleTapCallbacks on Component {
15+
mixin DoubleTapCallbacks on Component implements PointerInputCallbacks {
1616
/// This triggers when the pointer stops contacting the device after the
1717
/// second tap.
1818
void onDoubleTapUp(DoubleTapEvent event) {}

packages/flame/lib/src/events/callbacks/drag_callbacks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import 'package:meta/meta.dart';
1212
/// This mixin is the replacement of the Draggable mixin.
1313
///
1414
/// This callback uses [MultiDragScaleDispatcher] to route events.
15-
mixin DragCallbacks on Component {
15+
mixin DragCallbacks on Component implements PointerInputCallbacks {
1616
bool _isDragged = false;
1717

1818
/// Returns true while the component is being dragged.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// Marker interface implemented by every input callbacks mixin.
2+
///
3+
/// See `PointerInputCallbacks` for the positional subset.
4+
abstract interface class InputCallbacks {}

packages/flame/lib/src/events/callbacks/long_press_callbacks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import 'package:flutter/foundation.dart';
1717
/// - [onLongPressCancel]: called if the gesture is cancelled before completion.
1818
///
1919
/// This callback uses [LongPressDispatcher] to route events.
20-
mixin LongPressCallbacks on Component {
20+
mixin LongPressCallbacks on Component implements PointerInputCallbacks {
2121
bool _isLongPressing = false;
2222

2323
/// Returns true while a long press gesture is active on this component.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import 'package:flame/src/events/callbacks/input_callbacks.dart';
2+
3+
/// Marker interface implemented by every input callbacks mixin whose events
4+
/// carry a position (taps, drags, scrolls, hover) and thus participate in
5+
/// hit-testing.
6+
///
7+
/// Non-positional input, such as keyboard, implements [InputCallbacks].
8+
abstract interface class PointerInputCallbacks implements InputCallbacks {}

packages/flame/lib/src/events/callbacks/pointer_move_callbacks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'package:meta/meta.dart';
66
/// pointer movement events.
77
///
88
/// This callback uses [PointerMoveDispatcher] to route events.
9-
mixin PointerMoveCallbacks on Component {
9+
mixin PointerMoveCallbacks on Component implements PointerInputCallbacks {
1010
void onPointerMove(PointerMoveEvent event) {}
1111

1212
void onPointerMoveStop(PointerMoveEvent event) {}

packages/flame/lib/src/events/callbacks/scale_callbacks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'package:flame/events.dart';
33
import 'package:flutter/foundation.dart';
44

55
/// Mixin for components that respond to scale (pinch/zoom/rotate) gestures.
6-
mixin ScaleCallbacks on Component {
6+
mixin ScaleCallbacks on Component implements PointerInputCallbacks {
77
bool _isScaling = false;
88

99
/// Returns true while the component is being scaled.

packages/flame/lib/src/events/callbacks/scroll_callbacks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'package:meta/meta.dart';
66
/// pointer scroll (mouse wheel) events.
77
///
88
/// This callback uses [ScrollDispatcher] to route events.
9-
mixin ScrollCallbacks on Component {
9+
mixin ScrollCallbacks on Component implements PointerInputCallbacks {
1010
void onScroll(ScrollEvent event) {}
1111

1212
@override

0 commit comments

Comments
 (0)