Skip to content

Commit

Permalink
feat: Add basic infrastructure for a Context API
Browse files Browse the repository at this point in the history
  • Loading branch information
luanpotter committed Dec 15, 2024
1 parent ce7822a commit a6631d3
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 1 deletion.
33 changes: 32 additions & 1 deletion packages/flame/lib/src/components/core/component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:collection/collection.dart';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/src/cache/value_cache.dart';
import 'package:flame/src/components/core/component_context.dart';
import 'package:flame/src/components/core/component_tree_root.dart';
import 'package:flame/src/effects/provider_interfaces.dart';
import 'package:flutter/painting.dart';
Expand Down Expand Up @@ -534,13 +535,31 @@ class Component {
void render(Canvas canvas) {}

void renderTree(Canvas canvas) {
final context = createContext();
if (context != null) {
_contexts.add(context);
}

render(canvas);
_children?.forEach((c) => c.renderTree(canvas));
_children?.forEach((c) {
final hasContext = _contexts.isNotEmpty;
if (hasContext) {
c._contexts.addAll(_contexts);
}
c.renderTree(canvas);
if (hasContext) {
c._contexts.removeRange(_contexts.length, c._contexts.length);
}
});

// Any debug rendering should be rendered on top of everything
if (debugMode) {
renderDebugMode(canvas);
}

if (context != null) {
_contexts.removeLast();
}
}

//#endregion
Expand Down Expand Up @@ -1007,6 +1026,18 @@ class Component {

//#endregion

//#region Context

final QueueList<ComponentContext> _contexts = QueueList();

ComponentContext? createContext() => null;

T? findContext<T extends ComponentContext>() {
return _contexts.whereType<T>().lastOrNull;
}

//#endregion

//#region Debugging assistance

/// Returns whether this [Component] is in debug mode or not.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
abstract class ComponentContext {}
112 changes: 112 additions & 0 deletions packages/flame/test/components/component_context_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import 'dart:ui';

import 'package:canvas_test/canvas_test.dart';
import 'package:flame/components.dart';
import 'package:flame/src/components/core/component_context.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('Component Context', () {
testWithFlameGame('simple parent and child', (game) async {
final child = _ChildReadsContext();
final parent = _ParentWithContext(
startingValue: 42,
children: [
child,
],
);
game.add(parent);

await game.ready();
game.render(MockCanvas());

expect(child.myContext, 42);
});

testWithFlameGame('complex parent and child', (game) async {
final child1 = _ChildReadsContext();
final child2 = _ChildReadsContext();
final child3 = _ChildReadsContext();

final parent = Component(
children: [
child1,
_ParentWithContext(
startingValue: 1,
children: [
child2,
_ParentWithContext(
startingValue: 2,
children: [
child3,
],
),
],
),
],
);
game.add(parent);

await game.ready();
game.render(MockCanvas());

expect(child1.myContext, null);
expect(child2.myContext, 1);
expect(child3.myContext, 2);
});

testWithFlameGame('mutating context', (game) async {
final child = _ChildReadsContext();

final parent = _ParentWithContext(
startingValue: 10,
children: [
child,
],
);
game.add(parent);

await game.ready();
final canvas = MockCanvas();

game.render(canvas);
expect(child.myContext, 10);

parent._myContext.value = 20;
game.render(canvas);
expect(child.myContext, 20);
});
});
}

class _IntContext extends ComponentContext {
int value;

_IntContext(this.value);
}

class _ParentWithContext extends Component {
final int startingValue;
late final _IntContext _myContext = _IntContext(startingValue);

_ParentWithContext({
required this.startingValue,
super.children,
});

@override
_IntContext? createContext() => _myContext;
}

class _ChildReadsContext extends Component {
int? myContext;

@override
void render(Canvas canvas) {
final context = findContext<_IntContext>();
myContext = context?.value;

super.render(canvas);
}
}

0 comments on commit a6631d3

Please sign in to comment.