在 NSTextView 中显示隐藏字符

发布于 2024-07-08 10:08:30 字数 127 浏览 13 评论 0原文

我正在为 Mac OS X 编写一个文本编辑器。我需要在 NSTextView 中显示隐藏字符(例如空格、制表符和特殊字符)。 我花了很多时间寻找如何做到这一点,但到目前为止我还没有找到答案。 如果有人能指出我正确的方向,我将不胜感激。

I am writing a text editor for Mac OS X. I need to display hidden characters in an NSTextView (such as spaces, tabs, and special characters). I have spent a lot of time searching for how to do this but so far I have not found an answer. If anyone could point me in the right direction I would be grateful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

韶华倾负 2024-07-15 10:08:30

这是一个完全有效且干净的实现

@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

要安装,请使用:

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

要查找字体字形名称,您必须转到 CoreGraphics:

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

Here's a fully working and clean implementation

@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

To install, use:

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

To find font glyph names, you have to go to CoreGraphics:

CGFontRef font = CGFontCreateWithFontName(CFSTR("Menlo-Regular"));
for (size_t i = 0; i < CGFontGetNumberOfGlyphs(font); ++i) {
  printf("%s\n", [CFBridgingRelease(CGFontCopyGlyphNameForGlyph(font, i)) UTF8String]);
}
无尽的现实 2024-07-15 10:08:30

看一下 NSLayoutManager 类。 你的 NSTextView 将有一个与之关联的布局管理器,布局管理器负责将字符(空格、制表符等)与字形(在屏幕上绘制的该字符的图像)相关联。

就您而言,您可能对 replaceGlyphAtIndex:withGlyph: 方法,该方法允许您替换单个字形。

Have a look at the NSLayoutManager class. Your NSTextView will have a layout manager associated with it, and the layout manager is responsible for associating a character (space, tab, etc.) with a glyph (the image of that character drawn on the screen).

In your case, you would probably be most interested in the replaceGlyphAtIndex:withGlyph: method, which would allow you to replace individual glyphs.

心的憧憬 2024-07-15 10:08:30

几年前我写了一个文本编辑器 - 这里有一些毫无意义的代码,应该能让你(希望)找到正确的方向(顺便说一句,这是一个 NSLayoutManager 子类 - 是的,我知道它像众所周知的厨房水槽一样泄漏):

- (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];
}

I wrote a text editor a few years back - here's some meaningless code that should get you looking in (hopefully) the right direction (this is an NSLayoutManager subclass btw - and yes I know it's leaking like the proverbial kitchen sink):

- (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];
}
伏妖词 2024-07-15 10:08:30

我解决了NSGlyphs和NSTextView中相应的unichar之间转换的问题。 下面的代码工作得很好,并用项目符号替换了可见文本的空格:

- (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];
}

I solved the problem of converting between NSGlyphs and the corresponding unichar in the NSTextView. The code below works beautifully and replaces spaces with bullets for visible 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];
}
苍暮颜 2024-07-15 10:08:30

也许 -[NSLayoutManager setShowsControlCharacters:] 和/或 -[NSLayoutManager setShowsInvisibleCharacters:] 会做你想要的。

Perhaps -[NSLayoutManager setShowsControlCharacters:] and/or -[NSLayoutManager setShowsInvisibleCharacters:] will do what you want.

渔村楼浪 2024-07-15 10:08:30

这是 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)
    }
}

并列出字形名称:

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

Here is Pol's solution 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)
    }
}

And to list the glyph names:

   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)")
            }
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文