Skip to content
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

feat: Devtools overlay navigation #3449

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'dart:convert';
import 'dart:developer';

import 'package:flame/src/devtools/dev_tools_connector.dart';

/// The [OverlayNavConnector] is responsible of getting the names of all
/// registered overlays and navigating to the overlay with the given name.
class OverlayNavConnector extends DevToolsConnector {
@override
void init() {
// Get the names of all registered overlays
registerExtension(
'ext.flame_devtools.getOverlays',
(method, parameters) async {
return ServiceExtensionResponse.result(
json.encode({
'overlays': game.overlays.registeredOverlays,
}),
);
},
);

// Navigate to the overlay with the given name
registerExtension(
'ext.flame_devtools.navigateToOverlay',
(method, parameters) async {
final overlayName = parameters['overlay'];

if (overlayName == null) {
return ServiceExtensionResponse.error(
-32602,
'Missing overlay parameter',
);
}

if (!game.overlays.registeredOverlays.contains(overlayName)) {
return ServiceExtensionResponse.error(
-32602,
'Unknown overlay: $overlayName',
);
}

game.overlays.clear();
game.overlays.add(overlayName);
return ServiceExtensionResponse.result(json.encode({'success': true}));
},
);
}
}
2 changes: 2 additions & 0 deletions packages/flame/lib/src/devtools/dev_tools_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flame/src/devtools/connectors/component_snapshot_connector.dart'
import 'package:flame/src/devtools/connectors/component_tree_connector.dart';
import 'package:flame/src/devtools/connectors/debug_mode_connector.dart';
import 'package:flame/src/devtools/connectors/game_loop_connector.dart';
import 'package:flame/src/devtools/connectors/overlay_nav_connector.dart';
import 'package:flame/src/devtools/connectors/position_component_attributes_connector.dart';
import 'package:flame/src/devtools/dev_tools_connector.dart';

Expand Down Expand Up @@ -39,6 +40,7 @@ class DevToolsService {
GameLoopConnector(),
ComponentSnapshotConnector(),
PositionComponentAttributesConnector(),
OverlayNavConnector(),
];

/// This method is called every time a new game is set in the service and it
Expand Down
5 changes: 5 additions & 0 deletions packages/flame/lib/src/game/overlay_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class OverlayManager {
return UnmodifiableListView(_activeOverlays.map((overlay) => overlay.name));
}

/// The names of all registered overlays
UnmodifiableListView<String> get registeredOverlays {
return UnmodifiableListView(_builders.keys);
}

/// Returns if the given [overlayName] is active
bool isActive(String overlayName) =>
_activeOverlays.any((overlay) => overlay.name == overlayName);
Expand Down
2 changes: 2 additions & 0 deletions packages/flame_devtools/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:devtools_extensions/devtools_extensions.dart';
import 'package:flame_devtools/widgets/component_tree.dart';
import 'package:flame_devtools/widgets/debug_mode_button.dart';
import 'package:flame_devtools/widgets/game_loop_controls.dart';
import 'package:flame_devtools/widgets/overlay_nav.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

Expand All @@ -26,6 +27,7 @@ class FlameDevTools extends StatelessWidget {
].withSpacing(),
),
const Expanded(child: ComponentTree()),
const Flexible(child: OverlayNav()),
].withSpacing(),
),
),
Expand Down
15 changes: 15 additions & 0 deletions packages/flame_devtools/lib/repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ sealed class Repository {
);
}

static Future<List<String>> getOverlays() async {
final overlaysResponse =
await serviceManager.callServiceExtensionOnMainIsolate(
'ext.flame_devtools.getOverlays',
);
return List<String>.from(overlaysResponse.json!['overlays'] as List);
}

static Future<void> navigateToOverlay(String overlay) async {
await serviceManager.callServiceExtensionOnMainIsolate(
'ext.flame_devtools.navigateToOverlay',
args: {'overlay': overlay},
);
}

static Future<bool> swapDebugMode({int? id}) async {
final nextDebugMode = !(await getDebugMode(id: id));
await serviceManager.callServiceExtensionOnMainIsolate(
Expand Down
49 changes: 49 additions & 0 deletions packages/flame_devtools/lib/widgets/overlay_nav.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:devtools_app_shared/ui.dart' as devtools_ui;
import 'package:flame_devtools/repository.dart';
import 'package:flutter/material.dart';

class OverlayNav extends StatefulWidget {
const OverlayNav({super.key});

@override
State<OverlayNav> createState() => _DebugModeButtonState();
}

class _DebugModeButtonState extends State<OverlayNav> {
Future<List<String>>? _overlays;

@override
void initState() {
_overlays = Repository.getOverlays();
super.initState();
}

@override
Widget build(BuildContext context) {
return FutureBuilder<List<String>>(
future: _overlays,
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const SizedBox();
}

return devtools_ui.RoundedOutlinedBorder(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const devtools_ui.AreaPaneHeader(title: Text('Overlays')),
for (final overlay in snapshot.data!)
ListTile(
dense: true,
leading: const Icon(Icons.layers, size: 20),
title: Text(overlay),
onTap: () => Repository.navigateToOverlay(overlay),
),
],
),
);
},
);
}
}
Loading