AppKit: Disable Undo and Redo when a text view does not allow undo

A text view handled undo: and redo: and registered its changes even when allowsUndo was NO, so Edit > Undo stayed enabled and did nothing.

Behaviour now follows canonical Cocoa: a text view that does not allow undo registers nothing and passes undo: and redo: on to the window, and Undo and Redo are enabled only when there is a change to undo or redo.

* CPTextView/CPTextView.j: Register changes only when undo is allowed, stop responding to undo: and redo: when it is not, and validate the Undo and Redo items against the undo manager.

* CPWindow/CPWindow.j: Validate the Undo and Redo items against the window's undo manager.

* Tests/AppKit/CPTextViewTest.j: Add tests for responding to undo:, registering changes and validating the Undo item.

* Tests/Manual/TextEditor/DocumentWindowController.j: Add toggleAllowsUndo: and validateMenuItem:, so that undo can be turned on and off for the active document.

* Tests/Manual/TextEditor/Resources/MainMenu.xib, Resources/MainMenu.cib: Add Edit > Allow Undo.

* Tests/Manual/TextEditor/WORKLIST.md: Remove the entry for this gap, record Allow Undo as tested, and add the key equivalent behaviour of disabled menu items for discussion.
This commit is contained in:
David Richardson committed 2026-10-23 17:20:27 -06:00
1 parent 92b8039c71
commit 66ea130c8d
7 files changed
+171 -14

No files matched your search

+50
View File
@@ -136,6 +136,56 @@
[delegateSpy verifyThatAllExpectationsHaveBeenMet];
}
- (void)testTextViewRespondsToUndoOnlyWhenUndoIsAllowed
{
[textView setAllowsUndo:YES];
[self assertTrue:[textView respondsToSelector:@selector(undo:)]];
[self assertTrue:[textView respondsToSelector:@selector(redo:)]];
[textView setAllowsUndo:NO];
[self assertFalse:[textView respondsToSelector:@selector(undo:)] message:@"undo: should continue to the window"];
[self assertFalse:[textView respondsToSelector:@selector(redo:)] message:@"redo: should continue to the window"];
}
- (void)testTextViewRegistersUndoOnlyWhenUndoIsAllowed
{
var undoManager = [theWindow undoManager];
[self assertTrue:[theWindow makeFirstResponder:textView]];
[textView setAllowsUndo:NO];
[undoManager removeAllActions];
[textView insertText:@"Not recorded"];
[self assertFalse:[undoManager canUndo] message:@"a text view that does not allow undo should register nothing"];
[textView setAllowsUndo:YES];
[textView insertText:@"Recorded"];
[self assertTrue:[undoManager canUndo] message:@"a text view that allows undo should register its change"];
}
- (void)testUndoItemIsEnabledOnlyWhenThereIsAChangeToUndo
{
var undoManager = [theWindow undoManager],
undoItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@""],
redoItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@""];
[self assertTrue:[theWindow makeFirstResponder:textView]];
[undoManager removeAllActions];
[self assertFalse:[textView validateUserInterfaceItem:undoItem]];
[self assertFalse:[theWindow validateUserInterfaceItem:undoItem]];
[self assertFalse:[textView validateUserInterfaceItem:redoItem]];
[textView insertText:@"Recorded"];
[self assertTrue:[textView validateUserInterfaceItem:undoItem]];
[self assertTrue:[theWindow validateUserInterfaceItem:undoItem]];
}
@end
@implementation CPTextViewTest (CPTextViewTestDelegate)