mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-26 16:46:51 +08:00
Basic implementation of undo/redo
It's not great, as the granularity is too low. But it's a good first step. It only works in the RawEditor so far.
This commit is contained in:
@ -24,6 +24,9 @@ class EditorBottomBar extends StatelessWidget {
|
||||
final Func0<void> onZenModeChanged;
|
||||
final bool metaDataEditable;
|
||||
|
||||
final bool undoAllowed;
|
||||
final bool redoAllowed;
|
||||
|
||||
final Func0<void> onUndoSelected;
|
||||
final Func0<void> onRedoSelected;
|
||||
|
||||
@ -37,6 +40,8 @@ class EditorBottomBar extends StatelessWidget {
|
||||
@required this.metaDataEditable,
|
||||
@required this.onUndoSelected,
|
||||
@required this.onRedoSelected,
|
||||
@required this.undoAllowed,
|
||||
@required this.redoAllowed,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -84,17 +89,24 @@ class EditorBottomBar extends StatelessWidget {
|
||||
maintainState: true,
|
||||
maintainInteractivity: false,
|
||||
),
|
||||
// TODO: Add the undo and redo icons
|
||||
Expanded(
|
||||
child: FlatButton.icon(
|
||||
icon: const Icon(Icons.folder),
|
||||
label: Text(parentFolder.publicName),
|
||||
onPressed: () {
|
||||
var note = editorState.getNote();
|
||||
editor.moveNoteToFolderSelected(note);
|
||||
},
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.undo),
|
||||
onPressed: undoAllowed ? onUndoSelected : null,
|
||||
),
|
||||
FlatButton.icon(
|
||||
icon: const Icon(Icons.folder),
|
||||
label: Text(parentFolder.publicName),
|
||||
onPressed: () {
|
||||
var note = editorState.getNote();
|
||||
editor.moveNoteToFolderSelected(note);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.redo),
|
||||
onPressed: redoAllowed ? onRedoSelected : null,
|
||||
),
|
||||
const Spacer(),
|
||||
menuIcon,
|
||||
],
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
@ -185,6 +185,8 @@ class ChecklistEditorState extends State<ChecklistEditor>
|
||||
),
|
||||
onUndoSelected: _undo,
|
||||
onRedoSelected: _redo,
|
||||
undoAllowed: false,
|
||||
redoAllowed: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,8 @@ class JournalEditorState extends State<JournalEditor>
|
||||
body: editor,
|
||||
onUndoSelected: _undo,
|
||||
onRedoSelected: _redo,
|
||||
undoAllowed: false,
|
||||
redoAllowed: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -142,6 +142,8 @@ class MarkdownEditorState extends State<MarkdownEditor>
|
||||
body: editor,
|
||||
onUndoSelected: _undo,
|
||||
onRedoSelected: _redo,
|
||||
undoAllowed: false,
|
||||
redoAllowed: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import 'package:gitjournal/core/md_yaml_doc_codec.dart';
|
||||
import 'package:gitjournal/core/note.dart';
|
||||
import 'package:gitjournal/editors/common.dart';
|
||||
import 'package:gitjournal/editors/disposable_change_notifier.dart';
|
||||
import 'package:gitjournal/editors/undo_redo.dart';
|
||||
import 'package:gitjournal/widgets/editor_scroll_view.dart';
|
||||
|
||||
class RawEditor extends StatefulWidget implements Editor {
|
||||
@ -56,12 +57,14 @@ class RawEditorState extends State<RawEditor>
|
||||
implements EditorState {
|
||||
Note note;
|
||||
bool _noteModified;
|
||||
TextEditingController _textController = TextEditingController();
|
||||
TextEditingController _textController;
|
||||
UndoRedoStack _undoRedoStack;
|
||||
|
||||
final serializer = MarkdownYAMLCodec();
|
||||
|
||||
RawEditorState(this.note) {
|
||||
_textController = TextEditingController(text: serializer.encode(note.data));
|
||||
_undoRedoStack = UndoRedoStack();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -106,6 +109,8 @@ class RawEditorState extends State<RawEditor>
|
||||
body: editor,
|
||||
onUndoSelected: _undo,
|
||||
onRedoSelected: _redo,
|
||||
undoAllowed: _undoRedoStack.undoPossible,
|
||||
redoAllowed: _undoRedoStack.redoPossible,
|
||||
);
|
||||
}
|
||||
|
||||
@ -118,6 +123,12 @@ class RawEditorState extends State<RawEditor>
|
||||
void _noteTextChanged() {
|
||||
notifyListeners();
|
||||
|
||||
var editState = TextEditorState.fromValue(_textController.value);
|
||||
var redraw = _undoRedoStack.textChanged(editState);
|
||||
if (redraw) {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
if (_noteModified) return;
|
||||
setState(() {
|
||||
_noteModified = true;
|
||||
@ -136,9 +147,21 @@ class RawEditorState extends State<RawEditor>
|
||||
@override
|
||||
bool get noteModified => _noteModified;
|
||||
|
||||
Future<void> _undo() async {}
|
||||
Future<void> _undo() async {
|
||||
var es = _undoRedoStack.undo();
|
||||
_textController.value = es.toValue();
|
||||
setState(() {
|
||||
// To Redraw the undo/redo button state
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _redo() async {}
|
||||
Future<void> _redo() async {
|
||||
var es = _undoRedoStack.redo();
|
||||
_textController.value = es.toValue();
|
||||
setState(() {
|
||||
// To Redraw the undo/redo button state
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _NoteEditor extends StatelessWidget {
|
||||
|
@ -24,6 +24,9 @@ class EditorScaffold extends StatefulWidget {
|
||||
final Func0<void> onUndoSelected;
|
||||
final Func0<void> onRedoSelected;
|
||||
|
||||
final bool undoAllowed;
|
||||
final bool redoAllowed;
|
||||
|
||||
EditorScaffold({
|
||||
@required this.editor,
|
||||
@required this.editorState,
|
||||
@ -33,6 +36,8 @@ class EditorScaffold extends StatefulWidget {
|
||||
@required this.parentFolder,
|
||||
@required this.onUndoSelected,
|
||||
@required this.onRedoSelected,
|
||||
@required this.undoAllowed,
|
||||
@required this.redoAllowed,
|
||||
this.extraButton,
|
||||
});
|
||||
|
||||
@ -169,6 +174,8 @@ class _EditorScaffoldState extends State<EditorScaffold> {
|
||||
metaDataEditable: note != null ? note.canHaveMetadata : false,
|
||||
onUndoSelected: widget.onUndoSelected,
|
||||
onRedoSelected: widget.onRedoSelected,
|
||||
undoAllowed: widget.undoAllowed,
|
||||
redoAllowed: widget.redoAllowed,
|
||||
),
|
||||
)
|
||||
],
|
||||
|
57
lib/editors/undo_redo.dart
Normal file
57
lib/editors/undo_redo.dart
Normal file
@ -0,0 +1,57 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:gitjournal/editors/common.dart';
|
||||
|
||||
// FIXME: Possibly conserve memory by only storing the difference between the
|
||||
// texts? Or by saving it to disk?
|
||||
|
||||
// FIXME: This should probably also have what field was changed?
|
||||
// How does this work for the title and the checklist editor?
|
||||
|
||||
// FIXME: Instead of storing if each note has been modified that should be
|
||||
// be just taken from this class
|
||||
//
|
||||
class UndoRedoStack {
|
||||
var _versions = <TextEditorState>[];
|
||||
int _index = -1;
|
||||
|
||||
/// Returns if UI should be redrawn
|
||||
bool textChanged(TextEditorState es) {
|
||||
var _redo = redoPossible;
|
||||
var _undo = undoPossible;
|
||||
if (_redo) {
|
||||
var i = max(0, _index);
|
||||
_versions.removeRange(i, _versions.length - 1);
|
||||
}
|
||||
if (_versions.isEmpty) {
|
||||
_versions.add(es);
|
||||
_index = _versions.length - 1;
|
||||
return true;
|
||||
}
|
||||
var last = _versions.last;
|
||||
if (last.text == es.text) {
|
||||
return false;
|
||||
}
|
||||
_versions.add(es);
|
||||
_index = _versions.length - 1;
|
||||
|
||||
return redoPossible != _redo || undoPossible != _undo;
|
||||
}
|
||||
|
||||
TextEditorState undo() {
|
||||
var i = _index;
|
||||
_index--;
|
||||
return _versions[i];
|
||||
}
|
||||
|
||||
TextEditorState redo() {
|
||||
_index++;
|
||||
return _versions[_index];
|
||||
}
|
||||
|
||||
bool get undoPossible => _index >= 0;
|
||||
bool get redoPossible => _index < _versions.length - 1;
|
||||
bool get modified => _versions.isNotEmpty;
|
||||
}
|
||||
|
||||
// FIXME: Only save it every x seconds?
|
Reference in New Issue
Block a user