===
原文地址:
网络: http://trac.clozure.com/ccl/wiki/CocoaBridge
原文标题:
Cocoa Bridge
翻译者:
FreeBlues 2013-07-21
===
如同在 OpenMCL FFI 章节中讨论的那样(http://trac.clozure.com/ccl/wiki/OpenMclFfi), OpenMCL 拥有一个非常强大的接口, 对于存在于 Lisp 的映像之外的接口库和组件的世界而言. 其中最重要的是 Cocoa 桥,一个 Mac OS X 用户接口的绑定层。
一本很好的介绍 Cocoa 的读物是 Aaron Hillegass 的 (http://www.amazon.com/Cocoa-Programming-Mac-OS-2nd/dp/0321213149).
这里有一个非常简单的例子: 如何创建和绘制一个窗口.
(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 (#/autorelease (make-instance 'red-view)))
(#/center w)
(#/orderFront: w nil)
(#/contentView w))))
加载包括这些语句形式的文件(直接把上述代码拷贝到 CCL 的 REPL 中, 然后按回车即可), 然后对下属语句形式求值:
(show-red-window)
你就会看到一个红色的窗口
在 Cocoa 中一般的假设是你会在您的自定义视图的 drawRect: 方法里做您的绘图。不过,如果能够在 lisp 顶层通过求值形式来绘制, 往往是不错的。
有一个随之而来的问题,至少在 OpenMCL 中,部分的 Cocoa 不是线程安全的。幸运的是,支持在辅助线程里创建窗口。因此绘制一个视图,通过调用 lockFocusIfCanDraw 和 unlockFocus 来提供绘制。
因此,要保持忘记 unlockFocus,定义这个简单的宏。(如果你忘记 unlockFocus,你会得到一个旋转的沙滩球并且不得不干掉 Lisp。)
(defmacro with-focused-view (view &body forms)
`(when (#/lockFocusIfCanDraw ,view)
(unwind-protect
(progn ,@forms)
(#/unlockFocus ,view)
(#/flushGraphics (#/currentContext ns:ns-graphics-context))
(#/flushWindow (#/window ,view)))))
flushGraphics/flushWindow 确保您的即时绘图被显示出来。
首先,创建一个窗口。(注意,函数 show-red-window 返回一个视图实例。)
(setf *v* (show-red-window))
现在, 有了一个视图实例在手, 你可以绘图了
(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)
(#/drawAtPoint:withAttributes: #@"hello world"
(ns:make-ns-point 10 100)
+null-ptr+)))
开始有点爱好者,我们可以编写代码来从网络上抓取一个图像并把它绘制在我们的视图中. 这一次, 我们使用 Objective-C 语言风格习惯创建一个 NSImage 对象实例。我们也可以写成这种 Lisp 语言风格:
(make-instance 'ns:ns-image :with-contents-of-url url)
请注意,您必须释放 NSImage 对象实例,不管你是如何创建它的。(当窗口关闭时, 在上述 show-red-window 中创建的 NSWindow 实例将被释放,所以没有内存泄漏. 但是,这是 NSWindow 类的一种特殊功能。)
(defun draw-earth (view)
(let* ((url (#/URLWithString: ns:ns-url #@"http://nssdc.gsfc.nasa.gov/thumbnail/planetary/earth/apollo17_earth.gif"))
(image (#/initWithContentsOfURL: (#/alloc ns:ns-image) url))
(alpha (float 1.0 ns:+cgfloat-zero+)))
(with-focused-view view
(ns:with-ns-rect (z 0 0 0 0)
(#/drawAtPoint:fromRect:operation:fraction:
image (ns:make-ns-point 40 40)
z
#$NSCompositeCopy
alpha)))
(#/release image)))
进行实际的绘制
(draw-earth *v*)
我们通过在 listener(即 REPL) 中求值形式进行的绘制, 可以被看作是“直接模式”绘图。这是一个有趣的实验,但如果视图被告知重新显示其本身时(例如,如果你最小化,然后恢复窗口), 你的绘图将被一扫而光。你需要为您的绘图程序安排, 使其被视图的 drawRect:方法来调用,因为它的绘图是“永久的”。
附录:完整的绘图代码:
(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 (#/autorelease (make-instance 'red-view)))
(#/center w)
(#/orderFront: w nil)
(#/contentView w))))
(show-red-window)
(defmacro with-focused-view (view &body forms)
`(when (#/lockFocusIfCanDraw ,view)
(unwind-protect
(progn ,@forms)
(#/unlockFocus ,view)
(#/flushGraphics (#/currentContext ns:ns-graphics-context))
(#/flushWindow (#/window ,view)))))
(setf *v* (show-red-window))
(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)
(#/drawAtPoint:withAttributes: #@"hello world"
(ns:make-ns-point 10 100)
+null-ptr+)))
(defun draw-earth (view)
(let* ((url (#/URLWithString: ns:ns-url #@"http://nssdc.gsfc.nasa.gov/thumbnail/planetary/earth/apollo17_earth.gif"))
(image (#/initWithContentsOfURL: (#/alloc ns:ns-image) url))
(alpha (float 1.0 ns:+cgfloat-zero+)))
(with-focused-view view
(ns:with-ns-rect (z 0 0 0 0)
(#/drawAtPoint:fromRect:operation:fraction:
image (ns:make-ns-point 40 40)
z
#$NSCompositeCopy
alpha)))
(#/release image)))
(draw-earth *v*)
可参考:
GradientWindow(http://trac.clozure.com/ccl/wiki/GradientWindow), EasyGuiCurrencyConverter(http://trac.clozure.com/ccl/wiki/EasyGuiCurrencyConverter).