Skip to content

Commit

Permalink
Add a maxLines parameter for multiline Input. (#6328)
Browse files Browse the repository at this point in the history
If maxLines is 1, it's a single line Input that scrolls horizontally.
Otherwise, overflowed text wraps and scrolls vertically, taking up at
most `maxLines`.

Also fixed scrolling behavior so that the Input scrolls ensuring the
cursor is always visible.

Fixes #6271
  • Loading branch information
mpcomplete committed Oct 19, 2016
1 parent 97dbd9e commit d39eb84
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 14 deletions.
14 changes: 6 additions & 8 deletions packages/flutter/lib/src/rendering/editable_line.dart
Expand Up @@ -184,7 +184,7 @@ class RenderEditable extends RenderBox {
if (selection.isCollapsed) {
// TODO(mpcomplete): This doesn't work well at an RTL/LTR boundary.
Offset caretOffset = _textPainter.getOffsetForCaret(selection.extent, _caretPrototype);
Point start = new Point(0.0, constraints.constrainHeight(_preferredLineHeight)) + caretOffset + offset;
Point start = new Point(0.0, _preferredLineHeight) + caretOffset + offset;
return <TextSelectionPoint>[new TextSelectionPoint(localToGlobal(start), null)];
} else {
List<ui.TextBox> boxes = _textPainter.getBoxesForSelection(selection);
Expand All @@ -206,10 +206,9 @@ class RenderEditable extends RenderBox {
/// Returns the Rect in local coordinates for the caret at the given text
/// position.
Rect getLocalRectForCaret(TextPosition caretPosition) {
double lineHeight = constraints.constrainHeight(_preferredLineHeight);
Offset caretOffset = _textPainter.getOffsetForCaret(caretPosition, _caretPrototype);
// This rect is the same as _caretPrototype but without the vertical padding.
return new Rect.fromLTWH(0.0, 0.0, _kCaretWidth, lineHeight).shift(caretOffset + _paintOffset);
return new Rect.fromLTWH(0.0, 0.0, _kCaretWidth, _preferredLineHeight).shift(caretOffset + _paintOffset);
}

Size _contentSize;
Expand All @@ -223,7 +222,7 @@ class RenderEditable extends RenderBox {
// TODO(abarth): ParagraphBuilder#build's argument should be optional.
// TODO(abarth): These min/max values should be the default for ui.Paragraph.
_layoutTemplate = builder.build(new ui.ParagraphStyle())
..layout(new ui.ParagraphConstraints(width: _maxContentWidth));
..layout(new ui.ParagraphConstraints(width: double.INFINITY));
}
return _layoutTemplate.height;
}
Expand All @@ -241,7 +240,7 @@ class RenderEditable extends RenderBox {

@override
double computeMaxIntrinsicHeight(double width) {
return _preferredLineHeight;
return _preferredLineHeight * maxLines;
}

@override
Expand Down Expand Up @@ -303,12 +302,11 @@ class RenderEditable extends RenderBox {
@override
void performLayout() {
Size oldSize = hasSize ? size : null;
double lineHeight = constraints.constrainHeight(_preferredLineHeight);
_caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth, lineHeight - 2.0 * _kCaretHeightOffset);
_caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth, _preferredLineHeight - 2.0 * _kCaretHeightOffset);
_selectionRects = null;
_textPainter.layout(maxWidth: _maxContentWidth);
size = new Size(constraints.maxWidth, constraints.constrainHeight(
_textPainter.height.clamp(lineHeight, lineHeight * _maxLines)
_textPainter.height.clamp(_preferredLineHeight, _preferredLineHeight * _maxLines)
));
Size contentSize = new Size(_textPainter.width + _kCaretGap + _kCaretWidth, _textPainter.height);
assert(_selection != null);
Expand Down
7 changes: 5 additions & 2 deletions packages/flutter/lib/src/widgets/scrollable.dart
Expand Up @@ -513,6 +513,11 @@ class ScrollableState<T extends Scrollable> extends State<T> with SingleTickerPr
/// If there are no in-progress scrolling physics, this function scrolls to
/// the given offset instead.
void didUpdateScrollBehavior(double newScrollOffset) {
_setStateMaybeDuringBuild(() {
_contentExtent = scrollBehavior.contentExtent;
_containerExtent = scrollBehavior.containerExtent;
});

// This does not call setState, because if anything below actually
// changes our build, it will itself independently trigger a frame.
assert(_controller.isAnimating || _simulation == null);
Expand All @@ -536,8 +541,6 @@ class ScrollableState<T extends Scrollable> extends State<T> with SingleTickerPr
/// [didUpdateScrollBehavior].
/// 3. Updating this object's gesture detector with [updateGestureDetector].
void handleExtentsChanged(double contentExtent, double containerExtent) {
_contentExtent = contentExtent;
_containerExtent = containerExtent;
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: contentExtent,
containerExtent: containerExtent,
Expand Down
11 changes: 7 additions & 4 deletions packages/flutter/test/widget/input_test.dart
Expand Up @@ -311,7 +311,7 @@ void main() {
await gesture.moveTo(newHandlePos);
await tester.pump();
await gesture.up();
await tester.pump();
await tester.pumpWidget(builder());

expect(inputValue.selection.baseOffset, selection.baseOffset);
expect(inputValue.selection.extentOffset, selection.extentOffset+2);
Expand Down Expand Up @@ -565,7 +565,7 @@ void main() {
await gesture.moveTo(newHandlePos);
await tester.pump();
await gesture.up();
await tester.pump();
await tester.pumpWidget(builder());

expect(inputValue.selection.baseOffset, 76);
expect(inputValue.selection.extentOffset, 108);
Expand Down Expand Up @@ -635,7 +635,11 @@ void main() {
TestGesture gesture = await tester.startGesture(firstPos, pointer: 7);
await tester.pump();
await gesture.moveBy(new Offset(0.0, -1000.0));
await tester.pump();
await tester.pump(const Duration(seconds: 2));
// Wait and drag again to trigger https://github.com/flutter/flutter/issues/6329
// (No idea why this is necessary, but the bug wouldn't repro without it.)
await gesture.moveBy(new Offset(0.0, -1000.0));
await tester.pump(const Duration(seconds: 2));
await gesture.up();
await tester.pump();

Expand Down Expand Up @@ -679,5 +683,4 @@ void main() {
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFirstPos)), isTrue);
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFourthPos)), isFalse);
});

}

0 comments on commit d39eb84

Please sign in to comment.