[fix]: cursorConnt.color notify the text_line to repaint if it was disposed

pull/427/head
appflowy 4 years ago
parent d1320c4236
commit c929bb1ae3
  1. 200
      lib/src/widgets/text_line.dart

@ -56,16 +56,8 @@ class TextLine extends StatelessWidget {
strutStyle: strutStyle, strutStyle: strutStyle,
textScaleFactor: MediaQuery.textScaleFactorOf(context), textScaleFactor: MediaQuery.textScaleFactorOf(context),
); );
return RichTextProxy( return RichTextProxy(child, textSpan.style!, textAlign, textDirection!, 1, Localizations.localeOf(context),
child, strutStyle, TextWidthBasis.parent, null);
textSpan.style!,
textAlign,
textDirection!,
1,
Localizations.localeOf(context),
strutStyle,
TextWidthBasis.parent,
null);
} }
InlineSpan _getTextSpanForWholeLine(BuildContext context) { InlineSpan _getTextSpanForWholeLine(BuildContext context) {
@ -84,8 +76,7 @@ class TextLine extends StatelessWidget {
textNodes = LinkedList<Node>(); textNodes = LinkedList<Node>();
} }
// Here it should be image // Here it should be image
final embed = WidgetSpan( final embed = WidgetSpan(child: EmbedProxy(embedBuilder(context, child, readOnly)));
child: EmbedProxy(embedBuilder(context, child, readOnly)));
textSpanChildren.add(embed); textSpanChildren.add(embed);
continue; continue;
} }
@ -115,11 +106,8 @@ class TextLine extends StatelessWidget {
return TextAlign.start; return TextAlign.start;
} }
TextSpan _buildTextSpan(DefaultStyles defaultStyles, LinkedList<Node> nodes, TextSpan _buildTextSpan(DefaultStyles defaultStyles, LinkedList<Node> nodes, TextStyle lineStyle) {
TextStyle lineStyle) { final children = nodes.map((node) => _getTextSpanFromNode(defaultStyles, node)).toList(growable: false);
final children = nodes
.map((node) => _getTextSpanFromNode(defaultStyles, node))
.toList(growable: false);
return TextSpan(children: children, style: lineStyle); return TextSpan(children: children, style: lineStyle);
} }
@ -163,8 +151,7 @@ class TextLine extends StatelessWidget {
return textStyle; return textStyle;
} }
TextStyle _applyCustomAttributes( TextStyle _applyCustomAttributes(TextStyle textStyle, Map<String, Attribute> attributes) {
TextStyle textStyle, Map<String, Attribute> attributes) {
if (customStyleBuilder == null) { if (customStyleBuilder == null) {
return textStyle; return textStyle;
} }
@ -200,8 +187,7 @@ class TextLine extends StatelessWidget {
if (color?.value is String) { if (color?.value is String) {
textColor = stringToColor(color?.value); textColor = stringToColor(color?.value);
} }
res = _merge(res.copyWith(decorationColor: textColor), res = _merge(res.copyWith(decorationColor: textColor), s!.copyWith(decorationColor: textColor));
s!.copyWith(decorationColor: textColor));
} else { } else {
res = _merge(res, s!); res = _merge(res, s!);
} }
@ -263,9 +249,7 @@ class TextLine extends StatelessWidget {
if (b.decoration != null) { if (b.decoration != null) {
decorations.add(b.decoration); decorations.add(b.decoration);
} }
return a.merge(b).apply( return a.merge(b).apply(decoration: TextDecoration.combine(List.castFrom<dynamic, TextDecoration>(decorations)));
decoration: TextDecoration.combine(
List.castFrom<dynamic, TextDecoration>(decorations)));
} }
} }
@ -305,21 +289,12 @@ class EditableTextLine extends RenderObjectWidget {
@override @override
RenderObject createRenderObject(BuildContext context) { RenderObject createRenderObject(BuildContext context) {
return RenderEditableTextLine( return RenderEditableTextLine(line, textDirection, textSelection, enableInteractiveSelection, hasFocus,
line, devicePixelRatio, _getPadding(), color, cursorCont);
textDirection,
textSelection,
enableInteractiveSelection,
hasFocus,
devicePixelRatio,
_getPadding(),
color,
cursorCont);
} }
@override @override
void updateRenderObject( void updateRenderObject(BuildContext context, covariant RenderEditableTextLine renderObject) {
BuildContext context, covariant RenderEditableTextLine renderObject) {
renderObject renderObject
..setLine(line) ..setLine(line)
..setPadding(_getPadding()) ..setPadding(_getPadding())
@ -333,10 +308,7 @@ class EditableTextLine extends RenderObjectWidget {
} }
EdgeInsetsGeometry _getPadding() { EdgeInsetsGeometry _getPadding() {
return EdgeInsetsDirectional.only( return EdgeInsetsDirectional.only(start: indentWidth, top: verticalSpacing.item1, bottom: verticalSpacing.item2);
start: indentWidth,
top: verticalSpacing.item1,
bottom: verticalSpacing.item2);
} }
} }
@ -413,7 +385,7 @@ class RenderEditableTextLine extends RenderEditableBox {
color = c; color = c;
if (containsTextSelection()) { if (containsTextSelection()) {
markNeedsPaint(); safeMarkNeedsPaint();
} }
} }
@ -425,7 +397,7 @@ class RenderEditableTextLine extends RenderEditableBox {
final containsSelection = containsTextSelection(); final containsSelection = containsTextSelection();
if (attached && containsCursor()) { if (attached && containsCursor()) {
cursorCont.removeListener(markNeedsLayout); cursorCont.removeListener(markNeedsLayout);
cursorCont.color.removeListener(markNeedsPaint); cursorCont.color.removeListener(safeMarkNeedsPaint);
} }
textSelection = t; textSelection = t;
@ -433,11 +405,11 @@ class RenderEditableTextLine extends RenderEditableBox {
_containsCursor = null; _containsCursor = null;
if (attached && containsCursor()) { if (attached && containsCursor()) {
cursorCont.addListener(markNeedsLayout); cursorCont.addListener(markNeedsLayout);
cursorCont.color.addListener(markNeedsPaint); cursorCont.color.addListener(safeMarkNeedsPaint);
} }
if (containsSelection || containsTextSelection()) { if (containsSelection || containsTextSelection()) {
markNeedsPaint(); safeMarkNeedsPaint();
} }
} }
@ -478,17 +450,14 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
bool containsTextSelection() { bool containsTextSelection() {
return line.documentOffset <= textSelection.end && return line.documentOffset <= textSelection.end && textSelection.start <= line.documentOffset + line.length - 1;
textSelection.start <= line.documentOffset + line.length - 1;
} }
bool containsCursor() { bool containsCursor() {
return _containsCursor ??= textSelection.isCollapsed && return _containsCursor ??= textSelection.isCollapsed && line.containsOffset(textSelection.baseOffset);
line.containsOffset(textSelection.baseOffset);
} }
RenderBox? _updateChild( RenderBox? _updateChild(RenderBox? old, RenderBox? newChild, TextLineSlot slot) {
RenderBox? old, RenderBox? newChild, TextLineSlot slot) {
if (old != null) { if (old != null) {
dropChild(old); dropChild(old);
children.remove(slot); children.remove(slot);
@ -527,46 +496,35 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
@override @override
TextSelectionPoint getExtentEndpointForSelection( TextSelectionPoint getExtentEndpointForSelection(TextSelection textSelection) {
TextSelection textSelection) {
return _getEndpointForSelection(textSelection, false); return _getEndpointForSelection(textSelection, false);
} }
TextSelectionPoint _getEndpointForSelection( TextSelectionPoint _getEndpointForSelection(TextSelection textSelection, bool first) {
TextSelection textSelection, bool first) {
if (textSelection.isCollapsed) { if (textSelection.isCollapsed) {
return TextSelectionPoint( return TextSelectionPoint(
Offset(0, preferredLineHeight(textSelection.extent)) + Offset(0, preferredLineHeight(textSelection.extent)) + getOffsetForCaret(textSelection.extent), null);
getOffsetForCaret(textSelection.extent),
null);
} }
final boxes = _getBoxes(textSelection); final boxes = _getBoxes(textSelection);
assert(boxes.isNotEmpty); assert(boxes.isNotEmpty);
final targetBox = first ? boxes.first : boxes.last; final targetBox = first ? boxes.first : boxes.last;
return TextSelectionPoint( return TextSelectionPoint(Offset(first ? targetBox.start : targetBox.end, targetBox.bottom), targetBox.direction);
Offset(first ? targetBox.start : targetBox.end, targetBox.bottom),
targetBox.direction);
} }
@override @override
TextRange getLineBoundary(TextPosition position) { TextRange getLineBoundary(TextPosition position) {
final lineDy = getOffsetForCaret(position) final lineDy = getOffsetForCaret(position).translate(0, 0.5 * preferredLineHeight(position)).dy;
.translate(0, 0.5 * preferredLineHeight(position)) final lineBoxes = _getBoxes(TextSelection(baseOffset: 0, extentOffset: line.length - 1))
.dy; .where((element) => element.top < lineDy && element.bottom > lineDy)
final lineBoxes = .toList(growable: false);
_getBoxes(TextSelection(baseOffset: 0, extentOffset: line.length - 1))
.where((element) => element.top < lineDy && element.bottom > lineDy)
.toList(growable: false);
return TextRange( return TextRange(
start: start: getPositionForOffset(Offset(lineBoxes.first.left, lineDy)).offset,
getPositionForOffset(Offset(lineBoxes.first.left, lineDy)).offset,
end: getPositionForOffset(Offset(lineBoxes.last.right, lineDy)).offset); end: getPositionForOffset(Offset(lineBoxes.last.right, lineDy)).offset);
} }
@override @override
Offset getOffsetForCaret(TextPosition position) { Offset getOffsetForCaret(TextPosition position) {
return _body!.getOffsetForCaret(position, _caretPrototype) + return _body!.getOffsetForCaret(position, _caretPrototype) + (_body!.parentData as BoxParentData).offset;
(_body!.parentData as BoxParentData).offset;
} }
@override @override
@ -581,10 +539,8 @@ class RenderEditableTextLine extends RenderEditableBox {
TextPosition? _getPosition(TextPosition textPosition, double dyScale) { TextPosition? _getPosition(TextPosition textPosition, double dyScale) {
assert(textPosition.offset < line.length); assert(textPosition.offset < line.length);
final offset = getOffsetForCaret(textPosition) final offset = getOffsetForCaret(textPosition).translate(0, dyScale * preferredLineHeight(textPosition));
.translate(0, dyScale * preferredLineHeight(textPosition)); if (_body!.size.contains(offset - (_body!.parentData as BoxParentData).offset)) {
if (_body!.size
.contains(offset - (_body!.parentData as BoxParentData).offset)) {
return getPositionForOffset(offset); return getPositionForOffset(offset);
} }
return null; return null;
@ -592,8 +548,7 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
TextPosition getPositionForOffset(Offset offset) { TextPosition getPositionForOffset(Offset offset) {
return _body!.getPositionForOffset( return _body!.getPositionForOffset(offset - (_body!.parentData as BoxParentData).offset);
offset - (_body!.parentData as BoxParentData).offset);
} }
@override @override
@ -613,9 +568,7 @@ class RenderEditableTextLine extends RenderEditableBox {
double get cursorWidth => cursorCont.style.width; double get cursorWidth => cursorCont.style.width;
double get cursorHeight => double get cursorHeight => cursorCont.style.height ?? preferredLineHeight(const TextPosition(offset: 0));
cursorCont.style.height ??
preferredLineHeight(const TextPosition(offset: 0));
void _computeCaretPrototype() { void _computeCaretPrototype() {
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
@ -642,7 +595,7 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
if (containsCursor()) { if (containsCursor()) {
cursorCont.addListener(markNeedsLayout); cursorCont.addListener(markNeedsLayout);
cursorCont.color.addListener(markNeedsPaint); cursorCont.color.addListener(safeMarkNeedsPaint);
} }
} }
@ -654,7 +607,7 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
if (containsCursor()) { if (containsCursor()) {
cursorCont.removeListener(markNeedsLayout); cursorCont.removeListener(markNeedsLayout);
cursorCont.color.removeListener(markNeedsPaint); cursorCont.color.removeListener(safeMarkNeedsPaint);
} }
} }
@ -690,14 +643,8 @@ class RenderEditableTextLine extends RenderEditableBox {
_resolvePadding(); _resolvePadding();
final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right; final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom; final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
final leadingWidth = _leading == null final leadingWidth = _leading == null ? 0 : _leading!.getMinIntrinsicWidth(height - verticalPadding).ceil();
? 0 final bodyWidth = _body == null ? 0 : _body!.getMinIntrinsicWidth(math.max(0, height - verticalPadding)).ceil();
: _leading!.getMinIntrinsicWidth(height - verticalPadding).ceil();
final bodyWidth = _body == null
? 0
: _body!
.getMinIntrinsicWidth(math.max(0, height - verticalPadding))
.ceil();
return horizontalPadding + leadingWidth + bodyWidth; return horizontalPadding + leadingWidth + bodyWidth;
} }
@ -706,14 +653,8 @@ class RenderEditableTextLine extends RenderEditableBox {
_resolvePadding(); _resolvePadding();
final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right; final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom; final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
final leadingWidth = _leading == null final leadingWidth = _leading == null ? 0 : _leading!.getMaxIntrinsicWidth(height - verticalPadding).ceil();
? 0 final bodyWidth = _body == null ? 0 : _body!.getMaxIntrinsicWidth(math.max(0, height - verticalPadding)).ceil();
: _leading!.getMaxIntrinsicWidth(height - verticalPadding).ceil();
final bodyWidth = _body == null
? 0
: _body!
.getMaxIntrinsicWidth(math.max(0, height - verticalPadding))
.ceil();
return horizontalPadding + leadingWidth + bodyWidth; return horizontalPadding + leadingWidth + bodyWidth;
} }
@ -723,9 +664,7 @@ class RenderEditableTextLine extends RenderEditableBox {
final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right; final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom; final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (_body != null) { if (_body != null) {
return _body! return _body!.getMinIntrinsicHeight(math.max(0, width - horizontalPadding)) + verticalPadding;
.getMinIntrinsicHeight(math.max(0, width - horizontalPadding)) +
verticalPadding;
} }
return verticalPadding; return verticalPadding;
} }
@ -736,9 +675,7 @@ class RenderEditableTextLine extends RenderEditableBox {
final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right; final horizontalPadding = _resolvedPadding!.left + _resolvedPadding!.right;
final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom; final verticalPadding = _resolvedPadding!.top + _resolvedPadding!.bottom;
if (_body != null) { if (_body != null) {
return _body! return _body!.getMaxIntrinsicHeight(math.max(0, width - horizontalPadding)) + verticalPadding;
.getMaxIntrinsicHeight(math.max(0, width - horizontalPadding)) +
verticalPadding;
} }
return verticalPadding; return verticalPadding;
} }
@ -746,8 +683,7 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
double computeDistanceToActualBaseline(TextBaseline baseline) { double computeDistanceToActualBaseline(TextBaseline baseline) {
_resolvePadding(); _resolvePadding();
return _body!.getDistanceToActualBaseline(baseline)! + return _body!.getDistanceToActualBaseline(baseline)! + _resolvedPadding!.top;
_resolvedPadding!.top;
} }
@override @override
@ -767,22 +703,16 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
final innerConstraints = constraints.deflate(_resolvedPadding!); final innerConstraints = constraints.deflate(_resolvedPadding!);
final indentWidth = textDirection == TextDirection.ltr final indentWidth = textDirection == TextDirection.ltr ? _resolvedPadding!.left : _resolvedPadding!.right;
? _resolvedPadding!.left
: _resolvedPadding!.right;
_body!.layout(innerConstraints, parentUsesSize: true); _body!.layout(innerConstraints, parentUsesSize: true);
(_body!.parentData as BoxParentData).offset = (_body!.parentData as BoxParentData).offset = Offset(_resolvedPadding!.left, _resolvedPadding!.top);
Offset(_resolvedPadding!.left, _resolvedPadding!.top);
if (_leading != null) { if (_leading != null) {
final leadingConstraints = innerConstraints.copyWith( final leadingConstraints =
minWidth: indentWidth, innerConstraints.copyWith(minWidth: indentWidth, maxWidth: indentWidth, maxHeight: _body!.size.height);
maxWidth: indentWidth,
maxHeight: _body!.size.height);
_leading!.layout(leadingConstraints, parentUsesSize: true); _leading!.layout(leadingConstraints, parentUsesSize: true);
(_leading!.parentData as BoxParentData).offset = (_leading!.parentData as BoxParentData).offset = Offset(0, _resolvedPadding!.top);
Offset(0, _resolvedPadding!.top);
} }
size = constraints.constrain(Size( size = constraints.constrain(Size(
@ -822,19 +752,13 @@ class RenderEditableTextLine extends RenderEditableBox {
_paintSelection(context, effectiveOffset); _paintSelection(context, effectiveOffset);
} }
if (hasFocus && if (hasFocus && cursorCont.show.value && containsCursor() && !cursorCont.style.paintAboveText) {
cursorCont.show.value &&
containsCursor() &&
!cursorCont.style.paintAboveText) {
_paintCursor(context, effectiveOffset, line.hasEmbed); _paintCursor(context, effectiveOffset, line.hasEmbed);
} }
context.paintChild(_body!, effectiveOffset); context.paintChild(_body!, effectiveOffset);
if (hasFocus && if (hasFocus && cursorCont.show.value && containsCursor() && cursorCont.style.paintAboveText) {
cursorCont.show.value &&
containsCursor() &&
cursorCont.style.paintAboveText) {
_paintCursor(context, effectiveOffset, line.hasEmbed); _paintCursor(context, effectiveOffset, line.hasEmbed);
} }
} }
@ -848,14 +772,12 @@ class RenderEditableTextLine extends RenderEditableBox {
} }
} }
void _paintCursor( void _paintCursor(PaintingContext context, Offset effectiveOffset, bool lineHasEmbed) {
PaintingContext context, Offset effectiveOffset, bool lineHasEmbed) {
final position = TextPosition( final position = TextPosition(
offset: textSelection.extentOffset - line.documentOffset, offset: textSelection.extentOffset - line.documentOffset,
affinity: textSelection.base.affinity, affinity: textSelection.base.affinity,
); );
_cursorPainter.paint( _cursorPainter.paint(context.canvas, effectiveOffset, position, lineHasEmbed);
context.canvas, effectiveOffset, position, lineHasEmbed);
} }
@override @override
@ -866,8 +788,7 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
Rect getLocalRectForCaret(TextPosition position) { Rect getLocalRectForCaret(TextPosition position) {
final caretOffset = getOffsetForCaret(position); final caretOffset = getOffsetForCaret(position);
var rect = var rect = Rect.fromLTWH(0, 0, cursorWidth, cursorHeight).shift(caretOffset);
Rect.fromLTWH(0, 0, cursorWidth, cursorHeight).shift(caretOffset);
final cursorOffset = cursorCont.style.offset; final cursorOffset = cursorCont.style.offset;
// Add additional cursor offset (generally only if on iOS). // Add additional cursor offset (generally only if on iOS).
if (cursorOffset != null) rect = rect.shift(cursorOffset); if (cursorOffset != null) rect = rect.shift(cursorOffset);
@ -876,13 +797,20 @@ class RenderEditableTextLine extends RenderEditableBox {
@override @override
TextPosition globalToLocalPosition(TextPosition position) { TextPosition globalToLocalPosition(TextPosition position) {
assert(getContainer().containsOffset(position.offset), assert(getContainer().containsOffset(position.offset), 'The provided text position is not in the current node');
'The provided text position is not in the current node');
return TextPosition( return TextPosition(
offset: position.offset - getContainer().documentOffset, offset: position.offset - getContainer().documentOffset,
affinity: position.affinity, affinity: position.affinity,
); );
} }
void safesafeMarkNeedsPaint() {
if (!attached) {
//Should not paint if it was unattach.
return;
}
markNeedsPaint();
}
} }
class _TextLineElement extends RenderObjectElement { class _TextLineElement extends RenderObjectElement {
@ -894,8 +822,7 @@ class _TextLineElement extends RenderObjectElement {
EditableTextLine get widget => super.widget as EditableTextLine; EditableTextLine get widget => super.widget as EditableTextLine;
@override @override
RenderEditableTextLine get renderObject => RenderEditableTextLine get renderObject => super.renderObject as RenderEditableTextLine;
super.renderObject as RenderEditableTextLine;
@override @override
void visitChildren(ElementVisitor visitor) { void visitChildren(ElementVisitor visitor) {
@ -942,8 +869,7 @@ class _TextLineElement extends RenderObjectElement {
} }
@override @override
void moveRenderObjectChild( void moveRenderObjectChild(RenderObject child, dynamic oldSlot, dynamic newSlot) {
RenderObject child, dynamic oldSlot, dynamic newSlot) {
throw UnimplementedError(); throw UnimplementedError();
} }

Loading…
Cancel
Save