changeset 151:cb108c6fa079

Layers are now called Views.
author Mikhail Kryshen <mikhail@kryshen.net>
date Mon, 07 Apr 2014 15:23:58 +0400
parents 86d0358d38c7
children 9997ac717c3c
files src/net/kryshen/indyvon/async.clj src/net/kryshen/indyvon/component.clj src/net/kryshen/indyvon/core.clj src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj src/net/kryshen/indyvon/viewport.clj src/net/kryshen/indyvon/views.clj
diffstat 7 files changed, 542 insertions(+), 541 deletions(-) [+]
line wrap: on
line diff
--- a/src/net/kryshen/indyvon/async.clj	Mon Apr 07 14:24:16 2014 +0400
+++ b/src/net/kryshen/indyvon/async.clj	Mon Apr 07 15:23:58 2014 +0400
@@ -1,5 +1,5 @@
 ;;
-;; Copyright 2010, 2011 Mikhail Kryshen <mikhail@kryshen.net>
+;; Copyright 2010-2014 Mikhail Kryshen <mikhail@kryshen.net>
 ;;
 ;; This file is part of Indyvon.
 ;;
@@ -40,15 +40,15 @@
 ;;   :free
 ;;      not in use
 
-(defn- create-image [async-layer ^GraphicsConfiguration device-conf]
+(defn- create-image [async-view ^GraphicsConfiguration device-conf]
   ;; TODO: support different image types.
   (.createCompatibleImage device-conf
-                          (:width async-layer)
-                          (:height async-layer)
+                          (:width async-view)
+                          (:height async-view)
                           Transparency/TRANSLUCENT))
 
-(defn- create-buffer [async-layer device-conf]
-  (Buffer. (Object.) (create-image async-layer device-conf) 0 :free))
+(defn- create-buffer [async-view device-conf]
+  (Buffer. (Object.) (create-image async-view device-conf) 0 :free))
 
 (defn- find-buffer
   "Find a buffer with the one of the specified states given
@@ -110,40 +110,40 @@
        (finally
         (release-buffer al# ~name)))))
 
-(defn- draw-offscreen [async-layer]
+(defn- draw-offscreen [async-view]
   ;;(Thread/sleep 1000)
-  (with-buffer async-layer :back [b]
+  (with-buffer async-view :back [b]
     (let [g (.createGraphics ^BufferedImage (:image b))]
       ;; Clear the buffer.
       (.setComposite g AlphaComposite/Clear)
-      (.fillRect g 0 0 (:width async-layer) (:height async-layer))
+      (.fillRect g 0 0 (:width async-view) (:height async-view))
       (.setComposite g AlphaComposite/Src)
-      (draw-scene! (:scene async-layer)
+      (draw-scene! (:scene async-view)
                    g
-                   (:width async-layer)
-                   (:height async-layer)))
-    (update async-layer)))
+                   (:width async-view)
+                   (:height async-view)))
+    (update async-view)))
 
-(defn- draw-offscreen-async [async-layer]
-  (.execute ^ThreadPoolExecutor (:executor async-layer)
-            #(draw-offscreen async-layer)))
+(defn- draw-offscreen-async [async-view]
+  (.execute ^ThreadPoolExecutor (:executor async-view)
+            #(draw-offscreen async-view)))
 
-(defrecord AsyncLayer [scene width height executor buffers]
-  Layer
-  (render! [layer]
-    (repaint-on-update layer)
-    (add-context-observer scene (fn [_ _] (draw-offscreen-async layer)))
+(defrecord AsyncView [scene width height executor buffers]
+  View
+  (render! [view]
+    (repaint-on-update view)
+    (add-context-observer scene (fn [_ _] (draw-offscreen-async view)))
     (when-not @buffers
       ;; TODO: dynamic size, recreate buffers when size increases.
       (let [device-conf (.getDeviceConfiguration *graphics*)
             new-buffers (repeatedly 2
-                          (partial create-buffer layer device-conf))]
+                          (partial create-buffer view device-conf))]
         (dosync
          (ref-set buffers new-buffers)))
-      (draw-offscreen-async layer))
-    (with-buffer layer :front [b]
+      (draw-offscreen-async view))
+    (with-buffer view :front [b]
       (.drawImage *graphics* ^Image (:image b) 0 0 nil)))
-  (geometry [layer]
+  (geometry [view]
     (->Size width height)))
 
 (defn- create-thread-factory [priority]
@@ -164,15 +164,15 @@
          (ThreadPoolExecutor$DiscardOldestPolicy.))
     (.setThreadFactory (create-thread-factory priority))))
 
-(defn async-layer 
-  "Creates layer that draws the content asynchronously using
+(defn async-view 
+  "Creates a View that draws the content asynchronously using an
    offscreen buffer."
   ([content width height]
-     (async-layer content width height nil))
+     (async-view content width height nil))
   ([content width height priority]
      ;; TODO: use operational event dispatcher.
-     (->AsyncLayer (make-scene content)
-                   width
-                   height
-                   (create-executor priority)
-                   (ref nil))))
+     (->AsyncView (make-scene content)
+                  width
+                  height
+                  (create-executor priority)
+                  (ref nil))))
--- a/src/net/kryshen/indyvon/component.clj	Mon Apr 07 14:24:16 2014 +0400
+++ b/src/net/kryshen/indyvon/component.clj	Mon Apr 07 15:23:58 2014 +0400
@@ -1,5 +1,5 @@
 ;;
-;; Copyright 2010, 2011, 2012 Mikhail Kryshen <mikhail@kryshen.net>
+;; Copyright 2010-2014 Mikhail Kryshen <mikhail@kryshen.net>
 ;;
 ;; This file is part of Indyvon.
 ;;
@@ -38,12 +38,12 @@
     (Dimension. (width geom) (height geom))))
 
 (defn ^JPanel make-jpanel
-  ([layer]
-     (make-jpanel layer (root-event-dispatcher)))
-  ([layer event-dispatcher]
+  ([view]
+     (make-jpanel view (root-event-dispatcher)))
+  ([view event-dispatcher]
      (let [panel (proxy [JPanel] [])
            scene (make-scene
-                  layer event-dispatcher panel
+                  view event-dispatcher panel
                   (.getDesktopProperty (java.awt.Toolkit/getDefaultToolkit)
                                        "awt.font.desktophints"))]
        (update-proxy
@@ -60,9 +60,9 @@
        (listen! event-dispatcher panel)
        panel)))
 
-(defn ^JFrame make-jframe [^String title layer]
+(defn ^JFrame make-jframe [^String title view]
   (doto (JFrame. title)
-    (.. (getContentPane) (add (make-jpanel layer)))
+    (.. (getContentPane) (add (make-jpanel view)))
     (.pack)))
 
 (defn message [m]
--- a/src/net/kryshen/indyvon/core.clj	Mon Apr 07 14:24:16 2014 +0400
+++ b/src/net/kryshen/indyvon/core.clj	Mon Apr 07 15:23:58 2014 +0400
@@ -1,5 +1,5 @@
 ;;
-;; Copyright 2010, 2011, 2012, 2013 Mikhail Kryshen <mikhail@kryshen.net>
+;; Copyright 2010-2014 Mikhail Kryshen <mikhail@kryshen.net>
 ;;
 ;; This file is part of Indyvon.
 ;;
@@ -29,7 +29,7 @@
    com.google.common.collect.MapMaker))
 
 ;;
-;; Layer context
+;; View context
 ;;
 
 (def ^:dynamic ^Graphics2D *graphics*)
@@ -89,15 +89,15 @@
 ;; Core protocols and types
 ;;
 
-(defprotocol Layer
+(defprotocol View
   "Basic UI element."
-  (render! [layer]
-    "Draws layer in the current *graphics* context.")
-  (geometry [layer]
-    "Returns the preferred layer Geometry."))
+  (render! [view]
+    "Draws the view in the current *graphics* context.")
+  (geometry [view]
+    "Returns the preferred Geometry for the view."))
 
 (defprotocol Geometry
-  "Describes geometry of a Layer. Prefer using the available
+  "Describes geometry of a View.  Prefer using the available
   implementations (Size, FixedGeometry and NestedGeometry) over
   extending this protocol directly as it is likely to be changed in
   the future versions."
@@ -234,7 +234,7 @@
 
 ;;
 ;; Observers
-;; The mechanism used by layers to request repaints
+;; The mechanism used by views to request repaints
 ;;
 
 (def ^ConcurrentMap observers
@@ -318,24 +318,24 @@
 ;;
 
 (defn ^FontRenderContext font-context
-  "Returns FontRenderContext for the current Layer context."
+  "Returns FontRenderContext for the current view context."
   []
   (if (bound? (var *graphics*))
     (.getFontRenderContext *graphics*)
     *font-context*))
 
 (defn ^AffineTransform relative-transform
-  "Returns AffineTransform: layer context -> AWT component."
+  "Returns AffineTransform: view context -> AWT component."
   []
   (let [tr (.getTransform *graphics*)]
     (.preConcatenate tr *inverse-initial-transform*)
     tr))
 
 (defn ^AffineTransform inverse-relative-transform
-  "Returns AffineTransform: AWT component -> layer context."
+  "Returns AffineTransform: AWT component -> view context."
   []
   (let [tr (.getTransform *graphics*)]
-    (.invert tr)                          ; absolute -> layer
+    (.invert tr)                          ; absolute -> view
     (.concatenate tr *initial-transform*) ; component -> absolute
     tr))
 
@@ -502,40 +502,40 @@
         (.translate g# (- x#) (- y#))))))
 
 (defn draw!
-  "Draws layer."
-  ([layer]
+  "Draws the View."
+  ([view]
      (let [graphics (create-graphics)]
        (try
          (binding [*graphics* graphics]
-           (render! layer))
+           (render! view))
          (finally
           (.dispose graphics)))))
-  ([layer x y]
-     (draw! layer x y true))
-  ([layer x y clip?]
-     (let [geom (geometry layer)]
-       (draw! layer x y (width geom) (height geom) clip?)))
-  ([layer x y width height]
-     (draw! layer x y width height true))
-  ([layer x y width height clip?]
+  ([view x y]
+     (draw! view x y true))
+  ([view x y clip?]
+     (let [geom (geometry view)]
+       (draw! view x y (width geom) (height geom) clip?)))
+  ([view x y width height]
+     (draw! view x y width height true))
+  ([view x y width height clip?]
      (if clip?
-       (with-bounds* x y width height render! layer)
-       (with-bounds-noclip* x y width height render! layer))))
+       (with-bounds* x y width height render! view)
+       (with-bounds-noclip* x y width height render! view))))
 
 (defn draw-aligned!
-  "Draws layer. Location is relative to the layer's anchor point for
-   the specified alignment."
-  ([layer h-align v-align x y]
-     (let [geom (geometry layer)
+  "Draws the View.  Location is relative to the view's anchor point
+   for the specified alignment."
+  ([view h-align v-align x y]
+     (let [geom (geometry view)
            w (width geom)
            h (height geom)]
-       (draw! layer
+       (draw! view
               (- x (anchor-x geom h-align w))
               (- y (anchor-y geom v-align h))
               w h)))
-  ([layer h-align v-align x y w h]
-     (let [geom (geometry layer)]
-       (draw! layer
+  ([view h-align v-align x y w h]
+     (let [geom (geometry view)]
+       (draw! view
               (- x (anchor-x geom h-align w))
               (- y (anchor-y geom v-align h))
               w h))))
@@ -792,7 +792,7 @@
 ;; Scene
 ;;
 
-(defrecord Scene [layer
+(defrecord Scene [view
                   event-dispatcher
                   component
                   rendering-hints
@@ -807,15 +807,15 @@
    RenderingHints/VALUE_FRACTIONALMETRICS_DEFAULT})
 
 (defn make-scene
-  ([layer]
-     (make-scene layer dummy-event-dispatcher nil))
-  ([layer event-dispatcher]
-     (make-scene layer event-dispatcher nil))
-  ([layer event-dispatcher ^Component component]
-     (make-scene layer event-dispatcher component nil))
-  ([layer event-dispatcher ^Component component hints]
+  ([view]
+     (make-scene view dummy-event-dispatcher nil))
+  ([view event-dispatcher]
+     (make-scene view event-dispatcher nil))
+  ([view event-dispatcher ^Component component]
+     (make-scene view event-dispatcher component nil))
+  ([view event-dispatcher ^Component component hints]
      (let [hints (merge default-rendering-hints hints)]
-       (->Scene layer
+       (->Scene view
                 event-dispatcher
                 component
                 hints
@@ -853,7 +853,7 @@
       ;; appear in both groups until tmp-watcher is removed.
       (replace-observers-watcher scene tmp-watcher)
       (try
-        (render! (:layer scene))
+        (render! (:view scene))
         (finally
          (remove-observers tmp-watcher)
          (commit (:event-dispatcher scene)))))))
@@ -874,7 +874,7 @@
 (defn scene-geometry [scene]
   (binding [*scene* scene
             *font-context* (scene-font-context scene)]
-    (geometry (:layer scene))))
+    (geometry (:view scene))))
 
 (defn set-cursor! [^Cursor cursor]
   (when-let [^Component component (:component *scene*)]
--- a/src/net/kryshen/indyvon/demo.clj	Mon Apr 07 14:24:16 2014 +0400
+++ b/src/net/kryshen/indyvon/demo.clj	Mon Apr 07 15:23:58 2014 +0400
@@ -21,14 +21,14 @@
   "Indyvon demo and experiments."
   (:gen-class)
   (:use
-   (net.kryshen.indyvon core layers viewport component))
+   (net.kryshen.indyvon core views viewport component))
   (:import
    (java.awt Color)
    (javax.swing JFrame)))
 
 (defn draw-button!
-  "Draws button immediately (but uses callback for button action
-   unlike IMGUI)."
+  "Draws a button immediately (but uses callback for the action unlike
+   IMGUI)."
   [id content callback & args]
   (with-handlers id
     (let [shadow-offset 2
@@ -65,8 +65,8 @@
       (Color. (aget rgb 0) (aget rgb 1) (aget rgb 2) (aget rgb 3)))))
 
 (defn animate
-  "Changes atom value according to specified range, speed, and current
-   frame interval. Invokes repaint if change happens."
+  "Changes the value of atom according to the specified range, speed,
+   and current frame interval.  Invokes repaint if change happens."
   [atom from to speed]
   (let [prev @atom
         state (cond
@@ -83,7 +83,7 @@
          val))))
 
 (defn animated-button
-  "Create animated button layer."
+  "Creates an animated button."
   [content callback & args]
   (let [padding 4
         border-width 1
@@ -91,9 +91,9 @@
         face (border content padding border-width)
         highlight (atom 0)
         animation-speed (atom 0)]
-    (interval-layer
+    (interval-view
      (reify
-      Layer
+      View
       (render! [button]
         (with-handlers button
           (let [hovered (hovered? button)
@@ -132,12 +132,12 @@
 (def button2 (animated-button (label "Animated button 2")
                               println "Animated button 2 clicked"))
 
-(def test-layer1
+(def test-view1
   (reify
-   Layer
-   (render! [layer]
-     (with-handlers layer
-       (with-color (if (hovered? layer) Color/ORANGE Color/RED)
+   View
+   (render! [view]
+     (with-handlers view
+       (with-color (if (hovered? view) Color/ORANGE Color/RED)
          (.fillRect *graphics* 0 0 *width* *height*))
        (:mouse-entered e
         (repaint)
@@ -147,32 +147,32 @@
         (println e))
        (:mouse-moved e
         (println e))))
-   (geometry [layer]
+   (geometry [view]
      (->Size 30 20))))
 
-(def test-layer1b (border test-layer1 2 3))
+(def test-view1b (border test-view1 2 3))
 
-(def test-layer2
+(def test-view2
   (reify
-   Layer
-   (render! [layer]
+   View
+   (render! [view]
      (doto *graphics*
        (.setColor Color/YELLOW)
        (.fillRect 0 0 *width* *height*))
      (with-rotate 0.5 0 0
-       (draw! test-layer1b 30 25))
-     (draw! test-layer1 55 5))
-   (geometry [layer]
+       (draw! test-view1b 30 25))
+     (draw! test-view1 55 5))
+   (geometry [view]
      (->Size 70 65))))
 
-(def test-layer2m (miniature test-layer2 30 30))
+(def test-view2m (miniature test-view2 30 30))
 
-(def test-layer3 (border (label "Sample\ntext" :right :bottom)))
+(def test-view3 (border (label "Sample\ntext" :right :bottom)))
 
 (def root
   (reify
-   Layer
-   (render! [layer]
+   View
+   (render! [view]
      ;;(repaint)
      (doto *graphics*
        (.drawLine 0 0 *width* *height*)
@@ -180,9 +180,9 @@
        ;; Random color to see when repaint happens.
        (.setColor (rand-nth [Color/BLACK Color/BLUE Color/RED]))
        (.fillOval 5 5 20 20))
-     (draw! test-layer2 30 20)
-     (draw! test-layer2m 120 50)
-     (draw! test-layer3 100 100 80 50)
+     (draw! test-view2 30 20)
+     (draw! test-view2m 120 50)
+     (draw! test-view3 100 100 80 50)
      (draw! button1 50 160)
      (with-rotate (/ Math/PI 6) 250 200
        (draw! button1 210 140))
@@ -191,7 +191,7 @@
        (draw-button! :button
         (label "Immediate button" :center :center)
         #(println "Button clicked!"))))
-   (geometry [layer]
+   (geometry [view]
      (->Size 400 300))))
 
 ;; Main viewport
@@ -202,8 +202,8 @@
 
 ;; Main scene
 (def scene
-  (fps-layer
-   (decorate-layer vp [_]
+  (fps-view
+   (decorate-view vp [_]
      (draw! vp)
      (draw-aligned!
       (label (str "Drag mouse to pan," \newline
@@ -211,8 +211,8 @@
       :left :bottom 5 (- *height* 5))
      (draw! vp-miniature (- *width* 105) 5))))
 
-(defn show-frame [layer]
-  (doto (make-jframe "Test" layer)
+(defn show-frame [view]
+  (doto (make-jframe "Test" view)
     (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
     (.setVisible true)))
 
--- a/src/net/kryshen/indyvon/layers.clj	Mon Apr 07 14:24:16 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,408 +0,0 @@
-;;
-;; Copyright 2010, 2011, 2012, 2013 Mikhail Kryshen <mikhail@kryshen.net>
-;;
-;; This file is part of Indyvon.
-;;
-;; Indyvon is free software: you can redistribute it and/or modify it
-;; under the terms of the GNU Lesser General Public License version 3
-;; only, as published by the Free Software Foundation.
-;;
-;; Indyvon is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;; Lesser General Public License for more details.
-;;
-;; You should have received a copy of the GNU Lesser General Public
-;; License along with Indyvon.  If not, see
-;; <http://www.gnu.org/licenses/>.
-;;
-
-(ns net.kryshen.indyvon.layers
-  "Implementations of Layer protocol."
-  (:use
-   (net.kryshen.indyvon core async))
-  (:import
-   (java.awt Font Image Toolkit)
-   java.awt.image.ImageObserver
-   (java.awt.geom Area AffineTransform Rectangle2D$Double Point2D
-                  Point2D$Double)
-   (java.awt.font FontRenderContext TextLayout)
-   java.util.concurrent.TimeUnit
-   (com.google.common.cache Cache CacheBuilder CacheLoader)))
-
-(defmacro decorate-layer
-  "Decorate Layer replacing render! implementation."
-  [layer & render-tail]
-  `(let [layer# ~layer]
-     (reify
-       Layer
-       (render! ~@render-tail)
-       (geometry [t#] (geometry layer#)))))
-
-(defrecord Empty []
-  Layer
-  (render! [_])
-  (geometry [_]
-    (->Size 0 0)))
-
-(def empty-layer (->Empty))
-
-;; TODO: change argument order for decorators, content should be the
-;; last.
-
-(defn padding
-  "Decorates layer adding padding."
-  ([content pad]
-     (padding content pad pad pad pad))
-  ([content top left bottom right]
-     (if (== 0 top left bottom right)
-       content
-       (reify
-        Layer
-        (render! [l]
-           (draw! content
-                  left top
-                  (- *width* left right)
-                  (- *height* top bottom)
-                  false))
-        (geometry [l]
-          (->NestedGeometry (geometry content) top left bottom right))))))
-
-(defn border
-  "Decorate layer with a border."
-  ([content]
-     (border content 1))
-  ([content thikness]
-     (border content thikness 0))
-  ([content thikness gap]
-     (let [layer (padding content (+ thikness gap))
-           t (double thikness)]
-       (decorate-layer layer [_]
-         (render! layer)
-         (with-color :border-color
-           (let [w (double *width*)
-                 h (double *height*)
-                 outer (Area. (Rectangle2D$Double. 0.0 0.0 w h))
-                 inner (Area. (Rectangle2D$Double. t t (- w t t) (- h t t)))]
-             (.subtract outer inner)
-             (.fill *graphics* outer)))))))
-
-;; TODO: opacity and blur.
-(defn shadow
-  "Add shadow to content layer."
-  ([content]
-     (shadow content 1 1))
-  ([content x-offset y-offset]
-     (let [x (if (neg? x-offset) (- x-offset) 0)
-           y (if (neg? y-offset) (- y-offset) 0)
-           abs-x (if (neg? x-offset) (- x-offset) x-offset)
-           abs-y (if (neg? y-offset) (- y-offset) y-offset)
-           shadow-x (+ x-offset x)
-           shadow-y (+ y-offset y)]
-       (reify
-        Layer
-        (render! [_]
-          (let [w (- *width* abs-x)
-                h (- *height* abs-y)]
-            (with-color :shadow-color
-              (.fillRect *graphics* shadow-x shadow-y w h))
-            (draw! content x y w h)))
-        (geometry [_]
-          (->NestedGeometry (geometry content)
-                            y x shadow-y shadow-x))))))
-
-(defn panel
-  "Opaque layer using theme's alt-back-color or a custom background color."
-  ([content]
-     (panel :alt-back-color content))
-  ([back-color content]
-     (decorate-layer content [_]
-       (with-color back-color
-         (.fill *graphics* (Rectangle2D$Double. 0.0 0.0 *width* *height*)))
-       (render! content))))
-
-(defn hbox
-  "Creates layer that draws the specified content layers placing them
-   horizontally."
-  [& contents]
-  (reify
-   Layer
-   (render! [_]
-     (let [widths (map #(width (geometry %)) contents)
-           xs (cons 0 (reductions + widths))
-           widths-sum (last xs)
-           scale (/ *width* widths-sum)]
-       (doseq [[c w x] (map vector contents widths xs)]
-         (draw! c x 0 w *height*))))
-   (geometry [_]
-     (reduce #(->Size (+ (width %1) (width %2))
-                      (max (height %1) (height %2)))
-             (->Size 0 0)
-             (map geometry contents)))))
-
-(defn vbox
-  "Creates layer that draws the specified content layers placing them
-   vertically."
-  [& contents]
-  (reify
-   Layer
-   (render! [_]
-     (let [heights (map #(height (geometry %)) contents)
-           ys (cons 0 (reductions + heights))
-           heights-sum (last ys)
-           scale (/ *height* heights-sum)]
-       (doseq [[c h y] (map vector contents heights ys)]
-         (draw! c 0 y *width* h))))
-   (geometry [_]
-     (reduce #(->Size (max (width %1) (width %2))
-                      (+ (height %1) (height %2)))
-             (->Size 0 0)
-             (map geometry contents)))))
-
-(defn- re-split [^java.util.regex.Pattern re s]
-  (seq (.split re s)))
-
-(def ^:private ^Cache text-layout-cache
-  (-> (CacheBuilder/newBuilder)
-      (.softValues)
-      (.expireAfterAccess (long 1) TimeUnit/SECONDS)
-      (.build)))
-
-(defn- get-text-layout
-  [^String line ^Font font ^FontRenderContext font-context]
-  (.get text-layout-cache [line font font-context]
-        #(TextLayout. line font font-context)))
-
-(defn- layout-text
-  [lines font font-context]
-  (map #(get-text-layout % font font-context) lines))
-
-(defn- text-width [layouts]
-  (reduce (fn [w ^TextLayout tl] (max w (.getAdvance tl))) 0 layouts))
-
-(defn- text-height [layouts]
-  (reduce (fn [w ^TextLayout tl]
-            (+ w (.getAscent tl)
-               (.getDescent tl)
-               (.getLeading tl)))
-          0 layouts))
-
-(defn label
-  "Creates a layer to display multiline text."
-  ([text]
-     (label text :left :top))
-  ([text h-align v-align]
-     (let [lines (re-split #"\r\n|\n|\r|\u0085|\u2028|\u2029" (str text))]
-       (reify Layer
-        (render! [layer]
-          (let [w *width*
-                h *height*
-                font (.getFont *graphics*)
-                layouts (layout-text lines font (font-context))
-                y (align-y v-align (text-height layouts) h)]
-            (loop [layouts layouts, y y]
-              (when-first [^TextLayout layout layouts]
-                (let [ascent (.getAscent layout)
-                      lh (+ ascent (.getDescent layout) (.getLeading layout))
-                      x (align-x h-align (.getAdvance layout) w)]
-                  (.draw layout *graphics* x (+ y ascent))
-                  (recur (next layouts) (+ y lh)))))))
-        (geometry [layer]
-          (let [layouts (layout-text lines (:font *theme*) (font-context))
-                w (text-width layouts)
-                h (text-height layouts)]
-            (->Size w h)))))))
-
-(defn- ^ImageObserver image-observer [layer]
-  (reify
-   ImageObserver
-   (imageUpdate [this img infoflags x y width height]
-     (update layer)
-     (zero? (bit-and infoflags
-                     (bit-or ImageObserver/ALLBITS
-                             ImageObserver/ABORT))))))
-
-(defn image-layer
-  [image-or-uri]
-  (let [^Image image (if (instance? Image image-or-uri)
-                       image-or-uri
-                       (.getImage (Toolkit/getDefaultToolkit)
-                                  ^java.net.URL image-or-uri))]
-    (.prepareImage (Toolkit/getDefaultToolkit) image -1 -1 nil)
-    (reify
-     Layer
-     (render! [layer]
-       (repaint-on-update layer)
-       (.drawImage *graphics* image 0 0 (image-observer layer)))
-     (geometry [layer]
-       (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))))))
-
-(def ^:dynamic *miniature-thread-priority* 2)
-
-(defn ref-layer
-  [layer-ref]
-  (let [l (reify
-           Layer
-           (render! [l]
-             (repaint-on-update l)
-             (if-let [layer @layer-ref]
-               (render! layer)))
-           (geometry [_]
-             (if-let [layer @layer-ref]
-               (geometry layer)
-               (->Size 1 1))))]
-    (add-watch layer-ref l (fn [_ _ _ _] (update l)))
-    l))
-
-;;
-;; Layer context decorators.
-;;
-
-(defmacro handler [layer & handlers]
-  "Decorate layer to handle events."
-  `(let [layer# ~layer]
-     (decorate-layer layer# [t#]
-       (with-handlers t#
-         (render! layer#)
-         ~@handlers))))
-
-(defn themed [layer & map-or-keyvals]
-  (let [theme (if (== (count map-or-keyvals) 1)
-                (first map-or-keyvals)
-                (apply array-map map-or-keyvals))]
-    (reify
-     Layer
-     (render! [_]
-       (with-theme theme
-         (render! layer)))
-     (geometry [_]
-       (with-theme theme
-         (geometry layer))))))
-
-(defn hinted [layer & map-or-keyvals]
-  (let [hints (if (== (count map-or-keyvals) 1)
-                (first map-or-keyvals)
-                (apply array-map map-or-keyvals))]
-    (decorate-layer layer [_]
-      (with-hints* hints render! layer))))
-
-;;
-;; Measuring time
-;;
-
-(def ^:dynamic *interval*)
-
-(defn interval-layer
-  "Creates layer that measures time between repaints ant draws it's
-   content with the *interval* var bound to the measured time."
-  [content]
-  (let [last-time (atom nil)]
-    (decorate-layer content [_]
-      (compare-and-set! last-time nil *time*)
-      (let [lt @last-time]
-        (binding [*interval* (if (compare-and-set! last-time lt *time*)
-                               (- *time* lt)
-                               0)] ; already measured on parallel thread
-          (render! content))))))
-
-(defn- fps-label [text]
-  (padding (label text :right :bottom) 5))
-
-(defn fps-layer
-  "Creates layer that draws content and displays
-   the frames per seconds rate." 
-  [content]
-  (let [update-interval 2E8 ; 0.2 s in nanoseconds
-        frames (ref 0)
-        prev-time (ref nil)
-        display (ref (fps-label "fps n/a"))]
-    (decorate-layer content [_]
-      (draw! content)
-      (draw!
-       (dosync
-        (alter frames inc)
-        (if @prev-time
-          (let [elapsed (- *time* @prev-time)]
-            (when (> elapsed update-interval)
-              (let [fps (/ @frames (/ elapsed 1E9))]
-                (ref-set display (fps-label (format "%.1f" fps)))
-                (ref-set frames 0)
-                (ref-set prev-time *time*))))
-          (ref-set prev-time *time*))
-        @display)))))
-
-;;
-;; Overlayer.
-;;
-
-(def ^:private ^:dynamic *above*)
-
-(defn- overlay* [f & args]
-  (var-set #'*above* (conj *above* (apply partial f args))))
-
-(defn- ^Point2D to-graphics-coords
-  [^AffineTransform transform x y]
-  (let [p (Point2D$Double. x y)]
-    (.transform transform p p)
-    (.transform (.createInverse (.getTransform *graphics*)) p p)
-    p))
-
-(defn- draw-relative!
-  ([layer transform x y]
-     (let [p (to-graphics-coords transform x y)]
-       (draw! layer (.getX p) (.getY p))))
-  ([layer transform x y w h]
-     (let [p (to-graphics-coords transform x y)]
-       (draw! layer (.getX p) (.getY p) w h))))
-
-(defn- draw-relative-aligned!
-  [layer transform h-align v-align x y]
-  (let [geom (geometry layer)
-        w (width geom)
-        h (height geom)
-        p (to-graphics-coords transform x y)
-        x (- (.getX p) (anchor-x geom h-align w))
-        y (- (.getY p) (anchor-y geom v-align h))]
-    (draw! layer x y w h)))
-
-(defn overlay!
-  "Draws layer in the overlayer context above the other layers."
-  ([layer]
-     (overlay* draw-relative! layer (.getTransform *graphics*) 0 0))
-  ([layer x y]
-     (overlay* draw-relative! layer (.getTransform *graphics*) x y))
-  ([layer x y w h]
-     (overlay* draw-relative! layer (.getTransform *graphics*) x y w h)))
-
-(defn overlay-aligned! [layer h-align v-align x y]
-  (overlay* draw-relative-aligned!
-            layer (.getTransform *graphics*)
-            h-align v-align x y))
-
-(defn with-overlays* [rec? f & args]
-  (binding [*above* []]
-    (apply f args)
-    (if rec?
-      (loop [above *above*]
-        (when (seq above)
-          (var-set #'*above* [])
-          (doseq [f above]
-            (f))
-          (recur *above*)))
-      (doseq [of *above*]
-        (of)))))
-
-(defmacro with-overlays [rec? & body]
-  `(with-overlays* ~rec? (fn [] ~@body)))
-
-(defn overlayer
-  ([content]
-     (overlayer content true))
-  ([content rec?]
-     (decorate-layer content [_]
-       (with-overlays* rec? render! content))))
--- a/src/net/kryshen/indyvon/viewport.clj	Mon Apr 07 14:24:16 2014 +0400
+++ b/src/net/kryshen/indyvon/viewport.clj	Mon Apr 07 15:23:58 2014 +0400
@@ -1,5 +1,5 @@
 ;;
-;; Copyright 2010, 2011, 2012, 2013 Mikhail Kryshen <mikhail@kryshen.net>
+;; Copyright 2010-2014 Mikhail Kryshen <mikhail@kryshen.net>
 ;;
 ;; This file is part of Indyvon.
 ;;
@@ -20,7 +20,7 @@
 (ns net.kryshen.indyvon.viewport
   "Scrollable viewport and miniature."
   (:use
-   (net.kryshen.indyvon core async layers))
+   (net.kryshen.indyvon core async views))
   (:import
    java.awt.Cursor
    java.awt.geom.AffineTransform))
@@ -80,15 +80,15 @@
       :transform transform)))
 
 (defrecord Viewport [content h-align v-align state]
-  Layer
-  (render! [layer]
-    (repaint-on-update layer)
-    (with-handlers layer
+  View
+  (render! [view]
+    (repaint-on-update view)
+    (with-handlers view
       (let [geom (geometry content)
             new-state (swap! state update-viewport geom h-align v-align)
             transform (:transform new-state)]
         ;; TODO: notify observers when size changes.
-        (binding [*viewport* layer
+        (binding [*viewport* view
                   *viewport-transform* transform]
           (with-transform transform
             (draw! content 0 0 (width geom) (height geom) false))))
@@ -109,10 +109,10 @@
                               (- (:y-on-screen e) (:fix-y s)))
                   :fix-x (:x-on-screen e)
                   :fix-y (:y-on-screen e))))
-       (update layer))
+       (update view))
       (:mouse-wheel e
        (scale-viewport!
-        layer
+        view
         (Math/pow *viewport-scaling-step* (:wheel-rotation e))
         true (:x e) (:y e)))))
   (geometry [_]
@@ -126,7 +126,7 @@
    0 0))
 
 (defn viewport
-  "Creates scrollable viewport layer."
+  "Creates scrollable viewport view."
   ([content]
      (viewport content :left :top))
   ([content h-align v-align]
@@ -175,12 +175,12 @@
        (/ max-height height)))
 
 (defn miniature
-  "Creates layer that asynchronously renders view of the content
-  scaled to the specified size."
+  "Creates a view that asynchronously renders the content view scaled to
+  the specified size."
   [content mw mh]
-  (async-layer
+  (async-view
    (reify
-    Layer
+    View
     (render! [this]
       (let [geom (geometry content)
             cw (width geom)
@@ -199,7 +199,7 @@
   "Creates miniature view of the viewport's contents."
   [viewport m-width m-height]
   (let [miniature (miniature (:content viewport) m-width m-height)]
-    (decorate-layer miniature [l]
+    (decorate-view miniature [l]
       (repaint-on-update viewport)
       (let [geom (geometry (:content viewport))
             s (scaling (width geom) (height geom) m-width m-height)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/kryshen/indyvon/views.clj	Mon Apr 07 15:23:58 2014 +0400
@@ -0,0 +1,409 @@
+;;
+;; Copyright 2010-2014 Mikhail Kryshen <mikhail@kryshen.net>
+;;
+;; This file is part of Indyvon.
+;;
+;; Indyvon is free software: you can redistribute it and/or modify it
+;; under the terms of the GNU Lesser General Public License version 3
+;; only, as published by the Free Software Foundation.
+;;
+;; Indyvon is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; Lesser General Public License for more details.
+;;
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with Indyvon.  If not, see
+;; <http://www.gnu.org/licenses/>.
+;;
+
+(ns net.kryshen.indyvon.views
+  "Implementations of the View protocol."
+  (:use
+   (net.kryshen.indyvon core async))
+  (:import
+   (java.awt Font Image Toolkit)
+   java.awt.image.ImageObserver
+   (java.awt.geom Area AffineTransform Rectangle2D$Double Point2D
+                  Point2D$Double)
+   (java.awt.font FontRenderContext TextLayout)
+   java.util.concurrent.TimeUnit
+   (com.google.common.cache Cache CacheBuilder CacheLoader)))
+
+(defmacro decorate-view
+  "Decorate the view replacing render! implementation."
+  [view & render-tail]
+  `(let [view# ~view]
+     (reify
+       View
+       (render! ~@render-tail)
+       (geometry [t#] (geometry view#)))))
+
+(defrecord Empty []
+  View
+  (render! [_])
+  (geometry [_]
+    (->Size 0 0)))
+
+(def empty-view (->Empty))
+
+;; TODO: change argument order for decorators, content should be the
+;; last.
+
+(defn padding
+  "Adds padding to the content view."
+  ([content pad]
+     (padding content pad pad pad pad))
+  ([content top left bottom right]
+     (if (== 0 top left bottom right)
+       content
+       (reify
+        View
+        (render! [l]
+           (draw! content
+                  left top
+                  (- *width* left right)
+                  (- *height* top bottom)
+                  false))
+        (geometry [l]
+          (->NestedGeometry (geometry content) top left bottom right))))))
+
+(defn border
+  "Adds a border to the content view."
+  ([content]
+     (border content 1))
+  ([content thikness]
+     (border content thikness 0))
+  ([content thikness gap]
+     (let [view (padding content (+ thikness gap))
+           t (double thikness)]
+       (decorate-view view [_]
+         (render! view)
+         (with-color :border-color
+           (let [w (double *width*)
+                 h (double *height*)
+                 outer (Area. (Rectangle2D$Double. 0.0 0.0 w h))
+                 inner (Area. (Rectangle2D$Double. t t (- w t t) (- h t t)))]
+             (.subtract outer inner)
+             (.fill *graphics* outer)))))))
+
+;; TODO: opacity and blur.
+(defn shadow
+  "Adds a shadow to the content view."
+  ([content]
+     (shadow content 1 1))
+  ([content x-offset y-offset]
+     (let [x (if (neg? x-offset) (- x-offset) 0)
+           y (if (neg? y-offset) (- y-offset) 0)
+           abs-x (if (neg? x-offset) (- x-offset) x-offset)
+           abs-y (if (neg? y-offset) (- y-offset) y-offset)
+           shadow-x (+ x-offset x)
+           shadow-y (+ y-offset y)]
+       (reify
+        View
+        (render! [_]
+          (let [w (- *width* abs-x)
+                h (- *height* abs-y)]
+            (with-color :shadow-color
+              (.fillRect *graphics* shadow-x shadow-y w h))
+            (draw! content x y w h)))
+        (geometry [_]
+          (->NestedGeometry (geometry content)
+                            y x shadow-y shadow-x))))))
+
+(defn panel
+  "An opaque view using theme's alt-back-color or a custom background
+  color."
+  ([content]
+     (panel :alt-back-color content))
+  ([back-color content]
+     (decorate-view content [_]
+       (with-color back-color
+         (.fill *graphics* (Rectangle2D$Double. 0.0 0.0 *width* *height*)))
+       (render! content))))
+
+(defn hbox
+  "Creates a view that draws the specified content views placing them
+   horizontally."
+  [& contents]
+  (reify
+   View
+   (render! [_]
+     (let [widths (map #(width (geometry %)) contents)
+           xs (cons 0 (reductions + widths))
+           widths-sum (last xs)
+           scale (/ *width* widths-sum)]
+       (doseq [[c w x] (map vector contents widths xs)]
+         (draw! c x 0 w *height*))))
+   (geometry [_]
+     (reduce #(->Size (+ (width %1) (width %2))
+                      (max (height %1) (height %2)))
+             (->Size 0 0)
+             (map geometry contents)))))
+
+(defn vbox
+  "Creates a view that draws the specified content views placing them
+   vertically."
+  [& contents]
+  (reify
+   View
+   (render! [_]
+     (let [heights (map #(height (geometry %)) contents)
+           ys (cons 0 (reductions + heights))
+           heights-sum (last ys)
+           scale (/ *height* heights-sum)]
+       (doseq [[c h y] (map vector contents heights ys)]
+         (draw! c 0 y *width* h))))
+   (geometry [_]
+     (reduce #(->Size (max (width %1) (width %2))
+                      (+ (height %1) (height %2)))
+             (->Size 0 0)
+             (map geometry contents)))))
+
+(defn- re-split [^java.util.regex.Pattern re s]
+  (seq (.split re s)))
+
+(def ^:private ^Cache text-layout-cache
+  (-> (CacheBuilder/newBuilder)
+      (.softValues)
+      (.expireAfterAccess (long 1) TimeUnit/SECONDS)
+      (.build)))
+
+(defn- get-text-layout
+  [^String line ^Font font ^FontRenderContext font-context]
+  (.get text-layout-cache [line font font-context]
+        #(TextLayout. line font font-context)))
+
+(defn- layout-text
+  [lines font font-context]
+  (map #(get-text-layout % font font-context) lines))
+
+(defn- text-width [layouts]
+  (reduce (fn [w ^TextLayout tl] (max w (.getAdvance tl))) 0 layouts))
+
+(defn- text-height [layouts]
+  (reduce (fn [w ^TextLayout tl]
+            (+ w (.getAscent tl)
+               (.getDescent tl)
+               (.getLeading tl)))
+          0 layouts))
+
+(defn label
+  "Creates a view to display multiline text."
+  ([text]
+     (label text :left :top))
+  ([text h-align v-align]
+     (let [lines (re-split #"\r\n|\n|\r|\u0085|\u2028|\u2029" (str text))]
+       (reify View
+        (render! [view]
+          (let [w *width*
+                h *height*
+                font (.getFont *graphics*)
+                layouts (layout-text lines font (font-context))
+                y (align-y v-align (text-height layouts) h)]
+            (loop [layouts layouts, y y]
+              (when-first [^TextLayout layout layouts]
+                (let [ascent (.getAscent layout)
+                      lh (+ ascent (.getDescent layout) (.getLeading layout))
+                      x (align-x h-align (.getAdvance layout) w)]
+                  (.draw layout *graphics* x (+ y ascent))
+                  (recur (next layouts) (+ y lh)))))))
+        (geometry [view]
+          (let [layouts (layout-text lines (:font *theme*) (font-context))
+                w (text-width layouts)
+                h (text-height layouts)]
+            (->Size w h)))))))
+
+(defn- ^ImageObserver image-observer [view]
+  (reify
+   ImageObserver
+   (imageUpdate [this img infoflags x y width height]
+     (update view)
+     (zero? (bit-and infoflags
+                     (bit-or ImageObserver/ALLBITS
+                             ImageObserver/ABORT))))))
+
+(defn image-view
+  [image-or-uri]
+  (let [^Image image (if (instance? Image image-or-uri)
+                       image-or-uri
+                       (.getImage (Toolkit/getDefaultToolkit)
+                                  ^java.net.URL image-or-uri))]
+    (.prepareImage (Toolkit/getDefaultToolkit) image -1 -1 nil)
+    (reify
+     View
+     (render! [view]
+       (repaint-on-update view)
+       (.drawImage *graphics* image 0 0 (image-observer view)))
+     (geometry [view]
+       (let [observer (image-observer view)
+             width (.getWidth image observer)
+             height (.getHeight image observer)
+             width (if (pos? width) width 1)
+             height (if (pos? height) height 1)]
+         (->Size width height))))))
+
+(def ^:dynamic *miniature-thread-priority* 2)
+
+(defn ref-view
+  [view-ref]
+  (let [l (reify
+           View
+           (render! [l]
+             (repaint-on-update l)
+             (if-let [view @view-ref]
+               (render! view)))
+           (geometry [_]
+             (if-let [view @view-ref]
+               (geometry view)
+               (->Size 1 1))))]
+    (add-watch view-ref l (fn [_ _ _ _] (update l)))
+    l))
+
+;;
+;; View context decorators
+;;
+
+(defmacro handler [view & handlers]
+  "Adds event handling to the view."
+  `(let [view# ~view]
+     (decorate-view view# [t#]
+       (with-handlers t#
+         (render! view#)
+         ~@handlers))))
+
+(defn themed [view & map-or-keyvals]
+  (let [theme (if (== (count map-or-keyvals) 1)
+                (first map-or-keyvals)
+                (apply array-map map-or-keyvals))]
+    (reify
+     View
+     (render! [_]
+       (with-theme theme
+         (render! view)))
+     (geometry [_]
+       (with-theme theme
+         (geometry view))))))
+
+(defn hinted [view & map-or-keyvals]
+  (let [hints (if (== (count map-or-keyvals) 1)
+                (first map-or-keyvals)
+                (apply array-map map-or-keyvals))]
+    (decorate-view view [_]
+      (with-hints* hints render! view))))
+
+;;
+;; Measuring time
+;;
+
+(def ^:dynamic *interval*)
+
+(defn interval-view
+  "Creates a view that measures time between repaints ant draws it's
+  content with the *interval* var bound to the measured time."
+  [content]
+  (let [last-time (atom nil)]
+    (decorate-view content [_]
+      (compare-and-set! last-time nil *time*)
+      (let [lt @last-time]
+        (binding [*interval* (if (compare-and-set! last-time lt *time*)
+                               (- *time* lt)
+                               0)] ; already measured on parallel thread
+          (render! content))))))
+
+(defn- fps-label [text]
+  (padding (label text :right :bottom) 5))
+
+(defn fps-view
+  "Creates a view that draws content and displays the
+  frames per second rate."
+  [content]
+  (let [update-interval 2E8 ; 0.2 s in nanoseconds
+        frames (ref 0)
+        prev-time (ref nil)
+        display (ref (fps-label "fps n/a"))]
+    (decorate-view content [_]
+      (draw! content)
+      (draw!
+       (dosync
+        (alter frames inc)
+        (if @prev-time
+          (let [elapsed (- *time* @prev-time)]
+            (when (> elapsed update-interval)
+              (let [fps (/ @frames (/ elapsed 1E9))]
+                (ref-set display (fps-label (format "%.1f" fps)))
+                (ref-set frames 0)
+                (ref-set prev-time *time*))))
+          (ref-set prev-time *time*))
+        @display)))))
+
+;;
+;; Overlays
+;;
+
+(def ^:private ^:dynamic *above*)
+
+(defn- overlay* [f & args]
+  (var-set #'*above* (conj *above* (apply partial f args))))
+
+(defn- ^Point2D to-graphics-coords
+  [^AffineTransform transform x y]
+  (let [p (Point2D$Double. x y)]
+    (.transform transform p p)
+    (.transform (.createInverse (.getTransform *graphics*)) p p)
+    p))
+
+(defn- draw-relative!
+  ([view transform x y]
+     (let [p (to-graphics-coords transform x y)]
+       (draw! view (.getX p) (.getY p))))
+  ([view transform x y w h]
+     (let [p (to-graphics-coords transform x y)]
+       (draw! view (.getX p) (.getY p) w h))))
+
+(defn- draw-relative-aligned!
+  [view transform h-align v-align x y]
+  (let [geom (geometry view)
+        w (width geom)
+        h (height geom)
+        p (to-graphics-coords transform x y)
+        x (- (.getX p) (anchor-x geom h-align w))
+        y (- (.getY p) (anchor-y geom v-align h))]
+    (draw! view x y w h)))
+
+(defn overlay!
+  "Draws view in the overlay context above the other views."
+  ([view]
+     (overlay* draw-relative! view (.getTransform *graphics*) 0 0))
+  ([view x y]
+     (overlay* draw-relative! view (.getTransform *graphics*) x y))
+  ([view x y w h]
+     (overlay* draw-relative! view (.getTransform *graphics*) x y w h)))
+
+(defn overlay-aligned! [view h-align v-align x y]
+  (overlay* draw-relative-aligned!
+            view (.getTransform *graphics*)
+            h-align v-align x y))
+
+(defn with-overlays* [rec? f & args]
+  (binding [*above* []]
+    (apply f args)
+    (if rec?
+      (loop [above *above*]
+        (when (seq above)
+          (var-set #'*above* [])
+          (doseq [f above]
+            (f))
+          (recur *above*)))
+      (doseq [of *above*]
+        (of)))))
+
+(defmacro with-overlays [rec? & body]
+  `(with-overlays* ~rec? (fn [] ~@body)))
+
+(defn layered
+  ([content]
+     (layered content true))
+  ([content rec?]
+     (decorate-view content [_]
+       (with-overlays* rec? render! content))))