Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 42 additions & 25 deletions packages/flame/lib/src/components/core/component_tree_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,30 @@ class ComponentTreeRoot extends Component {
..parent = parent;
}

/// Cancels the pending ADD event for [child] into [parent].
///
/// Scans the queue without using its iterator, so this is safe to call while
/// [processLifecycleEvents] is iterating over the queue (for example from a
/// component's [Component.onMount]).
@internal
void dequeueAdd(Component child, Component parent) {
for (final event in queue) {
if (event.kind == LifecycleEventKind.add &&
var found = false;
queue.forEachWhere(
(event) =>
!found &&
event.kind == LifecycleEventKind.add &&
event.child == child &&
event.parent == parent) {
event.parent == parent,
(event) {
event.kind = LifecycleEventKind.unknown;
return;
}
}
throw AssertionError(
'Cannot find a lifecycle event Add(child=$child, parent=$parent)',
found = true;
},
);
if (!found) {
throw AssertionError(
'Cannot find a lifecycle event Add(child=$child, parent=$parent)',
);
}
}

@internal
Expand All @@ -58,13 +69,18 @@ class ComponentTreeRoot extends Component {
..parent = parent;
}

/// Cancels all pending REMOVE events for [child].
///
/// Scans the queue without using its iterator, so this is safe to call while
/// [processLifecycleEvents] is iterating over the queue (for example from a
/// component's [Component.onMount]).
@internal
void dequeueRemove(Component child) {
for (final event in queue) {
if (event.kind == LifecycleEventKind.remove && event.child == child) {
event.kind = LifecycleEventKind.unknown;
}
}
queue.forEachWhere(
(event) =>
event.kind == LifecycleEventKind.remove && event.child == child,
(event) => event.kind = LifecycleEventKind.unknown,
);
}

/// Finds all children in [candidates] that have a pending REMOVE event,
Expand Down Expand Up @@ -205,25 +221,26 @@ class ComponentTreeRoot extends Component {
@internal
void handleResize(Vector2 size) {
super.handleResize(size);
for (final event in queue) {
if ((event.kind == LifecycleEventKind.add) &&
(event.child!.isLoading || event.child!.isLoaded)) {
event.child!.onGameResize(size);
}
}
queue.forEachWhere(
_isPendingAddOfLoadingOrLoadedChild,
(event) => event.child!.onGameResize(size),
);
}

@mustCallSuper
@override
@internal
void handleHotReload() {
super.handleHotReload();
for (final event in queue) {
if ((event.kind == LifecycleEventKind.add) &&
(event.child!.isLoading || event.child!.isLoaded)) {
event.child!.onHotReload();
}
}
queue.forEachWhere(
_isPendingAddOfLoadingOrLoadedChild,
(event) => event.child!.onHotReload(),
);
}

static bool _isPendingAddOfLoadingOrLoadedChild(LifecycleEvent event) {
return event.kind == LifecycleEventKind.add &&
(event.child!.isLoading || event.child!.isLoaded);
}

@mustCallSuper
Expand Down
58 changes: 58 additions & 0 deletions packages/flame/test/components/component_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,41 @@ void main() {
expect(parent.parent, isNull);
},
);

testWithFlameGame(
'removing a queued sibling from onMount does not mount twice',
(game) async {
final sibling = _LifecycleComponent('sibling');
final component = _SiblingRemovingOnMountComponent(sibling);
game.world.add(component);
game.world.add(sibling);
await game.ready();

expect(component.isMounted, true);
expect(component.countEvents('onMount'), 1);
expect(sibling.isMounted, false);
expect(sibling.parent, isNull);
expect(game.world.children, [component]);
},
);

testWithFlameGame(
're-adding a removing component from onMount keeps it in the tree',
(game) async {
final existing = _LifecycleComponent('existing');
await game.world.ensureAdd(existing);
final component = _ReAddingOnMountComponent(existing);
game.world.add(component);
game.world.remove(existing);
await game.ready();

expect(component.isMounted, true);
expect(existing.isMounted, true);
expect(existing.isRemoving, false);
expect(existing.parent, game.world);
expect(game.world.children, containsAll([component, existing]));
},
);
});

group('Moving components', () {
Expand Down Expand Up @@ -2174,6 +2209,29 @@ class _SelfRemovingOnMountComponent extends Component {
}
}

class _SiblingRemovingOnMountComponent extends _LifecycleComponent {
_SiblingRemovingOnMountComponent(this.sibling) : super('remover');

final Component sibling;

@override
void onMount() {
super.onMount();
parent!.remove(sibling);
}
}

class _ReAddingOnMountComponent extends Component {
_ReAddingOnMountComponent(this.component);

final Component component;

@override
void onMount() {
parent!.add(component);
}
}

class _Pair {
_Pair(this.component, this.points);
final Component component;
Expand Down
Loading