NSSearchField

Thank you for posting the link to declaresub.com. I have been doing declares for the Windows API for years, but when it comes to Apple, as their documentation is geared exclusively towards Objective-C, I find it harder to decipher.

The part ‘Translating C to realBasic’ is extremely precious to me.

You‘re (as always) very welcome, Michel. Please feel free to share your insights from that book – I still struggle with exactly that part :wink:

I am struggling to. Sad case of allergic reaction every time I try to touch Objective C. Cannot help it. Tried several times. Yuk !

Here is a way I proceed that works :

setAlphaValue can be used to make a window partially transparent, so I wanted to see if I could get an effect kind of Yosemite compatible.

In typical Apple jargon here is the description of the Library :

- (void)setAlphaValue:(CGFloat)windowAlpha

The book already helps, but still the thing remains alien. So I go to MacOSLib and search for “setAlphaValue” :

declare sub setAlphaValue lib CocoaLib selector "setAlphaValue:" (obj_id as Ptr, windowAlpha as Single)

It already gets a LOT clearer to me. And I happened to remember a project where I had seen setAlphaValue before :

Soft Declare Sub setAlphaValue Lib "Cocoa" selector "setAlphaValue:" (windowRef As Integer, windowAlpha As Single)

It is not the first time I see a declare where for the same parameter a Ptr or an integer are used.

In the end, I learn more from MacOSLib than from trying to ingurgitate unsavory Objective-C. I haven’t got there yet, but I believe it should be possible to create a new declare by finding analogous jargon structure and using the transliterated MacOSLib as template.

I have been using the Windows API declares for years much comfortably, but just found recently how to tap into the Mac framework. Sam Rowlands also helped me greatly in https://forum.xojo.com/5179-mac-os-custom-fonts

[quote=116170:@Michel Bujardet]- (void)setAlphaValue: (CGFloat)windowAlpha
[/quote]
Here’s a very quick explanation… I’m just going to cover the very basics here.

First thing to look at is the “-” at the start of the line. This means that this is an instance method rather than a plus “+” which would indicate a class method (aka shared method). If there was neither, this would be a flat api function call (not an object selector).

Next in line is the return type. (void) means that there is no return value, so it will be declared with “declare sub” rather than “declare function … as (return type)”

Next is the selector… “setAlphaValue:” here we have a single selector expecting a value, hence the “:”.
(CGFloat)windowAlpha means the windowAlpha parameter is a CGFloat (aka a Xojo single)
There can be any number of selector parameters. ie “selectorOne:(aType)parameterOne selectorTwo:(aType)parameterTwo” etc… multiple selectors will be appended like so “selectorOne:selectorTwo:”

A function returning a value would look like this
- (CGFloat)alphaValue
“-” means it is an instance call
(CGFloat) is the return type
alphaValue is the selector (notice the lack of “:” since there is no parameter).

If you are calling a selector, you are calling it on an object (instance or class) and therefore must pass in a reference.
You’ll pass a ptr to the object handling the call as the first parameter ie
setAlphaValue(ptr(self.handle),1.0)

A call to a Class will be similar, but first you’ll need a reference to the class ala
Class NSClassFromString ( NSString *aClassName);
in Xojo is…
declare function NSClassFromString lib “Cocoa” (className as cfStringRef) as ptr

So if you need an NSColor for example
+ (NSColor *)greenColor
In Xojo…
declare function greenColor lib “Cocoa” selector “greenColor” (class_obj as ptr) as ptr

to call it you’ll need a reference to the NSColor class, we can get it inline…

  dim myGreenNSColor as ptr = greenColor( NSClassFromString("NSColor"))

could also be (if you’re doing multiple calls to the same class, or for readability)

  dim NSColorClassRef as ptr = NSClassFromString("NSColor")
  dim myGreenNSColor as ptr = greenColor( NSColorClassRef)

Hope that helps clarify basic translation between obj-c and Xojo

Thank you Jim. I will study that tomorrow. I am grateful for your help. I admire your declare knowledge.

I should add that an object reference can be passed in as an integer(ie window.handle) or a ptr, but I prefer to always use ptrs so that a ptr returned from one declare can be easily used in another.

Also, when I said -multiple selectors will be appended like so “selectorOne:selectorTwo:”

I should have clarified usage like…
declare sub [theMethodName] lib “Cocoa” selector “selectorOne:selectorTwo:” (the_obj as ptr, parameterOne as aType, parameterTwo as aType)

I also think there is a better way to say it… selectorOne:selectorTwo: isn’t right as it’s one selector with multiple parameters(messages)
… maybe “selectorParamOne:paramTwo:” makes more sense…?

Also the Method Name is only used on the Xojo side of things when a selector is used. This is important because you can have multiple declares where different parameter types are used ie.

declare sub setObjectValueInteger lib “Cocoa” selector “setObjectValue:” (receiver as ptr, value as integer)
declare sub setObjectValueString lib “Cocoa” selector “setObjectValue:” (receiver as ptr, value as cfStringRef)
declare sub setObjectValueObject lib “Cocoa” selector “setObjectValue:” (receiver as ptr, value as ptr)

