Unverified Commit e70a1d1d authored by Gary Qian's avatar Gary Qian Committed by GitHub

Support WidgetSpan in RenderEditable (#83537)

parent 18b15788
......@@ -18,7 +18,7 @@ import 'object.dart';
const String _kEllipsis = '\u2026';
/// Parent data for use with [RenderParagraph].
/// Parent data for use with [RenderParagraph] and [RenderEditable].
class TextParentData extends ContainerBoxParentData<RenderBox> {
/// The scaling of the text.
double? scale;
......@@ -434,14 +434,12 @@ class RenderParagraph extends RenderBox
@override
bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
// Hit test text spans.
late final bool hitText;
bool hitText = false;
final TextPosition textPosition = _textPainter.getPositionForOffset(position);
final InlineSpan? span = _textPainter.text!.getSpanForPosition(textPosition);
if (span != null && span is HitTestTarget) {
result.add(HitTestEntry(span as HitTestTarget));
hitText = true;
} else {
hitText = false;
}
// Hit test render object children
......@@ -545,17 +543,15 @@ class RenderParagraph extends RenderBox
);
childSize = child.size;
switch (_placeholderSpans[childIndex].alignment) {
case ui.PlaceholderAlignment.baseline: {
case ui.PlaceholderAlignment.baseline:
baselineOffset = child.getDistanceToBaseline(
_placeholderSpans[childIndex].baseline!,
);
break;
}
default: {
default:
baselineOffset = null;
break;
}
}
} else {
assert(_placeholderSpans[childIndex].alignment != ui.PlaceholderAlignment.baseline);
childSize = child.getDryLayout(boxConstraints);
......@@ -597,16 +593,14 @@ class RenderParagraph extends RenderBox
switch (span.alignment) {
case ui.PlaceholderAlignment.baseline:
case ui.PlaceholderAlignment.aboveBaseline:
case ui.PlaceholderAlignment.belowBaseline: {
case ui.PlaceholderAlignment.belowBaseline:
return false;
}
case ui.PlaceholderAlignment.top:
case ui.PlaceholderAlignment.middle:
case ui.PlaceholderAlignment.bottom: {
case ui.PlaceholderAlignment.bottom:
continue;
}
}
}
return true;
}
......
......@@ -31,6 +31,7 @@ import 'text.dart';
import 'text_editing_action.dart';
import 'text_selection.dart';
import 'ticker_provider.dart';
import 'widget_span.dart';
export 'package:flutter/services.dart' show SelectionChangedCause, TextEditingValue, TextSelection, TextInputType, SmartQuotesType, SmartDashesType;
......@@ -197,9 +198,8 @@ class TextEditingController extends ValueNotifier<TextEditingValue> {
if (!value.isComposingRangeValid || !withComposing) {
return TextSpan(style: style, text: text);
}
final TextStyle composingStyle = style!.merge(
const TextStyle(decoration: TextDecoration.underline),
);
final TextStyle composingStyle = style?.merge(const TextStyle(decoration: TextDecoration.underline))
?? const TextStyle(decoration: TextDecoration.underline);
return TextSpan(
style: style,
children: <TextSpan>[
......@@ -2651,7 +2651,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
key: _editableKey,
startHandleLayerLink: _startHandleLayerLink,
endHandleLayerLink: _endHandleLayerLink,
textSpan: buildTextSpan(),
inlineSpan: buildTextSpan(),
value: _value,
cursorColor: _cursorColor,
backgroundCursorColor: widget.backgroundCursorColor,
......@@ -2730,10 +2730,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
class _Editable extends LeafRenderObjectWidget {
const _Editable({
class _Editable extends MultiChildRenderObjectWidget {
_Editable({
Key? key,
required this.textSpan,
required this.inlineSpan,
required this.value,
required this.startHandleLayerLink,
required this.endHandleLayerLink,
......@@ -2778,9 +2778,22 @@ class _Editable extends LeafRenderObjectWidget {
required this.clipBehavior,
}) : assert(textDirection != null),
assert(rendererIgnoresPointer != null),
super(key: key);
super(key: key, children: _extractChildren(inlineSpan));
// Traverses the InlineSpan tree and depth-first collects the list of
// child widgets that are created in WidgetSpans.
static List<Widget> _extractChildren(InlineSpan span) {
final List<Widget> result = <Widget>[];
span.visitChildren((InlineSpan span) {
if (span is WidgetSpan) {
result.add(span.child);
}
return true;
});
return result;
}
final TextSpan textSpan;
final InlineSpan inlineSpan;
final TextEditingValue value;
final Color? cursorColor;
final LayerLink startHandleLayerLink;
......@@ -2827,7 +2840,7 @@ class _Editable extends LeafRenderObjectWidget {
@override
RenderEditable createRenderObject(BuildContext context) {
return RenderEditable(
text: textSpan,
text: inlineSpan,
cursorColor: cursorColor,
startHandleLayerLink: startHandleLayerLink,
endHandleLayerLink: endHandleLayerLink,
......@@ -2872,7 +2885,7 @@ class _Editable extends LeafRenderObjectWidget {
@override
void updateRenderObject(BuildContext context, RenderEditable renderObject) {
renderObject
..text = textSpan
..text = inlineSpan
..cursorColor = cursorColor
..startHandleLayerLink = startHandleLayerLink
..endHandleLayerLink = endHandleLayerLink
......
......@@ -1056,12 +1056,12 @@ void main() {
await tester.pump();
String editText = findRenderEditable(tester).text!.text!;
String editText = (findRenderEditable(tester).text! as TextSpan).text!;
expect(editText.substring(editText.length - 1), newChar);
await tester.pump(const Duration(seconds: 2));
editText = findRenderEditable(tester).text!.text!;
editText = (findRenderEditable(tester).text! as TextSpan).text!;
expect(editText.substring(editText.length - 1), '\u2022');
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }));
......@@ -1096,7 +1096,7 @@ void main() {
await tester.pump();
final String editText = findRenderEditable(tester).text!.text!;
final String editText = (findRenderEditable(tester).text! as TextSpan).text!;
expect(editText.substring(editText.length - 1), '\u2022');
}, variant: const TargetPlatformVariant(<TargetPlatform>{
TargetPlatform.macOS,
......
......@@ -2841,7 +2841,7 @@ void main() {
),
));
expect(findRenderEditable(tester).text!.text, expectedValue);
expect((findRenderEditable(tester).text! as TextSpan).text, expectedValue);
expect(
semantics,
......@@ -2906,7 +2906,7 @@ void main() {
));
final String expectedValue = obscuringCharacter * originalText.length;
expect(findRenderEditable(tester).text!.text, expectedValue);
expect((findRenderEditable(tester).text! as TextSpan).text, expectedValue);
});
group('a11y copy/cut/paste', () {
......@@ -3808,17 +3808,17 @@ void main() {
final RenderEditable renderEditable = findRenderEditable(tester);
// The actual text span is split into 3 parts with the middle part underlined.
expect(renderEditable.text!.children!.length, 3);
final TextSpan textSpan = renderEditable.text!.children![1] as TextSpan;
expect((renderEditable.text! as TextSpan).children!.length, 3);
final TextSpan textSpan = (renderEditable.text! as TextSpan).children![1] as TextSpan;
expect(textSpan.text, 'composing');
expect(textSpan.style!.decoration, TextDecoration.underline);
focusNode.unfocus();
await tester.pump();
expect(renderEditable.text!.children, isNull);
// Everything's just formatted the same way now.
expect(renderEditable.text!.text, 'text composing text');
expect((renderEditable.text! as TextSpan).children, isNull);
// Everything's just formated the same way now.
expect((renderEditable.text! as TextSpan).text, 'text composing text');
expect(renderEditable.text!.style!.decoration, isNull);
});
......@@ -6137,7 +6137,7 @@ void main() {
));
final RenderEditable renderEditable = findRenderEditable(tester);
final TextSpan textSpan = renderEditable.text!;
final TextSpan textSpan = renderEditable.text! as TextSpan;
expect(textSpan.style!.color, color);
});
});
......
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