Implement RenderEditableTextBlock class

pull/13/head
singerdmx 4 years ago
parent 9bce3f94fe
commit ecbede7793
  1. 12
      lib/widgets/editor.dart
  2. 374
      lib/widgets/text_block.dart
  3. 14
      lib/widgets/text_selection.dart

@ -6,7 +6,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_quill/models/documents/document.dart'; import 'package:flutter_quill/models/documents/document.dart';
import 'package:flutter_quill/models/documents/nodes/container.dart' import 'package:flutter_quill/models/documents/nodes/container.dart'
as container; as containerNode;
import 'package:flutter_quill/models/documents/nodes/node.dart'; import 'package:flutter_quill/models/documents/nodes/node.dart';
import 'package:flutter_quill/utils/diff_delta.dart'; import 'package:flutter_quill/utils/diff_delta.dart';
import 'package:flutter_quill/widgets/default_styles.dart'; import 'package:flutter_quill/widgets/default_styles.dart';
@ -1010,7 +1010,7 @@ class RenderEditableContainerBox extends RenderBox
EditableContainerParentData>, EditableContainerParentData>,
RenderBoxContainerDefaultsMixin<RenderEditableBox, RenderBoxContainerDefaultsMixin<RenderEditableBox,
EditableContainerParentData> { EditableContainerParentData> {
container.Container _container; containerNode.Container _container;
TextDirection _textDirection; TextDirection _textDirection;
EdgeInsetsGeometry _padding; EdgeInsetsGeometry _padding;
EdgeInsets _resolvedPadding; EdgeInsets _resolvedPadding;
@ -1024,7 +1024,11 @@ class RenderEditableContainerBox extends RenderBox
addAll(children); addAll(children);
} }
setContainer(container.Container c) { containerNode.Container getContainer() {
return _container;
}
setContainer(containerNode.Container c) {
assert(c != null); assert(c != null);
if (_container == c) { if (_container == c) {
return; return;
@ -1052,6 +1056,8 @@ class RenderEditableContainerBox extends RenderBox
_markNeedsPaddingResolution(); _markNeedsPaddingResolution();
} }
EdgeInsets get resolvedPadding => _resolvedPadding;
_resolvePadding() { _resolvePadding() {
if (_resolvedPadding != null) { if (_resolvedPadding != null) {
return; return;

@ -1,8 +1,14 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_quill/models/documents/nodes/block.dart'; import 'package:flutter_quill/models/documents/nodes/block.dart';
import 'package:flutter_quill/models/documents/nodes/node.dart';
import 'package:flutter_quill/widgets/text_selection.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import 'box.dart';
import 'delegate.dart'; import 'delegate.dart';
import 'editor.dart';
class EditableTextBlock extends StatelessWidget { class EditableTextBlock extends StatelessWidget {
final Block block; final Block block;
@ -30,12 +36,370 @@ class EditableTextBlock extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// TODO: implement build assert(debugCheckHasMediaQuery(context));
throw UnimplementedError(); throw UnimplementedError();
} }
} }
//class RenderEditableTextBlock extends RenderEditableContainerBox class RenderEditableTextBlock extends RenderEditableContainerBox
// implements RenderEditableBox { implements RenderEditableBox {
// RenderEditableTextBlock({
// } List<RenderEditableBox> children,
@required Block block,
@required TextDirection textDirection,
@required EdgeInsetsGeometry padding,
@required Decoration decoration,
ImageConfiguration configuration = ImageConfiguration.empty,
EdgeInsets contentPadding = EdgeInsets.zero,
}) : assert(block != null),
assert(textDirection != null),
assert(decoration != null),
assert(padding != null),
assert(contentPadding != null),
_decoration = decoration,
_configuration = configuration,
_savedPadding = padding,
_contentPadding = contentPadding,
super(
children,
block,
textDirection,
padding.add(contentPadding),
);
EdgeInsetsGeometry _savedPadding;
EdgeInsets _contentPadding;
set contentPadding(EdgeInsets value) {
assert(value != null);
if (_contentPadding == value) return;
_contentPadding = value;
super.setPadding(_savedPadding.add(_contentPadding));
}
@override
setPadding(EdgeInsetsGeometry value) {
super.setPadding(value.add(_contentPadding));
_savedPadding = value;
}
BoxPainter _painter;
Decoration get decoration => _decoration;
Decoration _decoration;
set decoration(Decoration value) {
assert(value != null);
if (value == _decoration) return;
_painter?.dispose();
_painter = null;
_decoration = value;
markNeedsPaint();
}
ImageConfiguration get configuration => _configuration;
ImageConfiguration _configuration;
set configuration(ImageConfiguration value) {
assert(value != null);
if (value == _configuration) return;
_configuration = value;
markNeedsPaint();
}
@override
TextRange getLineBoundary(TextPosition position) {
RenderEditableBox child = childAtPosition(position);
TextRange rangeInChild = child.getLineBoundary(TextPosition(
offset: position.offset - child.getContainer().getOffset(),
affinity: position.affinity,
));
return TextRange(
start: rangeInChild.start + child.getContainer().getOffset(),
end: rangeInChild.end + child.getContainer().getOffset(),
);
}
@override
Offset getOffsetForCaret(TextPosition position) {
RenderEditableBox child = childAtPosition(position);
return child.getOffsetForCaret(TextPosition(
offset: position.offset - child.getContainer().getOffset(),
affinity: position.affinity,
)) +
(child.parentData as BoxParentData).offset;
}
@override
TextPosition getPositionForOffset(Offset offset) {
RenderEditableBox child = childAtOffset(offset);
BoxParentData parentData = child.parentData;
TextPosition localPosition =
child.getPositionForOffset(offset - parentData.offset);
return TextPosition(
offset: localPosition.offset + child.getContainer().getOffset(),
affinity: localPosition.affinity,
);
}
@override
TextRange getWordBoundary(TextPosition position) {
RenderEditableBox child = childAtPosition(position);
int nodeOffset = child.getContainer().getOffset();
TextRange childWord = child
.getWordBoundary(TextPosition(offset: position.offset - nodeOffset));
return TextRange(
start: childWord.start + nodeOffset,
end: childWord.end + nodeOffset,
);
}
@override
TextPosition getPositionAbove(TextPosition position) {
assert(position.offset < getContainer().length);
RenderEditableBox child = childAtPosition(position);
TextPosition childLocalPosition = TextPosition(
offset: position.offset - child.getContainer().getOffset());
TextPosition result = child.getPositionAbove(childLocalPosition);
if (result != null) {
return TextPosition(
offset: result.offset + child.getContainer().getOffset());
}
RenderEditableBox sibling = childBefore(child);
if (sibling == null) {
return null;
}
Offset caretOffset = child.getOffsetForCaret(childLocalPosition);
TextPosition testPosition =
TextPosition(offset: sibling.getContainer().length - 1);
Offset testOffset = sibling.getOffsetForCaret(testPosition);
Offset finalOffset = Offset(caretOffset.dx, testOffset.dy);
return TextPosition(
offset: sibling.getContainer().getOffset() +
sibling.getPositionForOffset(finalOffset).offset);
}
@override
TextPosition getPositionBelow(TextPosition position) {
assert(position.offset < getContainer().length);
RenderEditableBox child = childAtPosition(position);
TextPosition childLocalPosition = TextPosition(
offset: position.offset - child.getContainer().getOffset());
TextPosition result = child.getPositionBelow(childLocalPosition);
if (result != null) {
return TextPosition(
offset: result.offset + child.getContainer().getOffset());
}
RenderEditableBox sibling = childAfter(child);
if (sibling == null) {
return null;
}
Offset caretOffset = child.getOffsetForCaret(childLocalPosition);
Offset testOffset = sibling.getOffsetForCaret(TextPosition(offset: 0));
Offset finalOffset = Offset(caretOffset.dx, testOffset.dy);
return TextPosition(
offset: sibling.getContainer().getOffset() +
sibling.getPositionForOffset(finalOffset).offset);
}
@override
double preferredLineHeight(TextPosition position) {
RenderEditableBox child = childAtPosition(position);
return child.preferredLineHeight(TextPosition(
offset: position.offset - child.getContainer().getOffset()));
}
@override
TextSelectionPoint getBaseEndpointForSelection(TextSelection selection) {
if (selection.isCollapsed) {
return TextSelectionPoint(
Offset(0.0, preferredLineHeight(selection.extent)) +
getOffsetForCaret(selection.extent),
null);
}
Node baseNode = getContainer().queryChild(selection.start, false).node;
var baseChild = firstChild;
while (baseChild != null) {
if (baseChild.getContainer() == baseNode) {
break;
}
baseChild = childAfter(baseChild);
}
assert(baseChild != null);
TextSelectionPoint basePoint = baseChild.getBaseEndpointForSelection(
localSelection(baseChild.getContainer(), selection, true));
return TextSelectionPoint(
basePoint.point + (baseChild.parentData as BoxParentData).offset,
basePoint.direction);
}
@override
TextSelectionPoint getExtentEndpointForSelection(TextSelection selection) {
if (selection.isCollapsed) {
return TextSelectionPoint(
Offset(0.0, preferredLineHeight(selection.extent)) +
getOffsetForCaret(selection.extent),
null);
}
Node extentNode = getContainer().queryChild(selection.end, false).node;
var extentChild = firstChild;
while (extentChild != null) {
if (extentChild.getContainer() == extentNode) {
break;
}
extentChild = childAfter(extentChild);
}
assert(extentChild != null);
TextSelectionPoint extentPoint = extentChild.getExtentEndpointForSelection(
localSelection(extentChild.getContainer(), selection, true));
return TextSelectionPoint(
extentPoint.point + (extentChild.parentData as BoxParentData).offset,
extentPoint.direction);
}
@override
void detach() {
_painter?.dispose();
_painter = null;
super.detach();
markNeedsPaint();
}
@override
paint(PaintingContext context, Offset offset) {
_paintDecoration(context, offset);
defaultPaint(context, offset);
}
_paintDecoration(PaintingContext context, Offset offset) {
assert(size.width != null);
assert(size.height != null);
_painter ??= _decoration.createBoxPainter(markNeedsPaint);
EdgeInsets decorationPadding = resolvedPadding - _contentPadding;
ImageConfiguration filledConfiguration =
configuration.copyWith(size: decorationPadding.deflateSize(size));
int debugSaveCount = context.canvas.getSaveCount();
final decorationOffset =
offset.translate(decorationPadding.left, decorationPadding.top);
_painter.paint(context.canvas, decorationOffset, filledConfiguration);
if (debugSaveCount != context.canvas.getSaveCount()) {
throw ('${_decoration.runtimeType} painter had mismatching save and restore calls.');
}
if (decoration.isComplex) {
context.setIsComplexHint();
}
}
@override
bool hitTestChildren(BoxHitTestResult result, {Offset position}) {
return defaultHitTestChildren(result, position: position);
}
}
class _EditableBlock extends MultiChildRenderObjectWidget {
final Block block;
final TextDirection textDirection;
final Tuple2<double, double> padding;
final Decoration decoration;
final EdgeInsets contentPadding;
_EditableBlock(this.block, this.textDirection, this.padding, this.decoration,
this.contentPadding, List<Widget> children)
: assert(block != null),
assert(textDirection != null),
assert(padding != null),
assert(decoration != null),
assert(children != null),
super(children: children);
EdgeInsets get _padding =>
EdgeInsets.only(top: padding.item1, bottom: padding.item2);
EdgeInsets get _contentPadding => contentPadding ?? EdgeInsets.zero;
@override
RenderEditableTextBlock createRenderObject(BuildContext context) {
return RenderEditableTextBlock(
block: block,
textDirection: textDirection,
padding: _padding,
decoration: decoration,
contentPadding: _contentPadding,
);
}
@override
void updateRenderObject(
BuildContext context, covariant RenderEditableTextBlock renderObject) {
renderObject.setContainer(block);
renderObject.setTextDirection(textDirection);
renderObject.setPadding(_padding);
renderObject.decoration = decoration;
renderObject.contentPadding = _contentPadding;
}
}
class _NumberPoint extends StatelessWidget {
final int index;
final int count;
final TextStyle style;
final double width;
final bool withDot;
final double padding;
const _NumberPoint({
Key key,
@required this.index,
@required this.count,
@required this.style,
@required this.width,
this.withDot = true,
this.padding = 0.0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: AlignmentDirectional.topEnd,
child: Text(withDot ? '$index.' : '$index', style: style),
width: width,
padding: EdgeInsetsDirectional.only(end: padding),
);
}
}
class _BulletPoint extends StatelessWidget {
final TextStyle style;
final double width;
const _BulletPoint({
Key key,
@required this.style,
@required this.width,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: AlignmentDirectional.topEnd,
child: Text('', style: style),
width: width,
padding: EdgeInsetsDirectional.only(end: 13.0),
);
}
}

@ -1,9 +1,22 @@
import 'dart:math' as math;
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_quill/models/documents/nodes/node.dart';
import 'editor.dart'; import 'editor.dart';
TextSelection localSelection(Node node, TextSelection selection, fromParent) {
int base = fromParent ? node.getOffset() : node.getDocumentOffset();
assert(base <= selection.end && selection.start <= base + node.length - 1);
int offset = fromParent ? node.getOffset() : node.getDocumentOffset();
return selection.copyWith(
baseOffset: math.max(selection.start - offset, 0),
extentOffset: math.min(selection.end - offset, node.length - 1));
}
class EditorTextSelectionOverlay { class EditorTextSelectionOverlay {
TextEditingValue value; TextEditingValue value;
bool handlesVisible = false; bool handlesVisible = false;
@ -103,5 +116,4 @@ class EditorTextSelectionOverlay {
hide(); hide();
_toolbarController.dispose(); _toolbarController.dispose();
} }
} }

Loading…
Cancel
Save