From be865e1e1a6b8ad45d0efdb0edd4781ef3e4a863 Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 25 Feb 2016 13:52:40 -0800 Subject: [PATCH] SizeObserver crusade: RawInputLine --- .../lib/src/rendering/editable_line.dart | 16 ++--- .../flutter/lib/src/widgets/editable.dart | 70 +++++++++---------- .../flutter/lib/src/widgets/scrollable.dart | 5 ++ 3 files changed, 47 insertions(+), 44 deletions(-) diff --git a/packages/flutter/lib/src/rendering/editable_line.dart b/packages/flutter/lib/src/rendering/editable_line.dart index f381e327c6..e9ffd4a433 100644 --- a/packages/flutter/lib/src/rendering/editable_line.dart +++ b/packages/flutter/lib/src/rendering/editable_line.dart @@ -9,6 +9,7 @@ import 'package:flutter/gestures.dart'; import 'basic_types.dart'; import 'box.dart'; import 'object.dart'; +import 'viewport.dart'; const _kCaretGap = 1.0; // pixels const _kCaretHeightOffset = 2.0; // pixels @@ -24,9 +25,9 @@ class RenderEditableLine extends RenderBox { bool showCursor: false, Color selectionColor, TextSelection selection, - Offset paintOffset: Offset.zero, this.onSelectionChanged, - this.onContentSizeChanged + Offset paintOffset: Offset.zero, + this.onPaintOffsetUpdateNeeded }) : _textPainter = new TextPainter(text), _cursorColor = cursorColor, _showCursor = showCursor, @@ -45,8 +46,8 @@ class RenderEditableLine extends RenderBox { ..onTapCancel = _handleTapCancel; } - ValueChanged onContentSizeChanged; ValueChanged onSelectionChanged; + ViewportDimensionsChangeCallback onPaintOffsetUpdateNeeded; /// The text to display TextSpan get text => _textPainter.text; @@ -201,16 +202,15 @@ class RenderEditableLine extends RenderBox { Rect _caretPrototype; void performLayout() { + Size oldSize = hasSize ? size : null; size = new Size(constraints.maxWidth, constraints.constrainHeight(_preferredHeight)); _caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth, size.height - 2.0 * _kCaretHeightOffset); _selectionRects = null; _layoutText(new BoxConstraints(minHeight: constraints.minHeight, maxHeight: constraints.maxHeight)); Size contentSize = new Size(_textPainter.width + _kCaretGap + _kCaretWidth, _textPainter.height); - if (_contentSize != contentSize) { - _contentSize = contentSize; - if (onContentSizeChanged != null) - onContentSizeChanged(_contentSize); - } + if (onPaintOffsetUpdateNeeded != null && (size != oldSize || contentSize != _contentSize)) + onPaintOffsetUpdateNeeded(new ViewportDimensions(containerSize: size, contentSize: contentSize)); + _contentSize = contentSize; } void _paintCaret(Canvas canvas, Offset effectiveOffset) { diff --git a/packages/flutter/lib/src/widgets/editable.dart b/packages/flutter/lib/src/widgets/editable.dart index a0ed888550..1968daeee4 100644 --- a/packages/flutter/lib/src/widgets/editable.dart +++ b/packages/flutter/lib/src/widgets/editable.dart @@ -189,9 +189,6 @@ class RawInputLineState extends ScrollableState { Timer _cursorTimer; bool _showCursor = false; - double _contentWidth = 0.0; - double _containerWidth = 0.0; - _KeyboardClientImpl _keyboardClient; KeyboardHandle _keyboardHandle; @@ -218,24 +215,28 @@ class RawInputLineState extends ScrollableState { bool get _isAttachedToKeyboard => _keyboardHandle != null && _keyboardHandle.attached; - void _handleContainerSizeChanged(Size newSize) { - _containerWidth = newSize.width; - _updateScrollBehavior(); - } + double _contentWidth = 0.0; + double _containerWidth = 0.0; - void _handleContentSizeChanged(Size newSize) { - _contentWidth = newSize.width; - _updateScrollBehavior(); - } - - void _updateScrollBehavior() { - // Set the scroll offset to match the content width so that the cursor - // (which is always at the end of the text) will be visible. + Offset _handlePaintOffsetUpdateNeeded(ViewportDimensions dimensions) { + // We make various state changes here but don't have to do so in a + // setState() callback because we are called during layout and all + // we're updating is the new offset, which we are providing to the + // render object via our return value. + _containerWidth = dimensions.containerSize.width; + _contentWidth = dimensions.contentSize.width; scrollTo(scrollBehavior.updateExtents( contentExtent: _contentWidth, containerExtent: _containerWidth, - scrollOffset: _contentWidth + // Set the scroll offset to match the content width so that the + // cursor (which is always at the end of the text) will be + // visible. + // TODO(ianh): We should really only do this when text is added, + // not generally any time the size changes. + scrollOffset: pixelOffsetToScrollOffset(-_contentWidth) )); + updateGestureDetector(); + return scrollOffsetToPixelDelta(scrollOffset); } void _attachOrDetachKeyboard(bool focused) { @@ -327,19 +328,16 @@ class RawInputLineState extends ScrollableState { else if (_cursorTimer != null && (!focused || !config.value.selection.isCollapsed)) _stopCursorTimer(); - return new SizeObserver( - onSizeChanged: _handleContainerSizeChanged, - child: new _EditableLineWidget( - value: config.value, - style: config.style, - cursorColor: config.cursorColor, - showCursor: _showCursor, - selectionColor: config.selectionColor, - hideText: config.hideText, - onContentSizeChanged: _handleContentSizeChanged, - onSelectionChanged: _handleSelectionChanged, - paintOffset: new Offset(-scrollOffset, 0.0) - ) + return new _EditableLineWidget( + value: config.value, + style: config.style, + cursorColor: config.cursorColor, + showCursor: _showCursor, + selectionColor: config.selectionColor, + hideText: config.hideText, + onSelectionChanged: _handleSelectionChanged, + paintOffset: scrollOffsetToPixelDelta(scrollOffset), + onPaintOffsetUpdateNeeded: _handlePaintOffsetUpdateNeeded ); } } @@ -353,9 +351,9 @@ class _EditableLineWidget extends LeafRenderObjectWidget { this.showCursor, this.selectionColor, this.hideText, - this.onContentSizeChanged, this.onSelectionChanged, - this.paintOffset + this.paintOffset, + this.onPaintOffsetUpdateNeeded }) : super(key: key); final InputValue value; @@ -364,9 +362,9 @@ class _EditableLineWidget extends LeafRenderObjectWidget { final bool showCursor; final Color selectionColor; final bool hideText; - final ValueChanged onContentSizeChanged; final ValueChanged onSelectionChanged; final Offset paintOffset; + final ViewportDimensionsChangeCallback onPaintOffsetUpdateNeeded; RenderEditableLine createRenderObject() { return new RenderEditableLine( @@ -375,9 +373,9 @@ class _EditableLineWidget extends LeafRenderObjectWidget { showCursor: showCursor, selectionColor: selectionColor, selection: value.selection, - onContentSizeChanged: onContentSizeChanged, onSelectionChanged: onSelectionChanged, - paintOffset: paintOffset + paintOffset: paintOffset, + onPaintOffsetUpdateNeeded: onPaintOffsetUpdateNeeded ); } @@ -389,9 +387,9 @@ class _EditableLineWidget extends LeafRenderObjectWidget { ..showCursor = showCursor ..selectionColor = selectionColor ..selection = value.selection - ..onContentSizeChanged = onContentSizeChanged ..onSelectionChanged = onSelectionChanged - ..paintOffset = paintOffset; + ..paintOffset = paintOffset + ..onPaintOffsetUpdateNeeded = onPaintOffsetUpdateNeeded; } TextSpan get _styledTextSpan { diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index ab5764e698..b81f483663 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -380,6 +380,10 @@ abstract class ScrollableState extends State { /// /// If a non-null [duration] is provided, the widget will animate to the new /// scroll offset over the given duration with the given curve. + /// + /// This function does not accept a zero duration. To jump-scroll to + /// the new offset, do not provide a duration, rather than providing + /// a zero duration. Future scrollTo(double newScrollOffset, { Duration duration, Curve curve: Curves.ease }) { if (newScrollOffset == _scrollOffset) return new Future.value(); @@ -390,6 +394,7 @@ abstract class ScrollableState extends State { return new Future.value(); } + assert(duration > Duration.ZERO); return _animateTo(newScrollOffset, duration, curve); }