AppKit: Add CPTextAttachment and image support to CPTextView

AppKit lacked a public text attachment class, preventing rich text documents
from embedding or inserting images.

* AppKit/CPTextView/CPTextAttachment.j:
  - Implement CPTextAttachment (modelled on NSTextAttachment) supporting
    initialization via data and file type, contents, file types, images,
    bounds, and keyed archiving.
  - Adopt a TextKit 2-style view rendering architecture that scales images
    to fit text container width and triggers layout updates upon asynchronous loading.

* AppKit/CPTextView/CPTextStorage.j:
  - Update attributedStringWithAttachment: to accept CPTextAttachment instances
    alongside direct views, routing rendering through existing attachment pathways.

* AppKit/CPTextView/CPTextView.j:
  - Implement the insertImage: action (invoking browser file pickers) and
    Command-V paste handling for images.
  - Configure inserted attachments with responsive view instances scaled to
    text width, omitting live views during pasteboard archiving.

* AppKit/CPTextView/CPTextContainer.j:
  - Adjust attachment bounds to match updated container widths during layout invalidation.

* AppKit/AppKit.j:
  - Export CPTextAttachment.

* Tests/AppKit/CPTextAttachmentTest.j:
  - Add test coverage for data URLs, archiving, width scaling, insertion,
    and serialized text output.

* Tests/Manual/TextEditor/Resources/MainMenu.xib, MainMenu.cib:
  - Add Edit > Insert Image… menu item.

* Tests/Manual/TextEditor/WORKLIST.md:
  - Mark image insertion testing as completed; log remaining image tasks as open.

Rich text documents now support embedded images inserted via file selection or clipboard paste.
This commit is contained in:
David Richardson committed 2026-10-24 12:34:51 -06:00
1 parent 96cf42c763
commit df78974183
9 files changed
+760 -10

No files matched your search

+13 -5
View File
@@ -26,6 +26,7 @@
@import "CPText.j"
@import "CPFont.j"
@import "CPTextAttachment.j"
@class CPLayoutManager;
@@ -263,16 +264,23 @@ var CPTextStorageDelegate_textStorageWillProcessEditing_ = 1 << 1,
}
/*!
Returns an instance of CPTextStorage that contains the provided instance of CPView.
This can be used to insert arbitrary views into the text. These views are treated as individual characters during editing.
This works only with views that conform to the CPCoding protocol
Returns an instance of CPTextStorage that contains one attachment character for anAttachment.
The attachment is treated as an individual character during editing.
anAttachment is either a CPTextAttachment, as in Cocoa, or a view. A CPTextAttachment is
stored as the character's CPAttachmentAttributeName attribute, and its view is laid out as
the character. A view is inserted into the text directly; this works only with views that
conform to the CPCoding protocol.
*/
+ (id)attributedStringWithAttachment:(CPView)someView
+ (id)attributedStringWithAttachment:(id)anAttachment
{
var result = [[self alloc] initWithString:_CPAttachmentCharacterAsString];
[result setAttributes:@{_CPAttachmentView:someView} range:CPMakeRange(0, 1)];
if ([anAttachment isKindOfClass:[CPTextAttachment class]])
[result setAttributes:@{CPAttachmentAttributeName:anAttachment, _CPAttachmentView:[anAttachment _attachmentView]} range:CPMakeRange(0, 1)];
else
[result setAttributes:@{_CPAttachmentView:anAttachment} range:CPMakeRange(0, 1)];
return result;
}