-
-
Notifications
You must be signed in to change notification settings - Fork 1k
refactor!: Migrate Force Press events to new API #3986
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
luanpotter
wants to merge
1
commit into
main
Choose a base branch
from
luan.force-press
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
packages/flame/lib/src/events/callbacks/force_press_callbacks.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import 'package:flame/components.dart'; | ||
| import 'package:flame/events.dart'; | ||
| import 'package:flutter/foundation.dart'; | ||
|
|
||
| /// This mixin can be added to a [Component] allowing it to receive force press | ||
| /// events, i.e. touches that report how hard the user is pressing. | ||
| /// | ||
| /// In addition to adding this mixin, the component must also implement the | ||
| /// [containsLocalPoint] method -- only a gesture that starts on top of the | ||
| /// component will be delivered to it. | ||
| /// | ||
| /// The following callbacks are available: | ||
| /// - [onForcePressStart]: the press crossed the pressure threshold at which | ||
| /// the gesture is recognized. | ||
| /// - [onForcePressPeak]: the press crossed the "peak" pressure threshold. | ||
| /// - [onForcePressUpdate]: the pressure changed during an active force press. | ||
| /// - [onForcePressEnd]: the pointer was lifted. | ||
| /// | ||
| /// Note that force press requires a pressure-sensitive screen; see | ||
| /// [ForcePressEvent] for the details of which devices support it. | ||
| /// | ||
| /// This callback uses [ForcePressDispatcher] to route events. | ||
| mixin ForcePressCallbacks on Component { | ||
| bool _isForcePressed = false; | ||
|
|
||
| /// Returns true while a force press gesture is active on this component. | ||
| bool get isForcePressed => _isForcePressed; | ||
|
|
||
| @mustCallSuper | ||
| void onForcePressStart(ForcePressEvent event) { | ||
| _isForcePressed = true; | ||
| } | ||
|
|
||
| void onForcePressPeak(ForcePressEvent event) {} | ||
|
|
||
| void onForcePressUpdate(ForcePressEvent event) {} | ||
|
|
||
| @mustCallSuper | ||
| void onForcePressEnd(ForcePressEvent event) { | ||
| _isForcePressed = false; | ||
| } | ||
|
|
||
| @override | ||
| @mustCallSuper | ||
| void onMount() { | ||
| super.onMount(); | ||
| ForcePressDispatcher.addDispatcher(this); | ||
| } | ||
| } |
119 changes: 119 additions & 0 deletions
119
packages/flame/lib/src/events/dispatchers/force_press_dispatcher.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import 'package:flame/components.dart'; | ||
| import 'package:flame/events.dart'; | ||
| import 'package:flame/game.dart'; | ||
| import 'package:flutter/gestures.dart'; | ||
| import 'package:meta/meta.dart'; | ||
|
|
||
| /// **ForcePressDispatcher** facilitates dispatching of force press events to | ||
| /// the [ForcePressCallbacks] components in the component tree. It will be | ||
| /// attached to the [FlameGame] instance automatically whenever any | ||
| /// [ForcePressCallbacks] components are mounted into the component tree. | ||
| /// | ||
| /// Flutter's [ForcePressGestureRecognizer] tracks a single pointer at a time, | ||
| /// so this dispatcher only needs to remember the set of components that | ||
| /// accepted the current gesture, rather than keying them by pointer id. | ||
| class ForcePressDispatcher extends Dispatcher<FlameGame> { | ||
| /// The components that received the current gesture's start event, and which | ||
| /// will therefore receive its peak, update and end events. | ||
| final Set<ForcePressCallbacks> _components = {}; | ||
|
|
||
| @mustCallSuper | ||
| void onForcePressStart(ForcePressEvent event) { | ||
| event.deliverAtPoint( | ||
| rootComponent: game, | ||
| eventHandler: (ForcePressCallbacks component) { | ||
| _components.add(component..onForcePressStart(event)); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| @mustCallSuper | ||
| void onForcePressPeak(ForcePressEvent event) { | ||
| _forEachActiveComponent((component) => component.onForcePressPeak(event)); | ||
| } | ||
|
|
||
| @mustCallSuper | ||
| void onForcePressUpdate(ForcePressEvent event) { | ||
| _forEachActiveComponent((component) => component.onForcePressUpdate(event)); | ||
| } | ||
|
|
||
| @mustCallSuper | ||
| void onForcePressEnd(ForcePressEvent event) { | ||
| _forEachActiveComponent((component) => component.onForcePressEnd(event)); | ||
| _components.clear(); | ||
| } | ||
|
|
||
| /// Delivers to every component that accepted the gesture and is still | ||
| /// mounted, dropping the ones that were removed mid-gesture. | ||
| void _forEachActiveComponent(void Function(ForcePressCallbacks) handler) { | ||
| _components.removeWhere((component) => !component.isMounted); | ||
| for (final component in _components) { | ||
| handler(component); | ||
| } | ||
| } | ||
|
|
||
| //#region Gesture recognizer handlers | ||
|
|
||
| @internal | ||
| void handleForcePressStart(ForcePressDetails details) { | ||
| onForcePressStart(ForcePressEvent(game, details)); | ||
| } | ||
|
|
||
| @internal | ||
| void handleForcePressPeak(ForcePressDetails details) { | ||
| onForcePressPeak(ForcePressEvent(game, details)); | ||
| } | ||
|
|
||
| @internal | ||
| void handleForcePressUpdate(ForcePressDetails details) { | ||
| onForcePressUpdate(ForcePressEvent(game, details)); | ||
| } | ||
|
|
||
| @internal | ||
| void handleForcePressEnd(ForcePressDetails details) { | ||
| onForcePressEnd(ForcePressEvent(game, details)); | ||
| } | ||
|
|
||
| //#endregion | ||
|
|
||
| static void addDispatcher(Component component) { | ||
| Dispatcher.addDispatcher( | ||
| component, | ||
| const ForcePressDispatcherKey(), | ||
| ForcePressDispatcher.new, | ||
| ); | ||
| } | ||
|
|
||
| @override | ||
| void onMount() { | ||
| game.gestureDetectors.register<ForcePressGestureRecognizer>( | ||
| ForcePressGestureRecognizer.new, | ||
| (ForcePressGestureRecognizer instance) { | ||
| instance | ||
| ..onStart = handleForcePressStart | ||
| ..onPeak = handleForcePressPeak | ||
| ..onUpdate = handleForcePressUpdate | ||
| ..onEnd = handleForcePressEnd; | ||
| }, | ||
| ); | ||
| super.onMount(); | ||
| } | ||
|
|
||
| @override | ||
| void onRemove() { | ||
| game.gestureDetectors.unregister<ForcePressGestureRecognizer>(); | ||
| Dispatcher.removeDispatcher(game, const ForcePressDispatcherKey()); | ||
| } | ||
| } | ||
|
|
||
| /// Unique key for the [ForcePressDispatcher] so the game can identify it. | ||
| class ForcePressDispatcherKey implements ComponentKey { | ||
| const ForcePressDispatcherKey(); | ||
|
|
||
| @override | ||
| int get hashCode => 'ForcePressDispatcherKey'.hashCode; | ||
|
|
||
| @override | ||
| bool operator ==(Object other) => | ||
| other is ForcePressDispatcherKey && other.hashCode == hashCode; | ||
| } |
39 changes: 39 additions & 0 deletions
39
packages/flame/lib/src/events/messages/force_press_event.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import 'package:flame/events.dart'; | ||
| import 'package:flame/extensions.dart'; | ||
| import 'package:flutter/gestures.dart'; | ||
|
|
||
| /// The event propagated through the Flame engine during a force press gesture, | ||
| /// i.e. a touch that also reports how hard the user is pressing. | ||
| /// | ||
| /// The same event class is used for all four phases of the gesture (start, | ||
| /// peak, update and end) because Flutter describes every one of them with a | ||
| /// single [ForcePressDetails] object. | ||
| /// | ||
| /// This is a [PositionEvent], where the position is the point of contact. | ||
| /// | ||
| /// Note that force press requires a pressure-sensitive screen: Apple's 3D | ||
| /// Touch, which shipped on the iPhone 6s through the iPhone XS, or a small | ||
| /// number of Android devices. On any other device the gesture is never | ||
| /// recognized and these events are never delivered. | ||
| class ForcePressEvent extends PositionEvent<ForcePressDetails> { | ||
| ForcePressEvent(super.game, ForcePressDetails details) | ||
| : pressure = details.pressure, | ||
| super( | ||
| raw: details, | ||
| devicePosition: details.globalPosition.toVector2(), | ||
| ); | ||
|
|
||
| /// How hard the user is pressing, normalized to the `[0, 1]` range across | ||
| /// the pressure range that the device reports. | ||
| /// | ||
| /// The gesture is only recognized once this value crosses the recognizer's | ||
| /// `startPressure` (`0.4` by default), and `onForcePressPeak` fires when it | ||
| /// crosses `peakPressure` (`0.85` by default). | ||
| final double pressure; | ||
|
|
||
| @override | ||
| String toString() => | ||
| 'ForcePressEvent(canvasPosition: $canvasPosition, ' | ||
| 'devicePosition: $devicePosition, ' | ||
| 'pressure: $pressure)'; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is wrong, it does work at game level, because componentsAtPoint includes the game itself
that being said there are other new detectors missing here, which is indeed a bug, and this is not tested, but it is not tested for other detectors either
so I will followup with a broad approach here, but not part of this PR