From befbbb9c3b9a2d32cd323a7474a44090e0d7c998 Mon Sep 17 00:00:00 2001 From: David Richardson Date: Sat, 24 Oct 2026 12:59:18 -0600 Subject: [PATCH] AppKit: Apply font changes without a selection to CPTextView typing attributes When no text was selected, CPTextView's changeFont: implementation erroneously extracted the font to convert from the text storage at the insertion point rather than from current typing attributes. Consequently, repeated adjustments like Bigger or Smaller failed to accumulate, blank document font sizing remained locked after one step, and output arguments risked overwriting selection state. * AppKit/CPTextView/CPTextView.j: - Update changeFont: to derive and convert fonts directly from typing attributes when no selection is active, and post corresponding typing attribute update notifications. - Eliminate selection range output arguments that could inadvertently modify active selection bounds. * Tests/AppKit/CPTextViewTest.j: - Add unit tests verifying that repeated font modifications accumulate correctly at zero-length insertion points. * Tests/Manual/TextEditor/WORKLIST.md: - Mark Bigger and Smaller actions without a selection as tested. Bigger and Smaller menu commands now correctly compound font size adjustments for subsequent typed text, matching Cocoa behavior. --- AppKit/CPTextView/CPTextView.j | 8 ++++---- Tests/AppKit/CPTextViewTest.j | 29 +++++++++++++++++++++++++++++ Tests/Manual/TextEditor/WORKLIST.md | 1 + 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/AppKit/CPTextView/CPTextView.j b/AppKit/CPTextView/CPTextView.j index 0c4f4491d..3d579e14d 100644 --- a/AppKit/CPTextView/CPTextView.j +++ b/AppKit/CPTextView/CPTextView.j @@ -2198,11 +2198,11 @@ Sets the selection to a range of characters in response to user action. } else { - attributes = [_textStorage attributesAtIndex:_selectionRange.location - longestEffectiveRange:_selectionRange - inRange:_selectionRange]; - oldFont = [attributes objectForKey:CPFontAttributeName] || [self font]; + // As in Cocoa, without a selection the change applies to the typing attributes, so + // repeated changes, such as Bigger, build on each other. + oldFont = [_typingAttributes objectForKey:CPFontAttributeName] || [self font]; [_typingAttributes setObject:[sender convertFont:oldFont] forKey:CPFontAttributeName]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self]; } } else diff --git a/Tests/AppKit/CPTextViewTest.j b/Tests/AppKit/CPTextViewTest.j index c30247030..8af19a6ce 100644 --- a/Tests/AppKit/CPTextViewTest.j +++ b/Tests/AppKit/CPTextViewTest.j @@ -254,6 +254,23 @@ [self assertTrue:([[CPPasteboard generalPasteboard] stringForType:_CPASPboardType] == nil) message:@"the out of date rich text is removed"]; } +- (void)testRepeatedFontChangeWithoutSelectionAccumulates +{ + var biggerSender = [[_CPTextViewTestBiggerFontSender alloc] init], + startSize = [[textView font] size]; + + [textView setRichText:YES]; + [textView setString:@""]; + [self assertTrue:[theWindow makeFirstResponder:textView]]; + [textView setSelectedRange:CPMakeRange(0, 0)]; + + [textView changeFont:biggerSender]; + [textView changeFont:biggerSender]; + [textView changeFont:biggerSender]; + + [self assert:startSize + 3.0 equals:[[[textView typingAttributes] objectForKey:CPFontAttributeName] size]]; +} + - (void)testCopyAndPasteStyle { var font = [CPFont boldSystemFontOfSize:20.0], @@ -399,3 +416,15 @@ } @end + +/* + Converts a font as the font manager's Bigger action does, one point larger. +*/ +@implementation _CPTextViewTestBiggerFontSender : CPObject + +- (CPFont)convertFont:(CPFont)aFont +{ + return [[CPFontManager sharedFontManager] convertFont:aFont toSize:[aFont size] + 1.0]; +} + +@end diff --git a/Tests/Manual/TextEditor/WORKLIST.md b/Tests/Manual/TextEditor/WORKLIST.md index 651d380ac..b618c20ac 100644 --- a/Tests/Manual/TextEditor/WORKLIST.md +++ b/Tests/Manual/TextEditor/WORKLIST.md @@ -85,4 +85,5 @@ How the text system works, for the entries below: `CPTextView` does not use CSS - Edit > Copy, Cut and Paste from the menu: plain text moves to and from other applications through the system clipboard; text copied and pasted within the application keeps its style. Command-V pastes text copied in another application, not an earlier copy within the application. - Edit > Insert Image…: a JPEG or PNG image is inserted inside the text and at the end of the text. - A wide image fits the text width, and scales with the window without growing beyond its natural size. At the end of the text it is not clipped, and the scroll bars follow it. +- Format > Font > Bigger and Smaller (Command-+ and Command--) without a selection change the size of the text typed next, step by step, also in a blank document. - Builds with both the legacy toolchain and capp-build.