@@ -16,30 +16,30 @@ import TextStory
16
16
/// - Grouping pasted text
17
17
///
18
18
/// If needed, the automatic undo grouping can be overridden using the `beginGrouping()` and `endGrouping()` methods.
19
- class CEUndoManager {
19
+ public class CEUndoManager {
20
20
/// An `UndoManager` subclass that forwards relevant actions to a `CEUndoManager`.
21
21
/// Allows for objects like `STTextView` to use the `UndoManager` API
22
22
/// while CETV manages the undo/redo actions.
23
- class DelegatedUndoManager : UndoManager {
23
+ public class DelegatedUndoManager : UndoManager {
24
24
weak var parent : CEUndoManager ?
25
25
26
- override var canUndo : Bool { parent? . canUndo ?? false }
27
- override var canRedo : Bool { parent? . canRedo ?? false }
26
+ public override var canUndo : Bool { parent? . canUndo ?? false }
27
+ public override var canRedo : Bool { parent? . canRedo ?? false }
28
28
29
- func registerMutation( _ mutation: TextMutation ) {
29
+ public func registerMutation( _ mutation: TextMutation ) {
30
30
parent? . registerMutation ( mutation)
31
31
removeAllActions ( )
32
32
}
33
33
34
- override func undo( ) {
34
+ public override func undo( ) {
35
35
parent? . undo ( )
36
36
}
37
37
38
- override func redo( ) {
38
+ public override func redo( ) {
39
39
parent? . redo ( )
40
40
}
41
41
42
- override func registerUndo( withTarget target: Any , selector: Selector , object anObject: Any ? ) {
42
+ public override func registerUndo( withTarget target: Any , selector: Selector , object anObject: Any ? ) {
43
43
// no-op, but just in case to save resources:
44
44
removeAllActions ( )
45
45
}
@@ -71,36 +71,41 @@ class CEUndoManager {
71
71
/// A stack of operations that can be redone.
72
72
private var redoStack : [ UndoGroup ] = [ ]
73
73
74
- private unowned let textView : STTextView
74
+ internal weak var textView : STTextView ?
75
75
private( set) var isGrouping : Bool = false
76
76
77
- public init ( textView: STTextView ) {
78
- self . textView = textView
77
+ public init ( ) {
79
78
self . manager = DelegatedUndoManager ( )
80
79
manager. parent = self
81
80
}
82
81
83
82
/// Performs an undo operation if there is one available.
84
83
public func undo( ) {
85
- guard let item = undoStack. popLast ( ) else {
84
+ guard let item = undoStack. popLast ( ) ,
85
+ let textView else {
86
86
return
87
87
}
88
88
isUndoing = true
89
89
for mutation in item. mutations. reversed ( ) {
90
+ NotificationCenter . default. post ( name: . NSUndoManagerWillUndoChange, object: self . manager)
90
91
textView. applyMutationNoUndo ( mutation. inverse)
92
+ NotificationCenter . default. post ( name: . NSUndoManagerDidUndoChange, object: self . manager)
91
93
}
92
94
redoStack. append ( item)
93
95
isUndoing = false
94
96
}
95
97
96
98
/// Performs a redo operation if there is one available.
97
99
public func redo( ) {
98
- guard let item = redoStack. popLast ( ) else {
100
+ guard let item = redoStack. popLast ( ) ,
101
+ let textView else {
99
102
return
100
103
}
101
104
isRedoing = true
102
105
for mutation in item. mutations {
106
+ NotificationCenter . default. post ( name: . NSUndoManagerWillRedoChange, object: self . manager)
103
107
textView. applyMutationNoUndo ( mutation. mutation)
108
+ NotificationCenter . default. post ( name: . NSUndoManagerDidRedoChange, object: self . manager)
104
109
}
105
110
undoStack. append ( item)
106
111
isRedoing = false
@@ -117,6 +122,8 @@ class CEUndoManager {
117
122
/// Calling this method while the manager is in an undo/redo operation will result in a no-op.
118
123
/// - Parameter mutation: The mutation to register for undo/redo
119
124
public func registerMutation( _ mutation: TextMutation ) {
125
+ guard let textView else { return }
126
+
120
127
if ( mutation. range. length == 0 && mutation. string. isEmpty) || isUndoing || isRedoing { return }
121
128
let newMutation = UndoGroup . Mutation ( mutation: mutation, inverse: textView. inverseMutation ( for: mutation) )
122
129
if !undoStack. isEmpty, let lastMutation = undoStack. last? . mutations. last {
0 commit comments