dartlangeditorflutterflutter-appsflutter-examplesflutter-packageflutter-widgetquillquill-deltaquilljsreactquillrich-textrich-text-editorwysiwygwysiwyg-editor
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
2.7 KiB
127 lines
2.7 KiB
import 'dart:collection'; |
|
|
|
import '../style.dart'; |
|
import 'node.dart'; |
|
|
|
/* Container of multiple nodes */ |
|
abstract class Container<T extends Node> extends Node { |
|
final LinkedList<Node> _children = LinkedList<Node>(); |
|
|
|
LinkedList<Node> get children => _children; |
|
|
|
int get childCount => _children.length; |
|
|
|
Node get first => _children.first; |
|
|
|
Node get last => _children.last; |
|
|
|
bool get isEmpty => _children.isEmpty; |
|
|
|
bool get isNotEmpty => _children.isNotEmpty; |
|
|
|
/// abstract methods begin |
|
|
|
T get defaultChild; |
|
|
|
/// abstract methods end |
|
|
|
add(T node) { |
|
assert(node.parent == null); |
|
node.parent = this; |
|
_children.add(node); |
|
} |
|
|
|
addFirst(T node) { |
|
assert(node.parent == null); |
|
node.parent = this; |
|
_children.addFirst(node); |
|
} |
|
|
|
void remove(T node) { |
|
assert(node.parent == this); |
|
node.parent = null; |
|
_children.remove(node); |
|
} |
|
|
|
void moveChildToNewParent(Container newParent) { |
|
if (isEmpty) { |
|
return; |
|
} |
|
|
|
T last = newParent.isEmpty ? null : newParent.last; |
|
while (isNotEmpty) { |
|
T child = first; |
|
child.unlink(); |
|
newParent.add(child); |
|
} |
|
|
|
if (last != null) last.adjust(); |
|
} |
|
|
|
ChildQuery queryChild(int offset, bool inclusive) { |
|
if (offset < 0) { |
|
throw ('Offset cannot be negative'); |
|
} |
|
if (offset > length) { |
|
throw ('offset $offset > $length'); |
|
} |
|
|
|
for (Node node in children) { |
|
int len = node.length; |
|
if (offset < len || (inclusive && offset == len && (node.isLast))) { |
|
return ChildQuery(node, offset); |
|
} |
|
offset -= len; |
|
} |
|
return ChildQuery(null, 0); |
|
} |
|
|
|
@override |
|
String toPlainText() => children.map((child) => child.toPlainText()).join(); |
|
|
|
@override |
|
int get length => _children.fold(0, (cur, node) => cur + node.length); |
|
|
|
@override |
|
insert(int index, Object data, Style style) { |
|
assert(index == 0 || (index > 0 && index < length)); |
|
|
|
if (isNotEmpty) { |
|
ChildQuery child = queryChild(index, false); |
|
child.node.insert(child.offset, data, style); |
|
return; |
|
} |
|
|
|
// empty |
|
assert(index == 0); |
|
T node = defaultChild; |
|
add(node); |
|
node.insert(index, data, style); |
|
} |
|
|
|
@override |
|
retain(int index, int length, Style attributes) { |
|
assert(isNotEmpty); |
|
ChildQuery child = queryChild(index, false); |
|
child.node.retain(child.offset, length, attributes); |
|
} |
|
|
|
@override |
|
delete(int index, int length) { |
|
assert(isNotEmpty); |
|
ChildQuery child = queryChild(index, false); |
|
child.node.delete(child.offset, length); |
|
} |
|
|
|
@override |
|
String toString() => _children.join('\n'); |
|
} |
|
|
|
/// Query of a child in a Container |
|
class ChildQuery { |
|
final Node node; // null if not found |
|
|
|
final int offset; |
|
|
|
ChildQuery(this.node, this.offset); |
|
}
|
|
|