Add comments from Zefyr to nodes folder

Since quite a lot of code was taken from Zefyr, the license should
be changed to BSD to meet the requirements of Zefyr.
pull/145/head
Till Friebe 4 years ago
parent 873bfbcee1
commit 8c663ad9f5
  1. 19
      lib/models/documents/nodes/block.dart
  2. 50
      lib/models/documents/nodes/container.dart
  3. 16
      lib/models/documents/nodes/embed.dart
  4. 108
      lib/models/documents/nodes/leaf.dart
  5. 139
      lib/models/documents/nodes/line.dart
  6. 75
      lib/models/documents/nodes/node.dart
  7. 18
      lib/widgets/editor.dart
  8. 9
      lib/widgets/raw_editor.dart
  9. 34
      lib/widgets/text_block.dart
  10. 10
      lib/widgets/text_line.dart
  11. 4
      lib/widgets/text_selection.dart

@ -3,7 +3,21 @@ import 'container.dart';
import 'line.dart';
import 'node.dart';
/// Represents a group of adjacent [Line]s with the same block style.
///
/// Block elements are:
/// - Blockquote
/// - Header
/// - Indent
/// - List
/// - Text Alignment
/// - Text Direction
/// - Code Block
class Block extends Container<Line?> {
/// Creates new unmounted [Block].
@override
Node newInstance() => Block();
@override
Line get defaultChild => Line();
@ -55,9 +69,4 @@ class Block extends Container<Line?> {
}
return buffer.toString();
}
@override
Node newInstance() {
return Block();
}
}

