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

Make table text selectable #6919

Merged
merged 8 commits into from Dec 29, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
137 changes: 70 additions & 67 deletions packages/devtools_app/lib/src/shared/table/table.dart
Expand Up @@ -1105,18 +1105,34 @@ class _TableState<T> extends State<_Table<T>> with AutoDisposeMixin {
// TODO(kenz): add horizontal scrollbar.
return LayoutBuilder(
builder: (context, constraints) {
return SizedBox(
width: max(
constraints.widthConstraints().maxWidth,
tableWidth,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (showColumnGroupHeader)
TableRow<T>.tableColumnGroupHeader(
return SelectionArea(
child: SizedBox(
width: max(
constraints.widthConstraints().maxWidth,
tableWidth,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (showColumnGroupHeader)
TableRow<T>.tableColumnGroupHeader(
linkedScrollControllerGroup:
_linkedHorizontalScrollControllerGroup,
columnGroups: columnGroups,
columnWidths: widget.columnWidths,
sortColumn: sortColumn,
sortDirection: tableUiState.sortDirection,
secondarySortColumn:
widget.tableController.secondarySortColumn,
onSortChanged: widget.tableController.sortDataAndNotify,
tall: widget.tallHeaders,
backgroundColor: widget.headerColor,
),
TableRow<T>.tableColumnHeader(
key: const Key('Table header'),
linkedScrollControllerGroup:
_linkedHorizontalScrollControllerGroup,
columns: widget.tableController.columns,
columnGroups: columnGroups,
columnWidths: widget.columnWidths,
sortColumn: sortColumn,
Expand All @@ -1127,69 +1143,56 @@ class _TableState<T> extends State<_Table<T>> with AutoDisposeMixin {
tall: widget.tallHeaders,
backgroundColor: widget.headerColor,
),
TableRow<T>.tableColumnHeader(
key: const Key('Table header'),
linkedScrollControllerGroup:
_linkedHorizontalScrollControllerGroup,
columns: widget.tableController.columns,
columnGroups: columnGroups,
columnWidths: widget.columnWidths,
sortColumn: sortColumn,
sortDirection: tableUiState.sortDirection,
secondarySortColumn: widget.tableController.secondarySortColumn,
onSortChanged: widget.tableController.sortDataAndNotify,
tall: widget.tallHeaders,
backgroundColor: widget.headerColor,
),
if (pinnedData.isNotEmpty) ...[
SizedBox(
height: _pinnedDataHeight(constraints),
child: Scrollbar(
thumbVisibility: true,
controller: pinnedScrollController,
child: ListView.builder(
if (pinnedData.isNotEmpty) ...[
SizedBox(
height: _pinnedDataHeight(constraints),
child: Scrollbar(
thumbVisibility: true,
controller: pinnedScrollController,
itemCount: pinnedData.length,
itemExtent: widget.rowItemExtent,
itemBuilder: (context, index) => _buildItem(
context,
index,
isPinned: true,
child: ListView.builder(
controller: pinnedScrollController,
itemCount: pinnedData.length,
itemExtent: widget.rowItemExtent,
itemBuilder: (context, index) => _buildItem(
context,
index,
isPinned: true,
),
),
),
),
),
const ThickDivider(),
],
Expanded(
child: Scrollbar(
thumbVisibility: true,
controller: scrollController,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTapDown: (a) => widget.focusNode?.requestFocus(),
child: Focus(
autofocus: true,
onKeyEvent: (_, event) => widget.handleKeyEvent != null
? widget.handleKeyEvent!(
event,
scrollController,
constraints,
)
: KeyEventResult.ignored,
focusNode: widget.focusNode,
child: ListView.builder(
controller: scrollController,
itemCount:
_dataRowCount(constraints, showColumnGroupHeader),
itemExtent: widget.rowItemExtent,
itemBuilder: _buildItem,
const ThickDivider(),
],
Expanded(
child: Scrollbar(
thumbVisibility: true,
controller: scrollController,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTapDown: (a) => widget.focusNode?.requestFocus(),
child: Focus(
autofocus: true,
onKeyEvent: (_, event) => widget.handleKeyEvent != null
? widget.handleKeyEvent!(
event,
scrollController,
constraints,
)
: KeyEventResult.ignored,
focusNode: widget.focusNode,
child: ListView.builder(
controller: scrollController,
itemCount:
_dataRowCount(constraints, showColumnGroupHeader),
itemExtent: widget.rowItemExtent,
itemBuilder: _buildItem,
),
),
),
),
),
),
],
],
),
),
);
},
Expand Down Expand Up @@ -1601,8 +1604,8 @@ class _TableRowState<T> extends State<TableRow<T>>
}
// If ColumnRenderer.build returns null, fall back to the default
// rendering.
content ??= RichText(
text: TextSpan(
content ??= Text.rich(
TextSpan(
text: column.getDisplayValue(node),
children: [
if (column.getCaption(node) != null)
Expand Down
Expand Up @@ -12,6 +12,7 @@ To learn more about DevTools, check out the

* Added a new feature for deep link validation, supporting deep link web checks on Android. - [#6935](https://github.com/flutter/devtools/pull/6935)
* Added the basic plumbing to allow connections to a Dart Tooling Daemon. - [#7009](https://github.com/flutter/devtools/pull/7009)
* Made table text selectable [#6919](https://github.com/flutter/devtools/pull/6919)

## Inspector updates

Expand Down
Expand Up @@ -577,7 +577,7 @@ void main() {
expect(deferredMenuItemFinder, findsOneWidget);

// Select the main unit.
await tester.tap(find.text('Main').hitTestable());
await tester.tap(find.richText('Main').hitTestable());
await tester.pumpAndSettle();

// Verify the main unit is shown for entire app.
Expand Down Expand Up @@ -751,6 +751,6 @@ Finder _findDropdownButton<T>() {
Finder _findMenuItemWithText<T>(String text) {
return find.descendant(
of: find.byType(DropdownMenuItem<T>),
matching: find.text(text).first,
matching: find.richText(text).hitTestable(),
);
}
Expand Up @@ -217,7 +217,7 @@ void main() {
await tester.pumpWidget(wrap(const VMFlagsDialog()));
expect(find.richText('VM Flags'), findsOneWidget);
expect(find.richText('flag 1 name'), findsOneWidget);
final RichText commentText = tester.firstWidget<RichText>(
final Text commentText = tester.firstWidget<Text>(
findSubstring('flag 1 comment'),
);
expect(commentText, isNotNull);
Expand Down