changeset 65:fd1bcb67bc32

New mechanism for layers to trigger repaints.
author Mikhail Kryshen <mikhail@kryshen.net>
date Fri, 27 Aug 2010 01:24:31 +0400
parents 702a4939312d
children a1999c1f7289
files src/net/kryshen/indyvon/component.clj src/net/kryshen/indyvon/core.clj src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj
diffstat 4 files changed, 136 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/src/net/kryshen/indyvon/component.clj	Thu Aug 26 06:29:30 2010 +0400
+++ b/src/net/kryshen/indyvon/component.clj	Fri Aug 27 01:24:31 2010 +0400
@@ -17,22 +17,6 @@
 (defn- font-context [^Component component]
   (.getFontRenderContext (.getFontMetrics component (.getFont component))))
 
-(defn paint-component
-  [^Component component layer ^Graphics2D graphics event-dispatcher]
-  (let [size (.getSize component)
-        width (.width size)
-        height (.height size)
-        update-fn #(.repaint component)]
-    (.clearRect graphics 0 0 width height)
-    (context-draw! layer component graphics event-dispatcher update-fn
-                   width height)))
-
-(defn preferred-size [component layer]
-  (binding [*target* component
-            *font-context*' (font-context component)]
-    (let [s (layer-size layer)]
-      (Dimension. (:width s) (:height s)))))
-
 (defn make-jpanel
   ([layer]
      (make-jpanel layer (root-event-dispatcher)))
@@ -40,9 +24,13 @@
      (let [panel
            (proxy [JPanel] []
              (paintComponent [g]
-                (paint-component this layer g event-dispatcher))
+                (let [size (.getSize ^Component this)]
+                  (draw-root! layer g (.width size) (.height size)
+                              event-dispatcher this)))
              (getPreferredSize []
-                (preferred-size this layer)))]
+                (let [s (root-size layer (font-context this) this)]
+                  (Dimension. (:width s) (:height s)))))]
        (.setBackground panel (:back-color *theme*))
+       (add-observer layer (fn [_] (.repaint panel)))
        (listen! event-dispatcher panel)
        panel)))
--- a/src/net/kryshen/indyvon/core.clj	Thu Aug 26 06:29:30 2010 +0400
+++ b/src/net/kryshen/indyvon/core.clj	Fri Aug 27 01:24:31 2010 +0400
@@ -29,10 +29,9 @@
 
 (def ^Shape *clip*)
 
-(def *event-dispatcher*)
+(def *root*)
 
