changeset 19:43f0d78057a9

New event dispatching code that uses weak references to allow listened layers to be garbage-collected.
author Mikhail Kryshen <mikhail@kryshen.net>
date Thu, 17 Jun 2010 20:59:22 +0400
parents 740b9d2bbc45
children 357bdd7d0550
files src/indyvon/component.clj src/indyvon/event.clj
diffstat 2 files changed, 57 insertions(+), 31 deletions(-) [+]
line diff
     1.1 --- a/src/indyvon/component.clj	Thu Jun 17 18:54:22 2010 +0400
     1.2 +++ b/src/indyvon/component.clj	Thu Jun 17 20:59:22 2010 +0400
     1.3 @@ -83,26 +83,19 @@
     1.4        (.pack)
     1.5        (.setVisible true))
     1.6  
     1.7 -    (defmethod handle-layer-event [layer1 MouseEvent/MOUSE_ENTERED]
     1.8 -      [layer context event]
     1.9 -      (println "1 ENTERED"))
    1.10 -    (defmethod handle-layer-event [layer1 MouseEvent/MOUSE_EXITED]
    1.11 -      [layer context event]
    1.12 -      (println "1 EXITED"))
    1.13 -    (defmethod handle-layer-event [layer1 MouseEvent/MOUSE_MOVED]
    1.14 -      [layer context event]
    1.15 -      (println "1 MOVED"))
    1.16 -    (defmethod handle-layer-event [layer2 MouseEvent/MOUSE_ENTERED]
    1.17 -      [layer context event]
    1.18 -      (println "2 ENTERED"))
    1.19 -    (defmethod handle-layer-event [layer2 MouseEvent/MOUSE_EXITED]
    1.20 -      [layer context event]
    1.21 -      (println "2 EXITED"))
    1.22 -    (defmethod handle-layer-event [layer2 MouseEvent/MOUSE_MOVED]
    1.23 -      [layer context event]
    1.24 -      (println "2 MOVED"))
    1.25 -    (defmethod handle-layer-event [layer2 MouseEvent/MOUSE_DRAGGED]
    1.26 -      [layer context event]
    1.27 -      (println "2 DRAGGED"))
    1.28 +    (add-listener layer1 MouseEvent/MOUSE_ENTERED
    1.29 +                  (fn [context event] (println "1 ENTERED")))
    1.30 +    (add-listener layer1 MouseEvent/MOUSE_EXITED
    1.31 +                  (fn [context event] (println "1 EXITED")))
    1.32 +    (add-listener layer1 MouseEvent/MOUSE_MOVED
    1.33 +                  (fn [context event] (println "1 MOVED")))
    1.34 +    (add-listener layer2 MouseEvent/MOUSE_ENTERED
    1.35 +                  (fn [context event] (println "2 ENTERED")))
    1.36 +    (add-listener layer2 MouseEvent/MOUSE_EXITED
    1.37 +                  (fn [context event] (println "2 EXITED")))
    1.38 +    (add-listener layer2 MouseEvent/MOUSE_MOVED
    1.39 +                  (fn [context event] (println "2 MOVED")))
    1.40 +    (add-listener layer2 MouseEvent/MOUSE_DRAGGED
    1.41 +                  (fn [context event] (println "2 DRAGGED")))
    1.42      )
    1.43    )
     2.1 --- a/src/indyvon/event.clj	Thu Jun 17 18:54:22 2010 +0400
     2.2 +++ b/src/indyvon/event.clj	Thu Jun 17 20:59:22 2010 +0400
     2.3 @@ -6,13 +6,47 @@
     2.4  
     2.5  (ns indyvon.event
     2.6    (:use indyvon.core)
     2.7 -  (:import (java.awt.event MouseEvent MouseListener MouseMotionListener)))
     2.8 +  (:import (java.awt.event MouseEvent MouseListener MouseMotionListener)
     2.9 +           java.lang.ref.WeakReference))
    2.10  
    2.11 -(defmulti handle-layer-event 
    2.12 -  (fn [layer context event]
    2.13 -    [layer (.getID event)]))
    2.14 +;; map event-id -> [layer-weak-ref1 fn1, layer-weak-ref2 fn2...]
    2.15 +(def listeners-map (ref {}))
    2.16  
    2.17 -(defmethod handle-layer-event :default [layer context event])
    2.18 +(defn- assoc-conj [map key & vals]
    2.19 +  (assoc map key (apply conj (vec (get map key)) vals)))
    2.20 +
    2.21 +(defn add-listener
    2.22 +  "The supplied function will be invoked with context, event and
    2.23 +  additional args when an event with the specified id occurs on the
    2.24 +  specified layer."
    2.25 +  [layer event-id f & args]
    2.26 +  (let [f (if args #(apply f %1 %2 args) f)]
    2.27 +    (dosync
    2.28 +     (alter listeners-map assoc-conj event-id (WeakReference. layer) f))
    2.29 +    nil))
    2.30 +
    2.31 +(defn- listeners
    2.32 +  "Returns list of listener fns for event and target-layer. Listeners
    2.33 +  for garbage-collected layers are removed."
    2.34 +  [event-id target-layer]
    2.35 +  (dosync
    2.36 +   (loop [ref-vec (@listeners-map event-id) cleared-ref-vec [] listeners []]
    2.37 +     (if-let [layer-ref (first ref-vec)]
    2.38 +       (if-let [layer (.get layer-ref)]
    2.39 +         (let [lfn (second ref-vec)]
    2.40 +           (recur (nnext ref-vec)
    2.41 +                  (conj cleared-ref-vec layer-ref lfn)
    2.42 +                  (if (= layer target-layer)
    2.43 +                    (conj listeners lfn)
    2.44 +                    listeners)))
    2.45 +         (recur (nnext ref-vec) cleared-ref-vec listeners))
    2.46 +       (do
    2.47 +         (alter listeners-map assoc event-id cleared-ref-vec)
    2.48 +         listeners)))))
    2.49 +
    2.50 +(defn dispatch-event [context event]
    2.51 +  (doseq [listener (listeners (.getID event) (:layer context))]
    2.52 +    (listener context event)))
    2.53  
    2.54  (defprotocol EventDispatcher
    2.55    (listen! [this component])
    2.56 @@ -78,11 +112,10 @@
    2.57    ([contexts event]
    2.58       (translate-and-dispatch contexts event (.getID event)))
    2.59    ([contexts event id]
    2.60 -  (doseq [c contexts]
    2.61 -    (handle-layer-event
    2.62 -     (:layer c)
    2.63 -     c
    2.64 -     (translate-mouse-event event (:x c) (:y c) id)))))
    2.65 +  (doseq [context contexts]
    2.66 +    (dispatch-event
    2.67 +     context
    2.68 +     (translate-mouse-event event (:x context) (:y context) id)))))
    2.69  
    2.70  (defn- dispatch-mouse-motion*
    2.71    "Dispatches mouse motion events. Returns a new set of contexts which