There’s more to it of course if you need to override methods or handle notifications…

That‘s excellent, Jim, thanks a lot! This was a short tutorial in itself – think I should put up another thread pointing to this one.

And Michel: I can understand your allergy. I really do prefer languages where I don‘t have to enable my internal translator on each line I read.

I’ve added a channel on my forum for Xojo development stuff… I’ll be posting things there as well as on this forum that relate mostly to declares and Cocoa. Like mini-tutorials as I have time. I figured I’d put a channel on my forum since there isn’t much traffic there and it’ll be easy to find.

I will dash by your forum right away. Thanks.

Jim, thank you so much for your tutorial again!

It took me half the day to learn not to skip half of what you wrote, but I finally have made my first own declares into Mac OS (using MacOSLib as backup for some conversions). And here it is – NSGradients (I wanted to start with something that looked simple enough for the start – and it almost was not):

Now going for that glass of wine …

Congratulations, Ulrich :slight_smile:

My enormously grown hubris was already quite some reward :slight_smile:

In fact, the thing I stumbled upon mostly was to understand that InitSomething Functions need to be set up as Constructors. From there on, it was easy to extend with the next methods:

But of course that‘s just baby steps. I still don‘t have the faintest idea on how to set up something like this in Xojo:

[quote]initWithColorsAndLocations:
Initializes a newly allocated gradient object with a comma-separated list of arguments.

  • (id)initWithColorsAndLocations:(NSColor *)firstColor, …
    Parameters
    firstColor
    The first color in the gradient.

    A comma-separated list of alternating NSColor objects and location arguments (specified as CGFloat values). The first value after firstColor must be a location. Each location value must be between 0.0 and 1.0. The list must be nil-terminated.[/quote]

A comma-separated list? Containing mixed variables? And no dict?
How would one translate such a thing?

And another thing:

[quote]enum {
NSGradientDrawsBeforeStartingLocation = (1 << 0),
NSGradientDrawsAfterEndingLocation = (1 << 1),
};[/quote]

These are bitwise declarations as far as I can tell – the first one is true if bit 0 is true, the second one if bit 1 is set.
I can see I could work around this by some method, but could I set up a similar enumeration in Xojo?

To answer my last question myself: I was thinking too complicated. I guess such a structure will do:

Public Enum NSGradientDrawingOptions NSGradientDrawsBeforeStartingLocation = 1 NSGradientDrawsAfterEndingLocation = 2 NSGradientDrawsAfterBothLocations = 3 NSGradientStopsatBothLocations = 0

[quote=116879:@Ulrich Bogun]A comma-separated list? Containing mixed variables? And no dict?
How would one translate such a thing?

[/quote]
I did get CGGradientCreateWithColorComponents working by passing in a memoryblock of colors components and locations.
For NSGradient it looks like you might have better luck with

initWithColors:atLocations:colorSpace

Declare Function initWithColorsatLocationscolorSpace lib "Cocoa" selector "initWithColors:atLocations:colorSpace:" (obj_ref as ptr,colorArray as ptr,locations as ptr,colorSpace as ptr) as Ptr
It takes an NSArray for the list of colors, and a memoryblock can be used for the locations. ColorSpace can be genericRGB.
I can share my CGGradient example if you like. (It has the colorspace and memoryblock stuff you’ll need)

Yes, I‘d love to see that! Interesting: That works? I was worried because the Apple docs say that you need to send the location after the color.

And once I‘m done I will re-share this one here too.
BTW, what I didn‘t find: I guess GCGradient and NSGradient work on different layers or frameworks. Do you have any idea about their performance compared to each other?

As I understand it, NS graphics objects draw to the current context… so for example in the drawRect method of an NSView, the current context is the on-screen graphics of the NSView.
You can draw into a CGContext by creating an NSContext with graphicsContextWithGraphicsPort and setting it to be the current context with setCurrentContext… you can also draw to an NSBitmapImageRep with graphicsContextWithBitmapImageRep.

The CG methods draw to an explicit CGContext. In Xojo, the graphics.handle is a CGContextRef. So you can easily use CG methods with picture.graphics.handle or from a paint event using g.handle.

I find the simplest method for use in Xojo is to either use the CG methods directly on picture.graphics or in a paint event.

Here’s that CGGradient method-

[code]Private Sub ApplyGradient(byref p As Picture,colors() As color,pnts() As Double)
//draws a gradient in p based on colors and points (colors are regular Xojo colors and points are 0.0 to 1.0)

soft declare sub CGContextDrawLinearGradient lib “Cocoa” _
( context as ptr, gradient as ptr, startPoint as CGPoint, endPoint as CGPoint, options as UInt32)
soft declare function CGGradientCreateWithColorComponents lib “Cocoa” _
(space as ptr,components as ptr, locations as ptr,count as integer) as ptr
soft declare function CGColorSpaceCreateWithName lib “Cocoa” (name as CFStringRef) as ptr
soft declare sub CGColorSpaceRelease lib “Cocoa”(colorspace as ptr)
soft declare sub CGGradientRelease lib “Cocoa”(gradient as ptr)
const kCGColorSpaceGenericRGB=“kCGColorSpaceGenericRGB”