@ -1,48 +1,67 @@
import 'dart:collection';
import '../style.dart';
import 'leaf.dart';
import 'line.dart';
import 'node.dart';
/* Container of multiple nodes */
/// Container can accommodate other nodes.
///
/// Delegates insert, retain and delete operations to children nodes. For each
/// operation container looks for a child at specified index position and
/// forwards operation to that child.
///
/// Most of the operation handling logic is implemented by [Line] and [Text].
abstract class Container<T extends Node?> extends Node {
final LinkedList<Node> _children = LinkedList<Node>();
/// List of children.
LinkedList<Node> get children => _children;
/// Returns total number of child nodes in this container.
///
/// To get text length of this container see [length].
int get childCount => _children.length;
/// Returns the first child [Node].
Node get first => _children.first;
/// Returns the last child [Node].
Node get last => _children.last;
/// Returns `true` if this container has no child nodes.
bool get isEmpty => _children.isEmpty;
/// Returns `true` if this container has at least 1 child.
bool get isNotEmpty => _children.isNotEmpty;
/// abstract methods begin
/// Returns an instance of default child for this container node.
///
/// Always returns fresh instance.
T get defaultChild;
/// abstract methods end
/// Adds [node] to the end of this container children list.
void add(T node) {
assert(node?.parent == null);
node?.parent = this;
_children.add(node as Node);
}
/// Adds [node] to the beginning of this container children list.
void addFirst(T node) {
assert(node?.parent == null);
node?.parent = this;
_children.addFirst(node as Node);
}
/// Removes [node] from this container.
void remove(T node) {
assert(node?.parent == this);
node?.parent = null;
_children.remove(node as Node);
}
/// Moves children of this node to [newParent].
void moveChildToNewParent(Container? newParent) {
if (isEmpty) {
return;
@ -55,9 +74,19 @@ abstract class Container<T extends Node?> extends Node {
newParent.add(child);
}
/// In case [newParent] already had children we need to make sure
/// combined list is optimized.
if (last != null) last.adjust();
}
/// Queries the child [Node] at specified character [offset] in this container.
///
/// The result may contain the found node or `null` if no node is found
/// at specified offset.
///
/// [ChildQuery.offset] is set to relative offset within returned child node
/// which points at the same character position in the document as the
/// original [offset].
ChildQuery queryChild(int offset, bool inclusive) {
if (offset < 0 || offset > length) {
return ChildQuery(null, 0);
@ -76,6 +105,9 @@ abstract class Container<T extends Node?> extends Node {
@override
String toPlainText() => children.map((child) => child.toPlainText()).join();
/// Content length of this node's children.
///
/// To get number of children in this node use [childCount].
@override
int get length => _children.fold(0, (cur, node) => cur + node.length);
@ -114,11 +146,15 @@ abstract class Container<T extends Node?> extends Node {
String toString() => _children.join('\n');
}
/// Query of a child in a Container
/// Result of a child query in a [Container].
class ChildQuery {
ChildQuery(this.node, this.offset);
final Node? node; // null if not found
/// The child node if found, otherwise `null`.
final Node? node;
/// Starting offset within the child [node] which points at the same
/// character in the document as the original offset passed to
/// [Container.queryChild] method.
final int offset;
}

@ -1,7 +1,15 @@
/// An object which can be embedded into a Quill document.
///
/// See also:
///
/// * [BlockEmbed] which represents a block embed.
class Embeddable {
Embeddable(this.type, this.data);
/// The type of this object.
final String type;
/// The data payload of this object.
final dynamic data;
Map<String, dynamic> toJson() {
@ -17,10 +25,16 @@ class Embeddable {
}
}
/// An object which occupies an entire line in a document and cannot co-exist
/// inline with regular text.
///
/// There are two built-in embed types supported by Quill documents, however
/// the document model itself does not make any assumptions about the types
/// of embedded objects and allows users to define their own types.
class BlockEmbed extends Embeddable {
BlockEmbed(String type, String data) : super(type, data);
static final BlockEmbed horizontalRule = BlockEmbed('divider', 'hr');
static BlockEmbed horizontalRule = BlockEmbed('divider', 'hr');
static BlockEmbed image(String imageUrl) => BlockEmbed('image', imageUrl);
}

@ -6,8 +6,9 @@ import 'embed.dart';
import 'line.dart';
import 'node.dart';
/* A leaf node in document tree */
/// A leaf in Quill document tree.
abstract class Leaf extends Node {
/// Creates a new [Leaf] with specified [data].
factory Leaf(Object data) {
if (data is Embeddable) {
return Embed(data);
@ -19,9 +20,10 @@ abstract class Leaf extends Node {
Leaf.val(Object val) : _value = val;
Object _value;
/// Contents of this node, either a String if this is a [Text] or an
/// [Embed] if this is an [BlockEmbed].
Object get value => _value;
Object _value;
@override
void applyStyle(Style value) {
@ -99,14 +101,21 @@ abstract class Leaf extends Node {
}
}
/// Adjust this text node by merging it with adjacent nodes if they share
/// the same style.
@override
void adjust() {
if (this is Embed) {
// Embed nodes cannot be merged with text nor other embeds (in fact,
// there could be no two adjacent embeds on the same line since an
// embed occupies an entire line).
return;
}
// This is a text node and it can only be merged with other text nodes.
var node = this as Text;
// merging it with previous node if style is the same
// Merging it with previous node if style is the same.
final prev = node.previous;
if (!node.isFirst && prev is Text && prev.style == node.style) {
prev._value = prev.value + node.value;
@ -114,7 +123,7 @@ abstract class Leaf extends Node {
node = prev;
}
// merging it with next node if style is the same
// Merging it with next node if style is the same.
final next = node.next;
if (!node.isLast && next is Text && next.style == node.style) {
node._value = node.value + next.value;
@ -122,13 +131,17 @@ abstract class Leaf extends Node {
}
}
Leaf? cutAt(int index) {
assert(index >= 0 && index <= length);
final cut = splitAt(index);
cut?.unlink();
return cut;
}
/// Splits this leaf node at [index] and returns new node.
///
/// If this is the last node in its list and [index] equals this node's
/// length then this method returns `null` as there is nothing left to split.
/// If there is another leaf node after this one and [index] equals this
/// node's length then the next leaf node is returned.
///
/// If [index] equals to `0` then this node itself is returned unchanged.
///
/// In case a new node is actually split from this one, it inherits this
/// node's style.
Leaf? splitAt(int index) {
assert(index >= 0 && index <= length);
if (index == 0) {
@ -146,14 +159,33 @@ abstract class Leaf extends Node {
return split;
}
/// Cuts a leaf from [index] to the end of this node and returns new node
/// in detached state (e.g. [mounted] returns `false`).
///
/// Splitting logic is identical to one described in [splitAt], meaning this
/// method may return `null`.
Leaf? cutAt(int index) {
assert(index >= 0 && index <= length);
final cut = splitAt(index);
cut?.unlink();
return cut;
}
/// Formats this node and optimizes it with adjacent leaf nodes if needed.
void format(Style? style) {
if (style != null && style.isNotEmpty) {
applyStyle(style);
}
adjust();
}
/// Isolates a new leaf starting at [index] with specified [length].
///
/// Splitting logic is identical to one described in [splitAt], with one
/// exception that it is required for [index] to always be less than this
/// node's length. As a result this method always returns a [LeafNode]
/// instance. Returned node may still be the same as this node
/// if provided [index] is `0`.
Leaf _isolate(int index, int length) {
assert(
index >= 0 && index < this.length && (index + length <= this.length));
@ -162,39 +194,59 @@ abstract class Leaf extends Node {
}
}
/// A span of formatted text within a line in a Quill document.
///
/// Text is a leaf node of a document tree.
///
/// Parent of a text node is always a [Line], and as a consequence text
/// node's [value] cannot contain any line-break characters.
///
/// See also:
///
/// * [Embed], a leaf node representing an embeddable object.
/// * [Line], a node representing a line of text.
class Text extends Leaf {
Text([String text = ''])
: assert(!text.contains('\n')),
super.val(text);
@override
String get value => _value as String;
Node newInstance() => Text();
@override
String toPlainText() {
return value;
}
String get value => _value as String;
@override
Node newInstance() {
return Text();
}
String toPlainText() => value;
}
/// An embedded node such as image or video
/// An embed node inside of a line in a Quill document.
///
/// Embed node is a leaf node similar to [Text]. It represents an arbitrary
/// piece of non-textual content embedded into a document, such as, image,
/// horizontal rule, video, or any other object with defined structure,
/// like a tweet, for instance.
///
/// Embed node's length is always `1` character and it is represented with
/// unicode object replacement character in the document text.
///
/// Any inline style can be applied to an embed, however this does not
/// necessarily mean the embed will look according to that style. For instance,
/// applying "bold" style to an image gives no effect, while adding a "link" to
/// an image actually makes the image react to user's action.
class Embed extends Leaf {
Embed(Embeddable data) : super.val(data);
static const kObjectReplacementCharacter = '\uFFFC';
@override
Embeddable get value => super.value as Embeddable;
Node newInstance() => throw UnimplementedError();
@override
String toPlainText() {
return '\uFFFC';
}
Embeddable get value => super.value as Embeddable;
/// // Embed nodes are represented as unicode object replacement character in
// plain text.
@override
Node newInstance() {
throw UnimplementedError();
}
String toPlainText() => kObjectReplacementCharacter;
}

@ -9,6 +9,12 @@ import 'embed.dart';
import 'leaf.dart';
import 'node.dart';
/// A line of rich text in a Quill document.
///
/// Line serves as a container for [Leaf]s, like [Text] and [Embed].
///
/// When a line contains an embed, it fully occupies the line, no other embeds
/// or text nodes are allowed.
class Line extends Container<Leaf?> {
@override
Leaf get defaultChild => Text();
@ -16,6 +22,7 @@ class Line extends Container<Leaf?> {
@override
int get length => super.length + 1;
/// Returns `true` if this line contains an embedded object.
bool get hasEmbed {
if (childCount != 1) {
return false;
@ -24,6 +31,7 @@ class Line extends Container<Leaf?> {
return children.single is Embed;
}
/// Returns next [Line] or `null` if this is the last line in the document.
Line? get nextLine {
if (!isLast) {
return next is Block ? (next as Block).first as Line? : next as Line?;
@ -40,6 +48,9 @@ class Line extends Container<Leaf?> {
: parent!.next as Line?;
}
@override
Node newInstance() => Line();
@override
Delta toDelta() {
final delta = children
@ -67,34 +78,42 @@ class Line extends Container<Leaf?> {
@override
void insert(int index, Object data, Style? style) {
if (data is Embeddable) {
_insert(index, data, style);
// We do not check whether this line already has any children here as
// inserting an embed into a line with other text is acceptable from the
// Delta format perspective.
// We rely on heuristic rules to ensure that embeds occupy an entire line.
_insertSafe(index, data, style);
return;
}
final text = data as String;
final lineBreak = text.indexOf('\n');
if (lineBreak < 0) {
_insert(index, text, style);
_insertSafe(index, text, style);
// No need to update line or block format since those attributes can only
// be attached to `\n` character and we already know it's not present.
return;
}
final prefix = text.substring(0, lineBreak);
_insert(index, prefix, style);
_insertSafe(index, prefix, style);
if (prefix.isNotEmpty) {
index += prefix.length;
}
// Next line inherits our format.
final nextLine = _getNextLine(index);
// Reset our format and unwrap from a block if needed.
clearStyle();
if (parent is Block) {
_unwrap();
}
// Now we can apply new format and re-layout.
_format(style);
// Continue with the remaining
// Continue with remaining part.
final remain = text.substring(lineBreak + 1);
nextLine.insert(0, remain, style);
}
@ -104,16 +123,20 @@ class Line extends Container<Leaf?> {
if (style == null) {
return;
}
final thisLen = length;
final thisLength = length;
final local = math.min(thisLen - index, len!);
final local = math.min(thisLength - index, len!);
// If index is at newline character then this is a line/block style update.
final isLineFormat = (index + local == thisLength) && local == 1;
if (index + local == thisLen && local == 1) {
assert(style.values.every((attr) => attr.scope == AttributeScope.BLOCK));
if (isLineFormat) {
assert(style.values.every((attr) => attr.scope == AttributeScope.BLOCK),
'It is not allowed to apply inline attributes to line itself.');
_format(style);
} else {
// Otherwise forward to children as it's an inline format update.
assert(style.values.every((attr) => attr.scope == AttributeScope.INLINE));
assert(index + local != thisLen);
assert(index + local != thisLength);
super.retain(index, local, style);
}
@ -127,35 +150,47 @@ class Line extends Container<Leaf?> {
@override
void delete(int index, int? len) {
final local = math.min(length - index, len!);
final deleted = index + local == length;
if (deleted) {
final isLFDeleted = index + local == length; // Line feed
if (isLFDeleted) {
// Our newline character deleted with all style information.
clearStyle();
if (local > 1) {
// Exclude newline character from delete range for children.
super.delete(index, local - 1);
}
} else {
super.delete(index, local);
}
final remain = len - local;
if (remain > 0) {
final remaining = len - local;
if (remaining > 0) {
assert(nextLine != null);
nextLine!.delete(0, remain);
nextLine!.delete(0, remaining);
}
if (deleted && isNotEmpty) {
if (isLFDeleted && isNotEmpty) {
// Since we lost our line-break and still have child text nodes those must
// migrate to the next line.
// nextLine might have been unmounted since last assert so we need to
// check again we still have a line after us.
assert(nextLine != null);
// Move remaining children in this line to the next line so that all
// attributes of nextLine are preserved.
nextLine!.moveChildToNewParent(this);
moveChildToNewParent(nextLine);
}
if (deleted) {
final Node p = parent!;
if (isLFDeleted) {
// Now we can remove this line.
final block = parent!; // remember reference before un-linking.
unlink();
p.adjust();
block.adjust();
}
}
/// Formats this line.
void _format(Style? newStyle) {
if (newStyle == null || newStyle.isEmpty) {
return;
@ -165,7 +200,7 @@ class Line extends Container<Leaf?> {
final blockStyle = newStyle.getBlockExceptHeader();
if (blockStyle == null) {
return;
}
} // No block-level changes
if (parent is Block) {
final parentStyle = (parent as Block).style.getBlockExceptHeader();
@ -176,14 +211,18 @@ class Line extends Container<Leaf?> {
final block = Block()..applyAttribute(blockStyle);
_wrap(block);
block.adjust();
}
} // else the same style, no-op.
} else if (blockStyle.value != null) {
// Only wrap with a new block if this is not an unset
final block = Block()..applyAttribute(blockStyle);
_wrap(block);
block.adjust();
}
}
/// Wraps this line with new parent [block].
///
/// This line can not be in a [Block] when this method is called.
void _wrap(Block block) {
assert(parent != null && parent is! Block);
insertAfter(block);
@ -191,6 +230,9 @@ class Line extends Container<Leaf?> {
block.add(this);
}
/// Unwraps this line from it's parent [Block].
///
/// This method asserts if current [parent] of this line is not a [Block].
void _unwrap() {
if (parent is! Block) {
throw ArgumentError('Invalid parent');
@ -242,7 +284,7 @@ class Line extends Container<Leaf?> {
return line;
}
void _insert(int index, Object data, Style? style) {
void _insertSafe(int index, Object data, Style? style) {
assert(index == 0 || (index > 0 && index < length));
if (data is String) {
@ -252,46 +294,50 @@ class Line extends Container<Leaf?> {
}
}
if (isNotEmpty) {
if (isEmpty) {
final child = Leaf(data);
add(child);
child.format(style);
} else {
final result = queryChild(index, true);
result.node!.insert(result.offset, data, style);
return;
}
final child = Leaf(data);
add(child);
child.format(style);
}
@override
Node newInstance() {
return Line();
}
/// Returns style for specified text range.
///
/// Only attributes applied to all characters within this range are
/// included in the result. Inline and line level attributes are
/// handled separately, e.g.:
///
/// - line attribute X is included in the result only if it exists for
/// every line within this range (partially included lines are counted).
/// - inline attribute X is included in the result only if it exists
/// for every character within this range (line-break characters excluded).
Style collectStyle(int offset, int len) {
final local = math.min(length - offset, len);
var res = Style();
var result = Style();
final excluded = <Attribute>{};
void _handle(Style style) {
if (res.isEmpty) {
if (result.isEmpty) {
excluded.addAll(style.values);
} else {
for (final attr in res.values) {
for (final attr in result.values) {
if (!style.containsKey(attr.key)) {
excluded.add(attr);
}
}
}
final remain = style.removeAll(excluded);
res = res.removeAll(excluded);
res = res.mergeAll(remain);
final remaining = style.removeAll(excluded);
result = result.removeAll(excluded);
result = result.mergeAll(remaining);
}
final data = queryChild(offset, true);
var node = data.node as Leaf?;
if (node != null) {
res = res.mergeAll(node.style);
result = result.mergeAll(node.style);
var pos = node.length - data.offset;
while (!node!.isLast && pos < local) {
node = node.next as Leaf?;
@ -300,17 +346,18 @@ class Line extends Container<Leaf?> {
}
}
res = res.mergeAll(style);
result = result.mergeAll(style);
if (parent is Block) {
final block = parent as Block;
res = res.mergeAll(block.style);
result = result.mergeAll(block.style);
}
final remain = len - local;
if (remain > 0) {
_handle(nextLine!.collectStyle(0, remain));
final remaining = len - local;
if (remaining > 0) {
final rest = nextLine!.collectStyle(0, remaining);
_handle(rest);
}
return res;
return result;
}
}

@ -6,36 +6,37 @@ import '../style.dart';
import 'container.dart';
import 'line.dart';
/* node in a document tree */
/// An abstract node in a document tree.
///
/// Represents a segment of a Quill document with specified [offset]
/// and [length].
///
/// The [offset] property is relative to [parent]. See also [documentOffset]
/// which provides absolute offset of this node within the document.
///
/// The current parent node is exposed by the [parent] property.
abstract class Node extends LinkedListEntry<Node> {
/// Current parent of this node. May be null if this node is not mounted.
Container? parent;
Style _style = Style();
Style get style => _style;
Style _style = Style();
void applyAttribute(Attribute attribute) {
_style = _style.merge(attribute);
}
void applyStyle(Style value) {
_style = _style.mergeAll(value);
}
void clearStyle() {
_style = Style();
}
/// Returns `true` if this node is the first node in the [parent] list.
bool get isFirst => list!.first == this;
/// Returns `true` if this node is the last node in the [parent] list.
bool get isLast => list!.last == this;
/// Length of this node in characters.
int get length;
Node clone() {
return newInstance()..applyStyle(style);
}
Node clone() => newInstance()..applyStyle(style);
int getOffset() {
/// Offset in characters of this node relative to [parent] node.
///
/// To get offset of this node in the document see [documentOffset].
int get offset {
var offset = 0;
if (list == null || isFirst) {
@ -50,16 +51,31 @@ abstract class Node extends LinkedListEntry<Node> {
return offset;
}
int getDocumentOffset() {
final parentOffset = (parent is! Root) ? parent!.getDocumentOffset() : 0;
return parentOffset + getOffset();
/// Offset in characters of this node in the document.
int get documentOffset {
final parentOffset = (parent is! Root) ? parent!.documentOffset : 0;
return parentOffset + offset;
}
/// Returns `true` if this node contains character at specified [offset] in
/// the document.
bool containsOffset(int offset) {
final o = getDocumentOffset();
final o = documentOffset;
return o <= offset && offset < o + length;
}
void applyAttribute(Attribute attribute) {
_style = _style.merge(attribute);
}
void applyStyle(Style value) {
_style = _style.mergeAll(value);
}
void clearStyle() {
_style = Style();
}
@override
void insertBefore(Node entry) {
assert(entry.parent == null && parent != null);
@ -81,9 +97,7 @@ abstract class Node extends LinkedListEntry<Node> {
super.unlink();
}
void adjust() {
// do nothing
}
void adjust() {/* no-op */}
/// abstract methods begin
@ -100,11 +114,13 @@ abstract class Node extends LinkedListEntry<Node> {
void delete(int index, int? len);
/// abstract methods end
}
/* Root node of document tree */
/// Root node of document tree.
class Root extends Container<Container<Node?>> {
@override
Node newInstance() => Root();
@override
Container<Node?> get defaultChild => Line();
@ -112,9 +128,4 @@ class Root extends Container<Container<Node?>> {
Delta toDelta() => children
.map((child) => child.toDelta())
.fold(Delta(), (a, b) => a.concat(b));
@override
Node newInstance() {
return Root();
}
}

@ -577,8 +577,7 @@ class RenderEditor extends RenderEditableContainerBox
if (textSelection.isCollapsed) {
final child = childAtPosition(textSelection.extent);
final localPosition = TextPosition(
offset:
textSelection.extentOffset - child.getContainer().getOffset());
offset: textSelection.extentOffset - child.getContainer().offset);
final localOffset = child.getOffsetForCaret(localPosition);
final parentData = child.parentData as BoxParentData;
return <TextSelectionPoint>[
@ -677,7 +676,7 @@ class RenderEditor extends RenderEditableContainerBox
assert(_lastTapDownPosition != null);
final position = getPositionForOffset(_lastTapDownPosition!);
final child = childAtPosition(position);
final nodeOffset = child.getContainer().getOffset();
final nodeOffset = child.getContainer().offset;
final localPosition = TextPosition(
offset: position.offset - nodeOffset,
affinity: position.affinity,
@ -738,7 +737,7 @@ class RenderEditor extends RenderEditableContainerBox
@override
TextSelection selectWordAtPosition(TextPosition position) {
final child = childAtPosition(position);
final nodeOffset = child.getContainer().getOffset();
final nodeOffset = child.getContainer().offset;
final localPosition = TextPosition(
offset: position.offset - nodeOffset, affinity: position.affinity);
final localWord = child.getWordBoundary(localPosition);
@ -755,7 +754,7 @@ class RenderEditor extends RenderEditableContainerBox
@override
TextSelection selectLineAtPosition(TextPosition position) {
final child = childAtPosition(position);
final nodeOffset = child.getContainer().getOffset();
final nodeOffset = child.getContainer().offset;
final localPosition = TextPosition(
offset: position.offset - nodeOffset, affinity: position.affinity);
final localLineRange = child.getLineBoundary(localPosition);
@ -810,8 +809,8 @@ class RenderEditor extends RenderEditableContainerBox
@override
double preferredLineHeight(TextPosition position) {
final child = childAtPosition(position);
return child.preferredLineHeight(TextPosition(
offset: position.offset - child.getContainer().getOffset()));
return child.preferredLineHeight(
TextPosition(offset: position.offset - child.getContainer().offset));
}
@override
@ -823,7 +822,7 @@ class RenderEditor extends RenderEditableContainerBox
final localOffset = local - parentData.offset;
final localPosition = child.getPositionForOffset(localOffset);
return TextPosition(
offset: localPosition.offset + child.getContainer().getOffset(),
offset: localPosition.offset + child.getContainer().offset,
affinity: localPosition.affinity,
);
}
@ -842,8 +841,7 @@ class RenderEditor extends RenderEditableContainerBox
final caretTop = endpoint.point.dy -
child.preferredLineHeight(TextPosition(
offset:
selection.extentOffset - child.getContainer().getOffset())) -
offset: selection.extentOffset - child.getContainer().offset)) -
kMargin +
offsetInViewport;
final caretBottom = endpoint.point.dy + kMargin + offsetInViewport;

@ -209,8 +209,7 @@ class RawEditorState extends EditorState
final child = getRenderEditor()!.childAtPosition(originPosition);
final localPosition = TextPosition(
offset:
originPosition.offset - child.getContainer().getDocumentOffset());
offset: originPosition.offset - child.getContainer().documentOffset);
var position = upKey
? child.getPositionAbove(localPosition)
@ -231,12 +230,12 @@ class RawEditorState extends EditorState
.dy);
final siblingPosition = sibling.getPositionForOffset(finalOffset);
position = TextPosition(
offset: sibling.getContainer().getDocumentOffset() +
siblingPosition.offset);
offset:
sibling.getContainer().documentOffset + siblingPosition.offset);
}
} else {
position = TextPosition(
offset: child.getContainer().getDocumentOffset() + position.offset);
offset: child.getContainer().documentOffset + position.offset);
}
if (position.offset == newSelection.extentOffset) {

@ -312,12 +312,12 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
TextRange getLineBoundary(TextPosition position) {
final child = childAtPosition(position);
final rangeInChild = child.getLineBoundary(TextPosition(
offset: position.offset - child.getContainer().getOffset(),
offset: position.offset - child.getContainer().offset,
affinity: position.affinity,
));
return TextRange(
start: rangeInChild.start + child.getContainer().getOffset(),
end: rangeInChild.end + child.getContainer().getOffset(),
start: rangeInChild.start + child.getContainer().offset,
end: rangeInChild.end + child.getContainer().offset,
);
}
@ -325,7 +325,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
Offset getOffsetForCaret(TextPosition position) {
final child = childAtPosition(position);
return child.getOffsetForCaret(TextPosition(
offset: position.offset - child.getContainer().getOffset(),
offset: position.offset - child.getContainer().offset,
affinity: position.affinity,
)) +
(child.parentData as BoxParentData).offset;
@ -338,7 +338,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
final localPosition =
child.getPositionForOffset(offset - parentData.offset);
return TextPosition(
offset: localPosition.offset + child.getContainer().getOffset(),
offset: localPosition.offset + child.getContainer().offset,
affinity: localPosition.affinity,
);
}
@ -346,7 +346,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
@override
TextRange getWordBoundary(TextPosition position) {
final child = childAtPosition(position);
final nodeOffset = child.getContainer().getOffset();
final nodeOffset = child.getContainer().offset;
final childWord = child
.getWordBoundary(TextPosition(offset: position.offset - nodeOffset));
return TextRange(
@ -360,12 +360,11 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
assert(position.offset < getContainer().length);
final child = childAtPosition(position);
final childLocalPosition = TextPosition(
offset: position.offset - child.getContainer().getOffset());
final childLocalPosition =
TextPosition(offset: position.offset - child.getContainer().offset);
final result = child.getPositionAbove(childLocalPosition);
if (result != null) {
return TextPosition(
offset: result.offset + child.getContainer().getOffset());
return TextPosition(offset: result.offset + child.getContainer().offset);
}
final sibling = childBefore(child);
@ -379,7 +378,7 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
final testOffset = sibling.getOffsetForCaret(testPosition);
final finalOffset = Offset(caretOffset.dx, testOffset.dy);
return TextPosition(
offset: sibling.getContainer().getOffset() +
offset: sibling.getContainer().offset +
sibling.getPositionForOffset(finalOffset).offset);
}
@ -388,12 +387,11 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
assert(position.offset < getContainer().length);
final child = childAtPosition(position);
final childLocalPosition = TextPosition(
offset: position.offset - child.getContainer().getOffset());
final childLocalPosition =
TextPosition(offset: position.offset - child.getContainer().offset);
final result = child.getPositionBelow(childLocalPosition);
if (result != null) {
return TextPosition(
offset: result.offset + child.getContainer().getOffset());
return TextPosition(offset: result.offset + child.getContainer().offset);
}
final sibling = childAfter(child);
@ -405,15 +403,15 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
final testOffset = sibling.getOffsetForCaret(const TextPosition(offset: 0));
final finalOffset = Offset(caretOffset.dx, testOffset.dy);
return TextPosition(
offset: sibling.getContainer().getOffset() +
offset: sibling.getContainer().offset +
sibling.getPositionForOffset(finalOffset).offset);
}
@override
double preferredLineHeight(TextPosition position) {
final child = childAtPosition(position);
return child.preferredLineHeight(TextPosition(
offset: position.offset - child.getContainer().getOffset()));
return child.preferredLineHeight(
TextPosition(offset: position.offset - child.getContainer().offset));
}
@override

@ -402,8 +402,8 @@ class RenderEditableTextLine extends RenderEditableBox {
}
bool containsTextSelection() {
return line.getDocumentOffset() <= textSelection.end &&
textSelection.start <= line.getDocumentOffset() + line.length - 1;
return line.documentOffset <= textSelection.end &&
textSelection.start <= line.documentOffset + line.length - 1;
}
bool containsCursor() {
@ -735,8 +735,8 @@ class RenderEditableTextLine extends RenderEditableBox {
final parentData = _body!.parentData as BoxParentData;
final effectiveOffset = offset + parentData.offset;
if (enableInteractiveSelection &&
line.getDocumentOffset() <= textSelection.end &&
textSelection.start <= line.getDocumentOffset() + line.length - 1) {
line.documentOffset <= textSelection.end &&
textSelection.start <= line.documentOffset + line.length - 1) {
final local = localSelection(line, textSelection, false);
_selectedRects ??= _body!.getBoxesForSelection(
local,
@ -772,7 +772,7 @@ class RenderEditableTextLine extends RenderEditableBox {
void _paintCursor(PaintingContext context, Offset effectiveOffset) {
final position = TextPosition(
offset: textSelection.extentOffset - line.getDocumentOffset(),
offset: textSelection.extentOffset - line.documentOffset,
affinity: textSelection.base.affinity,
);
_cursorPainter.paint(context.canvas, effectiveOffset, position);

@ -12,10 +12,10 @@ import '../models/documents/nodes/node.dart';
import 'editor.dart';
TextSelection localSelection(Node node, TextSelection selection, fromParent) {
final base = fromParent ? node.getOffset() : node.getDocumentOffset();
final base = fromParent ? node.offset : node.documentOffset;
assert(base <= selection.end && selection.start <= base + node.length - 1);
final offset = fromParent ? node.getOffset() : node.getDocumentOffset();
final offset = fromParent ? node.offset : node.documentOffset;
return selection.copyWith(
baseOffset: math.max(selection.start - offset, 0),
extentOffset: math.min(selection.end - offset, node.length - 1));

Loading…
Cancel
Save