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

Split logging_screen to make null safety migration easier. #3833

Merged
merged 1 commit into from
Mar 11, 2022
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
66 changes: 66 additions & 0 deletions packages/devtools_app/lib/src/screens/logging/_kind_column.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2019 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.

// @dart=2.9

import 'package:flutter/material.dart';

import '../../shared/table.dart';
import '../../shared/table_data.dart';
import '../../shared/theme.dart';
import '../../shared/utils.dart';
import 'logging_controller.dart';

class KindColumn extends ColumnData<LogData>
implements ColumnRenderer<LogData> {
KindColumn()
: super(
'Kind',
fixedWidthPx: scaleByFontFactor(155),
);

@override
bool get supportsSorting => false;

@override
String getValue(LogData dataObject) => dataObject.kind;

@override
Widget build(
BuildContext context,
LogData item, {
bool isRowSelected = false,
VoidCallback onPressed,
}) {
final String kind = item.kind;

Color color = const Color.fromARGB(0xff, 0x61, 0x61, 0x61);

if (kind == 'stderr' || item.isError || kind == 'flutter.error') {
color = const Color.fromARGB(0xff, 0xF4, 0x43, 0x36);
} else if (kind == 'stdout') {
color = const Color.fromARGB(0xff, 0x78, 0x90, 0x9C);
} else if (kind.startsWith('flutter')) {
color = const Color.fromARGB(0xff, 0x00, 0x91, 0xea);
} else if (kind == 'gc') {
color = const Color.fromARGB(0xff, 0x42, 0x42, 0x42);
}

// Use a font color that contrasts with the colored backgrounds.
final textStyle = Theme.of(context).fixedFontStyle;

return Container(
padding: const EdgeInsets.symmetric(horizontal: 3.0),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(3.0),
),
child: Text(
kind,
overflow: TextOverflow.ellipsis,
style: textStyle,
),
);
}
}
111 changes: 111 additions & 0 deletions packages/devtools_app/lib/src/screens/logging/_log_details.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2019 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.

// @dart=2.9

import 'package:flutter/material.dart';

import '../../shared/common_widgets.dart';
import '../../shared/console.dart';
import '../../shared/theme.dart';
import 'logging_controller.dart';

class LogDetails extends StatefulWidget {
const LogDetails({Key key, @required this.log}) : super(key: key);

final LogData log;

@override
_LogDetailsState createState() => _LogDetailsState();

static const copyToClipboardButtonKey =
Key('log_details_copy_to_clipboard_button');
}

class _LogDetailsState extends State<LogDetails>
with SingleTickerProviderStateMixin {
String _lastDetails;
ScrollController scrollController;

@override
void initState() {
super.initState();
scrollController = ScrollController();
_computeLogDetails();
}

@override
void didUpdateWidget(LogDetails oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.log != oldWidget.log) {
_computeLogDetails();
}
}

Future<void> _computeLogDetails() async {
if (widget.log?.needsComputing ?? false) {
await widget.log.compute();
setState(() {});
}
}

bool showSimple(LogData log) => log != null && !log.needsComputing;

@override
Widget build(BuildContext context) {
return Container(
child: _buildContent(context, widget.log),
);
}

Widget _buildContent(BuildContext context, LogData log) {
// TODO(#1370): Handle showing flutter errors in a structured manner.
return Stack(
children: [
_buildSimpleLog(context, log),
if (log != null && log.needsComputing)
const CenteredCircularProgressIndicator(),
],
);
}

Widget _buildSimpleLog(BuildContext context, LogData log) {
final disabled = log?.details == null || log.details.isEmpty;

final details = log?.details;
if (details != _lastDetails) {
if (scrollController.hasClients) {
// Make sure we change the scroll if the log details shown have changed.
scrollController.jumpTo(0);
}
_lastDetails = details;
}

return OutlineDecoration(
child: ConsoleFrame(
title: AreaPaneHeader(
title: const Text('Details'),
needsTopBorder: false,
rightActions: [
CopyToClipboardControl(
dataProvider: disabled ? null : () => log?.prettyPrinted,
buttonKey: LogDetails.copyToClipboardButtonKey,
),
],
),
child: Padding(
padding: const EdgeInsets.all(denseSpacing),
child: SingleChildScrollView(
controller: scrollController,
child: SelectableText(
log?.prettyPrinted ?? '',
textAlign: TextAlign.left,
style: Theme.of(context).fixedFontStyle,
),
),
),
),
);
}
}
56 changes: 56 additions & 0 deletions packages/devtools_app/lib/src/screens/logging/_logs_table.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2019 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.

// @dart=2.9

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import '../../primitives/utils.dart';
import '../../shared/table.dart';
import '../../shared/table_data.dart';
import '_kind_column.dart';
import '_message_column.dart';
import '_when_column.dart';
import 'logging_controller.dart';

