[Drawkit] flipped out?
Graham Cox
graham.cox at bigpond.com
Fri May 30 18:34:14 PDT 2008
Hi James,
I got your note that you found the flag - cool ;)
Just to be clear, styles are set to be sharable by default, so
attaching the same style to several objects will literally use the
same style object. New styles take their initial value of "sharable"
from the class, so you can turn this off using
+setStylesAreSharableByDefault: for all subsequently created styles.
Or you can turn it off for the particular style you have with -
setStyleSharable: You need to do this before attaching the style to
the object.
I'm wondering though whether in this case it would be better for your
'bar' rasterizer to take its length from the object (the object is
passed in the render: method) so what it draws is different for each
note but still allows the style to be shared. Sharing styles is
intended to be pretty efficient (in memory terms) because eventually
you'll have a lot of objects that look more or less the same. If they
can share the style so much the better.
==========
Undoable properties
Where rasterizers are concerned, supporting Undo is really easy. You
need to support two methods: +observableKeyPaths and -
registerActionNames. The first you'll notice is a class method, since
the undoable properties of a rasterizer are the same for every
insyance of its class. In fact the action names are too, but it's
easier for the code that makes use of this to work with an instance at
that point. Anyway, the important one is +observablKeyPaths. This
returns an array of strings which are the properties that you want to
be undoable.
So if your rasterizer has a property called 'length' (i.e. it has the
accessors -setLength: and -length), your array contains the string
"length". However, you also want to keep any properties defined by
your superclass, so what you need to do is to grab the superclass's
list and append your own properties, thus:
+ (NSArray*) observableKeyPaths
{
return [[super observableKeyPaths] arrayByAddingObjectsFromArray:
[NSArray arrayWithObject:@"length"]];
}
If you have a look at the classes derived from DKRasterizer (e.g.
DKStroke, DKFill, DKHatching...) you'll see lots of examples of doing
this.
That's all you need to support undo - any changes you make to the
setLength: method will be magically undoable. Internally, this works
by using KVO.
The second part is supplying the action name. This is just a string
that ends up in the Undo menu to tell the user what will be undone,
i.e. "Undo Change Note Sustain" or something. The -registerActionNames
method is called to set up these strings. It will look like this:
- (void) registerActionNames
{
[super registerActionNames];
[self setActionName:@"#kind# Note Sustain" forKeyPath:@"length"];
}
so, first call super's implementation, so it registers anything it
wants. Then register yours. All it does is keep a dictionary of
strings keyed off the keypath. Note that the strings are still
localizable, because a localized version is looked up based on the
fixed string you pass when the string is used. The #kind# part of the
string is used to substitute a verb based on the type of change made -
for all property changes like this it will be "Change", but for adding
or removing items from containers it will be "Add" or "Delete". Note
that you are not required to use the #kind# substitution, or even this
entire method (though if you don't your property change will end up as
a generic "Undo").
Note that Undo anywhere else in DK doesn't use a similar "magic"
approach - elsewhere it is assumed you have access to an undo manager
and can just use the usual approaches like -
prepareWithInvocationTarget: etc. The above discussion applies only to
rasterizers.
cheers, Graham
On 31 May 2008, at 7:58 am, James Maxwell wrote:
> Hi Graham,
>
> I'm keeping this off the list, simply because it's picking up in the
> middle of a conversation.
>
> I tried implementing what you mention below - i.e., making a
> rasterizer with the line and adding it to the style of my glyph. It
> works, but the only funky thing is that, when I change the length of
> the sustain bar (line) on one note, it changes on all instantiated
> notes. This would seem to suggest that I'm only using a single
> instance of the renderer, for all the instantiated notes. But I'm
> certainly not doing this deliberately... Is there some default
> behaviour of rasterizers that makes them shared, in some way?
>
> Also, if you have a moment, maybe you could run me through the
> observable/undo thing you mentioned before??
>
> cheers,
>
> J.
>
>
> On 28-May-08, at 10:28 PM, Graham Cox wrote:
>
>> The best way to do this is to build a style which draws the bar (in
>> addition to the glyph).
>>
>> To do that you need to subclass DKRasterizer and implement the -
>> render: and -extraSpaceNeeded methods. In the first, you just draw
>> the bar however you want to using any graphics calls you want. In
>> the second, just return the size around the shape that your
>> rasterizer requires to draw its stuff. I'd guess that the bar
>> length would be a property you want to be adjustable, so your class
>> would have a setLength: method and a length method. You can make
>> these observable if you want so that property will be automatically
>> undoable (I'll go more into that if you need it). Since the sustain
>> time is more properly a property of the note, you can make your
>> style read the value from there.
>>
>> Once you have your rasterizer, just add it to the style that is
>> used to draw the glyph itself. Being part of a style means it will
>> go wherever the glyph goes.
>>
>> If these bars always extend to the right, an optimisation would be
>> to constrain your bounds to only add the extra space on the right -
>> by default the space is added around all object which means you'd
>> be updating an unnecessary area on the left of the glyph. But this
>> is an optimisation for after you get it working.
>>
>> G
>>
>> P.S. It might be an idea to address these questions to the DK
>> mailing list - there's a bit more traffic these days and so
>> everyone might benefit from the suggestions, plus the messages get
>> archived. It's up to you though.
>>
>> On 29 May 2008, at 3:03 pm, James Maxwell wrote:
>>
>>> Just wondering about something. I want my note objects to use a
>>> "sustain bar", which is a common contemporary notation for
>>> sustain, used in non-mensural or "proportional" scores. In my Java
>>> version, it looked like this:
>>>
>>> <Picture 2.png>
>>>
>>>
>>> What I'm wondering is whether it would be possible to get this
>>> sort of appearance, and functionality, by making a special version
>>> of a line (DKDrawablePath) which placed a glyph at the start
>>> (location)? Is that at all possible? Or is there some way to fake
>>> it - some way of adding a glyph at the origin of a line, in a way
>>> that makes a *single* object (i.e., not multiple objects, but just
>>> one entry on the layer)?
>>>
>>> thanks,
>>>
>>> J.
>>>
>>>
>>>
>>> On 28-May-08, at 6:02 PM, Graham Cox wrote:
>>>
>>>> Hi James,
>>>>
>>>> If you are using NSBezierPath's -
>>>> appendBezierPathWithGlyph:inFont: for example, you will find that
>>>> the path is vertically flipped.
>>>>
>>>> DK has a category method in NSBezierPath+Geometry simply called -
>>>> verticallyFlippedPath which will invert it for you, that's
>>>> probably the easiest thing to do for now. So just use that on the
>>>> path before making a shape with it. This wouldn't be so efficient
>>>> if you were flipping a whole string or block of text, and it
>>>> flips around the centre not the baseline, but in your case I
>>>> think it'll be fine.
>>>>
>>>> hth,
>>>>
>>>>
>>>> Graham
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On 29 May 2008, at 7:15 am, James Maxwell wrote:
>>>>
>>>>> Hey Graham,
>>>>>
>>>>> I've just noticed, by finally attempting to draw an asymmetrical
>>>>> glyph, that my font/glyph is rendering flipped. I've poked
>>>>> around, and discovered that this is just what cocoa does in a
>>>>> flipped context, but I can't figure out how to get the fonts/
>>>>> glyphs upright. The only way I can manage it is to unfip the
>>>>> drawing, but that seems kind of silly. I know it's possible, and
>>>>> I've found the example code from Apple's docs, but I can't seem
>>>>> to make it actually do anything... I have a feeling this is
>>>>> because I'm not putting the transform in the right place.
>>>>>
>>>>> Any tips greatly appreciated.
>>>>>
>>>>> cheers,
>>>>>
>>>>> J.
>>>>>
>>>>
>>>
>>
>
More information about the Drawkit
mailing list