Skip to content

Commit

Permalink
Add support for "Open DevTools in Browser" in Sidebar menu
Browse files Browse the repository at this point in the history
See #6563
  • Loading branch information
DanTup committed Nov 15, 2023
1 parent 420efcb commit df13c8d
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ enum VsCodeFlutterSidebar {

/// Analytics event that is sent when a device selection occurs from the list
/// of available devices in the sidebar.
changeSelectedDevice;
changeSelectedDevice,

/// Analytics event that is sent when DevTools is opened in the browser
/// without a specific page.
openDevToolsExternally;

static String get id => VsCodeFlutterSidebar.vsCodeFlutterSidebar.name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,17 @@ final class VsCodeApiImpl extends ToolApiImpl implements VsCodeApi {
}

@override
Future<void> openDevToolsPage(String debugSessionId, String page) {
Future<void> openDevToolsPage(
String debugSessionId, {
String? page,
bool? forceExternal,
}) {
return sendRequest(
VsCodeApi.jsonOpenDevToolsPageMethod,
{
VsCodeApi.jsonDebugSessionIdParameter: debugSessionId,
VsCodeApi.jsonOpenPageParameter: page,
VsCodeApi.jsonPageParameter: page,
VsCodeApi.jsonForceExternalParameter: forceExternal,
},
);
}
Expand Down Expand Up @@ -293,6 +298,10 @@ class VsCodeCapabilitiesImpl implements VsCodeCapabilities {
bool get openDevToolsPage =>
_raw?[VsCodeCapabilities.openDevToolsPageField] == true;

@override
bool get openDevToolsExternally =>
_raw?[VsCodeCapabilities.openDevToolsExternallyField] == true;

@override
bool get hotReload => _raw?[VsCodeCapabilities.hotReloadField] == true;

Expand Down
15 changes: 13 additions & 2 deletions packages/devtools_app/lib/src/standalone_ui/api/vs_code_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ abstract interface class VsCodeApi {
///
/// Depending on user settings, this may open embedded (the default) or in an
/// external browser window.
Future<void> openDevToolsPage(String debugSessionId, String page);
Future<void> openDevToolsPage(
String debugSessionId, {
String? page,
bool? forceExternal,
});

/// Sends a Hot Reload request to the debug session with ID [debugSessionId].
Future<void> hotReload(String debugSessionId);
Expand All @@ -87,7 +91,8 @@ abstract interface class VsCodeApi {
static const jsonCommandParameter = 'command';
static const jsonArgumentsParameter = 'arguments';
static const jsonIdParameter = 'id';
static const jsonOpenPageParameter = 'page';
static const jsonPageParameter = 'page';
static const jsonForceExternalParameter = 'forceExternal';
static const jsonDebugSessionIdParameter = 'debugSessionId';
static const jsonPlatformTypeParameter = 'platformType';
}
Expand Down Expand Up @@ -233,6 +238,11 @@ abstract interface class VsCodeCapabilities {
/// DevTools page.
bool get openDevToolsPage;

/// Whether the `openDevToolsPage` method can be called without a `page`
/// argument and with a 'forceExternal` flag to open DevTools in a browser
/// regardless of user settings.
bool get openDevToolsExternally;

/// Whether the `hotReload` method is available call to hot reload a specific
/// debug session.
bool get hotReload;
Expand All @@ -244,6 +254,7 @@ abstract interface class VsCodeCapabilities {
static const jsonExecuteCommandField = 'executeCommand';
static const jsonSelectDeviceField = 'selectDevice';
static const openDevToolsPageField = 'openDevToolsPage';
static const openDevToolsExternallyField = 'openDevToolsExternally';
static const hotReloadField = 'hotReload';
static const hotRestartField = 'hotRestart';
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class DebugSessions extends StatelessWidget {
isProfile: isProfile,
isRelease: isRelease,
isWeb: isWeb,
supportsOpenExternal: api.capabilities.openDevToolsExternally,
),
],
);
Expand All @@ -122,6 +123,7 @@ class _DevToolsMenu extends StatelessWidget {
required this.isProfile,
required this.isRelease,
required this.isWeb,
required this.supportsOpenExternal,
});

final VsCodeApi api;
Expand All @@ -131,6 +133,7 @@ class _DevToolsMenu extends StatelessWidget {
final bool isProfile;
final bool isRelease;
final bool isWeb;
final bool supportsOpenExternal;

@override
Widget build(BuildContext context) {
Expand All @@ -139,19 +142,13 @@ class _DevToolsMenu extends StatelessWidget {
? TextDirection.rtl
: TextDirection.ltr;

Widget devToolsButton(ScreenMetaData screen) {
final title = screen.title ?? screen.id;
String? disabledReason;
if (isRelease) {
disabledReason = 'Not available in release mode';
} else if (screen.requiresFlutter && !isFlutter) {
disabledReason = 'Only available for Flutter applications';
} else if (screen.requiresDebugBuild && !isDebug) {
disabledReason = 'Only available in debug mode';
} else if (screen.requiresDartVm && isWeb) {
disabledReason = 'Not available when running on the web';
}

Widget devToolsButton(
String title, {
IconData? icon,
String? screenId,
String? disabledReason,
bool? forceExternal,
}) {
// Because we flipped the direction so the menu is aligned to the end, we
// should revert the text direction back to normal for the label.
Widget text = Directionality(
Expand All @@ -168,20 +165,49 @@ class _DevToolsMenu extends StatelessWidget {
}

return MenuItemButton(
leadingIcon: Icon(screen.icon, size: actionsIconSize),
leadingIcon: Icon(icon, size: actionsIconSize),
onPressed: disabledReason != null
? null
: () {
ga.select(
gac.VsCodeFlutterSidebar.id,
gac.VsCodeFlutterSidebar.openDevToolsScreen(screen.id),
screenId != null && forceExternal != true
? gac.VsCodeFlutterSidebar.openDevToolsScreen(screenId)
: gac.VsCodeFlutterSidebar.openDevToolsExternally.name,
);
unawaited(
api.openDevToolsPage(
session.id,
page: screenId,
forceExternal: forceExternal,
),
);
unawaited(api.openDevToolsPage(session.id, screen.id));
},
child: text,
);
}

Widget devToolsScreenButton(ScreenMetaData screen) {
final title = screen.title ?? screen.id;
String? disabledReason;
if (isRelease) {
disabledReason = 'Not available in release mode';
} else if (screen.requiresFlutter && !isFlutter) {
disabledReason = 'Only available for Flutter applications';
} else if (screen.requiresDebugBuild && !isDebug) {
disabledReason = 'Only available in debug mode';
} else if (screen.requiresDartVm && isWeb) {
disabledReason = 'Not available when running on the web';
}

return devToolsButton(
title,
icon: screen.icon,
screenId: screen.id,
disabledReason: disabledReason,
);
}

return Directionality(
// Reverse the direction so the menu is anchored on the far side and
// expands in the opposite direction with the icons on the right.
Expand All @@ -190,10 +216,17 @@ class _DevToolsMenu extends StatelessWidget {
style: const MenuStyle(
alignment: AlignmentDirectional.bottomStart,
),
menuChildren: ScreenMetaData.values
.where(_shouldIncludeScreen)
.map(devToolsButton)
.toList(),
menuChildren: [
...ScreenMetaData.values
.where(_shouldIncludeScreen)
.map(devToolsScreenButton),
if (supportsOpenExternal)
devToolsButton(
'Open in Browser',
icon: Icons.open_in_browser,
forceExternal: true,
),
],
builder: (context, controller, child) => IconButton(
onPressed: () {
if (controller.isOpen) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class MockDartToolingApi extends DartToolingApiImpl {
'executeCommand': true,
'selectDevice': true,
'openDevToolsPage': true,
'openDevToolsExternally': true,
'hotReload': true,
'hotRestart': true,
};
Expand Down

0 comments on commit df13c8d

Please sign in to comment.