I have two NSTextfields
in a book layout, and I can't figure out a fast way to go back to the previous 'page'. Book size, font size, line size all change, so the string of text for the previous page has to be calculated on the fly. Picture:
The NSTextfields each have one NSTextContainer
, and they share a NSLayoutManager
and NSTextStorage
.
Going forward is easy: I take the character range of the visible text, and then create a substring starting from the next character along.
My going back method is a kludge. I figure out the maximum amount of characters that can be visible at once. I then make a string to that length, with the last character the one I want in the bottom right corner of the book. I then loop: removing characters from the start, checking what is visible each time until the character I want is in the bottom right. This is very, very, slow.
Can anyone suggest a faster way to do what I want to achieve? I had the thought of using scrollRangeToVisible
, but I couldn't figure out how to set up a NSScrollView
for this layout.
Can anyone help?
Textcontainers are set up like this:
-(void)setupTextViews {
articleString = [[NSAttributedString alloc] init];
articleStringPortion = [[NSAttributedString alloc] init];
bookTextStorage = [[NSTextStorage alloc] init];
bookLayoutManager = [[NSLayoutManager alloc] init];
[[self bookTextStorage] addLayoutManager:bookLayoutManager];
leftColumnRect = NSZeroRect;
rightColumnRect = NSZeroRect;
NSDivideRect(bookRect, &leftColumnRect, &rightColumnRect, NSWidth(bookRect) / 2, NSMinXEdge);
// First column
{
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:leftColumnRect.size];
leftColumnTextView = [[CRMouseOverTextView alloc] initWithFrame:leftColumnRect textContainer:textContainer];
[leftColumnTextView setDrawsBackground:NO];
[leftColumnTextView setEditable:NO];
[leftColumnTextView setup];
[bookView addSubview:leftColumnTextView];
[bookLayoutManager addTextContainer:textContainer];
[textContainer release];
[leftColumnTextView release];
}
// Second column
{
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:rightColumnRect.size];
rightColumnTextView = [[CRMouseOverTextView alloc] initWithFrame:rightColumnRect textContainer:textContainer];
[rightColumnTextView setDrawsBackground:NO];
[rightColumnTextView setEditable:NO];
[rightColumnTextView setup];
[bookView addSubview:rightColumnTextView];
[bookLayoutManager addTextContainer:textContainer];
[textContainer release];
[rightColumnTextView release];
}
}
There's no point posting my awful going backwards code, but I'm using this method I found to figure out what is visible each time:
-(NSRange)getViewableRange:(NSTextView *)tv {
NSLayoutManager *lm = [tv layoutManager];
NSRect visRect = [tv visibleRect];
NSPoint tco = [tv textContainerOrigin];
visRect.origin.x -= tco.x;
visRect.origin.y -= tco.y;
NSRange glyphRange = [lm glyphRangeForBoundingRect:visRect inTextContainer:[tv textContainer]];
NSRange charRange = [lm characterRangeForGlyphRange:glyphRange actualGlyphRange:nil];
return charRange;
}
I'm not sure this is the answer you're looking for, but if it were me, I'd probably just "cache" a bunch of those character ranges, for all the previous pages that have been viewed. You probably wouldn't even have any problem storing them all for a book with a lot of pages. Of course, then you still have to use your kludgy code for when the user re-sizes the text, or whatever. (Either that, or you could re-calculate from some suitable starting point... say the beginning of the book if it's fast enough, or the beginning of a chapter or something. Then you just find the page(range) that contains the text that is already being displayed and show the previous one.)