Skip to content

Commit

Permalink
Split logging_screen. (#3833)
Browse files Browse the repository at this point in the history
  • Loading branch information
polina-c authored Mar 11, 2022
1 parent 85a45e5 commit c41dc4d
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 309 deletions.
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

0 comments on commit c41dc4d

Please sign in to comment.