Layout manager's -rectArrayForCharacterRange:::: returns zero rectangles??

  • I'm trying to write some code which figures out where the insertion
    point is located, every time the selection changes. (Imagine a text
    editor which has a "current line number" indicator at the bottom of the
    screen that stays updated as the user types.)

    To do this, in my text view's delegate, I have implemented the delegate
    method -textViewDidChangeSelection:. From there I call -selectedRange
    and then ask the layout manager for the rectangles via:

        NSRectArray  rects = [[myTextView layoutManager]
    rectArrayForCharacterRange:selection

    withinSelectedCharacterRange:NSMakeRange(NSNotFound, 0)

    inTextContainer:[myTextView textContainer]

    rectCount:&rectCount];

    Normally, this works perfectly.

    However, if I go to the very end of the document and start making
    insertions or deletions, I don't get a rectangle back—"rectCount" is
    zero. I've checked [[[TextView() textStorage] string] length] and
    compared it against the selection, so I'm pretty confident that I'm not
    querying past the end of the text, which in the past is the only time
    I've seen behavior like this up until now.

    Any ideas as to what's wrong? This seems pretty straightforward to me so
    I'm not sure what I'm missing.
  • Hmm. I tried another approach and it's still no good. This produces
    NSZeroRect:

            NSLayoutManager* layout = [myTextView layoutManager];

            NSRange glyphRange = [layout glyphRangeForCharacterRange:selection
                                                actualCharacterRange:NULL];

            rect = [layout boundingRectForGlyphRange:glyphRange
                                    inTextContainer:[myTextView
    textContainer]];

    Interestingly, this only happens in the -textViewDidChangeSelection:
    delegate. If I defer the call until after -textViewDidChangeSelection:
    completes, then I can call the exact same function with the exact same
    inputs, and get a valid result.

    If no one else has any ideas, maybe that's what I'll end up doing…

    John Stiles wrote:
    > I'm trying to write some code which figures out where the insertion
    > point is located, every time the selection changes. (Imagine a text
    > editor which has a "current line number" indicator at the bottom of
    > the screen that stays updated as the user types.)
    >
    > To do this, in my text view's delegate, I have implemented the
    > delegate method -textViewDidChangeSelection:. From there I call
    > -selectedRange and then ask the layout manager for the rectangles via:
    >
    > NSRectArray  rects = [[myTextView layoutManager]
    > rectArrayForCharacterRange:selection
    >
    > withinSelectedCharacterRange:NSMakeRange(NSNotFound, 0)
    >
    > inTextContainer:[myTextView textContainer]
    >
    > rectCount:&rectCount];
    >
    > Normally, this works perfectly.
    >
    > However, if I go to the very end of the document and start making
    > insertions or deletions, I don't get a rectangle back—"rectCount" is
    > zero. I've checked [[[TextView() textStorage] string] length] and
    > compared it against the selection, so I'm pretty confident that I'm
    > not querying past the end of the text, which in the past is the only
    > time I've seen behavior like this up until now.
    >
    > Any ideas as to what's wrong? This seems pretty straightforward to me
    > so I'm not sure what I'm missing.
  • I managed to reproduce this in a small test app so it's filed as:
    rdar://5764057    NSLayoutManager cannot get bounding rect of glyph
    when deleting at end of text

    For now I will use -performSelector:inMainThread:afterDelay: to kludge
    around it. It's better than nothing.

    John Stiles wrote:
    > Hmm. I tried another approach and it's still no good. This produces
    > NSZeroRect:
    >
    > NSLayoutManager* layout = [myTextView layoutManager];
    >
    > NSRange glyphRange = [layout glyphRangeForCharacterRange:selection
    > actualCharacterRange:NULL];
    > rect = [layout boundingRectForGlyphRange:glyphRange
    > inTextContainer:[myTextView
    > textContainer]];
    >
    > Interestingly, this only happens in the -textViewDidChangeSelection:
    > delegate. If I defer the call until after -textViewDidChangeSelection:
    > completes, then I can call the exact same function with the exact same
    > inputs, and get a valid result.
    >
    > If no one else has any ideas, maybe that's what I'll end up doing…
    >
    >
    > John Stiles wrote:
    >> I'm trying to write some code which figures out where the insertion
    >> point is located, every time the selection changes. (Imagine a text
    >> editor which has a "current line number" indicator at the bottom of
    >> the screen that stays updated as the user types.)
    >>
    >> To do this, in my text view's delegate, I have implemented the
    >> delegate method -textViewDidChangeSelection:. From there I call
    >> -selectedRange and then ask the layout manager for the rectangles via:
    >>
    >> NSRectArray  rects = [[myTextView layoutManager]
    >> rectArrayForCharacterRange:selection
    >>
    >> withinSelectedCharacterRange:NSMakeRange(NSNotFound, 0)
    >>
    >> inTextContainer:[myTextView textContainer]
    >>
    >> rectCount:&rectCount];
    >>
    >> Normally, this works perfectly.
    >>
    >> However, if I go to the very end of the document and start making
    >> insertions or deletions, I don't get a rectangle back—"rectCount" is
    >> zero. I've checked [[[TextView() textStorage] string] length] and
    >> compared it against the selection, so I'm pretty confident that I'm
    >> not querying past the end of the text, which in the past is the only
    >> time I've seen behavior like this up until now.
    >>
    >> Any ideas as to what's wrong? This seems pretty straightforward to me
    >> so I'm not sure what I'm missing.