2011-04-27 10 views
6

La pregunta puede sonar extraña pero he estado luchando con ella durante unos días.Cómo agregar lista de balas mediante programación a NSTextView

Tengo un NSTextView que puede mostrar texto con algunas opciones de formato. Una de ellas es la posibilidad de activar/desactivar la lista de viñetas (la más fácil) para una selección o fila actual.

Sé que hay un método orderFrontListPanel: en NSTextView que abre la ventana con los parámetros de lista disponibles para seleccionar y editar (como en TextView cuando presiona Menú-> Formato-> Lista ...). Ya he descubierto e implementado la adición de viñetas a mano y el NSTextView parece comportarse correctamente con ellos casi. Al decir casi quiero decir que conserva las posiciones de las pestañas, continúa la lista en 'enter', etc. Pero hay algunas fallas menores que no me satisfacen y difieren de la implementación estándar.

He intentado encontrar la forma predeterminada de establecer listas programáticamente como si se hiciera a través del menú 'Lista ...' sin suerte.

Pido ayuda, cada pequeña información será apreciada :).

P.S .: He investigado el código fuente de TextView, he encontrado muchas cosas interesantes pero no hay señales ni pistas sobre cómo habilitar las listas mediante programación.

actualización

sigue investigando. Descubrí que cuando envía orderFrontListPanel: a su NSTextView y luego selecciona viñetas y presiona enter, no se envían mensajes especiales a NSTextView. Esto significa que la lista de viñetas se puede construir en algún lugar dentro de este panel emergente y establece directamente al contenedor de texto de Vista de Texto ...

Respuesta

12

Dos métodos de agregar mediante programación una lista con viñetas a una NSTextView:

Método 1:

los siguientes enlaces me llevaron a este primer método, pero es innecesariamente rotonda a menos que desee utilizar algún glifo especial no Unicode de la bala:

Esto requiere: (1) un controlador de disposición subclases que sustituye el glifo de bala por algún carácter arbitrario; y (2) un estilo de párrafo con firstLineHeadIndent, una tabulación ligeramente más grande que esa sangría, y un headIndent para líneas envolventes que combina los dos.

El controlador de distribución es el siguiente:

#import <Foundation/Foundation.h> 

@interface TickerLayoutManager : NSLayoutManager { 

// Might as well let this class hold all the fonts used by the progress ticker. 
// That way they're all defined in one place, the init method. 
NSFont *fontNormal; 
NSFont *fontIndent; // smaller, for indented lines 
NSFont *fontBold; 

NSGlyph glyphBullet; 
CGFloat fWidthGlyphPlusSpace; 

} 

@property (nonatomic, retain) NSFont *fontNormal; 
@property (nonatomic, retain) NSFont *fontIndent; 
@property (nonatomic, retain) NSFont *fontBold; 
@property NSGlyph glyphBullet; 
@property CGFloat fWidthGlyphPlusSpace; 

@end 

#import "TickerLayoutManager.h" 

@implementation TickerLayoutManager 

@synthesize fontNormal; 
@synthesize fontIndent; 
@synthesize fontBold; 
@synthesize glyphBullet; 
@synthesize fWidthGlyphPlusSpace; 

- (id)init { 
    self = [super init]; 
    if (self) { 
     self.fontNormal = [NSFont fontWithName:@"Baskerville" size:14.0f]; 
     self.fontIndent = [NSFont fontWithName:@"Baskerville" size:12.0f]; 
     self.fontBold = [NSFont fontWithName:@"Baskerville Bold" size:14.0f]; 
     // Get the bullet glyph. 
     self.glyphBullet = [self.fontIndent glyphWithName:@"bullet"]; 
     // To determine its point size, put it in a Bezier path and take its bounds. 
     NSBezierPath *bezierPath = [NSBezierPath bezierPath]; 
     [bezierPath moveToPoint:NSMakePoint(0.0f, 0.0f)]; // prevents "No current point for line" exception 
     [bezierPath appendBezierPathWithGlyph:self.glyphBullet inFont:self.fontIndent]; 
     NSRect rectGlyphOutline = [bezierPath bounds]; 
     // The bullet should be followed with a space, so get the combined size... 
     NSSize sizeSpace = [@" " sizeWithAttributes:[NSDictionary dictionaryWithObject:self.fontIndent forKey:NSFontAttributeName]]; 
     self.fWidthGlyphPlusSpace = rectGlyphOutline.size.width + sizeSpace.width; 
     // ...which is for some reason inexact. If this number is too low, your bulleted text will be thrown to the line below, so add some boost. 
     self.fWidthGlyphPlusSpace *= 1.5; // 
    } 

    return self; 
} 

