| Version 3 (modified by rme, 6 years ago) |
|---|
Cocoa Bridge
As discussed in the section on the OpenMCL FFI, OpenMCL has a very powerful interface to the world of libraries and components that exist outside of the Lisp image. Foremost among these is the Cocoa Bridge, a binding layer to the Mac OS X user interface.
A good introductory Cocoa text is Aaron Hillegass's Cocoa Programming for Mac OS X.
Elementary Usage
Here's a very simple example of how to create a window and draw to it.
(in-package "CL-USER")
(require "COCOA")
(defclass red-view (ns:ns-view)
()
(:metaclass ns:+ns-object))
(objc:defmethod (#/drawRect: :void) ((self red-view) (rect :<NSR>ect))
(#/set (#/redColor ns:ns-color))
(#_NSRectFill (#/bounds self)))
(defun show-red-window ()
(ccl::with-autorelease-pool
(let* ((rect (ns:make-ns-rect 0 0 300 300))
(w (make-instance 'ns:ns-window
:with-content-rect rect
:style-mask (logior #$NSTitledWindowMask
#$NSClosableWindowMask
#$NSMiniaturizableWindowMask)
:backing #$NSBackingStoreBuffered
:defer t)))
(#/setTitle: w "Red")
(#/setContentView: w (make-instance 'red-view))
(#/center w)
(#/makeKeyAndOrderFront: w nil)
(#/contentView w))))
Load the file containing these forms, evaluate (show-red-window), and you'll see a red window.
More drawing
The general assumption in Cocoa is that you will do your drawing in your custom view's drawRect: method. It's often nice, though, to be able to draw by evaluating forms at the lisp top-level.
One problem with that, at least in OpenMCL, is that parts of Cocoa are not thread-safe. Fortunately, creating windows on secondary threads is supported. So is drawing to a view, provided that the drawing is bracketed with calls to lockFocusIfCanDraw and unlockFocus.
So, to keep from forgetting to unlockFocus, define this simple macro. (If you forget to unlockFocus, you'll get a spinning beach ball and have to kill the lisp.)
(defmacro with-focused-view (view &body forms)
`(when (#/lockFocusIfCanDraw ,view)
,@forms
(#/unlockFocus ,view)))
Now, you can draw. Notice that show-red-view returns a view instance.
(setf *v* (show-red-view))
(with-focused-view *v*
(let* ((path (#/bezierPath ns:ns-bezier-path)))
(#/moveToPoint: path (ns:make-ns-point 10 10))
(#/lineToPoint: path (ns:make-ns-point 100 100))
(#/stroke path)))
