diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart index b81ffca0ab..9e193be761 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart +++ b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart @@ -43,6 +43,7 @@ class __TextNodeWidgetState extends State<_TextNodeWidget> TextNode get node => widget.node as TextNode; EditorState get editorState => widget.editorState; bool _metaKeyDown = false; + bool _shiftKeyDown = false; TextInputConnection? _textInputConnection; @@ -79,6 +80,7 @@ class __TextNodeWidgetState extends State<_TextNodeWidget> } KeyEventResult _onKey(FocusNode focusNode, RawKeyEvent event) { + debugPrint('key: $event'); if (event is RawKeyDownEvent) { final sel = _globalSelectionToLocal(node, editorState.cursorSelection); if (event.logicalKey == LogicalKeyboardKey.backspace) { @@ -90,14 +92,25 @@ class __TextNodeWidgetState extends State<_TextNodeWidget> } else if (event.logicalKey == LogicalKeyboardKey.metaLeft || event.logicalKey == LogicalKeyboardKey.metaRight) { _metaKeyDown = true; + } else if (event.logicalKey == LogicalKeyboardKey.shiftLeft || + event.logicalKey == LogicalKeyboardKey.shiftRight) { + _shiftKeyDown = true; } else if (event.logicalKey == LogicalKeyboardKey.keyZ && _metaKeyDown) { - editorState.undoManager.undo(); + if (_shiftKeyDown) { + editorState.undoManager.redo(); + } else { + editorState.undoManager.undo(); + } } } else if (event is RawKeyUpEvent) { if (event.logicalKey == LogicalKeyboardKey.metaLeft || event.logicalKey == LogicalKeyboardKey.metaRight) { _metaKeyDown = false; } + if (event.logicalKey == LogicalKeyboardKey.shiftLeft || + event.logicalKey == LogicalKeyboardKey.shiftRight) { + _shiftKeyDown = false; + } } return KeyEventResult.ignored; } diff --git a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart index 1e6e404385..ee0e625537 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart @@ -10,8 +10,10 @@ class ApplyOptions { /// whether the transaction should be recorded into /// the undo stack. final bool recordUndo; + final bool recordRedo; const ApplyOptions({ this.recordUndo = true, + this.recordRedo = false, }); } @@ -57,6 +59,12 @@ class EditorState { } undoItem.afterSelection = transaction.afterSelection; _debouncedSealHistoryItem(); + } else if (options.recordRedo) { + final redoItem = HistoryItem(); + redoItem.addAll(transaction.operations); + redoItem.beforeSelection = transaction.beforeSelection; + redoItem.afterSelection = transaction.afterSelection; + undoManager.redoStack.push(redoItem); } } diff --git a/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart b/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart index d01ffa351a..d47ec2359a 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart @@ -5,6 +5,7 @@ import 'package:flowy_editor/operation/operation.dart'; import 'package:flowy_editor/operation/transaction_builder.dart'; import 'package:flowy_editor/operation/transaction.dart'; import 'package:flowy_editor/editor_state.dart'; +import 'package:flutter/foundation.dart'; /// This class contains operations committed by users. /// If a [HistoryItem] is not sealed, operations can be added sequentially. @@ -68,6 +69,10 @@ class FixedSizeStack { return last; } + clear() { + _list.clear(); + } + HistoryItem get last => _list.last; bool get isEmpty => _list.isEmpty; @@ -92,6 +97,7 @@ class UndoManager { } final last = undoStack.last; if (last.sealed) { + redoStack.clear(); final item = HistoryItem(); undoStack.push(item); return item; @@ -100,6 +106,7 @@ class UndoManager { } undo() { + debugPrint('undo'); final s = state; if (s == null) { return; @@ -109,6 +116,30 @@ class UndoManager { return; } final transaction = historyItem.toTransaction(s); - s.apply(transaction, const ApplyOptions(recordUndo: false)); + s.apply( + transaction, + const ApplyOptions( + recordUndo: false, + recordRedo: true, + )); + } + + redo() { + debugPrint('redo'); + final s = state; + if (s == null) { + return; + } + final historyItem = redoStack.pop(); + if (historyItem == null) { + return; + } + final transaction = historyItem.toTransaction(s); + s.apply( + transaction, + const ApplyOptions( + recordUndo: true, + recordRedo: false, + )); } }