How to relayout content of NSTextView so that my tab characters are drawn with width of 4 characters
I'm working with an NSTextView and one of the requirements I have is that a tab character, '\t', shall have the same width as four spaces.
So the text-content would look like this:
AAAA
AAAA - 1 tab
AAAA - 4 spaces
And this is how I accomplish this:
// done when NSTextView first loaded and when
// delegate's textDidBeginEditing gets called: (perhaps overkill, but is a work in progress).
- (void)updateMyTextViewTextAttributes
{
NSMutableParagraphStyle* paragraphStyle = [[myTextView defaultParagraphStyle] mutableCopy];
if (paragraphStyle == nil) {
paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
}
float charWidth = [[myFont screenFontWithRenderingMode:NSFontDefaultRenderingMode] advancementForGlyph:(NSGlyph) ' '].width;
[paragraphStyle setDefaultTabInterval:(charWidth * 4)];
[paragraphStyle setTabStops:[NSArray array]];
[myTextView setDefaultParagraphStyle:para开发者_开发知识库graphStyle];
NSMutableDictionary* typingAttributes = [[myTextView typingAttributes] mutableCopy];
[typingAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
[typingAttributes setObject:scriptFont forKey:NSFontAttributeName];
[myTextView setTypingAttributes:typingAttributes];
}
This allows the appropriate layout to be shown with the initial text as well as keep the typing attributes the same.
The problem is that the end-user can change the font. And when that happens, the sample text becomes misaligned. Much like the below:
[smaller font]
AAAA
AAAA - 1 tab
AAAA - 4 spaces
[larger font]
AAAA
AAAA - 1 tab
AAAA - 4 spaces
I've tried calling myTextView's setNeedsDisplay:YES as I read that it ends up calling NSTextView's setNeedsDisplayInRect:avoidAdditionalLayout with a NO for the avoidAdditionalLayout parameter. This didn't change anything.
I've tried calling my updateMyTextViewTextAttributes call when myTextView has the new myFont set. That doesn't change a thing.
I've also tried telling the layoutManager of myTextView to ensureLayoutForTextContainer for the textContainer of myTextView. No change.
At this point, I'm not sure what to try next. Any suggestions?
Douglas Davidson of Apple provided me with the clues to get to the answer via the cocoa-dev@apple.lists.com email list.
I've resolved the problem by updating the updateMyTextViewTextAttributes function like so:
- (void)updateMyTextViewTextAttributes
{
NSMutableParagraphStyle* paragraphStyle = [[myTextView defaultParagraphStyle] mutableCopy];
if (paragraphStyle == nil) {
paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
}
float charWidth = [[myFont screenFontWithRenderingMode:NSFontDefaultRenderingMode] advancementForGlyph:(NSGlyph) ' '].width;
[paragraphStyle setDefaultTabInterval:(charWidth * 4)];
[paragraphStyle setTabStops:[NSArray array]];
[myTextView setDefaultParagraphStyle:paragraphStyle];
NSMutableDictionary* typingAttributes = [[myTextView typingAttributes] mutableCopy];
[typingAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
[typingAttributes setObject:scriptFont forKey:NSFontAttributeName];
[myTextView setTypingAttributes:typingAttributes];
/** ADDED CODE BELOW **/
NSRange rangeOfChange = NSMakeRange(0, [[myTextView string] length]);
[myTextView shouldChangeTextInRange:rangeOfChange replacementString:nil];
[[myTextView textStorage] setAttributes:typingAttributes range:rangeOfChange];
[myTextView didChangeText];
[paragraphStyle release];
[typingAttributes release];
}
Have you tried to compute space advancement with advancementForGlyph:
directly on the font instead of the screen font ?
float charWidth = [myFont advancementForGlyph:(NSGlyph) ' '].width;
Screen font are not meant to be used directly outside the window server:
Screen fonts are for direct use with the window server only. Never use them with Application Kit objects, such as in setFont: methods. Internally, the Application Kit automatically uses the corresponding screen font for a font object as long as the view is not rotated or scaled.
精彩评论