Unverified Commit 2338576a authored by chunhtai's avatar chunhtai Committed by GitHub

implement selectable text (#34019)

parent 41bc10fa
...@@ -93,6 +93,7 @@ export 'src/material/reorderable_list.dart'; ...@@ -93,6 +93,7 @@ export 'src/material/reorderable_list.dart';
export 'src/material/scaffold.dart'; export 'src/material/scaffold.dart';
export 'src/material/scrollbar.dart'; export 'src/material/scrollbar.dart';
export 'src/material/search.dart'; export 'src/material/search.dart';
export 'src/material/selectable_text.dart';
export 'src/material/shadows.dart'; export 'src/material/shadows.dart';
export 'src/material/slider.dart'; export 'src/material/slider.dart';
export 'src/material/slider_theme.dart'; export 'src/material/slider_theme.dart';
......
This diff is collapsed.
...@@ -17,6 +17,7 @@ import 'ink_well.dart' show InteractiveInkFeature; ...@@ -17,6 +17,7 @@ import 'ink_well.dart' show InteractiveInkFeature;
import 'input_decorator.dart'; import 'input_decorator.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'selectable_text.dart' show iOSHorizontalOffset;
import 'text_selection.dart'; import 'text_selection.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -932,14 +933,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -932,14 +933,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
cursorOpacityAnimates = true; cursorOpacityAnimates = true;
cursorColor ??= CupertinoTheme.of(context).primaryColor; cursorColor ??= CupertinoTheme.of(context).primaryColor;
cursorRadius ??= const Radius.circular(2.0); cursorRadius ??= const Radius.circular(2.0);
// An eyeballed value that moves the cursor slightly left of where it is cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
// rendered for text on Android so its positioning more accurately matches the
// native iOS text cursor positioning.
//
// This value is in device pixels, not logical pixels as is typically used
// throughout the codebase.
const int _iOSHorizontalOffset = -2;
cursorOffset = Offset(_iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
break; break;
case TargetPlatform.android: case TargetPlatform.android:
......
...@@ -652,7 +652,7 @@ class TextPainter { ...@@ -652,7 +652,7 @@ class TextPainter {
final double caretEnd = box.end; final double caretEnd = box.end;
final double dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd; final double dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd;
return Rect.fromLTRB(min(dx, width), box.top, min(dx, width), box.bottom); return Rect.fromLTRB(min(dx, _paragraph.width), box.top, min(dx, _paragraph.width), box.bottom);
} }
return null; return null;
} }
...@@ -694,7 +694,7 @@ class TextPainter { ...@@ -694,7 +694,7 @@ class TextPainter {
final TextBox box = boxes.last; final TextBox box = boxes.last;
final double caretStart = box.start; final double caretStart = box.start;
final double dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart; final double dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart;
return Rect.fromLTRB(min(dx, width), box.top, min(dx, width), box.bottom); return Rect.fromLTRB(min(dx, _paragraph.width), box.top, min(dx, _paragraph.width), box.bottom);
} }
return null; return null;
} }
......
...@@ -157,6 +157,9 @@ class RenderEditable extends RenderBox { ...@@ -157,6 +157,9 @@ class RenderEditable extends RenderBox {
this.onSelectionChanged, this.onSelectionChanged,
this.onCaretChanged, this.onCaretChanged,
this.ignorePointer = false, this.ignorePointer = false,
bool readOnly = false,
bool forceLine = true,
TextWidthBasis textWidthBasis = TextWidthBasis.parent,
bool obscureText = false, bool obscureText = false,
Locale locale, Locale locale,
double cursorWidth = 1.0, double cursorWidth = 1.0,
...@@ -185,10 +188,13 @@ class RenderEditable extends RenderBox { ...@@ -185,10 +188,13 @@ class RenderEditable extends RenderBox {
assert(textScaleFactor != null), assert(textScaleFactor != null),
assert(offset != null), assert(offset != null),
assert(ignorePointer != null), assert(ignorePointer != null),
assert(textWidthBasis != null),
assert(paintCursorAboveText != null), assert(paintCursorAboveText != null),
assert(obscureText != null), assert(obscureText != null),
assert(textSelectionDelegate != null), assert(textSelectionDelegate != null),
assert(cursorWidth != null && cursorWidth >= 0.0), assert(cursorWidth != null && cursorWidth >= 0.0),
assert(readOnly != null),
assert(forceLine != null),
assert(devicePixelRatio != null), assert(devicePixelRatio != null),
_textPainter = TextPainter( _textPainter = TextPainter(
text: text, text: text,
...@@ -197,6 +203,7 @@ class RenderEditable extends RenderBox { ...@@ -197,6 +203,7 @@ class RenderEditable extends RenderBox {
textScaleFactor: textScaleFactor, textScaleFactor: textScaleFactor,
locale: locale, locale: locale,
strutStyle: strutStyle, strutStyle: strutStyle,
textWidthBasis: textWidthBasis,
), ),
_cursorColor = cursorColor, _cursorColor = cursorColor,
_backgroundCursorColor = backgroundCursorColor, _backgroundCursorColor = backgroundCursorColor,
...@@ -216,7 +223,9 @@ class RenderEditable extends RenderBox { ...@@ -216,7 +223,9 @@ class RenderEditable extends RenderBox {
_devicePixelRatio = devicePixelRatio, _devicePixelRatio = devicePixelRatio,
_startHandleLayerLink = startHandleLayerLink, _startHandleLayerLink = startHandleLayerLink,
_endHandleLayerLink = endHandleLayerLink, _endHandleLayerLink = endHandleLayerLink,
_obscureText = obscureText { _obscureText = obscureText,
_readOnly = readOnly,
_forceLine = forceLine {
assert(_showCursor != null); assert(_showCursor != null);
assert(!_showCursor.value || cursorColor != null); assert(!_showCursor.value || cursorColor != null);
this.hasFocus = hasFocus ?? false; this.hasFocus = hasFocus ?? false;
...@@ -245,12 +254,15 @@ class RenderEditable extends RenderBox { ...@@ -245,12 +254,15 @@ class RenderEditable extends RenderBox {
/// The default value of this property is false. /// The default value of this property is false.
bool ignorePointer; bool ignorePointer;
/// Whether text is composed. /// {@macro flutter.widgets.text.DefaultTextStyle.textWidthBasis}
/// TextWidthBasis get textWidthBasis => _textPainter.textWidthBasis;
/// Text is composed when user selects it for editing. The [TextSpan] will have set textWidthBasis(TextWidthBasis value) {
/// children with composing effect and leave text property to be null. assert(value != null);
@visibleForTesting if (_textPainter.textWidthBasis == value)
bool get isComposingText => text.text == null; return;
_textPainter.textWidthBasis = value;
markNeedsTextLayout();
}
/// The pixel ratio of the current device. /// The pixel ratio of the current device.
/// ///
...@@ -444,7 +456,7 @@ class RenderEditable extends RenderBox { ...@@ -444,7 +456,7 @@ class RenderEditable extends RenderBox {
if (leftArrow && _extentOffset > 2) { if (leftArrow && _extentOffset > 2) {
final TextSelection textSelection = _selectWordAtOffset(TextPosition(offset: _extentOffset - 2)); final TextSelection textSelection = _selectWordAtOffset(TextPosition(offset: _extentOffset - 2));
newOffset = textSelection.baseOffset + 1; newOffset = textSelection.baseOffset + 1;
} else if (rightArrow && _extentOffset < text.text.length - 2) { } else if (rightArrow && _extentOffset < text.toPlainText().length - 2) {
final TextSelection textSelection = _selectWordAtOffset(TextPosition(offset: _extentOffset + 1)); final TextSelection textSelection = _selectWordAtOffset(TextPosition(offset: _extentOffset + 1));
newOffset = textSelection.extentOffset - 1; newOffset = textSelection.extentOffset - 1;
} }
...@@ -487,7 +499,7 @@ class RenderEditable extends RenderBox { ...@@ -487,7 +499,7 @@ class RenderEditable extends RenderBox {
// case that the user wants to unhighlight some text. // case that the user wants to unhighlight some text.
if (position.offset == _extentOffset) { if (position.offset == _extentOffset) {
if (downArrow) if (downArrow)
newOffset = text.text.length; newOffset = text.toPlainText().length;
else if (upArrow) else if (upArrow)
newOffset = 0; newOffset = 0;
_resetCursor = shift; _resetCursor = shift;
...@@ -554,16 +566,16 @@ class RenderEditable extends RenderBox { ...@@ -554,16 +566,16 @@ class RenderEditable extends RenderBox {
case _kCKeyCode: case _kCKeyCode:
if (!selection.isCollapsed) { if (!selection.isCollapsed) {
Clipboard.setData( Clipboard.setData(
ClipboardData(text: selection.textInside(text.text))); ClipboardData(text: selection.textInside(text.toPlainText())));
} }
break; break;
case _kXKeyCode: case _kXKeyCode:
if (!selection.isCollapsed) { if (!selection.isCollapsed) {
Clipboard.setData( Clipboard.setData(
ClipboardData(text: selection.textInside(text.text))); ClipboardData(text: selection.textInside(text.toPlainText())));
textSelectionDelegate.textEditingValue = TextEditingValue( textSelectionDelegate.textEditingValue = TextEditingValue(
text: selection.textBefore(text.text) text: selection.textBefore(text.toPlainText())
+ selection.textAfter(text.text), + selection.textAfter(text.toPlainText()),
selection: TextSelection.collapsed(offset: selection.start), selection: TextSelection.collapsed(offset: selection.start),
); );
} }
...@@ -601,15 +613,15 @@ class RenderEditable extends RenderBox { ...@@ -601,15 +613,15 @@ class RenderEditable extends RenderBox {
} }
void _handleDelete() { void _handleDelete() {
if (selection.textAfter(text.text).isNotEmpty) { if (selection.textAfter(text.toPlainText()).isNotEmpty) {
textSelectionDelegate.textEditingValue = TextEditingValue( textSelectionDelegate.textEditingValue = TextEditingValue(
text: selection.textBefore(text.text) text: selection.textBefore(text.toPlainText())
+ selection.textAfter(text.text).substring(1), + selection.textAfter(text.toPlainText()).substring(1),
selection: TextSelection.collapsed(offset: selection.start), selection: TextSelection.collapsed(offset: selection.start),
); );
} else { } else {
textSelectionDelegate.textEditingValue = TextEditingValue( textSelectionDelegate.textEditingValue = TextEditingValue(
text: selection.textBefore(text.text), text: selection.textBefore(text.toPlainText()),
selection: TextSelection.collapsed(offset: selection.start), selection: TextSelection.collapsed(offset: selection.start),
); );
} }
...@@ -758,6 +770,28 @@ class RenderEditable extends RenderBox { ...@@ -758,6 +770,28 @@ class RenderEditable extends RenderBox {
markNeedsSemanticsUpdate(); markNeedsSemanticsUpdate();
} }
/// Whether this rendering object will take a full line regardless the text width.
bool get forceLine => _forceLine;
bool _forceLine = false;
set forceLine(bool value) {
assert(value != null);
if (_forceLine == value)
return;
_forceLine = value;
markNeedsLayout();
}
/// Whether this rendering object is read only.
bool get readOnly => _readOnly;
bool _readOnly = false;
set readOnly(bool value) {
assert(value != null);
if (_readOnly == value)
return;
_readOnly = value;
markNeedsSemanticsUpdate();
}
/// The maximum number of lines for the text to span, wrapping if necessary. /// The maximum number of lines for the text to span, wrapping if necessary.
/// ///
/// If this is 1 (the default), the text will not wrap, but will extend /// If this is 1 (the default), the text will not wrap, but will extend
...@@ -983,6 +1017,8 @@ class RenderEditable extends RenderBox { ...@@ -983,6 +1017,8 @@ class RenderEditable extends RenderBox {
return enableInteractiveSelection ?? !obscureText; return enableInteractiveSelection ?? !obscureText;
} }
double get _caretMargin => _kCaretGap + cursorWidth;
@override @override
void describeSemanticsConfiguration(SemanticsConfiguration config) { void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config); super.describeSemanticsConfiguration(config);
...@@ -995,7 +1031,8 @@ class RenderEditable extends RenderBox { ...@@ -995,7 +1031,8 @@ class RenderEditable extends RenderBox {
..isMultiline = _isMultiline ..isMultiline = _isMultiline
..textDirection = textDirection ..textDirection = textDirection
..isFocused = hasFocus ..isFocused = hasFocus
..isTextField = true; ..isTextField = true
..isReadOnly = readOnly;
if (hasFocus && selectionEnabled) if (hasFocus && selectionEnabled)
config.onSetSelection = _handleSetSelection; config.onSetSelection = _handleSetSelection;
...@@ -1526,10 +1563,12 @@ class RenderEditable extends RenderBox { ...@@ -1526,10 +1563,12 @@ class RenderEditable extends RenderBox {
assert(constraintWidth != null); assert(constraintWidth != null);
if (_textLayoutLastWidth == constraintWidth) if (_textLayoutLastWidth == constraintWidth)
return; return;
final double caretMargin = _kCaretGap + cursorWidth; final double availableWidth = math.max(0.0, constraintWidth - _caretMargin);
final double availableWidth = math.max(0.0, constraintWidth - caretMargin);
final double maxWidth = _isMultiline ? availableWidth : double.infinity; final double maxWidth = _isMultiline ? availableWidth : double.infinity;
_textPainter.layout(minWidth: availableWidth, maxWidth: maxWidth); _textPainter.layout(
minWidth: forceLine ? availableWidth : 0,
maxWidth: maxWidth,
);
_textLayoutLastWidth = constraintWidth; _textLayoutLastWidth = constraintWidth;
} }
...@@ -1566,8 +1605,10 @@ class RenderEditable extends RenderBox { ...@@ -1566,8 +1605,10 @@ class RenderEditable extends RenderBox {
// though we currently don't use those here. // though we currently don't use those here.
// See also RenderParagraph which has a similar issue. // See also RenderParagraph which has a similar issue.
final Size textPainterSize = _textPainter.size; final Size textPainterSize = _textPainter.size;
size = Size(constraints.maxWidth, constraints.constrainHeight(_preferredHeight(constraints.maxWidth))); final double width = forceLine ? constraints.maxWidth : constraints
final Size contentSize = Size(textPainterSize.width + _kCaretGap + cursorWidth, textPainterSize.height); .constrainWidth(_textPainter.size.width + _caretMargin);
size = Size(width, constraints.constrainHeight(_preferredHeight(constraints.maxWidth)));
final Size contentSize = Size(textPainterSize.width + _caretMargin, textPainterSize.height);
_maxScrollExtent = _getMaxScrollExtent(contentSize); _maxScrollExtent = _getMaxScrollExtent(contentSize);
offset.applyViewportDimension(_viewportExtent); offset.applyViewportDimension(_viewportExtent);
offset.applyContentDimensions(0.0, _maxScrollExtent); offset.applyContentDimensions(0.0, _maxScrollExtent);
......
...@@ -150,6 +150,29 @@ class TextEditingController extends ValueNotifier<TextEditingValue> { ...@@ -150,6 +150,29 @@ class TextEditingController extends ValueNotifier<TextEditingValue> {
); );
} }
/// Builds [TextSpan] from current editing value.
///
/// By default makes text in composing range appear as underlined.
/// Descendants can override this method to customize appearance of text.
TextSpan buildTextSpan({TextStyle style , bool withComposing}) {
if (!value.composing.isValid || !withComposing) {
return TextSpan(style: style, text: text);
}
final TextStyle composingStyle = style.merge(
const TextStyle(decoration: TextDecoration.underline),
);
return TextSpan(
style: style,
children: <TextSpan>[
TextSpan(text: value.composing.textBefore(value.text)),
TextSpan(
style: composingStyle,
text: value.composing.textInside(value.text),
),
TextSpan(text: value.composing.textAfter(value.text)),
]);
}
/// The currently selected [text]. /// The currently selected [text].
/// ///
/// If the selection is collapsed, then this property gives the offset of the /// If the selection is collapsed, then this property gives the offset of the
...@@ -288,6 +311,8 @@ class EditableText extends StatefulWidget { ...@@ -288,6 +311,8 @@ class EditableText extends StatefulWidget {
this.maxLines = 1, this.maxLines = 1,
this.minLines, this.minLines,
this.expands = false, this.expands = false,
this.forceLine = true,
this.textWidthBasis = TextWidthBasis.parent,
this.autofocus = false, this.autofocus = false,
bool showCursor, bool showCursor,
this.showSelectionHandles = false, this.showSelectionHandles = false,
...@@ -320,6 +345,7 @@ class EditableText extends StatefulWidget { ...@@ -320,6 +345,7 @@ class EditableText extends StatefulWidget {
assert(autocorrect != null), assert(autocorrect != null),
assert(showSelectionHandles != null), assert(showSelectionHandles != null),
assert(readOnly != null), assert(readOnly != null),
assert(forceLine != null),
assert(style != null), assert(style != null),
assert(cursorColor != null), assert(cursorColor != null),
assert(cursorOpacityAnimates != null), assert(cursorOpacityAnimates != null),
...@@ -368,6 +394,9 @@ class EditableText extends StatefulWidget { ...@@ -368,6 +394,9 @@ class EditableText extends StatefulWidget {
/// {@endtemplate} /// {@endtemplate}
final bool obscureText; final bool obscureText;
/// {@macro flutter.widgets.text.DefaultTextStyle.textWidthBasis}
final TextWidthBasis textWidthBasis;
/// {@template flutter.widgets.editableText.readOnly} /// {@template flutter.widgets.editableText.readOnly}
/// Whether the text can be changed. /// Whether the text can be changed.
/// ///
...@@ -378,6 +407,18 @@ class EditableText extends StatefulWidget { ...@@ -378,6 +407,18 @@ class EditableText extends StatefulWidget {
/// {@endtemplate} /// {@endtemplate}
final bool readOnly; final bool readOnly;
/// Whether the text will take the full width regardless of the text width.
///
/// When this is set to false, the width will be based on text width, which
/// will also be affected by [textWidthBasis].
///
/// Defaults to true. Must not be null.
///
/// See also:
///
/// * [textWidthBasis], which controls the calculation of text width.
final bool forceLine;
/// Whether to show selection handles. /// Whether to show selection handles.
/// ///
/// When a selection is active, there will be two handles at each side of /// When a selection is active, there will be two handles at each side of
...@@ -396,7 +437,7 @@ class EditableText extends StatefulWidget { ...@@ -396,7 +437,7 @@ class EditableText extends StatefulWidget {
/// ///
/// See also: /// See also:
/// ///
/// * [showSelectionHandles], which controls the visibility of the selection handles.. /// * [showSelectionHandles], which controls the visibility of the selection handles.
/// {@endtemplate} /// {@endtemplate}
final bool showCursor; final bool showCursor;
...@@ -1622,6 +1663,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1622,6 +1663,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
showCursor: EditableText.debugDeterministicCursor showCursor: EditableText.debugDeterministicCursor
? ValueNotifier<bool>(widget.showCursor) ? ValueNotifier<bool>(widget.showCursor)
: _cursorVisibilityNotifier, : _cursorVisibilityNotifier,
forceLine: widget.forceLine,
readOnly: widget.readOnly,
hasFocus: _hasFocus, hasFocus: _hasFocus,
maxLines: widget.maxLines, maxLines: widget.maxLines,
minLines: widget.minLines, minLines: widget.minLines,
...@@ -1632,6 +1675,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1632,6 +1675,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
textAlign: widget.textAlign, textAlign: widget.textAlign,
textDirection: _textDirection, textDirection: _textDirection,
locale: widget.locale, locale: widget.locale,
textWidthBasis: widget.textWidthBasis,
obscureText: widget.obscureText, obscureText: widget.obscureText,
autocorrect: widget.autocorrect, autocorrect: widget.autocorrect,
offset: offset, offset: offset,
...@@ -1657,33 +1701,21 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1657,33 +1701,21 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
/// By default makes text in composing range appear as underlined. /// By default makes text in composing range appear as underlined.
/// Descendants can override this method to customize appearance of text. /// Descendants can override this method to customize appearance of text.
TextSpan buildTextSpan() { TextSpan buildTextSpan() {
// Read only mode should not paint text composing.
if (!widget.obscureText && _value.composing.isValid && !widget.readOnly) {
final TextStyle composingStyle = widget.style.merge(
const TextStyle(decoration: TextDecoration.underline),
);
return TextSpan(
style: widget.style,
children: <TextSpan>[
TextSpan(text: _value.composing.textBefore(_value.text)),
TextSpan(
style: composingStyle,
text: _value.composing.textInside(_value.text),
),
TextSpan(text: _value.composing.textAfter(_value.text)),
]);
}
String text = _value.text;
if (widget.obscureText) { if (widget.obscureText) {
String text = _value.text;
text = RenderEditable.obscuringCharacter * text.length; text = RenderEditable.obscuringCharacter * text.length;
final int o = final int o =
_obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null; _obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : null;
if (o != null && o >= 0 && o < text.length) if (o != null && o >= 0 && o < text.length)
text = text.replaceRange(o, o + 1, _value.text.substring(o, o + 1)); text = text.replaceRange(o, o + 1, _value.text.substring(o, o + 1));
}
return TextSpan(style: widget.style, text: text); return TextSpan(style: widget.style, text: text);
} }
// Read only mode should not paint text composing.
return widget.controller.buildTextSpan(
style: widget.style,
withComposing: !widget.readOnly,
);
}
} }
class _Editable extends LeafRenderObjectWidget { class _Editable extends LeafRenderObjectWidget {
...@@ -1696,6 +1728,9 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1696,6 +1728,9 @@ class _Editable extends LeafRenderObjectWidget {
this.cursorColor, this.cursorColor,
this.backgroundCursorColor, this.backgroundCursorColor,
this.showCursor, this.showCursor,
this.forceLine,
this.readOnly,
this.textWidthBasis,
this.hasFocus, this.hasFocus,
this.maxLines, this.maxLines,
this.minLines, this.minLines,
...@@ -1730,6 +1765,8 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1730,6 +1765,8 @@ class _Editable extends LeafRenderObjectWidget {
final LayerLink endHandleLayerLink; final LayerLink endHandleLayerLink;
final Color backgroundCursorColor; final Color backgroundCursorColor;
final ValueNotifier<bool> showCursor; final ValueNotifier<bool> showCursor;
final bool forceLine;
final bool readOnly;
final bool hasFocus; final bool hasFocus;
final int maxLines; final int maxLines;
final int minLines; final int minLines;
...@@ -1741,6 +1778,7 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1741,6 +1778,7 @@ class _Editable extends LeafRenderObjectWidget {
final TextDirection textDirection; final TextDirection textDirection;
final Locale locale; final Locale locale;
final bool obscureText; final bool obscureText;
final TextWidthBasis textWidthBasis;
final bool autocorrect; final bool autocorrect;
final ViewportOffset offset; final ViewportOffset offset;
final SelectionChangedHandler onSelectionChanged; final SelectionChangedHandler onSelectionChanged;
...@@ -1763,6 +1801,8 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1763,6 +1801,8 @@ class _Editable extends LeafRenderObjectWidget {
endHandleLayerLink: endHandleLayerLink, endHandleLayerLink: endHandleLayerLink,
backgroundCursorColor: backgroundCursorColor, backgroundCursorColor: backgroundCursorColor,
showCursor: showCursor, showCursor: showCursor,
forceLine: forceLine,
readOnly: readOnly,
hasFocus: hasFocus, hasFocus: hasFocus,
maxLines: maxLines, maxLines: maxLines,
minLines: minLines, minLines: minLines,
...@@ -1779,6 +1819,7 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1779,6 +1819,7 @@ class _Editable extends LeafRenderObjectWidget {
onCaretChanged: onCaretChanged, onCaretChanged: onCaretChanged,
ignorePointer: rendererIgnoresPointer, ignorePointer: rendererIgnoresPointer,
obscureText: obscureText, obscureText: obscureText,
textWidthBasis: textWidthBasis,
cursorWidth: cursorWidth, cursorWidth: cursorWidth,
cursorRadius: cursorRadius, cursorRadius: cursorRadius,
cursorOffset: cursorOffset, cursorOffset: cursorOffset,
...@@ -1797,6 +1838,8 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1797,6 +1838,8 @@ class _Editable extends LeafRenderObjectWidget {
..startHandleLayerLink = startHandleLayerLink ..startHandleLayerLink = startHandleLayerLink
..endHandleLayerLink = endHandleLayerLink ..endHandleLayerLink = endHandleLayerLink
..showCursor = showCursor ..showCursor = showCursor
..forceLine = forceLine
..readOnly = readOnly
..hasFocus = hasFocus ..hasFocus = hasFocus
..maxLines = maxLines ..maxLines = maxLines
..minLines = minLines ..minLines = minLines
...@@ -1812,6 +1855,7 @@ class _Editable extends LeafRenderObjectWidget { ...@@ -1812,6 +1855,7 @@ class _Editable extends LeafRenderObjectWidget {
..onSelectionChanged = onSelectionChanged ..onSelectionChanged = onSelectionChanged
..onCaretChanged = onCaretChanged ..onCaretChanged = onCaretChanged
..ignorePointer = rendererIgnoresPointer ..ignorePointer = rendererIgnoresPointer
..textWidthBasis = textWidthBasis
..obscureText = obscureText ..obscureText = obscureText
..cursorWidth = cursorWidth ..cursorWidth = cursorWidth
..cursorRadius = cursorRadius ..cursorRadius = cursorRadius
......
...@@ -973,7 +973,7 @@ void main() { ...@@ -973,7 +973,7 @@ void main() {
final RenderEditable renderEditable = findRenderEditable(tester); final RenderEditable renderEditable = findRenderEditable(tester);
// There should be no composing. // There should be no composing.
expect(renderEditable.isComposingText, false); expect(renderEditable.text, TextSpan(text:'readonly', style: renderEditable.text.style));
}); });
testWidgets('Dynamically switching between read only and not read only should hide or show collapse cursor', (WidgetTester tester) async { testWidgets('Dynamically switching between read only and not read only should hide or show collapse cursor', (WidgetTester tester) async {
...@@ -3231,6 +3231,30 @@ void main() { ...@@ -3231,6 +3231,30 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Read only TextField identifies as read only text field in semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: Center(
child: TextField(
maxLength: 10,
readOnly: true,
),
),
),
),
);
expect(
semantics,
includesNodeWith(flags: <SemanticsFlag>[SemanticsFlag.isTextField, SemanticsFlag.isReadOnly])
);
semantics.dispose();
});
void sendFakeKeyEvent(Map<String, dynamic> data) { void sendFakeKeyEvent(Map<String, dynamic> data) {
defaultBinaryMessenger.handlePlatformMessage( defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.keyEvent.name, SystemChannels.keyEvent.name,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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