2008-11-18 12 views
5

Ich schreibe einen Texteditor für Mac OS X. Ich muss versteckte Zeichen in einer NSTextView anzeigen (z. B. Leerzeichen, Tabulatoren und Sonderzeichen). Ich habe viel Zeit damit verbracht, nach dem Weg zu suchen, aber bis jetzt habe ich keine Antwort gefunden. Wenn mir jemand in die richtige Richtung zeigen könnte, wäre ich dankbar.Versteckte Zeichen in NSTextView anzeigen

Antwort

4

Werfen Sie einen Blick auf die NSLayoutManager-Klasse. In Ihrer NSTextView ist ein Layout-Manager enthalten, und der Layout-Manager ist dafür verantwortlich, ein Zeichen (Leerzeichen, Tab usw.) mit einem Symbol (dem Bild des auf dem Bildschirm gezeichneten Zeichens) zu verknüpfen.

In Ihrem Fall würden Sie wahrscheinlich am meisten an der Methode replaceGlyphAtIndex:withGlyph: interessiert sein, mit der Sie einzelne Glyphen ersetzen könnten.

+0

@ Pol hat die umfangreicheren Informationen zu einer harten Klasse gepostet, mit der man arbeiten kann, da es nur spärliche Dokumente gibt. Aber beide haben eine Einschränkung darin, dass diese Methode veraltet ist und die neuere Methode etwas schwieriger zu sein scheint, da sie C-Arrays verwendet: D Ich füge ein Beispiel dafür hinzu. – uchuugaka

0

Was ich getan habe, überschreiben die Methode unten in einer NSLayoutManager-Unterklasse.

- (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: 
    } 
}

Ich Schleife durch den Index jedes Zeichens. Das Problem, das ich jetzt habe, ist, wie man bestimmt, welches Zeichen an jedem Ort gefunden wird. Sollte ich eine NSLayoutManager-Methode verwenden oder die NSTextView selbst fragen? Sind Indizes in ersterer gleich wie in letzterer?

Ich kann eine individuelle Glyphe mit -glyphAtIndex bekommen: aber ich kann nicht herausfinden, wie man bestimmt, welchem ​​Zeichen es entspricht.

+0

Sie erhalten den Codepunkt des Zeichens auf diese Weise: NSString * completeString = [[self textStorage] string]; NSUInteger characterIndex = [selbst characterIndexForGlyphAtIndex: index]; unichar characterToCheck = [completeString characterAtIndex: ZeichenIndex]; – JanX2

2

Ich löste das Problem der Konvertierung zwischen NSGlyphs und der entsprechenden Unichar in der NSTextView. Der folgende Code funktioniert wunderbar und ersetzt Leerzeichen mit Kugeln für sichtbaren Text:

- (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

Vielleicht - [NSLayoutManager setShowsControlCharacters:] und/oder - [NSLayoutManager setShowsInvisibleCharacters:] wird tun, was Sie wollen.

3

Ich habe vor ein paar Jahren einen Texteditor geschrieben - hier ist ein bedeutungsloser Code, der dich (hoffentlich) in die richtige Richtung bringt (das ist eine NSLayoutManager-Unterklasse übrigens - und ja, ich weiß, dass es undicht ist wie die sprichwörtliche Spüle) :

- (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

Hier ist eine voll funktionsfähig und sauber Umsetzung

@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 

, Gebrauch zu installieren:

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

Um Schrift Glyphennamen zu finden, müssen Sie in Core Graphics gehen:

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

Vielen Dank für das Teilen. – uchuugaka

+0

Ich werde später Ergänzungen für die Ersetzungsfunktion für die veraltete Funktion sowie eine Änderung hinzufügen, um Probleme mit zusammenhängendem Layout zu vermeiden. – uchuugaka

+0

Ich würde hinzufügen, dass es besser ist, zu Core Text für den Font Name Info heute zu gehen. CTFont und Freunde ist viel besser. – uchuugaka

0

Hier ist die Lösung von Pol in 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) 
    } 
} 

Und die Glyphennamen zur Liste:

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)") 
      } 
     } 
    }