-
-
Notifications
You must be signed in to change notification settings - Fork 891
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add
IgnoreEvents
mixin to ignore events for the whole subtree (…
…#2811) Currently the event system is quite inefficient when you have a lot of components since it goes through the whole component tree, with this mixin you can avoid traversing down sub trees that you know don't handle any events. I'll add docs as soon as #2809 is merged to avoid merge conflicts.
- Loading branch information
Showing
6 changed files
with
180 additions
and
4 deletions.
There are no files selected for viewing
This file contains 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 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 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 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
15 changes: 15 additions & 0 deletions
15
packages/flame/lib/src/components/mixins/ignore_events.dart
This file contains 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,15 @@ | ||
import 'package:flame/src/components/core/component.dart'; | ||
|
||
/// This mixin allows a component and all it's descendants to ignore events. | ||
/// | ||
/// Do note that this will also ignore the component and its descendants in | ||
/// calls to [Component.componentsAtPoint]. | ||
/// | ||
/// If you want to dynamically use this mixin, you can add it and set | ||
/// [ignoreEvents] true or false at runtime. | ||
/// | ||
/// This mixin is to be used when you have a large subtree of components that | ||
/// shouldn't receive any events and you want to optimize the event handling. | ||
mixin IgnoreEvents on Component { | ||
bool ignoreEvents = true; | ||
} |
144 changes: 144 additions & 0 deletions
144
packages/flame/test/events/component_mixins/ignore_events_test.dart
This file contains 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,144 @@ | ||
import 'package:flame/components.dart'; | ||
import 'package:flame/events.dart'; | ||
import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; | ||
import 'package:flame_test/flame_test.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
void main() { | ||
group('IgnoreEvents', () { | ||
testWithFlameGame( | ||
'correctly ignores events all the way down the subtree', | ||
(game) async { | ||
final grandChild = _IgnoreTapCallbacksComponent(); | ||
final child = _IgnoreTapCallbacksComponent(children: [grandChild]); | ||
final component = _IgnoreTapCallbacksComponent( | ||
position: Vector2.all(10), | ||
children: [child], | ||
); | ||
|
||
await game.ensureAdd(component); | ||
final dispatcher = game.firstChild<MultiTapDispatcher>()!; | ||
|
||
dispatcher.onTapDown( | ||
createTapDownEvents( | ||
game: game, | ||
localPosition: const Offset(12, 12), | ||
globalPosition: const Offset(12, 12), | ||
), | ||
); | ||
expect(component.tapDownEvent, equals(0)); | ||
expect(component.tapUpEvent, equals(0)); | ||
expect(component.tapCancelEvent, equals(0)); | ||
expect(child.tapDownEvent, equals(0)); | ||
expect(child.tapUpEvent, equals(0)); | ||
expect(child.tapCancelEvent, equals(0)); | ||
expect(grandChild.tapDownEvent, equals(0)); | ||
expect(grandChild.tapUpEvent, equals(0)); | ||
expect(grandChild.tapCancelEvent, equals(0)); | ||
|
||
// [onTapUp] will call, if there was an [onTapDown] event before | ||
dispatcher.onTapUp( | ||
createTapUpEvents( | ||
game: game, | ||
localPosition: const Offset(12, 12), | ||
globalPosition: const Offset(12, 12), | ||
), | ||
); | ||
|
||
expect(component.tapDownEvent, equals(0)); | ||
expect(component.tapUpEvent, equals(0)); | ||
expect(component.tapCancelEvent, equals(0)); | ||
expect(child.tapDownEvent, equals(0)); | ||
expect(child.tapUpEvent, equals(0)); | ||
expect(child.tapCancelEvent, equals(0)); | ||
expect(grandChild.tapDownEvent, equals(0)); | ||
expect(grandChild.tapUpEvent, equals(0)); | ||
expect(grandChild.tapCancelEvent, equals(0)); | ||
}, | ||
); | ||
|
||
testWithFlameGame( | ||
'correctly accepts events all the way down the subtree when ignoreEvents ' | ||
'is false', | ||
(game) async { | ||
final grandChild = _IgnoreTapCallbacksComponent()..ignoreEvents = false; | ||
final child = _IgnoreTapCallbacksComponent(children: [grandChild]) | ||
..ignoreEvents = false; | ||
final component = _IgnoreTapCallbacksComponent( | ||
position: Vector2.all(10), | ||
children: [child], | ||
)..ignoreEvents = false; | ||
|
||
await game.ensureAdd(component); | ||
final dispatcher = game.firstChild<MultiTapDispatcher>()!; | ||
|
||
dispatcher.onTapDown( | ||
createTapDownEvents( | ||
game: game, | ||
localPosition: const Offset(12, 12), | ||
globalPosition: const Offset(12, 12), | ||
), | ||
); | ||
expect(component.tapDownEvent, equals(1)); | ||
expect(component.tapUpEvent, equals(0)); | ||
expect(component.tapCancelEvent, equals(0)); | ||
expect(child.tapDownEvent, equals(1)); | ||
expect(child.tapUpEvent, equals(0)); | ||
expect(child.tapCancelEvent, equals(0)); | ||
expect(grandChild.tapDownEvent, equals(1)); | ||
expect(grandChild.tapUpEvent, equals(0)); | ||
expect(grandChild.tapCancelEvent, equals(0)); | ||
|
||
// [onTapUp] will call, if there was an [onTapDown] event before | ||
dispatcher.onTapUp( | ||
createTapUpEvents( | ||
game: game, | ||
localPosition: const Offset(12, 12), | ||
globalPosition: const Offset(12, 12), | ||
), | ||
); | ||
|
||
expect(component.tapDownEvent, equals(1)); | ||
expect(component.tapUpEvent, equals(1)); | ||
expect(component.tapCancelEvent, equals(0)); | ||
expect(child.tapDownEvent, equals(1)); | ||
expect(child.tapUpEvent, equals(1)); | ||
expect(child.tapCancelEvent, equals(0)); | ||
expect(grandChild.tapDownEvent, equals(1)); | ||
expect(grandChild.tapUpEvent, equals(1)); | ||
expect(grandChild.tapCancelEvent, equals(0)); | ||
}, | ||
); | ||
}); | ||
} | ||
|
||
mixin _TapCounter on TapCallbacks { | ||
int tapDownEvent = 0; | ||
int tapUpEvent = 0; | ||
int longTapDownEvent = 0; | ||
int tapCancelEvent = 0; | ||
|
||
@override | ||
void onTapDown(TapDownEvent event) { | ||
event.continuePropagation = true; | ||
tapDownEvent++; | ||
} | ||
|
||
@override | ||
void onTapUp(TapUpEvent event) { | ||
event.continuePropagation = true; | ||
tapUpEvent++; | ||
} | ||
|
||
@override | ||
void onTapCancel(TapCancelEvent event) { | ||
event.continuePropagation = true; | ||
tapCancelEvent++; | ||
} | ||
} | ||
|
||
class _IgnoreTapCallbacksComponent extends PositionComponent | ||
with TapCallbacks, _TapCounter, IgnoreEvents { | ||
_IgnoreTapCallbacksComponent({super.position, super.children}) | ||
: super(size: Vector2.all(10)); | ||
} |