About me

About me


RSS feed

LispWorks drag and drop

14th January 2018

For most of my Lisp programming I use LispWorks, and one of its features I especially like is its CAPI interface to the Mac Cocoa user-interface, which allows you to write Lisp applications that look like native Mac applications.

Here's a simple example I wrote recently which shows how easy it is to add drag and drop to your applications in LispWorks. It creates windows representing lists of objects; here I've used Animals and Plants. You can drag and drop objects from one window to another window of the same type, to move an object from one list to another:


When you drop the object it is moved between the lists:


The first step is to define a new class for a drag and drop aware list panel, based on the CAPI list-panel class:

(defclass drag-list (capi:list-panel)
  ((flavor :initform nil :initarg :flavor :accessor flavor))
   :drag-callback #'drag-callback
   :drop-callback #'drop-callback
   :interaction :extended-selection))

We add a slot flavor to this, which will be used to specify the type of object being dragged; in this example, plant or animal.

The function drag-callback is called when you initiate a drag from one of the list panels. This simply returns a list consisting of the flavor, and a list the indices of the items being dragged:

(defun drag-callback (pane indices)
  (list (flavor pane)
        (cons pane (map 'list 
                        #'(lambda (i) (elt (capi:collection-items pane) i))

The function drop-callback is called when you drag an object over one of the list panels, and drop it. This defines three stages: a :formats stage, which defines the flavors that the list will receive, a :drag stage, which defines what happens when an object is dragged onto the list panel, and a :drop stage, which defines what will happen when an object is dropped onto the list panel:

(defmethod drop-callback (pane drop-object stage)
  (case stage
      (list (flavor pane))))
     (let ((format (flavor pane)))
       (when (capi:drop-object-provides-format drop-object format)
         ;; Ignore drop position
         (setf (capi:drop-object-collection-index drop-object)
               (values -1 :item))
         (setf (capi:drop-object-drop-effect drop-object) :move))))
     (let ((format (flavor pane)))
       (when (capi:drop-object-provides-format drop-object format)
         (let ((drag (capi:drop-object-get-object drop-object pane format)))
           (move-items (cdr drag) (car drag) pane)
           (setf (capi:drop-object-drop-effect drop-object) :move)))))))

The actual drop action here is implemented by the call to move-items:

(defun move-items (items from to)
  (capi:remove-items from items)
  (capi:append-items to items))

In this example there's only one type of drag, :move, but you can also provide a :copy action, for example with the alt key held down, in which case the mouse pointer changes to show a + icon, and you can provide an appropriate copy action.

Here's the whole example: Drag and drop example.

blog comments powered by Disqus