Is it possible to redirect a NSGradient drawinRect method to a picture?

I guess the headline gives enough information about what I am looking for :wink:
Between the Graphics.drawsomething commands no DrawGradient method exists.
NSGradient knows instance methods only to draw into some object, and I hope it is possible to redirect this draw method into a picture frame. But I wasn‘t successful in finding out how to – therefore grateful for helpful hints:

This is how it doesn‘t work:

[code]Sub BackgoundGradient(startcolor as color, endcolor as color, angle as single)
// sets the background image to a newly created gradient initialized from two colors

Declare sub DrawinRect lib cocoalib selector “drawInRect:angle:” (id as ptr, myrect as nsrect, angle as single) // draws a NSGradient
declare sub saveGraphicsState lib CocoaLib selector “saveGraphicsState” (id as ptr)
Declare sub setCurrentContext lib CocoaLib selector “setCurrentContext” (id as ptr, context as integer)
declare sub restoreGraphicsState lib CocoaLib selector “restoreGraphicsState” (id as ptr)

dim gradientptr as ptr = NSGradient (startcolor, endcolor) // create the nsgradient
dim NSGraphicsState as Ptr = NSClassFromString (“NSGraphicsState”)
dim pic as new picture (me.width, me.Height) // set up a picture
dim myrect as NSRect // Set up a NSRect structure to aint the gradient in
myrect.w = pic.Width
myrect.h = pic.Height

dim picptr as integer = pic.Graphics.handle (graphics.HandleTypeCGContextRef) // <- I think the problem lies here or in the rect declaration

saveGraphicsState (NSGraphicsState)
setCurrentContext (NSGraphicsState, picptr) // <- or here, of course

DrawInRect (gradientptr, myrect, angle ) // paint it

restoreGraphicsState (NSGraphicsState)
//Now set the button image to the newly created one

image=pic
ImagePosition = macosbutton.lmagePlacement.ImageOverlaps
ImageScaling = macosbutton.ImageScaling.AxesIndependently

End Sub
[/code]

Indeed; your problem is that you’re trying to use a CGContext instead of a NSGraphicsContext


There is no class called “NSGraphicsState”; I think you mean “NSGraphicsContext”.

I would look at CGGradient instead as its designed to work with CGContexts.

However if you want to work NSGradient, you can use the following declare to create a NSGraphicsContext from a CGContextRef.

[code] declare function graphicsContextWithGraphicsPort lib AppKit selector “graphicsContextWithGraphicsPort:flipped:” ( classRef as Ptr, inPort as Integer, inFlipped as boolean ) as Ptr
Dim NSGraphicsClassRef as Ptr = NSClassFromString( “NSGraphicsContext” )
Dim context as Ptr = graphicsContextWithGraphicsPort( NSGraphicsClassRef, g.Handle( g.HandleTypeCGContextRef ), false )

    saveGraphicsState( NSGraphicsClassRef )
    setCurrentContext( NSGraphicsClassRef, context )[/code]

Your declare for setCurrentContext will need to altered to accept a Ptr instead of a integer, you should only use integer for objectRefs when passing Xojo .handles, otherwise Ptr is a safer method.

The other thing that you’re doing which is very in efficient, is re-creating the gradient every time you want to draw it (which guessing at the code behind it is also convert a Xojo color to NSColor every time the gradient is drawn).

You should retain the gradient and then release it when you don’t need it any more.

Thanks a lot, Sam! I was hoping I could somehow move the NSGradient to a CG one because I have already set up that class – but not its CG counterpart yet. :wink:

And no, it‘s not that inefficient as it may look. I am trying to set up Mac OS-extended subclasses of Xojo controls – in this case a PushButton that inherits many properties and methods of a NSButton. The idea is to keep them as easy as possible to use with Xojo classes and objects, so while there will surely be some overhead because I do not want the intermingled state of classes like in MacOSLib and therefore add needed classes (in the form of Ptrs) and conversion methods to each control separately, the Gradient will be buffered by a instance property. This method is only called when a button needs to be set up with a custom Gradient as Background image.

What I am not sure about is: Do I have to call a retain on the gradient when I create it or will the instance Ptr property I assign this to hold it in memory as long as the Button instance exists? In the first case, I guess I would have to call a release on each NSObject subclass I create for this instance, wouldn‘t I?

(add an “in the instance destructor” to he last sentence, please – cannot edit my post).

You’re welcome.

I see what you’re saying. I would advise using a NSImage for this purpose, as it will be Retina compatible (Xojo pictures are not Retina compatible). However the process is a little more complicated than using a Xojo Picture.

Create a new NSimage of a certain size.

Lock focus on the NSImage.

Draw your gradient.

Unlock the focus.

Then set as the background.

What you should do, is to call retain on the Ptr after you’ve created it and then call ‘release’ once you’ve finished with it. This is basis of reference counting memory management, Xojo does this for Xojo objects, but not NSObjects, these declares are for NSObjects, so you need to do the memory management yourself. It’s very important that you match every ‘retain’ with a ‘release’ otherwise you’ll leak objects.

Most Objective-C APIs will also call ‘Retain’ and ‘release’ objects as they need them, but don’t be fooled into thinking that you don’t need to do anything about it. A good programmer will also inform the system when they need something and when they no longer need something. Once you get your head around this, you’re all set.

And thanks a lot again, Sam! Highly appreciated!
I wasn‘t aware of the resolution independence of NSImages. That‘s a valuable hint (and I can save some back-conversion because the Gradient buffering works like you proposed but with a conversion from NSImage to Xojo Picture). Excellent!

Maybe not calling retain is the reason some methods don‘t seem to work although from looking at the code they should. I guess one cannot tell how long a non-retained NSObject will exist then? I looked into retaincount and usually found more than a 1 in it but Apple says you shouldn‘t use this information, so I will give retain a try.

Unless you retain a NSObject, it’s released at the end of the run loop.

When you learn Objective-C, one of the first things you should learn is about memory management and thankfully retain/release is easy and efficient.

I see. I am still unsure if assigning it to a instance pointer property prolongs its life until the instance is released. But obviously it won‘t hurt to retain it and release it in the class‘ destructor.

I will surely look into the memory management thing. Anyway, I am still hoping I do not end learning too much Objective C. After all, Xojo is that much easier to use. And understand :wink:

Thanks again!

AFAIK it doesn’t, and even if it did, you are correct in that you calling retain and release wouldn’t disturb that.

Xojo is easier, but once you taste the power and flexibility of tapping into the almighty Cocoa frameworks yourself, you’ll be addicted for sure!

Well yes, I have a little inkling of that already. I have added some missing classes to MacOSLib (but they were neither denied nor accepted to the canon) and am therefore trying to combine the best of both worlds. With uncertain outcome yet, but the initial results look quite promising to me.

So time to mark this thread answered and get back to work. Thanks a lot again!