Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9d87187
Wrapped in scroll view, but not yet scrolling
elliette Aug 7, 2021
f5c75ca
Merge branch 'master' into starter-bug
elliette Aug 9, 2021
dc540cf
Horizontal scrollbar now visible
elliette Aug 10, 2021
9586bb5
Generated Linux config files
elliette Aug 10, 2021
573a8b3
Merge branch 'linux-config' into starter-bug
elliette Aug 10, 2021
7cacf59
Scrolling works in horizontal and vertical directions
elliette Aug 10, 2021
63a40ee
Code cleanup
elliette Aug 10, 2021
cd365e0
Make it clearer what notifications the vertical scrollbar listens for
elliette Aug 10, 2021
508972a
Working as expected
elliette Aug 10, 2021
6b3390e
More cleanup
elliette Aug 11, 2021
b0b196c
Merge branch 'master' into starter-bug
elliette Aug 11, 2021
381bc72
Delete empty test that was generated when running in Linux emulator
elliette Aug 11, 2021
1b81193
Always show horizontal scrollbar
elliette Aug 12, 2021
464d296
Responding to PR comments
elliette Aug 12, 2021
242f15d
Rename assumedGutterCharacterWidth back to assumedCharacterWidth
elliette Aug 12, 2021
a51668e
Constrain box to file width
elliette Aug 12, 2021
1057cd9
Use SizedBox instead of ConstrainedBox
elliette Aug 12, 2021
1147729
responding to review comments
elliette Aug 13, 2021
a055a4f
Merge branch 'master' into starter-bug
elliette Aug 13, 2021
0020420
Added a test case for the scrollbars
elliette Aug 17, 2021
70ef6c4
Clean up
elliette Aug 17, 2021
787e805
Merge branch 'master' into starter-bug
elliette Aug 17, 2021
51ac12e
Update test case and golden image
elliette Aug 17, 2021
e09de2d
Update the test
elliette Aug 18, 2021
32356c7
Remove unused import
elliette Aug 18, 2021
05f280a
Resolve merge conflict with master
elliette Aug 20, 2021
257fd73
Update function comment
elliette Aug 20, 2021
1d04387
Skip test on MacOS
elliette Aug 23, 2021
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
75 changes: 57 additions & 18 deletions packages/devtools_app/lib/src/debugger/codeview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ class CodeView extends StatefulWidget {
this.onSelected,
}) : super(key: key);

static const debuggerCodeViewHorizontalScrollbarKey =
Key('debuggerCodeViewHorizontalScrollbarKey');

static const debuggerCodeViewVerticalScrollbarKey =
Key('debuggerCodeViewVerticalScrollbarKey');

static double get rowHeight => scaleByFontFactor(20.0);
static double get assumedCharacterWidth => scaleByFontFactor(16.0);

Expand All @@ -65,6 +71,7 @@ class _CodeViewState extends State<CodeView>
LinkedScrollControllerGroup verticalController;
ScrollController gutterController;
ScrollController textController;
ScrollController horizontalController;

ScriptRef get scriptRef => widget.scriptRef;

Expand All @@ -77,6 +84,7 @@ class _CodeViewState extends State<CodeView>
verticalController = LinkedScrollControllerGroup();
gutterController = verticalController.addAndGet();
textController = verticalController.addAndGet();
horizontalController = ScrollController();

