Commit d39eb84a authored by Matt Perry's avatar Matt Perry Committed by GitHub

Add a maxLines parameter for multiline Input. (#6328)

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 https://github.com/flutter/flutter/issues/6271
parent 97dbd9ea
......@@ -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);
......@@ -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;
......@@ -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;
}
......@@ -241,7 +240,7 @@ class RenderEditable extends RenderBox {
@override
double computeMaxIntrinsicHeight(double width) {
return _preferredLineHeight;
return _preferredLineHeight * maxLines;
}
@override
......@@ -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);
......
......@@ -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);
......@@ -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,
......
......@@ -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);
......@@ -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);
......@@ -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();
......@@ -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);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment