feat: insert text at cursor

This commit is contained in:
Vincent Chan 2022-07-19 15:24:51 +08:00
parent ee5c9d410b
commit abe0658cd3
2 changed files with 77 additions and 17 deletions

View File

@ -95,11 +95,20 @@ class _TextNodeWidget extends StatefulWidget {
State<_TextNodeWidget> createState() => __TextNodeWidgetState();
}
String _textContentOfDelta(Delta delta) {
return delta.operations.fold("", (previousValue, element) {
if (element is TextInsert) {
return previousValue + element.content;
}
return previousValue;
});
}
class __TextNodeWidgetState extends State<_TextNodeWidget>
implements TextInputClient {
implements DeltaTextInputClient {
TextNode get node => widget.node as TextNode;
EditorState get editorState => widget.editorState;
TextEditingValue get textEditingValue => const TextEditingValue();
TextSelection? _localSelection;
TextInputConnection? _textInputConnection;
@ -112,20 +121,22 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
TextSpan(
children: node.toTextSpans(),
),
onTap: () {
onSelectionChanged: ((selection, cause) {
_textInputConnection?.close();
_textInputConnection = TextInput.attach(
this,
const TextInputConfiguration(
enableDeltaModel: false,
enableDeltaModel: true,
inputType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
),
);
debugPrint('selection: $selection');
_textInputConnection
?..show()
..setEditingState(textEditingValue);
},
..setEditingState(TextEditingValue(
text: _textContentOfDelta(node.delta), selection: selection));
}),
),
if (node.children.isNotEmpty)
...node.children.map(
@ -152,7 +163,9 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
@override
// TODO: implement currentTextEditingValue
TextEditingValue? get currentTextEditingValue => textEditingValue;
TextEditingValue? get currentTextEditingValue => TextEditingValue(
text: _textContentOfDelta(node.delta),
selection: _localSelection ?? const TextSelection.collapsed(offset: -1));
@override
void insertTextPlaceholder(Size size) {
@ -186,7 +199,23 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
@override
void updateEditingValue(TextEditingValue value) {
debugPrint(value.text);
debugPrint('offset: ${value.selection}');
}
@override
void updateEditingValueWithDeltas(List<TextEditingDelta> textEditingDeltas) {
for (final textDelta in textEditingDeltas) {
if (textDelta is TextEditingDeltaInsertion) {
TransactionBuilder(editorState)
..insertText(node, textDelta.insertionOffset, textDelta.textInserted)
..commit();
} else if (textDelta is TextEditingDeltaDeletion) {
TransactionBuilder(editorState)
..deleteText(node, textDelta.deletedRange.start,
textDelta.deletedRange.end - textDelta.deletedRange.start)
..commit();
}
}
}
@override

View File

@ -26,39 +26,70 @@ class TransactionBuilder {
TransactionBuilder(this.state);
/// Commit the operations to the state
commit() {
final transaction = _finish();
state.apply(transaction);
}
void insertNode(Path path, Node node) {
insertNode(Path path, Node node) {
cursorSelection = state.cursorSelection;
operations.add(InsertOperation(path: path, value: node));
add(InsertOperation(path: path, value: node));
}
void updateNode(Node node, Attributes attributes) {
updateNode(Node node, Attributes attributes) {
cursorSelection = state.cursorSelection;
operations.add(UpdateOperation(
add(UpdateOperation(
path: node.path,
attributes: Attributes.from(node.attributes)..addAll(attributes),
oldAttributes: node.attributes,
));
}
void deleteNode(Node node) {
deleteNode(Node node) {
cursorSelection = state.cursorSelection;
operations.add(DeleteOperation(path: node.path, removedValue: node));
add(DeleteOperation(path: node.path, removedValue: node));
}
void textEdit(TextNode node, Delta Function() f) {
textEdit(TextNode node, Delta Function() f) {
cursorSelection = state.cursorSelection;
final path = node.path;
final delta = f();
final inverted = delta.invert(node.delta);
operations
.add(TextEditOperation(path: path, delta: delta, inverted: inverted));
add(TextEditOperation(path: path, delta: delta, inverted: inverted));
}
insertText(TextNode node, int index, String content) {
textEdit(node, () => Delta().retain(index).insert(content));
}
formatText(TextNode node, int index, int length, Attributes attributes) {
textEdit(node, () => Delta().retain(index).retain(length, attributes));
}
deleteText(TextNode node, int index, int length) {
textEdit(node, () => Delta().retain(index).delete(length));
}
add(Operation op) {
final Operation? last = operations.isEmpty ? null : operations.last;
if (last != null) {
if (op is TextEditOperation &&
last is TextEditOperation &&
pathEquals(op.path, last.path)) {
final newOp = TextEditOperation(
path: op.path,
delta: last.delta.compose(op.delta),
inverted: op.inverted.compose(last.inverted),
);
operations[operations.length - 1] = newOp;
return;
}
}
operations.add(op);
}
Transaction _finish() {