|Version 3 (modified by rme, 6 years ago)|
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.
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.
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)))