Search code examples
cmonopinvokemonomac

MonoMac NSDrawThreePartImage / C Bindings


I'd like to use the function NSDrawThreePartImage (Application Kit Functions Reference) in my MonoMac application. However, it's not available to me.

Looking at the source for NSCell.cs, I can see it's commented out:

//APPKIT_EXTERN void NSDrawThreePartImage(NSRect frame, NSImage *startCap, NSImage *centerFill, NSImage *endCap, BOOL vertical, NSCompositingOperation op, CGFloat alphaFraction, BOOL flipped);

I would guess this just means, it was never implemented.

I understand it's a C API. What could be involved in manually calling this method? It looks like it could be done with a Pinvoke (Binding Objective-C Types). Can someone confirm that's the right approach?

Secondly, I'm unfamiliar with how to go about this since the method parameters are not simply primitives. Perhaps there is a good guide for doing this, or some existing examples I can refer to ?

Thanks


Solution

  • Since it's a C API, you can simply use P/Invoke to bind it.

    Regarding the complex types:

    • NSRect becomes RectangleF - see NSGraphics.RectClip in monomac/src/AppKit/NSGraphics.cs for an example.
    • NSImage * becomes IntPtr - use the constructor that takes an IntPtr to construct it its the Handle property to get the address.
    • GCFloat is simply a float.
    • BOOL is bool (see NSGraphics.BestDepth for an example).
    • Use int for the NSCompositingOperation enum.

    So, putting the pieces together, something like this should do it:

    [DllImport (Constants.AppKitLibrary)]
    extern static void NSDrawThreePartImage (RectangleF rect, IntPtr startCap, IntPtr centerFill, IntPtr endCap, bool vertial, int op, float alphaFraction, bool flipped);
    
    public void DrawThreePartImage (RectangleF frame, NSImage startCap, NSImage centerFill, NSImage endCap, bool vertical, NSCompositingOperation op, float alphaFraction, bool flipped)
    {
        // Or throw ArgumentNullException if NULL is not allowed here
        var startCapPtr = startCap != null ? startCap.Handle : IntPtr.Zero;
        var centerFillPtr = centerFill != null ? centerFill.Handle : IntPtr.Zero;
        var endCapPtr = endCap != null ? endCap.Handle : IntPtr.Zero;
        NSDrawThreePartImage (frame, startCapPtr, centerFillPtr, endCapPtr, vertical, (int)op, alphaFraction, flipped);
    }
    

    NSDrawNinePartImage should work in a similar way.

    No idea why it's commented out, most likely it just never got implemented. There are more missing pieces. Let me know whether it works for you and I'll add it to NSCell.cs for you.

    Edit: I just added this to NSCell.cs: https://github.com/mono/monomac/commit/7c60d5756c49331642d2348c7c83320b9ec3f549. Thanks a lot for testing this!