Ideal size of an NSScrollView?

  • I have a regular NSTextView inside an NSScrollView (with an auto-
    hiding vertical scroll bar). The contents of the text view are set
    via code depending on the outcome of some operations. The scroll view
    is set up to grow as my window is resized. So far, everything is
    working great.

    Now, I'd like to be able to determine how large the NSTextView ought
    to be to avoid scrolling. That way, I can set the window to its
    "optimal" size before showing it--just big enough to show the text,
    without having a ton of extra white space.

    Is there an easy way to do this?

    (I tried gradually growing the window until [scrollView
    hasVerticalScroller] returned NO, but that didn't work; apparently it
    returns YES even if the scroller is hidden. I also tried looking at
    the contentSize and documentVisibleRect but it didn't seem to do what
    I wanted.)
  • on 05/12/19 19:02, John Stiles at <JStiles...> wrote:

    > I have a regular NSTextView inside an NSScrollView (with an auto-
    > hiding vertical scroll bar). The contents of the text view are set
    > via code depending on the outcome of some operations. The scroll view
    > is set up to grow as my window is resized. So far, everything is
    > working great.
    >
    > Now, I'd like to be able to determine how large the NSTextView ought
    > to be to avoid scrolling. That way, I can set the window to its
    > "optimal" size before showing it--just big enough to show the text,
    > without having a ton of extra white space.
    >
    > Is there an easy way to do this?

    Nope.  No easy way.  Maybe in Mac OS 11.  Until then we have this:

    http://www.cocoabuilder.com/archive/message/2002/2/19/71224

    which you can find explained in the docs on your hard drive:

    ADC Home > Reference Library > Documentation > Cocoa > Text & Fonts > Text
    Layout Programming Guide > Calculating Text Height

    The above explains how to do it for constant width and variable height,
    which is the opposite of what you want, but their function is easily
    modified to do either way:

    float MeasureStringDrawing(NSString *myString, NSFont *desiredFont, float
    desiredWidth, float desiredHeight)
    // One of desiredHeight or desiredWidth must be set to 0.
    // The size of the one set to 0 will be returned.
    // Operation of this function is explained in:
    // ADC Home > Reference Library > Documentation > Cocoa > Text & Fonts >
    Text Layout Programming Guide > Calculating Text Height
    {
        BOOL wantWidth ;

        if (desiredWidth == 0)
        {
            desiredWidth = 1e7 ;
            wantWidth = YES ;
        }
        else if (desiredHeight == 0)
        {
            desiredHeight = 1e7 ;
            wantWidth = NO ;
        }
        else
            NSLog(@ "Internal error calling HeightForStringDrawing") ;

        NSTextStorage *textStorage = [[[NSTextStorage alloc]
    initWithString:myString] autorelease];
        NSTextContainer *textContainer = [[[NSTextContainer alloc]
    initWithContainerSize:NSMakeSize(desiredWidth, desiredHeight)] autorelease];
        NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init]
    autorelease];

        [textStorage addAttribute:NSFontAttributeName value:desiredFont
    range:NSMakeRange(0, [textStorage length])];
        [textContainer setLineFragmentPadding:0.0];    // padding usually is not
    appropriate for string drawing

        [layoutManager addTextContainer:textContainer];
        [textStorage addLayoutManager:layoutManager];

        (void)[layoutManager glyphRangeForTextContainer:textContainer];    //
    force layout

        if (wantWidth)
            return [layoutManager
    usedRectForTextContainer:textContainer].size.width;
        else
            return [layoutManager
    usedRectForTextContainer:textContainer].size.height;
    }

    P.S.  You may find that you need to add a little margin.  I don't know why,
    but to guarantee no wrapping I have to add 15 pixels to the returned
    measurement.  The required margin may depend on your font, size, etc.
  • Thanks to multiple suggestions, I found what appears to be a working
    solution. Thanks everyone :)

    NSLayoutManager *layoutManager = [textView layoutManager];
    NSRect rect = [layoutManager boundingRectForGlyphRange:NSMakeRange
    (0, [layoutManager numberOfGlyphs]) inTextContainer:[textView
    textContainer]];

    "usedRectForTextContainer" also appears to be a working solution, but
    it doesn't force glyph layout, so you can get stale information back.
    "boundingRectForGlyphRange," OTOH, does force layout.

    On Dec 19, 2005, at 7:02 PM, John Stiles wrote:

    > I have a regular NSTextView inside an NSScrollView (with an auto-
    > hiding vertical scroll bar). The contents of the text view are set
    > via code depending on the outcome of some operations. The scroll
    > view is set up to grow as my window is resized. So far, everything
    > is working great.
    >
    > Now, I'd like to be able to determine how large the NSTextView
    > ought to be to avoid scrolling. That way, I can set the window to
    > its "optimal" size before showing it--just big enough to show the
    > text, without having a ton of extra white space.
    >
    > Is there an easy way to do this?
    >
    > (I tried gradually growing the window until [scrollView
    > hasVerticalScroller] returned NO, but that didn't work; apparently
    > it returns YES even if the scroller is hidden. I also tried looking
    > at the contentSize and documentVisibleRect but it didn't seem to do
    > what I wanted.)
    > _______________________________________________
    > Do not post admin requests to the list. They will be ignored.
    > Cocoa-dev mailing list      (<Cocoa-dev...>)
    > Help/Unsubscribe/Update your Subscription:
    > http://lists.apple.com/mailman/options/cocoa-dev/jstiles%
    > 40blizzard.com
    >
    > This email sent to <jstiles...>
  • Actually, I think I might have found an easier answer.
    After forcing the glyphs to re-layout via your method of choice
    (there are many), [textView frame].size.height seems to contain the
    height. Neat.

    On Dec 20, 2005, at 1:53 PM, John Stiles wrote:

    > Thanks to multiple suggestions, I found what appears to be a
    > working solution. Thanks everyone :)
    >
    > NSLayoutManager *layoutManager = [textView layoutManager];
    > NSRect rect = [layoutManager boundingRectForGlyphRange:NSMakeRange
    > (0, [layoutManager numberOfGlyphs]) inTextContainer:[textView
    > textContainer]];
    >
    > "usedRectForTextContainer" also appears to be a working solution,
    > but it doesn't force glyph layout, so you can get stale information
    > back. "boundingRectForGlyphRange," OTOH, does force layout.
    >
    >
    > On Dec 19, 2005, at 7:02 PM, John Stiles wrote:
    >
    >> I have a regular NSTextView inside an NSScrollView (with an auto-
    >> hiding vertical scroll bar). The contents of the text view are set
    >> via code depending on the outcome of some operations. The scroll
    >> view is set up to grow as my window is resized. So far, everything
    >> is working great.
    >>
    >> Now, I'd like to be able to determine how large the NSTextView
    >> ought to be to avoid scrolling. That way, I can set the window to
    >> its "optimal" size before showing it--just big enough to show the
    >> text, without having a ton of extra white space.
    >>
    >> Is there an easy way to do this?
    >>
    >> (I tried gradually growing the window until [scrollView
    >> hasVerticalScroller] returned NO, but that didn't work; apparently
    >> it returns YES even if the scroller is hidden. I also tried
    >> looking at the contentSize and documentVisibleRect but it didn't
    >> seem to do what I wanted.)
    >> _______________________________________________
    >> Do not post admin requests to the list. They will be ignored.
    >> Cocoa-dev mailing list      (<Cocoa-dev...>)
    >> Help/Unsubscribe/Update your Subscription:
    >> http://lists.apple.com/mailman/options/cocoa-dev/jstiles%
    >> 40blizzard.com
    >>
    >> This email sent to <jstiles...>
    >