class LogsTable extends StatelessWidget {
LogsTable({
Key key,
@required this.data,
@required this.onItemSelected,
@required this.selectionNotifier,
@required this.searchMatchesNotifier,
@required this.activeSearchMatchNotifier,
}) : super(key: key);

final List<LogData> data;
final ItemCallback<LogData> onItemSelected;
final ValueListenable<LogData> selectionNotifier;
final ValueListenable<List<LogData>> searchMatchesNotifier;
final ValueListenable<LogData> activeSearchMatchNotifier;

final ColumnData<LogData> when = WhenColumn();
final ColumnData<LogData> kind = KindColumn();
final ColumnData<LogData> message = MessageColumn();

List<ColumnData<LogData>> get columns => [when, kind, message];

@override
Widget build(BuildContext context) {
return FlatTable<LogData>(
columns: columns,
data: data,
autoScrollContent: true,
keyFactory: (LogData data) => ValueKey<LogData>(data),
onItemSelected: onItemSelected,
selectionNotifier: selectionNotifier,
sortColumn: when,
secondarySortColumn: message,
sortDirection: SortDirection.ascending,
searchMatchesNotifier: searchMatchesNotifier,
activeSearchMatchNotifier: activeSearchMatchNotifier,
);
}
}
107 changes: 107 additions & 0 deletions packages/devtools_app/lib/src/screens/logging/_message_column.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2019 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.

// @dart=2.9

import 'dart:convert';

import 'package:flutter/material.dart';

import '../../primitives/utils.dart';
import '../../shared/table.dart';
import '../../shared/table_data.dart';
import '../../shared/theme.dart';
import '../../ui/colors.dart';
import 'logging_controller.dart';

@visibleForTesting
class MessageColumn extends ColumnData<LogData>
implements ColumnRenderer<LogData> {
MessageColumn() : super.wide('Message');

@override
bool get supportsSorting => false;

@override
String getValue(LogData dataObject) =>
dataObject.summary ?? dataObject.details;

@override
int compare(LogData a, LogData b) {
final String valueA = getValue(a);
final String valueB = getValue(b);
// Matches frame descriptions (e.g. '#12 11.4ms ')
final regex = RegExp(r'#(\d+)\s+\d+.\d+ms\s*');
final valueAIsFrameLog = valueA.startsWith(regex);
final valueBIsFrameLog = valueB.startsWith(regex);
if (valueAIsFrameLog && valueBIsFrameLog) {
final frameNumberA = regex.firstMatch(valueA)[1];
final frameNumberB = regex.firstMatch(valueB)[1];
return int.parse(frameNumberA).compareTo(int.parse(frameNumberB));
} else if (valueAIsFrameLog && !valueBIsFrameLog) {
return -1;
} else if (!valueAIsFrameLog && valueBIsFrameLog) {
return 1;
}
return valueA.compareTo(valueB);
}

@override
Widget build(
BuildContext context,
LogData data, {
bool isRowSelected = false,
VoidCallback onPressed,
}) {
TextStyle textStyle = Theme.of(context).fixedFontStyle;
if (isRowSelected) {
textStyle = textStyle.copyWith(color: defaultSelectionForegroundColor);
}

if (data.kind == 'flutter.frame') {
const Color color = Color.fromARGB(0xff, 0x00, 0x91, 0xea);
final Text text = Text(
getDisplayValue(data),
overflow: TextOverflow.ellipsis,
style: textStyle,
);

double frameLength = 0.0;
try {
final int micros = jsonDecode(data.details)['elapsed'];
frameLength = micros * 3.0 / 1000.0;
} catch (e) {
// ignore
}

return Row(
children: <Widget>[
text,
Flexible(
child: Container(
height: 12.0,
width: frameLength,
decoration: const BoxDecoration(color: color),
),
),
],
);
} else if (data.kind == 'stdout') {
return RichText(
text: TextSpan(
children: processAnsiTerminalCodes(
// TODO(helin24): Recompute summary length considering ansi codes.
// The current summary is generally the first 200 chars of details.
getDisplayValue(data),
textStyle,
),
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
);
} else {
return null;
}
}
}
26 changes: 26 additions & 0 deletions packages/devtools_app/lib/src/screens/logging/_when_column.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2019 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.

// @dart=2.9

import '../../shared/table_data.dart';
import '../../shared/utils.dart';
import 'logging_controller.dart';

class WhenColumn extends ColumnData<LogData> {
WhenColumn()
: super(
'When',
fixedWidthPx: scaleByFontFactor(120),
);

@override
bool get supportsSorting => false;

@override
String getValue(LogData dataObject) => dataObject.timestamp == null
? ''
: timeFormat
.format(DateTime.fromMillisecondsSinceEpoch(dataObject.timestamp));
}
Loading