2008-11-18 7 views
5

J'écris un éditeur de texte pour Mac OS X. J'ai besoin d'afficher les caractères cachés dans un NSTextView (tels que les espaces, les tabulations et les caractères spéciaux). J'ai passé beaucoup de temps à chercher comment faire cela mais jusqu'ici je n'ai pas trouvé de réponse. Si quelqu'un pouvait me diriger dans la bonne direction, je serais reconnaissant.Afficher les caractères cachés dans NSTextView

Répondre

4

Jetez un oeil à la classe NSLayoutManager. Votre NSTextView sera associé à un gestionnaire de disposition, et le gestionnaire de disposition est responsable de l'association d'un caractère (espace, tabulation, etc.) avec un glyphe (l'image de ce personnage est dessinée à l'écran).

Dans votre cas, la méthode replaceGlyphAtIndex:withGlyph: vous intéresserait probablement le plus, car elle vous permettrait de remplacer des glyphes individuels.

+0

@ Pol posté l'information plus complète sur un cours difficile à travailler en raison de documents rares. Mais les deux ont une limitation en ce que cette méthode est obsolète et la nouvelle méthode semble un peu plus difficile en ce sens qu'elle utilise des tableaux C: D Je vais ajouter un exemple basé sur cela. – uchuugaka

0

Ce que j'ai fait, c'est remplacer la méthode ci-dessous dans une sous-classe NSLayoutManager.

- (void)drawGlyphsForGlyphRange:(NSRange)range atPoint:(NSPoint)origin 
{ 
    [super drawGlyphsForGlyphRange:range atPoint:origin]; 

    for (int i = range.location; i != range.location + range.length; i++) 
    { 
     // test each character in this range 
     // if appropriate replace it with -replaceGlyphAtIndex:withGlyph: 
    } 
}

Je boucle l'index de chaque caractère. Le problème que j'ai maintenant est comment déterminer quel caractère est trouvé à chaque endroit. Dois-je utiliser une méthode NSLayoutManager ou demander à NSTextView lui-même? Les indices dans le premier sont-ils les mêmes que dans le second?

Je peux obtenir un glyphe individuel avec -glyphAtIndex: mais je n'arrive pas à comprendre comment déterminer le caractère auquel il correspond.

+0

Vous obtenez le point de code du caractère de cette façon: NSString * completeString = [[self textStorage] string]; NSUInteger characterIndex = [self characterIndexForGlyphAtIndex: index]; unichar characterToCheck = [completeString characterAtIndex: characterIndex]; – JanX2

2

J'ai résolu le problème de la conversion entre les NSGlyphs et l'Unichar correspondant dans NSTextView. Le code ci-dessous fonctionne magnifiquement et remplace les espaces de balles pour le texte visible:

- (void)drawGlyphsForGlyphRange:(NSRange)range atPoint:(NSPoint)origin 
{ 
    NSFont *font = [[CURRENT_TEXT_VIEW typingAttributes] 
         objectForKey:NSFontAttributeName]; 

    NSGlyph bullet = [font glyphWithName:@"bullet"]; 

    for (int i = range.location; i != range.location + range.length; i++) 
    { 
     unsigned charIndex = [self characterIndexForGlyphAtIndex:i]; 

     unichar c =[[[self textStorage] string] characterAtIndex:charIndex]; 

     if (c == ' ') 
      [self replaceGlyphAtIndex:charIndex withGlyph:bullet]; 
    } 

    [super drawGlyphsForGlyphRange:range atPoint:origin]; 
}
2

Peut-être - [NSLayoutManager setShowsControlCharacters:] et/ou - [NSLayoutManager setShowsInvisibleCharacters:] fera ce que vous voulez.

3

J'ai écrit un éditeur de texte il y a quelques années - voici un code sans signification qui devrait vous aider à regarder dans la bonne direction (c'est une sous-classe NSLayoutManager btw - et oui je sais qu'il fuit comme l'évier proverbial) :

