changeset 29:4cb70c5a6e0d

Event handlers are registered using listen macro instead of implementing a protocol.
author Mikhail Kryshen <mikhail@kryshen.net>
date Tue, 06 Jul 2010 06:05:28 +0400
parents 1237f7555029
children a8821f4b5ade
files src/indyvon/component.clj src/indyvon/core.clj src/indyvon/layers.clj
diffstat 3 files changed, 133 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/src/indyvon/component.clj	Mon Jun 21 04:00:45 2010 +0400
+++ b/src/indyvon/component.clj	Tue Jul 06 06:05:28 2010 +0400
@@ -6,7 +6,8 @@
 
 (ns indyvon.component
   (:use indyvon.core indyvon.layers)
-  (:import (java.awt Component Dimension Color)
+  (:import (indyvon.core Size Location)
+           (java.awt Component Dimension Color)
            (javax.swing JFrame JPanel)))
 
 (defn- font-context [component]
@@ -29,7 +30,7 @@
                   :target component
                   :font-context (font-context component))
         s (size layer context)]
-    (Dimension. (s 0) (s 1))))
+    (Dimension. (:width s) (:height s))))
 
 (defn make-jpanel
   ([layer]
@@ -57,10 +58,7 @@
           (render! [this context g]
              (.setColor g Color/RED)
              (.fillRect g 0 0 (:width context) (:height context)))
-          (size [this context] [30 20])
-          MouseHandler
-          (handle-mouse [this context event]
-             (println "layer1" event))))
+          (size [this context] (Size. 30 20))))
     
     (def layer1b (border-layer layer1 2 3))
     
@@ -72,10 +70,7 @@
              (.fillRect g 0 0 (:width context) (:height context))
              (draw! layer1b context g 10 5)
              (draw! layer1 context g 55 5))
-          (size [this context] [70 65])
-          MouseHandler
-          (handle-mouse [this context event]
-             (println "layer2" event))))
+          (size [this context] (Size. 70 65))))
     
     (def layer3
          (border-layer (text-layer "Sample\ntext" :right :center)))
@@ -111,7 +106,7 @@
              (draw! layer2 context g 15 20)
              (draw! layer3 context g 100 100 80 50)
              (draw! fps context g))
-           (size [this context] [400 300])))
+           (size [this context] (Size. 400 300))))
     
     (doto frame
       (.addWindowListener
--- a/src/indyvon/core.clj	Mon Jun 21 04:00:45 2010 +0400
+++ b/src/indyvon/core.clj	Tue Jul 06 06:05:28 2010 +0400
@@ -13,19 +13,16 @@
   (render! [this context graphics])
   (size [this context]))
 
+(defrecord Location [x y])
+(defrecord Size [width height])
+
 ;; TODO: modifiers
 (defrecord MouseEvent [id when x y x-on-screen y-on-screen button])
 
-(defprotocol MouseHandler
-  "Layers that also satisfy this protocol will recieve mouse events."
-  (handle-mouse [this context event]))
-
 (defprotocol EventDispatcher
   (listen! [this component])
-  (register [this context])
-  (commit [this])
-  (hovered? [this layer])
-  (picked? [this layer]))
+  (register [this context handlers])
+  (commit [this]))
 
 (defprotocol Anchored
   "Provide anchor point for Layers. Used by viewport."
@@ -39,16 +36,17 @@
   (anchor [this context h-align v-align]
           (if (and (= h-align :left)
                    (= v-align :top))
-            [0 0]
+            (Location. 0 0)
             (let [size (size this context)]
-              [(case h-align
-                 :top 0
-                 :center (/ (size 0) 2)
-                 :right (size 0))
+              (Location.
+               (case h-align
+                     :top 0
+                     :center (/ (:width size) 2)
+                     :right (:width size))
                (case v-align
-                 :left 0
-                 :center (/ (size 1) 2)
-                 :bottom (size 1))]))))
+                     :left 0
+                     :center (/ (:height size) 2)
+                     :bottom (:height size)))))))
 
 (defrecord Theme [fore-color back-color border-color font])
 
