Re: Properly wrapping non-contiguous NSTextViews
Re: Properly wrapping non-contiguous NSTextViews
- Subject: Re: Properly wrapping non-contiguous NSTextViews
- From: Nick Zitzmann <email@hidden>
- Date: Mon, 15 Aug 2011 16:19:09 -0600
On Aug 15, 2011, at 2:22 PM, Martin Wierschin wrote:
>>> I don't know the particulars of your situation or what might be triggering the cutoff display (are they just descenders?), but have you tried reducing the height of the NSTextContainer while keeping your NSTextView height the same? (You'll have to disable automatic resizing).
>
>>
>> I did
>
> Can you post your new code? Your first post shows that you are modifying the text view. It might help to see the rest of your code as well.
When the controlling class is created, it makes a new NSTextStorage and gives it a brand new NSLayoutManager. The NSLayoutManager's delegate is set to the controller, which implements this delegate method:
- (void)layoutManager:(NSLayoutManager *)layoutManager didCompleteLayoutForTextContainer:(NSTextContainer *)textContainer atEnd:(BOOL)atEnd
{
NSArray *containers = [layoutManager textContainers];
if (!atEnd || textContainer == nil)
{
// Either layout is not finished or it is but there are glyphs laid nowhere.
NSTextContainer *lastContainer = [containers lastObject];
if (textContainer == lastContainer || textContainer == nil)
{
// Add a new page only if the newly full container is the last container or the nowhere container.
[self addPage];
}
}
else
{
// Layout is done and it all fit. See if we can axe some pages.
NSUInteger lastUsedContainerIndex = [containers indexOfObjectIdenticalTo: textContainer];
NSUInteger numContainers = [containers count];
while (++lastUsedContainerIndex < numContainers)
[self removePage];
[self setNumberOfPages:[containers count]];
}
}
The -addPage method creates a new NSTextContainer object and new NSTextView object with the container, adds the text container to the layout manager, and adds the text view as a subview to the master view. At that point, their frame sizes are synchronized, and the text container is set to track the text view's width but not its height. The sizes are equal to the size of a piece of paper acquired from the print manager, minus some margins set by the user.
The -removePage method pops the last NSTextContainer and NSTextView from the layout manager and master view respectively.
Right after filling in the master text storage in the usual way using -beginEditing/-appendAttributedString:/-endEditing, I added the following loop:
NSUInteger i;
for (i = 0UL ; i < self.layoutManager.textContainers.count ; i++)
{
NSTextContainer *tc = [self.layoutManager.textContainers objectAtIndex:i];
NSRect usedRect;
NSSize containerSize;
(void)[self.layoutManager glyphRangeForTextContainer:tc]; // force layout
usedRect = [self.layoutManager usedRectForTextContainer:tc];
containerSize = tc.containerSize;
if (usedRect.size.height > containerSize.height)
{
containerSize.height = containerSize.height-(usedRect.size.height-containerSize.height);
tc.containerSize = containerSize;
}
}
The loop forces the NSLayoutManager to force layout of the text in the NSTextStorage, which ends up calling the delegate to add new NSTextStorage/NSTextView objects as necessary. (There is always one present no matter what.) I can't use fast enumeration here because the text containers that contain all pages beyond the first aren't guaranteed to exist until layout is first performed.
I added this loop to try and shrink the text container if the used rect is greater than the container size, since a larger used rect means text is being cut off at the bottom of the container. It works, but instead of sending the cut-off line to the next text container as I expected would happen, it places the line out of sight so it will never be seen or printed. This happens careless of whether I shrink the text storage with container height tracking off, or the text view with container height tracking on.
Of course, if there is a better way of using the Cocoa text system to layout multiple non-contiguous pages of text with margins, I'd like to hear about it. Maybe it'll even solve the problem I'm having.
>> what happens is the layout manager keeps the last line in the text container where it will not be drawn. It will not move the last line to the next container unless I resize the text container to be two pixels smaller than needed, which results in text getting partially cut off later in the document.
>
> You're saying that if you decrease the height of text container (N), forcing the last line onward, you see drawing artifacts in container (N + 1)? Can you post a screenshot?
I'll do better: I'll post PDFs. Here is what the text looks like without the resizing loop (all containers have exactly the same height): <http://dl.dropbox.com/u/13168713/Text cut-off.pdf>
The text on page 1 is being cut off at the bottom, and it's not just the descneders. Here is the output with the loop in place: <http://dl.dropbox.com/u/13168713/Invisible lines.pdf>
Notice how the last line on page 1 disappeared entirely.
When I shrink the text container by two pixels, the last line on page 1 gets bounced to page 2, but now descenders on both the first and second pages are being cut off at the bottom: <http://dl.dropbox.com/u/13168713/Descenders cut-off.pdf>
> Honestly this sounds unusual- do you have any other modifications at work? Also, at what point are you running this loop where you modify the text container heights? You're not doing it from a layout delegate method are you? NSLayoutManager is quite finicky about that (or at least it used to be).
The code adds containers & views from a layout delegate, and gives them an initial size, but doesn't resize them.
Nick Zitzmann
<http://www.chronosnet.com/>
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden