Getting the rects of a range inside an NSTextField

Discussion in 'Mac Programming' started by Starfox, Mar 13, 2012.

  1. Starfox macrumors regular

    Apr 7, 2011
    How can I get the rect(s) of an NSRange in an NSTextField's attributed string value?
  2. gnasher729 macrumors P6


    Nov 25, 2005
    The first step would be to say _exactly_ what you mean.

    And the usual question: What is it that you actually want to achieve?
  3. Starfox thread starter macrumors regular

    Apr 7, 2011
    I want to draw an overlay over a range of characters in an attributed string in a text field. I'm subclassing the textfield and I want to add a rect fill for the rect of characters in range R.
  4. Sydde macrumors 68020


    Aug 17, 2009
    You might want to investigate

    Core Text

    It is a bit like working with Core Foundation (C-level, not Objective-C), but seems tailored for exactly what you are doing.
  5. Starfox thread starter macrumors regular

    Apr 7, 2011
    Core text seems too low-level. I found a method that does what I want in NSLayoutManager (rectArrayForCharacterRange:withinSelectedCharacterRange:inTextContainer:rectCount), but it seems I'm not using it properly. The rects I get aren't aligned with the text. Here's my added code in my NSTextField subclass's drawRect right after a call to super's draw rect:

    NSLog(@"Filling rects!");
        NSLayoutManager* LM = [[NSLayoutManager alloc] init];
        NSAttributedString* AS = [self attributedStringValue];
        NSTextStorage* TS = [[NSTextStorage alloc] initWithAttributedString:AS];
        [TS setAttributedString:AS];
        [LM setTextStorage:TS];
        [TS addLayoutManager:LM];
        NSTextContainer* TC = [[NSTextContainer alloc] initWithContainerSize:[self frame].size];
        [LM addTextContainer:TC];
        NSUInteger rectCount = 0;
        NSRectArray linkRect = [LM rectArrayForCharacterRange:NSMakeRange(0, 2)
          withinSelectedCharacterRange:NSMakeRange(0, 0) 
        for(size_t i = 0; i < rectCount; ++i)
            cout << "Rect:\n";
            cout << "\t" << linkRect[i].size.width << " , " << linkRect[i].size.height << endl;
        for(size_t i = 0; i < activeURLRects.size(); ++i)
            NSRect rect = activeURLRects[i];
            NSColor* color = [NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:0.3];
            [color set];
    The rects are off by a few pixels: Shot 2012-03-14 at 4.03.31 PM.png

    Any idea as to what I'm doing wrong?
  6. Sydde macrumors 68020


    Aug 17, 2009
    As I look at it, NSAttributedString has some built-in measuring tools (AppKit additions) that you could use without having to resort to creating a layout manager, text container, etc. (In looking at the docs for NSTextContainer, I see that it wants to belong to a NSTextView, which is kind of missing in your code.) You could create substrings to measure the location of the fragment you want to highlight: one to measure from the start of the line to where you want to highlight (if your range start might not be zero) and one to measure the size of the box.

    Be sure that you are measuring in the correct graphic context (that focus is locked on your field, which may be automatic depending on the method you are in). I suspect the discrepancy you are experiencing may have to do with the fact that your get container is not in any view, so its coördinates do not quite match with your field's view.
  7. Starfox thread starter macrumors regular

    Apr 7, 2011
    Creating substrings sounds extremely inefficient - I'm going to have 5 ranges that I need to highlight in every string or so. I tried using my code with the layout manager of a text view and it worked fine, so the basic theory is OK. What am I doing with regards to setting up the layout manager that's wrong?
  8. robvas macrumors 68030

    Mar 29, 2009
    Google Chrome has the open source Chromium project - Maybe look in there to see how they highlight what you're searching for when you hit Command-F
  9. Starfox thread starter macrumors regular

    Apr 7, 2011
    Or I could just go around searching for needles in haystacks.

    Seriously though, I wrote a simple project using code from Apple's docs and put it on BitBucket here:

    Would anyone mind taking a look at it and telling me where I went wrong? The code for getting the rects of a range inside the text view works fine, but the text field one is messed up. You'll see how the rectangle is offset once you build and run the project.

Share This Page