indyvon
 
(Mikhail Kryshen)
2011-05-19: Added Scene record to enclose state retained between repaints.

Added Scene record to enclose state retained between repaints.

diff --git a/src/net/kryshen/indyvon/async.clj b/src/net/kryshen/indyvon/async.clj
--- a/src/net/kryshen/indyvon/async.clj
+++ b/src/net/kryshen/indyvon/async.clj
@@ -119,23 +119,21 @@
       (.setComposite g AlphaComposite/Clear)
       (.fillRect g 0 0 (:width async-layer) (:height async-layer))
       (.setComposite g AlphaComposite/Src)
-      (draw-root! (:content async-layer)
-                  g
-                  (:width async-layer)
-                  (:height async-layer)
-                  ;; TODO: use operational event dispatcher.
-                  dummy-event-dispatcher))
+      (draw-scene! (:scene async-layer)
+                   g
+                   (:width async-layer)
+                   (:height async-layer)))
     (update async-layer)))
 
 (defn- draw-offscreen-async [async-layer]
   (.execute ^ThreadPoolExecutor (:executor async-layer)
             #(draw-offscreen async-layer)))
 
-(defrecord AsyncLayer [content width height executor buffers]
+(defrecord AsyncLayer [scene width height executor buffers]
   Layer
   (render! [layer]
     (repaint-on-update layer)
-    (add-context-observer content (fn [_ _] (draw-offscreen-async layer)))
+    (add-context-observer scene (fn [_ _] (draw-offscreen-async layer)))
     (when-not @buffers
       ;; TODO: dynamic size, recreate buffers when size increases.
       (let [device-conf (.getDeviceConfiguration *graphics*)
@@ -173,6 +171,9 @@
   ([content width height]
      (async-layer content width height nil))
   ([content width height priority]
-     (AsyncLayer. content width height
+     ;; TODO: use operational event dispatcher.
+     (AsyncLayer. (make-scene content)
+                  width
+                  height
                   (create-executor priority)
                   (ref nil))))
diff --git a/src/net/kryshen/indyvon/component.clj b/src/net/kryshen/indyvon/component.clj
--- a/src/net/kryshen/indyvon/component.clj
+++ b/src/net/kryshen/indyvon/component.clj
@@ -36,23 +36,28 @@
                *font-context* (font-context c#)]
        ~@body)))
 
+(defn- paint-component [^Component c ^Graphics g scene]
+  (let [size (.getSize c)]
+    (.setColor g (:back-color *theme*))
+    (.fillRect g 0 0 (.width size) (.height size))
+    (draw-scene! scene g (.width size) (.height size))))
+
+(defn- preferred-size [^Component c scene]
+  (let [s (scene-size scene (font-context c))]
+    (Dimension. (:width s) (:height s))))
+
 (defn ^JPanel make-jpanel
   ([layer]
      (make-jpanel layer (root-event-dispatcher)))
   ([layer event-dispatcher]
-     (let [panel
-           (proxy [JPanel] []
-             (paintComponent [^Graphics g]
-               (let [size (.getSize ^Component this)]
-                 (.setColor g (:back-color *theme*))
-                 (.fillRect g 0 0 (.width size) (.height size))
-                 (draw-root! layer g (.width size) (.height size)
-                             event-dispatcher this)))
-             (getPreferredSize []
-               (let [s (root-size layer (font-context this) this)]
-                 (Dimension. (:width s) (:height s)))))]
+     (let [panel (proxy [JPanel] [])
+           scene (make-scene layer event-dispatcher panel)]
+       (update-proxy
+        panel
+        {"paintComponent" #(paint-component %1 %2 scene)
+         "getPreferredSize" #(preferred-size % scene)})
        (.setBackground panel (:back-color *theme*))
-       (add-observer panel layer (fn [w _]
+       (add-observer panel scene (fn [w _]
                                    ;; Use the first observer argument
                                    ;; instead of closing over panel to
                                    ;; allow the panel and associated
diff --git a/src/net/kryshen/indyvon/core.clj b/src/net/kryshen/indyvon/core.clj
--- a/src/net/kryshen/indyvon/core.clj
+++ b/src/net/kryshen/indyvon/core.clj
@@ -19,7 +19,8 @@
 
 (ns net.kryshen.indyvon.core
   (:import
-   (java.awt Graphics2D RenderingHints Component Color Font Shape)
+   (java.awt Graphics2D RenderingHints Component Color Font Shape
+             Cursor EventQueue)
    (java.awt.geom AffineTransform Point2D$Double Rectangle2D$Double Area)
    (java.awt.event MouseListener MouseMotionListener
                    MouseWheelListener MouseWheelEvent)
@@ -57,14 +58,14 @@
   *clip*)
 
 (def ^{:dynamic true
-       :doc "The root (background) layer of the scene."}
-  *root*)
-
-(def ^{:dynamic true
        :doc "Time in nanoseconds when the rendering of the current
              frame starts."}
   *time*)
 
+(def ^{:dynamic true
+       :doc "Encloses state that should be retained between repaints."}
+  *scene*)
+
 (def ^{:dynamic true}
   *event-dispatcher*)
 
@@ -217,22 +218,21 @@
 
 (defn add-context-observer
   "Observer registered with this function will be automatically
-  removed after the next frame rendering is complete."
+  removed after the next repaint is complete."
   [target f]
-  (let [root *root*]
-    (add-observer root target f)))
+  (add-observer *scene* target f))
 
 (defn repaint-on-update
   "Trigger repaint of the current scene when the target updates."
   [target]
-  (let [root *root*]
-    (if (not= root target)
-      (add-observer root target (fn [w _] (update w))))))
+  (let [scene *scene*]
+    (if-not (identical? scene target)
+      (add-observer scene target (fn [w _] (update w))))))
 
 (defn repaint
   "Repaint the current scene."
   []
-  (update *root*))
+  (update *scene*))
 
 ;;
 ;; Rendering
@@ -427,56 +427,6 @@
      (let [anchor (anchor layer h-align v-align)]
        (draw! layer (- x (:x anchor)) (- y (:y anchor)) w h))))
 
-(defn draw-root!
-  "Draws the root layer."
-  ([layer graphics width height event-dispatcher]
-     (draw-root! layer graphics width height event-dispatcher nil))
-  ([layer ^Graphics2D graphics width height event-dispatcher target]
-     ;; (.setRenderingHint graphics
-     ;;                    RenderingHints/KEY_INTERPOLATION
-     ;;                    RenderingHints/VALUE_INTERPOLATION_BILINEAR)
-     ;; (.setRenderingHint graphics
-     ;;                    RenderingHints/KEY_ALPHA_INTERPOLATION
-     ;;                    RenderingHints/VALUE_ALPHA_INTERPOLATION_QUALITY)
-     ;; (.setRenderingHint graphics
-     ;;                    RenderingHints/KEY_ANTIALIASING
-     ;;                    RenderingHints/VALUE_ANTIALIAS_ON)
-     ;; (.setRenderingHint graphics
-     ;;                    RenderingHints/KEY_TEXT_ANTIALIASING
-     ;;                    RenderingHints/VALUE_TEXT_ANTIALIAS_ON)
-     (binding [*root* layer
-               *target* target
-               *graphics* graphics
-               *font-context* (.getFontRenderContext graphics)
-               *initial-transform* (.getTransform graphics)
-               *inverse-initial-transform*
-                 (-> graphics .getTransform .createInverse)
-               *event-dispatcher* event-dispatcher
-               *width* width
-               *height* height
-               *clip* (Rectangle2D$Double. 0 0 width height)
-               *time* (System/nanoTime)]
-       (apply-theme)
-       (let [tmp-watcher (Object.)]
-         ;; Keep current context observers until the rendering is
-         ;; complete. Some observers may be invoked twice if they
-         ;; appear in both groups until tmp-watcher is removed.
-         (replace-observers-watcher layer tmp-watcher)
-         (try
-           (render! layer)
-           (finally
-            (remove-observers tmp-watcher)
-            (commit event-dispatcher)))))))
-
-(defn root-size
-  ([layer font-context]
-     (root-size layer font-context nil))
-  ([layer font-context target]
-     (binding [*root* layer
-               *target* target
-               *font-context* font-context]
-       (layer-size layer))))
-
 ;;
 ;; Event handling.
 ;;
@@ -667,3 +617,64 @@
        (translate-and-dispatch @picked true event))
      (mouseMoved [this event]
        (dispatch-mouse-motion hovered @tree this event)))))
+
+;;
+;; Scene
+;;
+
+(defrecord Scene [layer event-dispatcher component])
+
+(defn make-scene
+  ([layer]
+     (make-scene layer dummy-event-dispatcher nil))
+  ([layer event-dispatcher]
+     (make-scene layer event-dispatcher nil))
+  ([layer event-dispatcher component]
+     (Scene. layer event-dispatcher component)))
+
+(defn draw-scene!
+  [scene ^Graphics2D graphics width height]
+  ;; (.setRenderingHint graphics
+  ;;                    RenderingHints/KEY_INTERPOLATION
+  ;;                    RenderingHints/VALUE_INTERPOLATION_BILINEAR)
+  ;; (.setRenderingHint graphics
+  ;;                    RenderingHints/KEY_ALPHA_INTERPOLATION
+  ;;                    RenderingHints/VALUE_ALPHA_INTERPOLATION_QUALITY)
+  ;; (.setRenderingHint graphics
+  ;;                    RenderingHints/KEY_ANTIALIASING
+  ;;                    RenderingHints/VALUE_ANTIALIAS_ON)
+  ;; (.setRenderingHint graphics
+  ;;                    RenderingHints/KEY_TEXT_ANTIALIASING
+  ;;                    RenderingHints/VALUE_TEXT_ANTIALIAS_ON)
+  (binding [*scene* scene
+            *graphics* graphics
+            *font-context* (.getFontRenderContext graphics)
+            *initial-transform* (.getTransform graphics)
+            *inverse-initial-transform* (-> graphics
+                                            .getTransform
+                                            .createInverse)
+            *event-dispatcher* (:event-dispatcher scene)
+            *width* width
+            *height* height
+            *clip* (Rectangle2D$Double. 0 0 width height)
+            *time* (System/nanoTime)]
+    (apply-theme)
+    (let [tmp-watcher (Object.)]
+      ;; Keep current context observers until the rendering is
+      ;; complete. Some observers may be invoked twice if they
+      ;; appear in both groups until tmp-watcher is removed.
+      (replace-observers-watcher scene tmp-watcher)
+      (try
+        (render! (:layer scene))
+        (finally
+         (remove-observers tmp-watcher)
+         (commit (:event-dispatcher scene)))))))
+  
+(defn scene-size [scene font-context]
+  (binding [*scene* scene
+            *font-context* font-context]
+    (layer-size (:layer scene))))
+
+(defn set-cursor! [^Cursor cursor]
+  (when-let [^Component component (:component *scene*)]
+    (EventQueue/invokeLater #(.setCursor component cursor))))
diff --git a/src/net/kryshen/indyvon/layers.clj b/src/net/kryshen/indyvon/layers.clj
--- a/src/net/kryshen/indyvon/layers.clj
+++ b/src/net/kryshen/indyvon/layers.clj
@@ -333,11 +333,9 @@
        (dosync
         (ref-set fix-x (:x-on-screen e))
         (ref-set fix-y (:y-on-screen e)))
-       (when *target*
-         (->> Cursor/MOVE_CURSOR Cursor. (.setCursor *target*))))
+       (set-cursor! (Cursor. Cursor/MOVE_CURSOR)))
       (:mouse-released e
-       (when *target*
-         (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor *target*))))
+       (set-cursor! (Cursor. Cursor/DEFAULT_CURSOR)))
       (:mouse-dragged e
        (dosync
         (alter transform pre-translate