-(def ^{:doc "Fn to be called in a layer context to request redraw."}
-     *update*)
+(def *event-dispatcher*)
 
 (def ^{:tag AffineTransform
        :doc "Initial transform associated with the graphics context"}
@@ -143,6 +142,24 @@
   (doseq [f (reduce #(concat %1 (get %2 target)) nil (vals @observers))]
     (apply f target args)))
 
+(defn repaint
+  "Repaint the current scene."
+  []
+  (update *root*))
+
+(defn add-context-observer
+  "Observer registered with this function will be automatically
+  removed after the next frame rendering is complete."
+  [target f]
+  (let [root *root*]
+    (add-observer target f root)))
+
+(defn repaint-on-update
+  "Trigger repaint of the current scene when the target updates."
+  [target]
+  (let [root *root*]
+    (add-observer target (fn [_] (update root)) root)))
+
 ;;
 ;; Rendering
 ;;
@@ -283,33 +300,6 @@
   ([layer x y width height]
      (with-bounds* x y width height render! layer)))
 
-;; TODO: объект-сцена вместо context-draw!
-;; Сцена устанавливает контекст и рисует слой. Сцена - слой?
-;; Сцена идентифицирует группу обозревателя слоев.
-;; Обновления любого слоя, изображенного в сцене, вызывает обновление сцены.
-;; Обозреватель сцены вызывает repaint().
-
-(defn context-draw!
-  "Sets up layer context, draws layer and commits event dispatcher."
-  ([layer graphics event-dispatcher update-fn width height]
-     (context-draw! layer nil graphics event-dispatcher update-fn width height))
-  ([layer component ^Graphics2D graphics event-dispatcher update-fn
-    width height]
-     (binding [*graphics* graphics
-               *font-context* (.getFontRenderContext graphics)
-               *initial-transform* (.getTransform graphics)
-               *inverse-initial-transform*
-                 (-> graphics .getTransform .createInverse)
-               *target* component
-               *update* update-fn
-               *event-dispatcher* event-dispatcher
-               *width* width
-               *height* height
-               *clip* (Rectangle2D$Double. 0 0 width height)]
-       (apply-theme)
-       (render! layer)
-       (commit event-dispatcher))))
-
 (defn draw-anchored!
   "Draws layer. Location is relative to the layer's anchor point for
    the specified alignment."
@@ -320,6 +310,41 @@
      (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]
+     (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)]
+       (let [tmp-group (Object.)]
+         (apply-theme)
+         (.clearRect graphics 0 0 width height)
+         (change-group-id layer tmp-group)
+         (try
+           (render! layer)
+           (finally
+            (remove-group tmp-group)
+            (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))))
+
 ;;
 ;; EventDispatcher implementation
 ;;
--- a/src/net/kryshen/indyvon/demo.clj	Thu Aug 26 06:29:30 2010 +0400
+++ b/src/net/kryshen/indyvon/demo.clj	Fri Aug 27 01:24:31 2010 +0400
@@ -76,7 +76,7 @@
      (reify
       Layer
       (render! [layer]
-         ;;(*update*)
+         ;;(repaint)
          (doto *graphics*
            (.setColor (rand-nth [Color/BLACK Color/BLUE Color/RED]))
            (.drawLine 0 0 *width* *height*))
--- a/src/net/kryshen/indyvon/layers.clj	Thu Aug 26 06:29:30 2010 +0400
+++ b/src/net/kryshen/indyvon/layers.clj	Fri Aug 27 01:24:31 2010 +0400
@@ -145,11 +145,11 @@
                  height (text-height layouts)]
              (Size. width height)))))))
 
-(defn- ^ImageObserver image-observer [update-fn]
+(defn- ^ImageObserver image-observer [layer]
   (reify
    ImageObserver
    (imageUpdate [this img infoflags x y width height]
-      (update-fn)
+      (update layer)
       (zero? (bit-and infoflags
                       (bit-or ImageObserver/ALLBITS
                               ImageObserver/ABORT))))))
@@ -164,72 +164,83 @@
     (reify
      Layer
      (render! [layer]
-        (.drawImage *graphics* image 0 0 (image-observer *update*)))
+        (repaint-on-update layer)
+        (.drawImage *graphics* image 0 0 (image-observer layer)))
      (layer-size [layer]
-        (let [observer (image-observer *update*)
+        (let [observer (image-observer layer)
               width (.getWidth image observer)
               height (.getHeight image observer)
               width (if (pos? width) width 1)
               height (if (pos? height) height 1)]
           (Size. width height))))))
 