addAutoDisposeListener(
widget.controller.scriptLocation,
Expand All @@ -100,6 +108,7 @@ class _CodeViewState extends State<CodeView>
void dispose() {
gutterController.dispose();
textController.dispose();
horizontalController.dispose();
widget.controller.scriptLocation
.removeListener(_handleScriptLocationChanged);
super.dispose();
Expand Down Expand Up @@ -263,7 +272,12 @@ class _CodeViewState extends State<CodeView>
style: theme.fixedFontStyle,
child: Expanded(
child: Scrollbar(
Comment thread
elliette marked this conversation as resolved.
key: CodeView.debuggerCodeViewVerticalScrollbarKey,
controller: textController,
// Only listen for vertical scroll notifications (ignore those
Comment thread
elliette marked this conversation as resolved.
// from the nested horizontal SingleChildScrollView):
notificationPredicate: (ScrollNotification notification) =>

@xu-baolin xu-baolin Aug 12, 2021 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use notification.depth to predicate, otherwise, all the descendants of Scrollables with vertical axis will interfere.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, changed to use notificationDepth.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure this notificationPredicate is needed? If it is needed, there are likely multiple other places in the UI where we need notificationPredicates. I thought it wasn't needed as we explicitly specify the scrollController.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notificationPredicate is needed, otherwise the vertical scrollbar responds to horizontal scroll events (and shows up at the bottom along with the horizontal scrollbar, see image). I'm guessing scrollbars in other parts of the UI would be similarly broken if this was needed for them?

image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @xu-baolin! I see you added the notificationPredicate 😄 Could you clarify what the purpose of the notificationPredicate is vs. the scrollController? Is the scrollController no longer used? Thanks!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I will check that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Piinks Hey, what are your thoughts on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this may already be fixed, we noticed this recently in flutter/flutter#87697.
The scrollbar uses the scroll controller but also listens and responds to scroll events. This is useful like in this 2D case where the scrollbars might want to know about both axes and respond when either one scrolls. They just aren't behaving correctly here, should be fixed by flutter/flutter#87698.

notification.depth == 1,
child: ValueListenableBuilder<StackFrameAndSourcePosition>(
valueListenable: widget.controller.selectedStackFrame,
builder: (context, frame, _) {
Expand Down Expand Up @@ -298,15 +312,43 @@ class _CodeViewState extends State<CodeView>
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
return Lines(
constraints: constraints,
scrollController: textController,
lines: lines,
pausedFrame: pausedFrame,
searchMatchesNotifier:
widget.controller.searchMatches,
activeSearchMatchNotifier:
widget.controller.activeSearchMatch,
// Find the longest line to measure file width:
int longestLength = 0;
TextSpan longestLine;
for (var line in lines) {
final int currentLength =
line.toPlainText().length;
if (currentLength > longestLength) {
longestLength = currentLength;
longestLine = line;
}
}
final double fileWidth =
calculateTextSpanWidth(longestLine);

return Scrollbar(
Comment thread
elliette marked this conversation as resolved.
key: CodeView
.debuggerCodeViewHorizontalScrollbarKey,
isAlwaysShown: true,
controller: horizontalController,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: horizontalController,
child: SizedBox(
height: constraints.maxHeight,
width: fileWidth,
child: Lines(
height: constraints.maxHeight,
scrollController: textController,
lines: lines,
pausedFrame: pausedFrame,
searchMatchesNotifier:
widget.controller.searchMatches,
activeSearchMatchNotifier:
widget.controller.activeSearchMatch,
),
),
),
);
},
),
Expand Down Expand Up @@ -527,15 +569,15 @@ class GutterItem extends StatelessWidget {
class Lines extends StatefulWidget {
const Lines({
Key key,
@required this.constraints,
@required this.height,
@required this.scrollController,
@required this.lines,
@required this.pausedFrame,
@required this.searchMatchesNotifier,
@required this.activeSearchMatchNotifier,
}) : super(key: key);

final BoxConstraints constraints;
final double height;
final ScrollController scrollController;
final List<TextSpan> lines;
final StackFrameAndSourcePosition pausedFrame;
Expand Down Expand Up @@ -572,17 +614,14 @@ class _LinesState extends State<Lines> with AutoDisposeMixin {
if (activeSearch != null) {
final isOutOfViewTop = activeSearch.position.line * CodeView.rowHeight <
widget.scrollController.offset + CodeView.rowHeight;
final isOutOfViewBottom =
activeSearch.position.line * CodeView.rowHeight >
widget.scrollController.offset +
widget.constraints.maxHeight -
CodeView.rowHeight;
final isOutOfViewBottom = activeSearch.position.line *
CodeView.rowHeight >
widget.scrollController.offset + widget.height - CodeView.rowHeight;

if (isOutOfViewTop || isOutOfViewBottom) {
// Scroll this search token to the middle of the view.
final targetOffset = math.max(
activeSearch.position.line * CodeView.rowHeight -
widget.constraints.maxHeight / 2,
activeSearch.position.line * CodeView.rowHeight - widget.height / 2,
0.0,
);
widget.scrollController.animateTo(
Expand Down
11 changes: 11 additions & 0 deletions packages/devtools_app/lib/src/ui/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,17 @@ TextSpan truncateTextSpan(TextSpan span, int length) {
return truncateHelper(span);
}

/// Returns the width in pixels of the [span].
double calculateTextSpanWidth(TextSpan span) {
final textPainter = TextPainter(
text: span,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
)..layout();

return textPainter.width;
}

/// Scrollbar that is offset by the amount specified by an [offsetController].
///
/// This makes it possible to create a [ListView] with both vertical and
Expand Down
43 changes: 43 additions & 0 deletions packages/devtools_app/test/debugger_screen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

import 'package:ansicolor/ansicolor.dart';
import 'package:devtools_app/src/debugger/console.dart';
import 'package:devtools_app/src/debugger/controls.dart';
Expand Down Expand Up @@ -29,6 +31,7 @@ void main() {
setGlobal(ServiceConnectionManager, fakeServiceManager);

const windowSize = Size(4000.0, 4000.0);
const smallWindowSize = Size(1000.0, 1000.0);

group('DebuggerScreen', () {
Future<void> pumpDebuggerScreen(
Expand Down Expand Up @@ -174,6 +177,46 @@ void main() {
});
});

group('Codeview', () {
setUp(() {
final scriptsHistory = ScriptsHistory();
scriptsHistory.pushEntry(mockScript);
when(debuggerController.currentScriptRef)
.thenReturn(ValueNotifier(mockScriptRef));
when(debuggerController.currentParsedScript)
.thenReturn(ValueNotifier(mockParsedScript));
when(debuggerController.showSearchInFileField)
.thenReturn(ValueNotifier(false));
when(debuggerController.scriptsHistory).thenReturn(scriptsHistory);
when(debuggerController.searchMatches).thenReturn(ValueNotifier([]));
when(debuggerController.activeSearchMatch)
.thenReturn(ValueNotifier(null));
});

testWidgetsWithWindowSize(
'has a horizontal and a vertical scrollbar', smallWindowSize,
(WidgetTester tester) async {
await pumpDebuggerScreen(tester, debuggerController);

// TODO(elliette): https://github.com/flutter/flutter/pull/88152 fixes
// this so that forcing a scroll event is no longer necessary. Remove
// once the change is in the stable release.
debuggerController.showScriptLocation(ScriptLocation(mockScriptRef,
location: SourcePosition(line: 50, column: 50)));
await tester.pumpAndSettle();

expect(find.byType(Scrollbar), findsNWidgets(2));
expect(find.byKey(const Key('debuggerCodeViewVerticalScrollbarKey')),
findsOneWidget);
expect(find.byKey(const Key('debuggerCodeViewHorizontalScrollbarKey')),
findsOneWidget);
await expectLater(
find.byKey(DebuggerScreenBody.codeViewKey),
matchesGoldenFile('goldens/codeview_scrollbars.png'),
);
}, skip: !Platform.isMacOS);
});

testWidgetsWithWindowSize('Libraries hidden', windowSize,
(WidgetTester tester) async {
final scripts = [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading