Merge pull request #627 from vincentdchan/feat/transaction-builder

Feat: transaction builder
This commit is contained in:
Lucas.Xu 2022-07-18 19:41:37 +08:00 committed by GitHub
commit 32e5947bed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 131 additions and 30 deletions

View File

@ -33,10 +33,12 @@ class _ImageNodeWidget extends StatelessWidget {
return GestureDetector(
child: _build(context),
onTap: () {
editorState.update(node, {
TransactionBuilder(editorState)
..updateNode(node, {
'image_src':
"https://images.pexels.com/photos/9995076/pexels-photo-9995076.png?cs=srgb&dl=pexels-temmuz-uzun-9995076.jpg&fm=jpg&w=640&h=400"
});
})
..commit();
},
);
}

View File

@ -187,7 +187,6 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
@override
void updateEditingValue(TextEditingValue value) {
debugPrint(value.text);
editorState.update(node, {'content': value.text});
}
@override

View File

@ -159,12 +159,21 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
}
class TextNode extends Node {
final Delta delta;
Delta _delta;
TextNode({
required super.type,
required super.children,
required super.attributes,
required this.delta,
});
required Delta delta,
}) : _delta = delta;
Delta get delta {
return _delta;
}
set delta(Delta v) {
_delta = v;
notifyListeners();
}
}

View File

@ -1,5 +1,6 @@
import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/document/path.dart';
import 'package:flowy_editor/document/text_delta.dart';
import './attributes.dart';
class StateTree {
@ -35,6 +36,18 @@ class StateTree {
return true;
}
bool textEdit(Path path, Delta delta) {
if (path.isEmpty) {
return false;
}
var node = root.childAtPath(path);
if (node == null || node is! TextNode) {
return false;
}
node.delta = node.delta.compose(delta);
return false;
}
Node? delete(Path path) {
if (path.isEmpty) {
return null;

View File

@ -36,25 +36,6 @@ class EditorState {
}
}
// TODO: move to a better place.
void update(Node node, Attributes attributes) {
_applyOperation(UpdateOperation(
path: node.path,
attributes: Attributes.from(node.attributes)..addAll(attributes),
oldAttributes: node.attributes,
));
}
// TODO: move to a better place.
void delete(Node node) {
_applyOperation(
DeleteOperation(
path: node.path,
removedValue: node,
),
);
}
void _applyOperation(Operation op) {
if (op is InsertOperation) {
document.insert(op.path, op.value);
@ -62,6 +43,8 @@ class EditorState {
document.update(op.path, op.attributes);
} else if (op is DeleteOperation) {
document.delete(op.path);
} else if (op is TextEditOperation) {
document.textEdit(op.path, op.delta);
}
}
}

View File

@ -6,5 +6,6 @@ export 'package:flowy_editor/document/path.dart';
export 'package:flowy_editor/render/render_plugins.dart';
export 'package:flowy_editor/render/node_widget_builder.dart';
export 'package:flowy_editor/operation/transaction.dart';
export 'package:flowy_editor/operation/transaction_builder.dart';
export 'package:flowy_editor/operation/operation.dart';
export 'package:flowy_editor/editor_state.dart';

View File

@ -67,14 +67,16 @@ class DeleteOperation extends Operation {
class TextEditOperation extends Operation {
final Path path;
final Delta delta;
final Delta inverted;
TextEditOperation({
required this.path,
required this.delta,
required this.inverted,
});
@override
Operation invert() {
return TextEditOperation(path: path, delta: delta);
return TextEditOperation(path: path, delta: inverted, inverted: delta);
}
}

View File

@ -1,6 +1,28 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flowy_editor/document/selection.dart';
import './operation.dart';
/// This class to use to store the **changes**
/// will be applied to the editor.
///
/// This class is immutable version the the class
/// [[Transaction]]. Is used to stored and
/// transmit. If you want to build the transaction,
/// use [[Transaction]] directly.
///
/// There will be several ways to consume the transaction:
/// 1. Apply to the state to update the UI.
/// 2. Send to the backend to store and do operation transforming.
/// 3. Stored by the UndoManager to implement redo/undo.
///
@immutable
class Transaction {
final List<Operation> operations;
Transaction([this.operations = const []]);
final UnmodifiableListView<Operation> operations;
final Selection? cursorSelection;
const Transaction({
required this.operations,
this.cursorSelection,
});
}

View File

@ -0,0 +1,70 @@
import 'dart:collection';
import 'package:flowy_editor/editor_state.dart';
import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/document/path.dart';
import 'package:flowy_editor/document/text_delta.dart';
import 'package:flowy_editor/document/attributes.dart';
import 'package:flowy_editor/document/selection.dart';
import './operation.dart';
import './transaction.dart';
///
/// This class is used to
/// build the transaction from the state.
///
/// This class automatically save the
/// cursor from the state.
///
/// When the transaction is undo, the
/// cursor can be restored.
///
class TransactionBuilder {
final List<Operation> operations = [];
EditorState state;
Selection? cursorSelection;
TransactionBuilder(this.state);
commit() {
final transaction = _finish();
state.apply(transaction);
}
void insertNode(Path path, Node node) {
cursorSelection = state.cursorSelection;
operations.add(InsertOperation(path: path, value: node));
}
void updateNode(Node node, Attributes attributes) {
cursorSelection = state.cursorSelection;
operations.add(UpdateOperation(
path: node.path,
attributes: Attributes.from(node.attributes)..addAll(attributes),
oldAttributes: node.attributes,
));
}
void deleteNode(Node node) {
cursorSelection = state.cursorSelection;
operations.add(DeleteOperation(path: node.path, removedValue: node));
}
void 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));
}
Transaction _finish() {
return Transaction(
operations: UnmodifiableListView(operations),
cursorSelection: cursorSelection,
);
}
}