-(defn async-layer
+(defn- create-buffer [async-layer]
+  (BufferedImage. (:width async-layer) (:height async-layer)
+                  BufferedImage/TYPE_INT_ARGB))
+
+(defn- draw-offscreen [async-layer]
+  ;;(Thread/sleep 3000)
+  (let [buffers (:buffers async-layer)
+        ^Image b (dosync
+                  (let [b (peek @buffers)]
+                    (alter buffers pop)
+                    b))]
+    (try
+      ;; TODO: use operational event dispatcher.
+      (draw-root! (:content async-layer)
+                  (.getGraphics b)
+                  (:width async-layer)
+                  (:height async-layer)
+                  dummy-event-dispatcher)
+      (finally
+       (dosync
+        (alter buffers conj b)
+        (ref-set (:updated async-layer) true))))
+    (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 updated]
+  Layer
+  (render! [layer]
+     (repaint-on-update layer)
+     (add-context-observer content (fn [_] (draw-offscreen-async layer)))
+     (when-not @buffers
+       ;; TODO: dynamic size, recreate buffers when size increases.
+       (let [new-buffers [(create-buffer layer) (create-buffer layer)]]
+         (dosync
+          (ref-set buffers new-buffers)))
+       (draw-offscreen-async layer))
+     (let [buffer (dosync
+            (if @updated
+              (let [b (peek @buffers)]
+                (alter buffers pop)
+                (ref-set updated false)
+                b)
+              (let [b (first @buffers)]
+                (alter buffers subvec 1)
+                b)))]
+       (.drawImage *graphics* ^Image buffer 0 0 nil)
+       (dosync
+        (alter buffers #(vec (cons buffer %))))))
+  (layer-size [layer]
+     (Size. width height)))
+
+(defn async-layer 
   "Creates layer that draws the content asynchroniously in an
    offscreen buffer."
   [content width height]
-  (let [executor (ThreadPoolExecutor. (int 1) (int 1) (long 0) TimeUnit/SECONDS
-                   (ArrayBlockingQueue. 1)
-                   (ThreadPoolExecutor$DiscardOldestPolicy.))
-        buffers (ref nil)
-        updated (ref false)
-        update-fn (ref nil)
-        create-buffer
-          #(BufferedImage. width height BufferedImage/TYPE_INT_ARGB)
-        draw-offscreen
-          (fn draw-offscreen []
-            ;;(Thread/sleep 3000)
-            (let [^Image b (dosync
-                            (let [b (peek @buffers)]
-                              (alter buffers pop)
-                              b))]
-              (try
-                ;; TODO: use operational event dispatcher.
-                (context-draw! content
-                               (.getGraphics b)
-                               dummy-event-dispatcher
-                               #(.execute executor draw-offscreen)
-                               width height)
-                (finally
-                 (dosync
-                  (alter buffers conj b)
-                  (ref-set updated true))))
-                (update-fn)))]
-    (reify
-     Layer
-     (render! [layer]
-        (when-not @buffers
-          ;; TODO: dynamic size, recreate buffers when size increases.
-          (let [new-buffers [(create-buffer) (create-buffer)]]
-            (dosync
-             (ref-set buffers new-buffers)
-             (ref-set update-fn *update*)))
-          (.execute executor draw-offscreen))
-        (let [buffer (dosync
-                 (ref-set update-fn *update*)
-                 (if @updated
-                   (let [b (peek @buffers)]
-                     (alter buffers pop)
-                     (ref-set updated false)
-                     b)
-                   (let [b (first @buffers)]
-                     (alter buffers subvec 1)
-                     b)))]
-          (.drawImage *graphics* ^Image buffer 0 0 nil)
-          (dosync
-           (alter buffers #(vec (cons buffer %))))))
-     (layer-size [layer]
-        (Size. width height)))))
-
+  (AsyncLayer. content width height
+               (ThreadPoolExecutor.
+                (int 1) (int 1)
+                (long 0) TimeUnit/SECONDS
+                (ArrayBlockingQueue. 1)
+                (ThreadPoolExecutor$DiscardOldestPolicy.))
+               (ref nil)
+               (ref false)))
+    
 (defn miniature
   "Creates layer that asynchroniously renders view of the content
   scaled to the specified size."
@@ -272,16 +283,18 @@
            (dosync
             (ref-set fix-x (:x-on-screen e))
             (ref-set fix-y (:y-on-screen e)))
-           (->> Cursor/MOVE_CURSOR Cursor. (.setCursor *target*)))
+           (when *target*
+             (->> Cursor/MOVE_CURSOR Cursor. (.setCursor *target*))))
           (:mouse-released e
-           (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor *target*)))
+           (when *target*
+             (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor *target*))))
           (: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*))))
+           (repaint))))
      (layer-size [layer] (layer-size content))))))
 
 ;;