Martin, Ecir, and Quincey:  

Thank you all! All three answers turned out to be helpful. (Quincey, you led me 
to learn about how to get attributed substrings, should I ever need to do it.)

Martin, your chief difference from my code seems be calling the layout 
manager’s ensureGlyphsForCharacterRange: in order to synch up the layout 
manager with the current content of text storage. I took that and ran with it. 
Here is the resulting code that works for me, which I donate to the public 
domain in case anyone else finds it useful.

The methods below are found in a subclass of NSTextView:  

/// Set temporary foreground color for special characters in the given range  

-(void)setTemporaryForegroundColor:(NSColor*)color  
              forSpecialCharacters:(NSCharacterSet*)characterSet
                           inRange:(NSRange)range
{
  HighlightInfo* hi = [[HighlightInfo alloc] initWithColor:color
                                              characterSet:characterSet
                                                     range:range];

  // If we don't delay for a half-second, Apple's Smart Quotes stop working.  
  // I think this is a bug: why should altering temporary attributes lock out 
substitution?

  static double smartQuoteDelay = 0.5;  

  [self performSelector:@selector(setTemporaryForegroundColor:)  
             withObject:hi
             afterDelay:smartQuoteDelay];
}

/// Set temporary foreground color according to information passed in 
HighlightInfo  
/// @attention Keep this private and always call via 
performSelector:withObject:afterDelay:

-(void)setTemporaryForegroundColor:(HighlightInfo*)hi  
{
  NSLayoutManager* lm = self.layoutManager;
  NSTextStorage* ts = self.textStorage;

  NSRange searchRange = hi.range;  
  NSUInteger beyondIx = searchRange.location + searchRange.length;

  // Synchronize layoutManager with textStorage: otherwise temp attribute 
ranges don't agree  

  [lm ensureGlyphsForCharacterRange:searchRange];  
  [lm removeTemporaryAttribute:NSForegroundColorAttributeName
             forCharacterRange:searchRange];

  // Repeatedly search and apply temporary attributes to special characters in 
search range  

  NSString* str = ts.string;  

  //NSLog( @"Searching for special chars in: %@", [str 
substringWithRange:searchRange]);  

  NSCharacterSet* set = hi.characterSet;  
  NSDictionary* tempAttrs = @{ NSForegroundColorAttributeName : hi.color };

  while ( true ) {  
    NSRange foundRange = [str rangeOfCharacterFromSet:set
                                              options:0
                                                range:searchRange];
    if ( foundRange.location == NSNotFound ) {
      break;
    }
    [lm setTemporaryAttributes:tempAttrs forCharacterRange:foundRange];
    NSUInteger startIx = foundRange.location + foundRange.length;
    if ( startIx >= beyondIx ) {
      break;
    }
    searchRange = NSMakeRange( startIx, beyondIx - startIx );
  }

}

The definition of HighlightInfo should be obvious, but here it is anyway:

@interface HighlightInfo : NSObject  

@property (nonatomic) NSColor* color;  
@property (nonatomic) NSCharacterSet* characterSet;
@property (nonatomic) NSRange range;

-(instancetype)initWithColor:(NSColor*)color  
                characterSet:(NSCharacterSet*)characterSet
                       range:(NSRange)range;

@end

Cheers and happy holidays to all!

—

Charles Jenkins

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to