mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-24 22:57:12 -04:00
feat: redo
This commit is contained in:
parent
7ae153f5da
commit
a120853d06
3 changed files with 54 additions and 2 deletions
|
@ -43,6 +43,7 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
|
||||||
TextNode get node => widget.node as TextNode;
|
TextNode get node => widget.node as TextNode;
|
||||||
EditorState get editorState => widget.editorState;
|
EditorState get editorState => widget.editorState;
|
||||||
bool _metaKeyDown = false;
|
bool _metaKeyDown = false;
|
||||||
|
bool _shiftKeyDown = false;
|
||||||
|
|
||||||
TextInputConnection? _textInputConnection;
|
TextInputConnection? _textInputConnection;
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEventResult _onKey(FocusNode focusNode, RawKeyEvent event) {
|
KeyEventResult _onKey(FocusNode focusNode, RawKeyEvent event) {
|
||||||
|
debugPrint('key: $event');
|
||||||
if (event is RawKeyDownEvent) {
|
if (event is RawKeyDownEvent) {
|
||||||
final sel = _globalSelectionToLocal(node, editorState.cursorSelection);
|
final sel = _globalSelectionToLocal(node, editorState.cursorSelection);
|
||||||
if (event.logicalKey == LogicalKeyboardKey.backspace) {
|
if (event.logicalKey == LogicalKeyboardKey.backspace) {
|
||||||
|
@ -90,14 +92,25 @@ class __TextNodeWidgetState extends State<_TextNodeWidget>
|
||||||
} else if (event.logicalKey == LogicalKeyboardKey.metaLeft ||
|
} else if (event.logicalKey == LogicalKeyboardKey.metaLeft ||
|
||||||
event.logicalKey == LogicalKeyboardKey.metaRight) {
|
event.logicalKey == LogicalKeyboardKey.metaRight) {
|
||||||
_metaKeyDown = true;
|
_metaKeyDown = true;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.shiftLeft ||
|
||||||
|
event.logicalKey == LogicalKeyboardKey.shiftRight) {
|
||||||
|
_shiftKeyDown = true;
|
||||||
} else if (event.logicalKey == LogicalKeyboardKey.keyZ && _metaKeyDown) {
|
} else if (event.logicalKey == LogicalKeyboardKey.keyZ && _metaKeyDown) {
|
||||||
editorState.undoManager.undo();
|
if (_shiftKeyDown) {
|
||||||
|
editorState.undoManager.redo();
|
||||||
|
} else {
|
||||||
|
editorState.undoManager.undo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (event is RawKeyUpEvent) {
|
} else if (event is RawKeyUpEvent) {
|
||||||
if (event.logicalKey == LogicalKeyboardKey.metaLeft ||
|
if (event.logicalKey == LogicalKeyboardKey.metaLeft ||
|
||||||
event.logicalKey == LogicalKeyboardKey.metaRight) {
|
event.logicalKey == LogicalKeyboardKey.metaRight) {
|
||||||
_metaKeyDown = false;
|
_metaKeyDown = false;
|
||||||
}
|
}
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.shiftLeft ||
|
||||||
|
event.logicalKey == LogicalKeyboardKey.shiftRight) {
|
||||||
|
_shiftKeyDown = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ class ApplyOptions {
|
||||||
/// whether the transaction should be recorded into
|
/// whether the transaction should be recorded into
|
||||||
/// the undo stack.
|
/// the undo stack.
|
||||||
final bool recordUndo;
|
final bool recordUndo;
|
||||||
|
final bool recordRedo;
|
||||||
const ApplyOptions({
|
const ApplyOptions({
|
||||||
this.recordUndo = true,
|
this.recordUndo = true,
|
||||||
|
this.recordRedo = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +59,12 @@ class EditorState {
|
||||||
}
|
}
|
||||||
undoItem.afterSelection = transaction.afterSelection;
|
undoItem.afterSelection = transaction.afterSelection;
|
||||||
_debouncedSealHistoryItem();
|
_debouncedSealHistoryItem();
|
||||||
|
} else if (options.recordRedo) {
|
||||||
|
final redoItem = HistoryItem();
|
||||||
|
redoItem.addAll(transaction.operations);
|
||||||
|
redoItem.beforeSelection = transaction.beforeSelection;
|
||||||
|
redoItem.afterSelection = transaction.afterSelection;
|
||||||
|
undoManager.redoStack.push(redoItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_builder.dart';
|
||||||
import 'package:flowy_editor/operation/transaction.dart';
|
import 'package:flowy_editor/operation/transaction.dart';
|
||||||
import 'package:flowy_editor/editor_state.dart';
|
import 'package:flowy_editor/editor_state.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
/// This class contains operations committed by users.
|
/// This class contains operations committed by users.
|
||||||
/// If a [HistoryItem] is not sealed, operations can be added sequentially.
|
/// If a [HistoryItem] is not sealed, operations can be added sequentially.
|
||||||
|
@ -68,6 +69,10 @@ class FixedSizeStack {
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
HistoryItem get last => _list.last;
|
HistoryItem get last => _list.last;
|
||||||
|
|
||||||
bool get isEmpty => _list.isEmpty;
|
bool get isEmpty => _list.isEmpty;
|
||||||
|
@ -92,6 +97,7 @@ class UndoManager {
|
||||||
}
|
}
|
||||||
final last = undoStack.last;
|
final last = undoStack.last;
|
||||||
if (last.sealed) {
|
if (last.sealed) {
|
||||||
|
redoStack.clear();
|
||||||
final item = HistoryItem();
|
final item = HistoryItem();
|
||||||
undoStack.push(item);
|
undoStack.push(item);
|
||||||
return item;
|
return item;
|
||||||
|
@ -100,6 +106,7 @@ class UndoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
|
debugPrint('undo');
|
||||||
final s = state;
|
final s = state;
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -109,6 +116,30 @@ class UndoManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final transaction = historyItem.toTransaction(s);
|
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,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue