Re: NSLayoutManager and best override point for temporary attributes
Re: NSLayoutManager and best override point for temporary attributes
- Subject: Re: NSLayoutManager and best override point for temporary attributes
- From: Keith Blount <email@hidden>
- Date: Mon, 5 Oct 2009 12:12:54 -0700 (PDT)
Hi,
I spent the weekend on this and the only thing I'm still having problems with is finding the best place to apply the underline and strikethrough temporary attributes so that they get applied automatically and efficiently (using the showPackedGlyphs:... method worked perfectly for the text itself - thank you Aki!).
I tried saving the edited range of text in the text storage and then having my layout manager check this from:
-textStorage:edited:range:changeInLength:invalidatedRange:
Then my layout manager adds temp attribs in this method. Turns out that this is a Bad Thing (which I should have realised beforehand), as it can cause glyph generation before the text storage has ended editing. D'oh.
Another solution that occurs to me is to have the text storage tell all of its layout managers to apply any temporary attributes after it has processed editing, e.g:
- (void)processEditing
{
[super processEditing];
if (dirtyRange.length == 0)
return;
NSEnumerator *e = [[self layoutManagers] objectEnumerator];
NSLayoutManager *lm;
while (lm = [e nextObject])
[lm applyTemporaryAttributesToDirtyRangeIfNecessary:dirtyRange];
dirtyRange = NSMakeRange(NSNotFound,0);
}
I'm still unsure that this is the best route to take, though. (As I say, I previously did all of this stuff in the text view delegate, but that meant duplicating code for each text view and was hugely inefficient; and overriding NSLayoutManager's -temporaryAttributesAtRange: was just too slow.)
In fact, does anyone know how NSTextView handles keeping the temporary attributes up to date for NSLinkAttributeName? Essentially I'm just trying to do the same but with a custom attribute instead of NSLinkAttributeName. The problem is simply that if you apply a temporary attribute to a range of text, typing in that range doesn't take on the temporary attribute (because temporary attributes are handled by the layout manager and so the text view knows nothing about them presumably). But NSTextView or NSLayoutManager (actually presumably the latter) has some internal magic that handles applying the linkTextAttributes (which are, according to the docs, just temporary attributes too) to any typed text with NSLinkAttributeName applied. All I'm trying to achieve is the same thing but with a custom attribute, and it seems more difficult than it should be... But I'm probably just making it so.
Many thanks and all the best,
Keith
--- On Sat, 10/3/09, Aki Inoue <email@hidden> wrote:
> From: Aki Inoue <email@hidden>
> Subject: Re: NSLayoutManager and best override point for temporary attributes
> To: "Keith Blount" <email@hidden>
> Cc: "Martin Wierschin" <email@hidden>, email@hidden
> Date: Saturday, October 3, 2009, 1:03 AM
> The font and color attributes are
> just hint for the settings already applied to the current
> graphics context.
>
> You can do:
> NSGraphicsContext *context = [NSGraphicsContext
> currentContext];
>
> [context saveGraphicsState];
>
> [yourCustomColor set];
>
> [super showPackedGlyphs:...];
>
> [context restoreGraphicsState];
>
> > My custom attributes set only the colour, so this
> should be fine. (Although I'm not sure how I would use this
> method if I wanted, say, the underline to be a different
> colour from the text, which I need for certain link
> attributes.)
>
> There is no rendering primitive method like this for
> underlines. You still need to use temporary attributes
> for that purpose. I believe the temporary attribute
> approach is efficient enough for that kind of usage
> patterns.
>
> Aki
>
> On 2009/10/02, at 17:32, Keith Blount wrote:
>
> > Hi Aki,
> >
> > Many thanks for your reply, much appreciated. Would
> you mind giving me a little more information on how to
> override this method? The docs are a little sparse in this
> regard. For instance, if I try passing a different colour
> into super's method, it has no effect; instead, it seems
> that I need to use [color set] before calling super (which
> would seem to be hinted at by the docs, as they say "if for
> any reason you modify the set color or font..."). Do I call
> [customColor set] using my own colour, then call super, then
> call [color set] using the passed-in colour after calling
> super?
> >
> > My custom attributes set only the colour, so this
> should be fine. (Although I'm not sure how I would use this
> method if I wanted, say, the underline to be a different
> colour from the text, which I need for certain link
> attributes.)
> >
> > Many thanks again and all the best,
> > Keith
> >
> > --- On Fri, 10/2/09, Aki Inoue <email@hidden>
> wrote:
> >
> >> From: Aki Inoue <email@hidden>
> >> Subject: Re: NSLayoutManager and best override
> point for temporary attributes
> >> To: "Keith Blount" <email@hidden>
> >> Cc: "Martin Wierschin" <email@hidden>,
> email@hidden
> >> Date: Friday, October 2, 2009, 10:24 PM
> >> Keith,
> >>
> >> If your custom attributes modifies just the
> graphics state
> >> (don't affect the layout), you can override
> >> -[NSLayoutManager
> >>
> showPackedGlyphs:length:glyphRange:atPoint:font:color:printingAdjustment:].
> >>
> >> The method is the bottleneck for calling CG
> APIs. You
> >> can query the text attribute using
> glyphRange.location and
> >> see if there is one of your custom attributes.
> >>
> >> You should be able to customize alpha, color,
> blending
> >> mode, compositing mode, clipping, etc.
> >>
> >> Aki
> >>
> >> On 2009/10/02, at 7:04, Keith Blount wrote:
> >>
> >>> Hi,
> >>>
> >>> Looking at this again, would
> NSLayoutManager's
> >>>
> >>>
> >>
> -textStorage:edited:range:changeInLength:invalidatedRange:
> >>>
> >>> method be a good candidate for overriding to
> add
> >> temporary attributes? The text storage calls this
> whenever
> >> it's edited and provides it with the new range of
> >> characters. So it seems that I could add any
> temporary
> >> attributes to the range that gets passed in here,
> checking
> >> that changeInLength > 0.
> >>>
> >>> Or, what if in the -addAttribute:...,
> >> -setAttributes:... and removeAttribute:... methods
> of my
> >> NSTextStorage, I called through to all of the text
> storage's
> >> layout managers to ask them to add the temporary
> attribute
> >> if necessary, after -edited:range:changeInLength:
> gets
> >> called?
> >>>
> >>> Many thanks again!
> >>>
> >>> All the best,
> >>> Keith
> >>>
> >>> --- On Thu, 10/1/09, Martin Wierschin <email@hidden>
> >> wrote:
> >>>
> >>>> From: Martin Wierschin <email@hidden>
> >>>> Subject: Re: NSLayoutManager and best
> override
> >> point for temporary attributes
> >>>> To: "Keith Blount" <email@hidden>
> >>>> Cc: email@hidden
> >>>> Date: Thursday, October 1, 2009, 1:50 AM
> >>>> Hi Keith,
> >>>>
> >>>>> I have certain custom text attributes
> that are
> >> used in
> >>>> my NSTextStorage to which I would like to
> add
> >> temporary
> >>>> attributes via the NSLayoutManager.
> >>>>
> >>>> What version of OSX are you testing under?
> Under
> >> Leopard
> >>>> there's a bug in -[NSLayoutManager
> >>>>
> >>
> temporaryAttribute:atCharacterIndex:longestEffectiveRange:inRange:]
> >>>> that calculates effective ranges that are
> too
> >> short. For
> >>>> specific test cases this caused big
> inefficiencies
> >> in the
> >>>> text system. I believe this bug is fixed
> in Snow
> >> Leopard.
> >>>>
> >>>>> more recently I have taken to
> overriding
> >>>> NSLayoutManager's
> >>>>
> >>
> -temporaryAttributesAtCharacterIndex:effectiveRange:
> >>>>
> >>>> If this is too slow, then I'd look to
> using some
> >> kind of
> >>>> cache for your calculations. But really,
> >> NSLayoutManager's
> >>>> temporary attributes are already a cache;
> one
> >> likely to be
> >>>> specifically designed for high
> performance
> >> index/run access.
> >>>> I think your original idea of setting
> temporary
> >> attributes
> >>>> whenever text changes would be the most
> >> efficient.
> >>>>
> >>>> Perhaps you're recalculating too much, too
> often?
> >> I don't
> >>>> know the access patterns for temporary
> attributes,
> >> but I
> >>>> would guess they are only queried when
> associated
> >> text is
> >>>> displayed on screen. If that's the case,
> you could
> >> fix them
> >>>> up lazily, eg: whenever text changes just
> note
> >> down that the
> >>>> attributes are dirty in that range. Your
> temporary
> >> attribute
> >>>> methods in your NSLayoutManager subclass
> can then
> >> ensure
> >>>> that temporary attributes are not dirty
> before
> >> they are
> >>>> returned.
> >>>>
> >>>> If none of that is efficient enough, you
> could rig
> >> up a
> >>>> NSTextStorage subclass that has two sets
> of
> >> attributes: one
> >>>> set for private use and another derived
> set which
> >> only the
> >>>> layout system sees.
> >>>>
> >>>> Hopefully some of that helps,
> >>>>
> >>>> ~Martin
> >>>>
> >>>>
> >>>
> >>>
> >>>
> >>>
> _______________________________________________
> >>>
> >>> 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
> >>
> >>
> >
> >
> >
>
>
_______________________________________________
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