UP | HOME

Cocoa in Racket

Racket provides Objective-C bindings, so the best part here is that we can use Cocoa in Racket to build a GUI. In fact, racket/gui on macOS is based on Cocoa. The following program is relied on some works in dannypsnl/cocoa.

1. Fundamental

  1. we can use import-class to get class from Objective-C.

    (require ffi/unsafe
    	 ffi/unsafe/objc)
    
    (import-class NSColor
    	      NSBezierPath
    	  NSWindow
    	      NSApplication
    	      NSRect
    	      NSView)
    
  2. we have to disable signal handler

    ((get-ffi-obj 'signal #f (_fun _int _intptr -> _void)) 2 0)
    
  3. we setup app for global handle, and use call-with-autorelease to run main program with autorelease pool.

    (define app #f)
    
    (call-with-autorelease
     (lambda ()
       (define frame (make-NSRect (make-NSPoint 300 300) (make-NSSize 400 400)))
       (define mainView (tell (tell MyView alloc) initWithFrame: #:type _NSRect frame))
    
       (set! app (tell NSApplication sharedApplication))
       (define win (tell (tell NSWindow alloc)
    		     initWithContentRect: #:type _NSRect frame
    		     styleMask: #:type _int NSWindowStyleMaskTitled
    		     backing: #:type _int NSBackingStoreBuffered
    		     defer: NO))
       (tell win setTitle: #:type _NSString "test")
       (tell win setContentView: #:type _id mainView)
       (tell win makeKeyAndOrderFront: #f)
       (tell app run)))
    

2. Example

Finally, we define MyView by define-objc-class, this form helps us define an Objective-C class.

#lang racket/base
(require racket/math)

(define-cpointer-type _NSNotification)
(define-cocoa NSRectFill (_fun _id -> _void))

(define-objc-class MyView NSView
  ()
  (- _void (drawRect: [_NSRect rect])
     (define n 12)
     (define width 400)
     (define height 400)

     (tell (tell NSColor whiteColor) set)
     (NSRectFill (tell self bounds))

     (tell (tell NSColor blackColor) set)

     (define (x t) (* (add1 (sin t)) width 0.5))
     (define (y t) (* (add1 (cos t)) height 0.5))
     (for ([f (in-range 0 (* 2 pi)
			(/ (* 2 pi) n))])
       (for ([g (in-range 0 (* 2 pi)
			  (/ (* 2 pi) n))])
	 (define p1 (make-NSPoint (x f) (y f)))
	 (define p2 (make-NSPoint (x g) (y g)))
	 (tell NSBezierPath
	       strokeLineFromPoint: #:type _NSPoint p1
	       toPoint: #:type _NSPoint p2))))
  (- _void (windowWillClose: [_NSNotification notification])
     (tell app terminate: #:type _id self)))

Date: 2022-01-20 Thu 00:00

Author: Lîm Tsú-thuàn