[英]How to programmatically add bullet list to NSTextView
這個問題可能聽起來很奇怪,但我已經掙扎了好幾天了。
我有一個NSTextView,可以顯示一些帶有一些格式選項的文本。 其中之一是能夠為選擇或當前行打開/關閉子彈列表(最簡單的一個)。
我知道在NSTextView上有一個orderFrontListPanel:方法打開窗口,其中包含可供選擇和編輯的可用列表參數(例如,當您按Menu-> Format-> List ...時在TextView中)。 我已經想出並實現了手動添加項目符號,NSTextView似乎幾乎正確地使用它們。 通過說幾乎我的意思是它保留標簽位置,繼續“輸入”列表,等等。但是有一些小故障不適合我,不同於標准實施。
我試圖找到以編程方式設置列表的默認方式,就像通過“列表...”菜單完成一樣沒有運氣。
我請求幫助,每一點點的信息都會受到贊賞:)。
PS:我已經查看了TextView源代碼,發現了很多有趣但沒有跡象或線索如何以編程方式啟用列表。
更新
還在調查。 我發現當你發送orderFrontListPanel:到你的NSTextView然后選擇項目符號並按回車鍵時,沒有特殊的消息被發送到NSTextView。 這意味着可以在彈出式面板中的某處構建項目符號列表,並直接設置為TextView的文本容器...
以編程方式將項目符號列表添加到NSTextView的兩種方法:
方法1:
以下鏈接引導我使用第一種方法,但除非您想為子彈使用一些特殊的非Unicode字形,否則它是不必要的回旋:
這需要:(1)子類化布局管理器,用子彈字形替換某些任意字符; (2)帶有firstLineHeadIndent的段落樣式,比該縮進略大的制表位,以及用於組合兩者的包裹線的headIndent。
布局管理器如下所示:
#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
將布局管理器分配給窗口/視圖控制器的awakeFromNib中的textview,如下所示:
- (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.)
}
然后添加一個類似這樣的方法:
- (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];
}
測試出來:
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];
你得到這個:
方法2:
但子彈是一個常規的Unicode字符,在十六進制2022.所以你可以直接把它放在字符串中,並得到一個精確的測量,如下所示:
NSString *stringWithGlyph = [NSString stringWithUTF8String:"\u2022"];
NSString *stringWithGlyphPlusSpace = [stringWithGlyph stringByAppendingString:@" "];
NSSize sizeGlyphPlusSpace = [stringWithGlyphPlusSpace sizeWithAttributes:[NSDictionary dictionaryWithObject:self.fontIndent forKey:NSFontAttributeName]];
self.fWidthGlyphPlusSpace = sizeGlyphPlusSpace.width;
因此不需要自定義布局管理器。 只需像上面那樣設置paragStyle縮進,並將文本字符串附加到一個字符串,該字符串包含return + bullet char + space(或者+ tab,在這種情況下,您仍然希望該制表符停止)。
使用空格,這產生了更嚴格的結果:
想要使用子彈以外的角色嗎? 這是一個很好的Unicode圖表: http : //www.danshort.com/unicode/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.