- (void)drawGlyphsForGlyphRange:(NSRange)range 
         atPoint:(NSPoint)origin { 

    // The following prints only once, even though the textview's string is set 4 times, so this implementation is not too expensive. 
    printf("\nCalling TickerLayoutManager's drawGlyphs method."); 

    NSString *string = [[self textStorage] string]; 
    for (int i = range.location; i < range.length; i++) { 
     // Replace all occurrences of the ">" char with the bullet glyph. 
     if ([string characterAtIndex:i] == '>') 
      [self replaceGlyphAtIndex:i withGlyph:self.glyphBullet]; 
    } 

    [super drawGlyphsForGlyphRange:range atPoint:origin]; 
} 

@end 

asignar el controlador de distribución a la Vista de Texto en la ventana/awakeFromNib vista del controlador, así:

- (void) awakeFromNib { 

    // regular setup... 

    // Give the ticker display NSTextView its subclassed layout manager. 
    TickerLayoutManager *newLayoutMgr = [[TickerLayoutManager alloc] init]; 
    NSTextContainer *textContainer = [self.txvProgressTicker textContainer]; 
    // Use "replaceLM" rather than "setLM," in order to keep shared relnshps intact. 
    [textContainer replaceLayoutManager:newLayoutMgr]; 
    [newLayoutMgr release]; 
    // (Note: It is possible that all text-displaying controls in this class’s window will share this text container, as they would a field editor (a textview), although the fact that the ticker display is itself a textview might isolate it. Apple's "Text System Overview" is not clear on this point.) 

} 

y luego añadir un método algo parecido esto:

- (void) addProgressTickerLine:(NSString *)string 
        inStyle:(uint8_t)uiStyle { 

    // Null check. 
    if (!string) 
     return; 

    // Prepare the font. 
    // (As noted above, TickerLayoutManager holds all 3 ticker display fonts.) 
    NSFont *font = nil; 
    TickerLayoutManager *tickerLayoutMgr = (TickerLayoutManager *)[self.txvProgressTicker layoutManager]; 
    switch (uiStyle) { 
     case kTickerStyleNormal: 
      font = tickerLayoutMgr.fontNormal; 
      break; 
     case kTickerStyleIndent: 
      font = tickerLayoutMgr.fontIndent; 
      break; 
     case kTickerStyleBold: 
      font = tickerLayoutMgr.fontBold; 
      break; 
     default: 
      font = tickerLayoutMgr.fontNormal; 
      break; 
    } 


    // Prepare the paragraph style, to govern indentation.  
    // CAUTION: If you propertize it for re-use, make sure you don't mutate it once it has been assigned to an attributed string. (See warning in class ref.) 
    // At the same time, add the initial line break and, if indented, the tab. 
    NSMutableParagraphStyle *paragStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; // ALLOC 
    [paragStyle setAlignment:NSLeftTextAlignment]; // default, but just in case 
    if (uiStyle == kTickerStyleIndent) { 
     // (The custom layout mgr will replace ‘>’ char with a bullet, so it should be followed with an extra space.) 
     string = [@"\n>\t" stringByAppendingString:string]; 
     // Indent the first line up to where the bullet should appear. 
     [paragStyle setFirstLineHeadIndent:15.0f]; 
     // Define a tab stop to the right of the bullet glyph. 
     NSTextTab *textTabFllwgBullet = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:15.0f + tickerLayoutMgr.fWidthGlyphPlusSpace]; 
     [paragStyle setTabStops:[NSArray arrayWithObject:textTabFllwgBullet]]; 
     [textTabFllwgBullet release]; 
     // Set the indentation for the wrapped lines to the same place as the tab stop. 
     [paragStyle setHeadIndent:15.0f + tickerLayoutMgr.fWidthGlyphPlusSpace]; 
    } 
    else { 
     string = [@"\n" stringByAppendingString:string]; 
    } 


    // PUT IT ALL TOGETHER. 
    // Combine the above into a dictionary of attributes. 
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 
          font, NSFontAttributeName, 
          paragStyle, NSParagraphStyleAttributeName, 
          nil]; 
    // Use the attributes dictionary to make an attributed string out of the plain string. 
    NSAttributedString *attrs = [[NSAttributedString alloc] initWithString:string attributes:dict]; // ALLOC 
    // Append the attributed string to the ticker display. 
    [[self.txvProgressTicker textStorage] appendAttributedString:attrs]; 

    // RELEASE 
    [attrs release]; 
    [paragStyle release]; 

} 