@@ -85,7 +83,7 @@
   ([layer context graphics x y clip]
      (let [s (size layer context)]
        (draw! layer context graphics
-              x y (s 0) (s 1) clip)))
+              x y (:width s) (:height s) clip)))
   ([layer context graphics x y w h]
      (draw! layer context graphics
             x y w h true))
@@ -100,11 +98,23 @@
            graphics (make-graphics graphics x y w h clip)
            graphics (apply-theme graphics (:theme context))]
        (try
-         (register (:dispatcher context) context)
          (render! layer context graphics)
          (finally
           (.dispose graphics))))))
 
+(defn listen* [context & handlers]
+  (register (:dispatcher context) context (apply array-map handlers)))
+
+;; (listen context
+;;   (:mouse-entered e (println e))
+;;   (:mouse-exited e (println e)))
+(defmacro listen [context & specs]
+  `(register (:dispatcher ~context) ~context
+             ~(reduce #(assoc %1
+                         (first %2)
+                         `(fn [~(second %2)] ~@(nnext %2)))
+                      {} specs)))
+
 ;;
 ;; EventDispatcher implementation
 ;;
@@ -118,25 +128,38 @@
       java.awt.event.MouseEvent/MOUSE_PRESSED  :mouse-pressed
       java.awt.event.MouseEvent/MOUSE_RELEASED :mouse-released})
 
