Search code examples
objective-cmacosquartz-graphicsquartz-2drubymotion

RubyMotion: trying to follow Quartz 2D programming guide for Mac


I'm trying to follow Quartz 2D programming guide using RubyMotion.

Here's my AppDelegate:

class AppDelegate
  def applicationDidFinishLaunching(notification)
    buildMenu
    buildWindow
  end

  def buildWindow
    @window = NSWindow.alloc.initWithContentRect([[240, 180], [480, 360]],
      styleMask: NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask,
      backing: NSBackingStoreBuffered,
      defer: false)
    @window.title = NSBundle.mainBundle.infoDictionary['CFBundleName']
    @window.orderFrontRegardless

    @view = MyQuartzView.alloc.initWithFrame(@window.frame)
    @window.contentView.addSubview @view
  end
end

And here is my MyQuartzView, which is supposed to be direct translation from code in the guide:

class MyQuartzView < NSView
  def drawRect(rect)
    myContext = NSGraphicsContext.currentContext.graphicsPort
    CGContextSetRGBFillColor(myContext, 1, 0, 0, 1)
    CGContextFillRect(myContext, CGRectMake(0, 0, 200, 100))
    CGContextSetRGBFillColor(myContext, 0, 0, 1, 0.5)
    CGContextFillRect(myContext, CGRectMake(0, 0, 100, 200))
  end
end

I'm getting the following errors:

<Error>: CGContextSetRGBFillColor: invalid context 0x10222bad0
<Error>: CGContextFillRects: invalid context 0x10222bad0
<Error>: CGContextSetRGBFillColor: invalid context 0x10222bad0
<Error>: CGContextFillRects: invalid context 0x10222bad0

Why is the context invalid? I'm inside drawRect method.

EDIT If I change window rect to [[340, 380], [480, 360]] the error goes away, but drawRect is not called. However, when I resize window, it gets called with same errors.

EDIT 2 This is an OS X app.

EDIT 3 Interesting, the same program in Objective-C works fine:

// main.m
#import <Cocoa/Cocoa.h>
#import "MyQuartzView.h"

int main(int argc, const char * argv[])
{
    NSApplication *app = [NSApplication sharedApplication];
    NSRect frame = NSMakeRect(100., 100., 300., 300.);

    NSWindow *window = [[NSWindow alloc]
        initWithContentRect: frame
                  styleMask: NSTitledWindowMask | NSClosableWindowMask
                    backing: NSBackingStoreBuffered
                      defer: false];

    [window setTitle: @"Test"];

    id view = [[MyQuartzView alloc] initWithFrame: frame];
    [window setContentView: view];
    [window setDelegate: view];
    [window orderFrontRegardless];

    [app run];

    return EXIT_SUCCESS;
}

// MyQuartzView.m
#import "MyQuartzView.h"

@implementation MyQuartzView

- (id)initWithFrame:(NSRect)frame
{
    return[super initWithFrame:frame];
}

- (void)drawRect:(NSRect)dirtyRect
{
    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100));
    CGContextSetRGBFillColor (myContext, 0, 0, 1, .5);
    CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200));
}

@end

Solution

  • Here's the magic to make it work: You need to call to_object when you get the context:

    myContext = NSGraphicsContext.currentContext.graphicsPort.to_object
    

    I implemented your code in a RubyMotion project, and this was the result:

    enter image description here