AppKit: Add Copy Style and Paste Style to CPTextView

CPTextView lacked implementations for copyFont: and pasteFont:
(declared as abstract methods in CPText), causing standard Copy Style and Paste Style menu items to trigger exceptions when invoked.

* AppKit/CPTextView/CPTextView.j:
  - Implement copyFont: to copy character attributes (excluding paragraph style, matching Cocoa) of the initial selection character or current typing attributes to the font pasteboard.
  - Implement pasteFont: to apply copied character attributes to the selection or typing attributes with undo support, preserving individual paragraph styles.
  - Update menu validation logic to enable Copy Style in rich text and Paste Style in editable rich text when the font pasteboard contains valid styling.

* Tests/AppKit/CPTextViewTest.j:
  - Add unit tests covering style copying and pasting with and without active selections.
  - Add test coverage for menu validation.

* Tests/Manual/TextEditor/WORKLIST.md:
  - Record Copy Style and Paste Style as tested.

Copy Style and Paste Style now function consistently with Cocoa.
This commit is contained in:
David Richardson committed 2026-10-24 10:36:58 -06:00
1 parent 9d1504704f
commit 178a7d2aec
3 files changed
+171 -3

No files matched your search

+62
View File
@@ -208,6 +208,68 @@
[self assertTrue:([[textView typingAttributes] objectForKey:CPSuperscriptAttributeName] == nil)];
}
- (void)testCopyAndPasteStyle
{
var font = [CPFont boldSystemFontOfSize:20.0],
color = [CPColor redColor],
paragraphStyle = [[CPParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setAlignment:CPCenterTextAlignment];
[textView setRichText:YES];
[self assertTrue:[theWindow makeFirstResponder:textView]];
[[textView textStorage] addAttributes:@{CPFontAttributeName: font, CPForegroundColorAttributeName: color} range:CPMakeRange(0, 2)];
[[textView textStorage] addAttribute:CPParagraphStyleAttributeName value:paragraphStyle range:CPMakeRange(3, 6)];
[textView setSelectedRange:CPMakeRange(0, 2)];
[textView copyFont:nil];
[textView setSelectedRange:CPMakeRange(3, 6)];
[textView pasteFont:nil];
var storage = [textView textStorage];
[self assert:[font cssString] equals:[[storage attribute:CPFontAttributeName atIndex:5 effectiveRange:nil] cssString]];
[self assert:[color cssString] equals:[[storage attribute:CPForegroundColorAttributeName atIndex:5 effectiveRange:nil] cssString]];
[self assert:CPCenterTextAlignment equals:[[storage attribute:CPParagraphStyleAttributeName atIndex:5 effectiveRange:nil] alignment] message:@"Paste Style keeps the paragraph style"];
[self assertTrue:([storage attribute:CPForegroundColorAttributeName atIndex:9 effectiveRange:nil] == nil) message:@"Paste Style changes only the selection"];
}
- (void)testPasteStyleWithoutSelectionChangesTypingAttributes
{
var font = [CPFont boldSystemFontOfSize:20.0];
[textView setRichText:YES];
[self assertTrue:[theWindow makeFirstResponder:textView]];
[[textView textStorage] addAttribute:CPFontAttributeName value:font range:CPMakeRange(0, 2)];
[textView setSelectedRange:CPMakeRange(0, 2)];
[textView copyFont:nil];
[textView setSelectedRange:CPMakeRange(5, 0)];
[textView pasteFont:nil];
[self assert:[font cssString] equals:[[[textView typingAttributes] objectForKey:CPFontAttributeName] cssString]];
}
- (void)testStyleActionsAreEnabledOnlyInRichText
{
var copyItem = [[CPMenuItem alloc] initWithTitle:@"Copy Style" action:@selector(copyFont:) keyEquivalent:@""],
pasteItem = [[CPMenuItem alloc] initWithTitle:@"Paste Style" action:@selector(pasteFont:) keyEquivalent:@""];
[textView setRichText:NO];
[self assertFalse:[textView validateUserInterfaceItem:copyItem]];
[self assertFalse:[textView validateUserInterfaceItem:pasteItem]];
[textView setRichText:YES];
[textView copyFont:nil];
[self assertTrue:[textView validateUserInterfaceItem:copyItem]];
[self assertTrue:[textView validateUserInterfaceItem:pasteItem]];
[textView setEditable:NO];
[self assertFalse:[textView validateUserInterfaceItem:pasteItem]];
}
- (void)testBaselineActionsAreEnabledOnlyInEditableRichText
{
var item = [[CPMenuItem alloc] initWithTitle:@"Superscript" action:@selector(superscript:) keyEquivalent:@""];