view src/indyvon/core.clj @ 4:0771180bf7c2

Abandoned approach at event dispatching.
author Mikhail Kryshen <mikhail@kryshen.net>
date Thu, 10 Jun 2010 02:54:35 +0400
parents 6bc931b1b755
children 74f1f265c3d9
line wrap: on
line source

(ns indyvon.core
  (:import (java.awt Dimension Point Component Graphics2D AWTEvent)
           (java.awt.event MouseAdapter MouseMotionAdapter
                           MouseListener MouseMotionListener)))

(def ^{:private true} *rx* 0)
(def ^{:private true} *ry* 0)

(def *graphics*)

(def *width*)
(def *height*)

(def *lag*)

(def *update-fn*)

(defprotocol Layer
  (render! [this])
  (size [this])
  (anchor [this]))

(defmacro reify-layer [& fns]
  (let [method-map {'size [['_] [0 0]]
                    'anchor [['_] [0 0]]}
        method-map (loop [fns fns
                          mm method-map]
                     (if-let [form (first fns)]
                       (recur (next fns)
                              (conj mm [(first form) (next form)]))
                       mm))
        methods (for [m method-map]
                  (cons (first m) (second m)))]
    `(reify Layer ~@methods)))

(defn- make-graphics [g x y w h clip]
  (if clip
    (.create g x y w h)
    (doto (.create g)
      (.translate x y))))

(defn render-layer!
  "Render layer in a new graphics context."
  ([layer]
     (render-layer! layer 0 0 *width* *height*))
  ([layer x y]
     (render-layer! layer x y true))
  ([layer x y clip]
     (let [s (size layer)]
       (render-layer! layer x y (.width s) (.height s) clip)))
  ([layer x y w h]
     (render-layer! layer x y w h true))
  ([layer x y w h clip]
     (binding [*graphics* (make-graphics *graphics* x y w h clip)
               *rx* (+ *rx* x)
               *ry* (+ *ry* y)
               *width* w
               *height* h]
       (render! layer))))

;;
;; Event handling
;;

(defmulti handle-layer-event 
  (fn [layer event]
    [layer (.getID event)]))

(defmethod handle-layer-event :default [layer event]
           false)

(defprotocol EventDispatcher
  (register [this layer])
  (commit [this])
  (dispatch [this event]))

;;(defrecord LayerContextState [hovered contexts])

(defrecord LayerContext [layer rx ry width height update-fn])

;; LayerContext сам реализует EventDispatcher.
;; Дерево диспетчеров-контекстов.
;; Передача события от корня.

(defn make-event-dispatcher []
  (let [contexts-r (ref []) ; register
        contexts (ref [])   ; dispatch
        hovered (ref [])
        picked (ref [])]
    (reify
     EventDispatcher
     (register [this layer]
               (dosync
                (alter contexts-r conj
                       (LayerContext. layer *rx* *ry*
                                      *width* *height*
                                      *update-fn*))))
     (commit [this]
             (dosync (ref-set contexts @contexts-r)
                     (ref-set contexts-r [])))
     (dispatch [this event]
               (println "dispatch" this event)
               ;; TODO
               )
     MouseListener
     (mouseClicked [this event])
     (mouseEntered [this event])
     (mouseExited [this event])
     (mousePressed [this event])
     (mouseReleased [this event])
     MouseMotionListener
     (mouseDragged [this event])
     (mouseMoved [this event]))))

;;
;; Connection to AWT.
;;

(defn- make-update-fn [component]
  (fn [] (.repaint component)))

;; (defn make-component [layer]
;;   (proxy [Component] []
;;     (update [g] (.paint this g))
;;     (paint [g]
;;            (let [insets (.getInsets this)
;;                  top (.top insets)
;;                  left (.left insets)
;;                  bottom (.bottom insets)
;;                  right (.right insets)
;;                  size (.getSize this)
;;                  width (- (.width size) left right)
;;                  height (- (.height size) top bottom)]
;;              (binding [*graphics* g
;;                        *update-fn* (make-update-fn this)]
;;                (render-layer! layer top left width height false))))
;;     (getPreferredSize []
;;                       (size layer))))

(defn make-component
  ([layer]
     (make-component layer (make-event-dispatcher)))
  ([layer event-dispatcher]
     (doto
         (proxy [Component] []
           (update [g] (.paint this g))
           (paint [g]
                  (let [size (.getSize this)
                        width (.width size)
                        height (.height size)]
                    (binding [*graphics* g
                              *update-fn* (make-update-fn this)]
                      (render-layer! layer 0 0 width height false))))
           (getPreferredSize []
                             (let [s (size layer)]
                               (Dimension. (s 0) (s 1))))
           (processEvent [event]
                         (dispatch event-dispatcher event)))
       ;; No way to call protected final evenbleEvents even in gen-class,
       ;; have to use the following hack:
       (.addMouseListener (proxy [MouseAdapter] []))
       (.addMouseMotionListener (proxy [MouseMotionAdapter] [])))))

(comment
  (do 
    (def frame (java.awt.Frame. "Test"))
    (def layer
         (reify-layer
          (render! [this]
                   (.fillRect *graphics* 10 10 40 40))
          (size [this] [100 100])))
    (doto frame
      (.addWindowListener
       (proxy [java.awt.event.WindowAdapter] []
         (windowClosing [event] (.dispose frame))))
      (.add (make-component layer))
      (.pack)
      (.setVisible true))
    )
  )