+(defrecord DispatcherNode [layer x y width height handlers children])
+
+(defrecord DispatcherRootNode [children])
+
+(defn- make-node [c handlers]
+  (DispatcherNode. (:layer c) (:x c) (:y c) (:width c) (:height c)
+                   handlers nil))
+
+(defn- add-child [node child]
+  (assoc node :children (cons child (:children node))))
+
 (defn- registered-parent
   "Returns first context parent registered for event processing."
-  [context-tree context]
+  [tree context]
   (let [parent (:parent context)]
     (cond
      (nil? parent) nil
-     (contains? context-tree parent) parent
-     :default (recur context-tree parent))))
+     (contains? tree parent) parent
+     :default (recur tree parent))))
 
 (defn- add-context
-  [context-tree context]
-  (let [parent (registered-parent context-tree context)]
-    (assoc context-tree parent (cons context (context-tree parent))
-           context nil)))
+  [tree context handlers]
+  (let [parent (registered-parent tree context)
+        node (make-node context handlers)]
+    (assoc tree
+      parent (add-child (tree parent) node)
+      context node)))
 
 (defn- inside?
-  ([x y context]
-     (inside? x y (:x context) (:y context)
-              (:width context) (:height context)))
+  ([x y node]
+     (inside? x y (:x node) (:y node)
+              (:width node) (:height node)))
   ([px py x y w h]
      (and (>= px x)
           (>= py y)
@@ -144,30 +167,15 @@
           (< py (+ y h)))))
 
 (defn- under-cursor
-  "Returns a sequence of contexts under cursor."
-  ([context-tree x y]
-     (under-cursor context-tree x y nil))
-  ([context-tree x y context]
-     (some #(if (inside? x y %)
-              (conj (under-cursor context-tree x y %) %))
-           (context-tree context))))
+  "Returns a sequence of child nodes under cursor."
+  [x y node]
+  (some #(if (inside? x y %)
+           (conj (under-cursor x y %) %))
+        (:children node)))
 
 (defn- remove-all [coll1 coll2 pred]
   (filter #(not (some (partial pred %) coll2)) coll1))
 
-;; (defn- translate-mouse-event
-;;   [event x y id]
-;;   (proxy [MouseEvent] [(.getComponent event)
-;;                        id
-;;                        (.getWhen event)
-;;                        (.getModifiers event)
-;;                        (- (.getX event) x)
-;;                        (- (.getY event) y)
-;;                        (.getClickCount event)
-;;                        (.isPopupTrigger event)]
-;;     (getXOnScreen [] (.getXOnScreen event))
-;;     (getYOnScreen [] (.getYOnScreen event))))
-
 (defn- translate-mouse-event
   [event x y id]
   (MouseEvent. id (.getWhen event)
@@ -176,22 +184,22 @@
                (.getButton event)))
 
 (defn- translate-and-dispatch
-  ([contexts event]
-     (translate-and-dispatch contexts event (awt-events (.getID event))))
-  ([contexts event id]
-     (doseq [context contexts]
-       (handle-mouse
-        (:layer context) context 
-        (translate-mouse-event event (:x context) (:y context) id)))
+  ([nodes event]
+     (translate-and-dispatch nodes event (awt-events (.getID event))))
+  ([nodes event id]
+     (doseq [node nodes]
+       (when-let [handler (get (:handlers node) id)]
+         (handler
+          (translate-mouse-event event (:x node) (:y node) id))))
      id))
 
 (defn- dispatch-mouse-motion*
-  "Dispatches mouse motion events. Returns a new set of contexts which
+  "Dispatches mouse motion events. Returns a new set of nodes which
   currently are under cursor."
-  [hovered context-tree event]
+  [hovered tree event]
   (let [x (.getX event)
         y (.getY event)
-        hovered2 (under-cursor context-tree x y)
+        hovered2 (under-cursor x y (get tree nil))
         pred #(= (:layer %1) (:layer %2))
         exited (remove-all hovered hovered2 pred)
         entered (remove-all hovered2 hovered pred)
@@ -202,12 +210,12 @@
     hovered2))
 
 (defn- dispatch-mouse-motion
-  [hovered-ref context-tree event]
+  [hovered-ref tree event]
   (dosync
-   (alter hovered-ref dispatch-mouse-motion* context-tree event)))
+   (alter hovered-ref dispatch-mouse-motion* tree event)))
 
 (defn- dispatch-mouse-button*
-  "Dispatches mouse button events. Returns a new set of contexts which
+  "Dispatches mouse button events. Returns a new set of nodes which
   currently are picked with a pressed button."
   [picked hovered event]
   (if (= (translate-and-dispatch hovered event) :mouse-pressed)
@@ -220,8 +228,9 @@
    (alter picked-ref dispatch-mouse-button* @hovered-ref event)))
 
 (defn make-event-dispatcher []
-  (let [context-tree-r (ref {}) ; register
-        context-tree (ref {})   ; dispatch
+  (let [tree-i {nil (DispatcherRootNode. nil)} ; initial
+        tree-r (ref tree-i)                    ; register
+        tree (ref tree-i)                      ; dispatch
         hovered (ref '())
         picked (ref '())]
     (reify
@@ -230,19 +239,16 @@
         (doto component
           (.addMouseListener this)
           (.addMouseMotionListener this)))
-     (register [this context]
-        (when (satisfies? MouseHandler (:layer context))
-          (dosync (alter context-tree-r add-context context))))
+     (register [this context handlers]
+        (dosync (alter tree-r add-context context handlers)))
      (commit [this]
-        (dosync (ref-set context-tree @context-tree-r)
-                (ref-set context-tree-r {})))
-     (picked? [this layer] false)
-     (hovered? [this layer] false)
+        (dosync (ref-set tree @tree-r)
+                (ref-set tree-r tree-i)))
      MouseListener
      (mouseEntered [this event]
-        (dispatch-mouse-motion hovered @context-tree event))
+        (dispatch-mouse-motion hovered @tree event))
      (mouseExited [this event]
-        (dispatch-mouse-motion hovered @context-tree event))
+        (dispatch-mouse-motion hovered @tree event))
      (mouseClicked [this event]
         (dispatch-mouse-button picked hovered event))
      (mousePressed [this event]
@@ -253,4 +259,29 @@
      (mouseDragged [this event]
         (translate-and-dispatch @picked event))
      (mouseMoved [this event]
-        (dispatch-mouse-motion hovered @context-tree event)))))
+        (dispatch-mouse-motion hovered @tree event)))))
+
+;;
+;; ИДЕИ:
+;;
+;; Контекст: биндинги или запись?
+;;
+;; Установка обработчиков (в контексте слоя):
+;;
+;; (listen
+;;   (:mouse-entered e
+;;     ...)
+;;   (:mouse-exited e
+;;     ...))
+;;
+;; Не надо IMGUI.
+;; Построение сцены путем декорирования слоев:
+;;
+;; (listener
+;;  (:action e (println e))
+;;  (:mouse-dragged e (println e))
+;;  (theme :font "Helvetica-14"
+;;    (vbox
+;;      (button (text-layer "Button 1"))
+;;      (button (text-layer "Button 2")))))
+;;
--- a/src/indyvon/layers.clj	Mon Jun 21 04:00:45 2010 +0400
+++ b/src/indyvon/layers.clj	Tue Jul 06 06:05:28 2010 +0400
@@ -6,7 +6,8 @@
 
 (ns indyvon.layers
   (:use indyvon.core)
-  (:import (java.awt Cursor)
+  (:import (indyvon.core Size Location)
+           (java.awt Cursor)
            (java.awt.font FontRenderContext TextLayout)))
 
 ;; Define as macro to avoid unnecessary calculation of inner and outer
@@ -42,8 +43,8 @@
                     (- h offset offset))))
         (size [l c]
            (let [s (size content c)]
-             [(+ (s 0) offset offset)
-              (+ (s 1) offset offset)]))))))
+             (Size. (+ (:width s) offset offset)
+                    (+ (:height s) offset offset))))))))
 
 (defn- re-split [re s]
   (seq (.split re s)))
