Include the footer when DevTools is embedded (#4041)
diff --git a/packages/devtools_app/lib/src/app.dart b/packages/devtools_app/lib/src/app.dart index 0b495bd..f6586c8 100644 --- a/packages/devtools_app/lib/src/app.dart +++ b/packages/devtools_app/lib/src/app.dart
@@ -8,11 +8,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../devtools.dart' as devtools; import 'analytics/analytics.dart' as ga; import 'analytics/analytics_controller.dart'; import 'analytics/constants.dart' as analytics_constants; -import 'config_specific/launch_url/launch_url.dart'; import 'config_specific/server/server.dart'; import 'example/conditional_screen.dart'; import 'framework/framework_core.dart'; @@ -36,6 +34,7 @@ import 'screens/provider/provider_screen.dart'; import 'screens/vm_developer/vm_developer_tools_controller.dart'; import 'screens/vm_developer/vm_developer_tools_screen.dart'; +import 'shared/about_dialog.dart'; import 'shared/common_widgets.dart'; import 'shared/dialogs.dart'; import 'shared/globals.dart'; @@ -43,6 +42,7 @@ import 'shared/landing_screen.dart'; import 'shared/notifications.dart'; import 'shared/release_notes/release_notes.dart'; +import 'shared/report_feedback_button.dart'; import 'shared/routing.dart'; import 'shared/scaffold.dart'; import 'shared/screen.dart'; @@ -420,34 +420,6 @@ } } -class OpenAboutAction extends StatelessWidget { - @override - Widget build(BuildContext context) { - return DevToolsTooltip( - message: 'About DevTools', - child: InkWell( - onTap: () async { - unawaited( - showDialog( - context: context, - builder: (context) => DevToolsAboutDialog(), - ), - ); - }, - child: Container( - width: DevToolsScaffold.actionWidgetSize, - height: DevToolsScaffold.actionWidgetSize, - alignment: Alignment.center, - child: Icon( - Icons.help_outline, - size: actionsIconSize, - ), - ), - ), - ); - } -} - class OpenSettingsAction extends StatelessWidget { @override Widget build(BuildContext context) { @@ -476,84 +448,6 @@ } } -class ReportFeedbackButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - return DevToolsTooltip( - message: 'Report feedback', - child: InkWell( - onTap: () async { - ga.select( - analytics_constants.devToolsMain, - analytics_constants.feedbackButton, - ); - await launchUrl( - devToolsExtensionPoints.issueTrackerLink().url, - context, - ); - }, - child: Container( - width: DevToolsScaffold.actionWidgetSize, - height: DevToolsScaffold.actionWidgetSize, - alignment: Alignment.center, - child: Icon( - Icons.bug_report, - size: actionsIconSize, - ), - ), - ), - ); - } -} - -class DevToolsAboutDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - return DevToolsDialog( - title: dialogTitleText(theme, 'About DevTools'), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _aboutDevTools(context), - const SizedBox(height: defaultSpacing), - ...dialogSubHeader(theme, 'Feedback'), - Wrap( - children: [ - const Text('Encountered an issue? Let us know at '), - _createFeedbackLink(context), - const Text('.') - ], - ), - ], - ), - actions: [ - DialogCloseButton(), - ], - ); - } - - Widget _aboutDevTools(BuildContext context) { - return const SelectableText('DevTools version ${devtools.version}'); - } - - Widget _createFeedbackLink(BuildContext context) { - return RichText( - text: LinkTextSpan( - link: devToolsExtensionPoints.issueTrackerLink(), - context: context, - onTap: () { - ga.select( - analytics_constants.devToolsMain, - analytics_constants.feedbackLink, - ); - }, - ), - ); - } -} - // TODO(kenz): merge the checkbox functionality here with [NotifierCheckbox] class SettingsDialog extends StatelessWidget { @override
diff --git a/packages/devtools_app/lib/src/shared/about_dialog.dart b/packages/devtools_app/lib/src/shared/about_dialog.dart new file mode 100644 index 0000000..85327b2 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/about_dialog.dart
@@ -0,0 +1,92 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../devtools.dart' as devtools; +import '../analytics/analytics.dart' as ga; +import '../analytics/constants.dart' as analytics_constants; +import 'common_widgets.dart'; +import 'dialogs.dart'; +import 'globals.dart'; +import 'scaffold.dart'; +import 'theme.dart'; + +class DevToolsAboutDialog extends StatelessWidget { + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return DevToolsDialog( + title: dialogTitleText(theme, 'About DevTools'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _aboutDevTools(context), + const SizedBox(height: defaultSpacing), + ...dialogSubHeader(theme, 'Feedback'), + Wrap( + children: [ + const Text('Encountered an issue? Let us know at '), + _createFeedbackLink(context), + const Text('.') + ], + ), + ], + ), + actions: [ + DialogCloseButton(), + ], + ); + } + + Widget _aboutDevTools(BuildContext context) { + return const SelectableText('DevTools version ${devtools.version}'); + } + + Widget _createFeedbackLink(BuildContext context) { + return RichText( + text: LinkTextSpan( + link: devToolsExtensionPoints.issueTrackerLink(), + context: context, + onTap: () { + ga.select( + analytics_constants.devToolsMain, + analytics_constants.feedbackLink, + ); + }, + ), + ); + } +} + +class OpenAboutAction extends StatelessWidget { + @override + Widget build(BuildContext context) { + return DevToolsTooltip( + message: 'About DevTools', + child: InkWell( + onTap: () async { + unawaited( + showDialog( + context: context, + builder: (context) => DevToolsAboutDialog(), + ), + ); + }, + child: Container( + width: DevToolsScaffold.actionWidgetSize, + height: DevToolsScaffold.actionWidgetSize, + alignment: Alignment.center, + child: Icon( + Icons.help_outline, + size: actionsIconSize, + ), + ), + ), + ); + } +}
diff --git a/packages/devtools_app/lib/src/shared/report_feedback_button.dart b/packages/devtools_app/lib/src/shared/report_feedback_button.dart new file mode 100644 index 0000000..6eadc46 --- /dev/null +++ b/packages/devtools_app/lib/src/shared/report_feedback_button.dart
@@ -0,0 +1,43 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../analytics/analytics.dart' as ga; +import '../analytics/constants.dart' as analytics_constants; +import '../config_specific/launch_url/launch_url.dart'; +import 'common_widgets.dart'; +import 'globals.dart'; +import 'scaffold.dart'; +import 'theme.dart'; + +class ReportFeedbackButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return DevToolsTooltip( + message: 'Report feedback', + child: InkWell( + onTap: () async { + ga.select( + analytics_constants.devToolsMain, + analytics_constants.feedbackButton, + ); + await launchUrl( + devToolsExtensionPoints.issueTrackerLink().url, + context, + ); + }, + child: Container( + width: DevToolsScaffold.actionWidgetSize, + height: DevToolsScaffold.actionWidgetSize, + alignment: Alignment.center, + child: Icon( + Icons.bug_report, + size: actionsIconSize, + ), + ), + ), + ); + } +}
diff --git a/packages/devtools_app/lib/src/shared/scaffold.dart b/packages/devtools_app/lib/src/shared/scaffold.dart index f5a4216..7337f85 100644 --- a/packages/devtools_app/lib/src/shared/scaffold.dart +++ b/packages/devtools_app/lib/src/shared/scaffold.dart
@@ -65,7 +65,7 @@ /// The size that all actions on this widget are expected to have. static double get actionWidgetSize => scaleByFontFactor(48.0); - /// The border around the content in the DevTools UI. + /// The padding around the content in the DevTools UI. EdgeInsets get appPadding => EdgeInsets.fromLTRB( horizontalPadding.left, isEmbedded() ? 2.0 : defaultSpacing, @@ -358,7 +358,10 @@ initialFractions: const [0.8, 0.2], ) : content, - bottomNavigationBar: widget.embed ? null : _buildStatusLine(), + bottomNavigationBar: StatusLine( + currentScreen: _currentScreen, + isEmbedded: widget.embed, + ), ), ), ), @@ -449,30 +452,6 @@ ); } - Widget _buildStatusLine() { - final appPadding = widget.appPadding; - - return Container( - height: scaleByFontFactor(24.0) + - widget.appPadding.top + - widget.appPadding.bottom, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const PaddedDivider(padding: EdgeInsets.zero), - Padding( - padding: EdgeInsets.only( - left: appPadding.left, - right: appPadding.right, - bottom: appPadding.bottom, - ), - child: StatusLine(_currentScreen), - ), - ], - ), - ); - } - /// Returns the width of the scaffold title, tabs and default icons. double _wideWidth(String title, DevToolsScaffold widget) { final textTheme = Theme.of(context).textTheme;
diff --git a/packages/devtools_app/lib/src/shared/status_line.dart b/packages/devtools_app/lib/src/shared/status_line.dart index 2061bea..2f3b40d 100644 --- a/packages/devtools_app/lib/src/shared/status_line.dart +++ b/packages/devtools_app/lib/src/shared/status_line.dart
@@ -14,9 +14,11 @@ import '../service/isolate_manager.dart'; import '../service/service_manager.dart'; import '../ui/utils.dart'; +import 'about_dialog.dart'; import 'common_widgets.dart'; import 'device_dialog.dart'; import 'globals.dart'; +import 'report_feedback_button.dart'; import 'screen.dart'; import 'theme.dart'; import 'utils.dart'; @@ -28,17 +30,33 @@ /// This displays information global to the application, as well as gives pages /// a mechanism to display page-specific information. class StatusLine extends StatelessWidget { - const StatusLine(this.currentScreen); + const StatusLine({required this.currentScreen, required this.isEmbedded}); final Screen currentScreen; + final bool isEmbedded; + + /// The padding around the footer in the DevTools UI. + EdgeInsets get padding => const EdgeInsets.fromLTRB( + defaultSpacing, + defaultSpacing, + defaultSpacing, + denseSpacing, + ); @override Widget build(BuildContext context) { + final height = statusLineHeight + padding.top + padding.bottom; return ValueListenableBuilder<bool>( valueListenable: currentScreen.showIsolateSelector, builder: (context, showIsolateSelector, _) { return Container( - height: statusLineHeight, + decoration: BoxDecoration( + border: Border( + top: Divider.createBorderSide(context, width: 1.0), + ), + ), + padding: EdgeInsets.only(left: padding.left, right: padding.right), + height: height, alignment: Alignment.centerLeft, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -65,6 +83,16 @@ const BulletSpacer(), ], buildConnectionStatus(context, isExtraNarrow), + if (isEmbedded && !isExtraNarrow) ...[ + const BulletSpacer(), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + ReportFeedbackButton(), + OpenAboutAction(), + ], + ), + ] ]; }
diff --git a/packages/devtools_app/test/about_dialog_test.dart b/packages/devtools_app/test/about_dialog_test.dart index b4a6450..a10a1a4 100644 --- a/packages/devtools_app/test/about_dialog_test.dart +++ b/packages/devtools_app/test/about_dialog_test.dart
@@ -3,11 +3,11 @@ // found in the LICENSE file. import 'package:devtools_app/devtools.dart' as devtools; -import 'package:devtools_app/src/app.dart'; import 'package:devtools_app/src/config_specific/ide_theme/ide_theme.dart'; import 'package:devtools_app/src/extension_points/extensions_base.dart'; import 'package:devtools_app/src/extension_points/extensions_external.dart'; import 'package:devtools_app/src/service/service_manager.dart'; +import 'package:devtools_app/src/shared/about_dialog.dart'; import 'package:devtools_app/src/shared/globals.dart'; import 'package:devtools_test/devtools_test.dart'; import 'package:flutter_test/flutter_test.dart';