probarlo:

NSString *sTicker = NSLocalizedString(@"First normal line of ticker should wrap to left margin", @"First normal line of ticker should wrap to left margin"); 
[self addProgressTickerLine:sTicker inStyle:kTickerStyleNormal]; 
sTicker = NSLocalizedString(@"Indented ticker line should have bullet point and should wrap farther to right.", @"Indented ticker line should have bullet point and should wrap farther to right."); 
[self addProgressTickerLine:sTicker inStyle:kTickerStyleIndent]; 
sTicker = NSLocalizedString(@"Try a second indented line, to make sure both line up.", @"Try a second indented line, to make sure both line up."); 
[self addProgressTickerLine:sTicker inStyle:kTickerStyleIndent]; 
sTicker = NSLocalizedString(@"Final bold line", @"Final bold line"); 
[self addProgressTickerLine:sTicker inStyle:kTickerStyleBold]; 

Se obtiene lo siguiente:

enter image description here

Método 2:

Pero la bala es un char Unicode regular, en hexadecimal 2022. Así que usted puede ponerlo en la cadena directamente y obtener una medición exacta, así:

NSString *stringWithGlyph = [NSString stringWithUTF8String:"\u2022"]; 
    NSString *stringWithGlyphPlusSpace = [stringWithGlyph stringByAppendingString:@" "]; 
    NSSize sizeGlyphPlusSpace = [stringWithGlyphPlusSpace sizeWithAttributes:[NSDictionary dictionaryWithObject:self.fontIndent forKey:NSFontAttributeName]]; 
    self.fWidthGlyphPlusSpace = sizeGlyphPlusSpace.width; 

Por lo tanto, no es necesario el administrador de diseño personalizado. Simplemente configure las sangrías paragStyle como se indica arriba, y agregue su cadena de texto a una cadena que contenga la línea return + bullet char + space (o + tab, en cuyo caso aún querrá esa tabulación).

El uso de un espacio, esto produjo un resultado más ajustado:

enter image description here

desea utilizar un carácter distinto de la bala? Aquí hay un buen gráfico Unicode: http://www.danshort.com/unicode/

+0

Gracias por su gran respuesta, puede haber sido un poco tarde, pero este problema aún no se ha resuelto y el módulo está envuelto en la declaración DEBUG :). Trataré de aplicar el segundo método, creo que es la forma más fácil de lograr resultados. El problema fue (por lo que recuerdo) que agregar glifos a mano y agregarlos a través del menú dio resultados diferentes, pero lo verificaré dos veces. – GregoryM

+0

De nada. Me alegro de que el experimento aún sea relevante, espero que funcione para ti. – Wienke

Cuestiones relacionadas