@@ -85,7 +86,7 @@
                                       (:font-context c))
                  width (text-width layouts)
                  height (text-height layouts)]
-             [width height]))))))
+             (Size. width height)))))))
 
 (defn viewport
   "Creates scrollable viewport layer."
@@ -100,6 +101,21 @@
     (reify
      Layer
      (render! [layer c g]
+        (listen c
+         (:mouse-pressed e
+          (dosync
+           (ref-set fix-x (:x-on-screen e))
+           (ref-set fix-y (:y-on-screen e)))
+          (->> Cursor/MOVE_CURSOR Cursor. (.setCursor (:target c))))
+         (:mouse-released e
+          (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor (:target c))))
+         (:mouse-dragged e
+          (dosync
+           (alter x + (- @fix-x (:x-on-screen e)))
+           (alter y + (- @fix-y (:y-on-screen e)))
+           (ref-set fix-x (:x-on-screen e))
+           (ref-set fix-y (:y-on-screen e)))
+          (update c)))
         (let [anchor (anchor content c h-align v-align)
               width (:width c)
               height (:height c)]
@@ -109,26 +125,6 @@
            (ref-set last-width width)
            (ref-set last-height height))
           (draw! content c g
-                 (- 0 @x (anchor 0))
-                 (- 0 @y (anchor 1)))))
-     (size [layer c] (size content c))
-     MouseHandler
-     (handle-mouse [layer c e]
-       (case (:id e)
-         :mouse-pressed
-         (do
-           (dosync
-            (ref-set fix-x (:x-on-screen e))
-            (ref-set fix-y (:y-on-screen e)))
-           (->> Cursor/MOVE_CURSOR Cursor. (.setCursor (:target c))))
-         :mouse-released
-         (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor (:target c)))
-         :mouse-dragged
-         (do
-           (dosync
-            (alter x + (- @fix-x (:x-on-screen e)))
-            (alter y + (- @fix-y (:y-on-screen e)))
-            (ref-set fix-x (:x-on-screen e))
-            (ref-set fix-y (:y-on-screen e)))
-           (update c))
-         nil))))))
+                 (- 0 @x (:x anchor))
+                 (- 0 @y (:y anchor)))))
+     (size [layer c] (size content c))))))