2008-11-18 19 views
5

Estoy escribiendo un editor de texto para Mac OS X. Necesito mostrar caracteres ocultos en un NSTextView (como espacios, pestañas y caracteres especiales). He pasado mucho tiempo buscando cómo hacer esto, pero hasta ahora no he encontrado una respuesta. Si alguien pudiera señalarme en la dirección correcta, estaría agradecido.Mostrar caracteres ocultos en NSTextView

Respuesta

4

Eche un vistazo a la clase NSLayoutManager. Su NSTextView tendrá un administrador de diseño asociado, y el administrador de diseño es responsable de asociar un carácter (espacio, pestaña, etc.) con un glifo (la imagen de ese personaje dibujada en la pantalla).

En su caso, probablemente le interese más el método replaceGlyphAtIndex:withGlyph:, que le permitiría reemplazar glifos individuales.

+0

@ Pol publicó la información más completa sobre una clase difícil de trabajar debido a los escasos documentos. Pero ambos tienen una limitación en el sentido de que este método está en desuso y el método más nuevo parece ser un poco más difícil en el sentido de que usa matrices en C: D Agregaré algunos ejemplos basados ​​en eso. – uchuugaka

0

Lo que he hecho es anular el siguiente método en una subclase 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: 
    } 
}

Me desplazo por el índice de cada carácter. El problema que ahora tengo es cómo determinar qué personaje se encuentra en cada ubicación. ¿Debo usar un método NSLayoutManager o preguntar a NSTextView? ¿Los índices en el primero son los mismos que en el segundo?

Puedo obtener un glifo individual con -glyphAtIndex: pero no puedo averiguar cómo determinar a qué personaje corresponde.

+0

Obtiene el punto de código del personaje de esta manera: NSString * completeString = [[self textStorage] string]; NSUInteger characterIndex = [self characterIndexForGlyphAtIndex: index]; unichar characterToCheck = [completeString characterAtIndex: characterIndex]; – JanX2

2

Resolví el problema de convertir entre NSGlyphs y el unichar correspondiente en NSTextView. El código siguiente funciona muy bien y reemplaza espacios con las balas para texto 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

Quizás - [setShowsControlCharacters NSLayoutManager:] y/o [- setShowsInvisibleCharacters NSLayoutManager:] va a hacer lo que quiera.

3

Escribí un editor de texto hace unos años - aquí hay un código sin sentido que debería hacer que mires (con suerte) la dirección correcta (esta es una subclase de NSLayoutManager por cierto) y sí sé que está goteando como el proverbial fregadero de la cocina) :

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

Aquí hay una aplicación totalmente funcional y limpia

@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 

de instalar, usar:

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

para conocer los nombres de glifo de la fuente, usted tiene que ir a 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

Muchas gracias por compartir esto. – uchuugaka

+0

Publicaré adiciones más adelante para la función de reemplazo de la función en desuso, así como un cambio para evitar problemas con el diseño contiguo. – uchuugaka

+0

Yo agregaría que es mejor ir hoy a Texto principal para la información del nombre de la fuente. CTFont y amigos están mucho mejor. – uchuugaka

0

Aquí está la solución de Pol en 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) 
    } 
} 

y para indicar los nombres de glifo:

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