dim colorspace As Ptr=CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
dim colorblock As new MemoryBlock(16*(colors.Ubound+1))

//colorblock is a memoryblock ala r g b a r g b a r g b a
for i as integer=0 to colors.Ubound
colorblock.SingleValue(i16)=colors(i).red/255
colorblock.SingleValue(i
16+4)=colors(i).Green/255
colorblock.SingleValue(i16+8)=colors(i).Blue/255
colorblock.SingleValue(i
16+12)=1-colors(i).Alpha/255
next
dim gradient As ptr

if pnts=nil then //if pnts is empty the gradient color will be evenly distributed
gradient=CGGradientCreateWithColorComponents(colorspace,colorblock,nil,colors.Ubound+1)
else
//locs is just a list of CGFloats representing points for the gradient stops.
dim locs As new MemoryBlock(4*(pnts.Ubound+1))
for i as integer=0 to pnts.Ubound
locs.SingleValue(i*4)=pnts(i)
next
gradient=CGGradientCreateWithColorComponents(colorspace,colorblock,locs,colors.Ubound+1)
end if

dim gstart As CGPoint
dim gend as CGPoint
gstart.y=p.Height

CGContextDrawLinearGradient(ptr(p.Graphics.handle(Graphics.handletypecgcontextref)),gradient,gstart,gend,0)

//clean up things we own
CGColorSpaceRelease(colorspace)
CGGradientRelease(gradient)
End Sub
[/code]

Thanks again, Jim! Yes, that makes sense: The NSGradient class has direct drawing methods. Therefore I can draw to a Xojo Rectangle from the window‘s paint event for example. Or I can catch a Canvas‘ Paint event.

I remember having read someone looking for lines with gradients, this should be possible with DrawBezier (but the NSBezier class is still missing in MacOSLib).

Still very challenging to me is to have an exact look for the parameter definitions. I just spent two hours not realizing I still had the class in a function call, not a pointer … The debugger highlights the function‘s name instead of the parameter so I was looking in the wrong direction for some time.

And I really look forward to the Git Webinar. I cannot make this app run correctly (or I just don‘t understand it), but I found several pieces of MacOSLib I had to modify. So I would like to contribute those as well. (For example the line for ColorSpace in cocoa.NSObjectFromNSPtr was commented out, but I found it works and seems to return the ColorSpace. And the NSColorSpace constructors (InitWith…) methods are not yet implemented, it seems, so I could‘t Dim a colorSpace to forward it to the initWithColorsatLocationscolorSpace method.)

And say, would‘t initWithColorsAndLocations: that in its original wants an alternating comma-separated list work by passing a memory block with a repeating structure of

16 (Color) and
4 (Single Position)?

Is that how internally such lists are handled?
(I‘ll give it a try)

The answer is no – or I made a mistake.

I tried

[code]Sub Constructor(ColorArray() as NSColor, atLocations() as Double)
// Original Name: initWithColorsAndLocations

// Initializes a newly allocated gradient object with the specified colors and color locations.
// Locations are singles from 0.0 to 1.0
// Use: myGradient = new NSGradient (NSColorArray, LocationsArray)

#if TargetMacOS
Declare Function initWithColorsAndLocations lib “Cocoa” selector “initWithColorsAndLocations:” (id as ptr, ColorAndLocations as Ptr) as Ptr

    // InitWithColors in its original demands a comma-separated list of NSColors and their positions, so we create one:

dim colorblock As new MemoryBlock(20*(ColorArray.Ubound+1))

//colorblock is a memoryblock a la   r g b a r g b a r g b a + its position
// Thanks to Jim McKay for this information!

for i as integer=0 to ColorArray.Ubound
  colorblock.SingleValue(i*20)=ColorArray(i).RedComponent
  colorblock.SingleValue(i*20+4)=ColorArray(i).GreenComponent
  colorblock.SingleValue(i*20+8)=ColorArray(i).BlueComponent
  colorblock.SingleValue(i*20+12)=ColorArray(i).AlphaComponent
  colorblock.SingleValue(i*20+16)=atlocations(i)
next
      
super.Constructor ( initWithColorsAndLocations (Allocate("NSGradient"), colorblock), false)

#endif
End Sub[/code]

But the debugger quits without any further notice once I call the super.constructor.

Seems to me the API demands a different parameter setup. This method is unsafe anyway because ColorArray doesn‘t check for the ColorSpace and gives an exception if for example a calibratedwhite space is chosen. Maybe I better tweak this to call a initWithColors:atLocations:colorSpace: with the ColorSpace set to RGB.

One last question if you don‘t mind:
You translate the alpha value to your colorblock by counting 1 - (i*20) – why is that?