- (void)drawGlyphsForGlyphRange:(NSRange)glyphRange atPoint:(NSPoint)containerOrigin 
{ 
    if ([[[[MJDocumentController sharedDocumentController] currentDocument] editor] showInvisibles]) 
    { 
     //init glyphs 
     unichar crlf = 0x00B6; 
     NSString *CRLF = [[NSString alloc] initWithCharacters:&crlf length:1]; 
     unichar space = 0x00B7; 
     NSString *SPACE = [[NSString alloc] initWithCharacters:&space length:1]; 
     unichar tab = 0x2192; 
     NSString *TAB = [[NSString alloc] initWithCharacters:&tab length:1]; 

     NSString *docContents = [[self textStorage] string]; 
     NSString *glyph; 
     NSPoint glyphPoint; 
     NSRect glyphRect; 
     NSDictionary *attr = [[NSDictionary alloc] initWithObjectsAndKeys:[NSUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"invisiblesColor"]], NSForegroundColorAttributeName, nil]; 

     //loop thru current range, drawing glyphs 
     int i; 
     for (i = glyphRange.location; i < NSMaxRange(glyphRange); i++) 
     { 
      glyph = @""; 

      //look for special chars 
      switch ([docContents characterAtIndex:i]) 
      { 
       //space 
       case ' ': 
        glyph = SPACE; 
        break; 

       //tab 
       case '\t': 
        glyph = TAB; 
        break; 

       //eol 
       case 0x2028: 
       case 0x2029: 
       case '\n': 
       case '\r': 
        glyph = CRLF; 
        break; 

       //do nothing 
       default: 
        glyph = @""; 
        break;     
      } 

      //should we draw? 
      if ([glyph length]) 
      { 
       glyphPoint = [self locationForGlyphAtIndex:i]; 
       glyphRect = [self lineFragmentRectForGlyphAtIndex:i effectiveRange:NULL]; 
       glyphPoint.x += glyphRect.origin.x; 
       glyphPoint.y = glyphRect.origin.y; 
       [glyph drawAtPoint:glyphPoint withAttributes:attr]; 
      } 
     } 
    } 

    [super drawGlyphsForGlyphRange:glyphRange atPoint:containerOrigin]; 
} 
4

est ici une mise en œuvre pleinement et de propreté

@interface GILayoutManager : NSLayoutManager 
@end 

@implementation GILayoutManager 

- (void)drawGlyphsForGlyphRange:(NSRange)range atPoint:(NSPoint)point { 
    NSTextStorage* storage = self.textStorage; 
    NSString* string = storage.string; 
    for (NSUInteger glyphIndex = range.location; glyphIndex < range.location + range.length; glyphIndex++) { 
    NSUInteger characterIndex = [self characterIndexForGlyphAtIndex: glyphIndex]; 
    switch ([string characterAtIndex:characterIndex]) { 

     case ' ': { 
     NSFont* font = [storage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL]; 
     [self replaceGlyphAtIndex:glyphIndex withGlyph:[font glyphWithName:@"periodcentered"]]; 
     break; 
     } 

     case '\n': { 
     NSFont* font = [storage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL]; 
     [self replaceGlyphAtIndex:glyphIndex withGlyph:[font glyphWithName:@"carriagereturn"]]; 
     break; 
     } 

    } 
    } 

    [super drawGlyphsForGlyphRange:range atPoint:point]; 
} 

@end 

Pour installer, utilisez:

[myTextView.textContainer replaceLayoutManager:[[GILayoutManager alloc] init]]; 

Pour trouver des noms de glyphe de police, vous devez aller à CoreGraphics:

CGFontRef font = CGFontCreateWithFontName(CFSTR("Menlo-Regular")); 
for (size_t i = 0; i < CGFontGetNumberOfGlyphs(font); ++i) { 
    printf("%s\n", [CFBridgingRelease(CGFontCopyGlyphNameForGlyph(font, i)) UTF8String]); 
} 
+0

Merci beaucoup de partager cela. – uchuugaka

+0

Je posterai des ajouts plus tard pour la fonction de remplacement de la fonction obsolète ainsi qu'un changement pour éviter les problèmes avec la disposition contiguë. – uchuugaka

+0

Je voudrais ajouter qu'il est préférable d'aller au texte de base pour les informations de nom de police aujourd'hui. CTFont et ses amis vont beaucoup mieux. – uchuugaka

0

Voici la solution de Pol Swift:

class MyLayoutManager: NSLayoutManager { 
    override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) { 
     if let storage = self.textStorage { 
      let s = storage.string 
      let startIndex = s.startIndex 
      for var glyphIndex = glyphsToShow.location; glyphIndex < glyphsToShow.location + glyphsToShow.length; glyphIndex++ { 
       let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex) 
       let ch = s[startIndex.advancedBy(characterIndex)] 
       switch ch { 
       case " ": 
        let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil) 
        if let font = attrs[NSFontAttributeName] { 
         let g = font.glyphWithName("periodcentered") 
         self.replaceGlyphAtIndex(glyphIndex, withGlyph: g) 
        } 
       case "\n": 
        let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil) 
        if let font = attrs[NSFontAttributeName] { 
//      let g = font.glyphWithName("carriagereturn") 
         let g = font.glyphWithName("paragraph") 
         self.replaceGlyphAtIndex(glyphIndex, withGlyph: g) 
        } 
       case "\t": 
        let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil) 
        if let font = attrs[NSFontAttributeName] { 
         let g = font.glyphWithName("arrowdblright") 
         self.replaceGlyphAtIndex(glyphIndex, withGlyph: g) 
        } 
       default: 
        break 
       } 
      } 
     } 
     super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin) 
    } 
} 

Et d'énumérer les noms de glyphe:

func listFonts() { 
     let font = CGFontCreateWithFontName("Menlo-Regular") 
     for var i:UInt16 = 0; i < UInt16(CGFontGetNumberOfGlyphs(font)); i++ { 
      if let name = CGFontCopyGlyphNameForGlyph(font, i) { 
       print("name: \(name) at index \(i)") 
      } 
     } 
    } 
Questions connexes