Mercurial > hg > indyvon
changeset 106:f42e2b9e1ad9
Removed Anchored protocol, "layer-size" function in Layer replaced with "geometry" which returns a structure describing both layer size and anchor point.
Indyvon now requires Clojure 1.3.
author | Mikhail Kryshen <mikhail@kryshen.net> |
---|---|
date | Wed, 21 Sep 2011 02:27:11 +0300 |
parents | 24e98602b37e |
children | 5fdb0bb99f75 |
files | README.rst README.ru project.clj 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 |
diffstat | 8 files changed, 209 insertions(+), 144 deletions(-) [+] |
line wrap: on
line diff
--- a/README.rst Tue May 24 18:43:49 2011 +0400 +++ b/README.rst Wed Sep 21 02:27:11 2011 +0300 @@ -11,7 +11,7 @@ (defprotocol Layer (render! [layer] "Draws the layer.") - (layer-size [layer] + (geometry [layer] "Returns preferred size for the layer.")) Layer functions are called in the Layer context which is defined by
--- a/README.ru Tue May 24 18:43:49 2011 +0400 +++ b/README.ru Wed Sep 21 02:27:11 2011 +0300 @@ -15,7 +15,7 @@ (слой), который содержит всего две функции: render! — отрисовка, - layer-size — получение предпочтительного размера элемента. + geometry — получение предпочтительных размеров элемента. Слой, в отличии от обычных компонентов интерфейса или элементов графов сцены в системах визуализации, не имеет состояния (координаты, размер,
--- a/project.clj Tue May 24 18:43:49 2011 +0400 +++ b/project.clj Wed Sep 21 02:27:11 2011 +0300 @@ -1,9 +1,8 @@ (defproject indyvon "1.0.0-SNAPSHOT" :description "INteractive DYnamic VisualizatiON library" ;;:warn-on-reflection true - :dependencies [[org.clojure/clojure "1.2.1"] + :dependencies [[org.clojure/clojure "1.3.0-RC0"] [com.google.guava/guava "r09"]] - :dev-dependencies [[swank-clojure "1.3.1"]] ;;:aot [net.kryshen.indyvon.core ;; net.kryshen.indyvon.async ;; net.kryshen.indyvon.layers
--- a/src/net/kryshen/indyvon/async.clj Tue May 24 18:43:49 2011 +0400 +++ b/src/net/kryshen/indyvon/async.clj Wed Sep 21 02:27:11 2011 +0300 @@ -22,7 +22,6 @@ (:use net.kryshen.indyvon.core) (:import - (net.kryshen.indyvon.core Size Location) java.awt.GraphicsConfiguration (java.awt Image AlphaComposite Transparency) (java.awt.image BufferedImage) @@ -146,8 +145,8 @@ (draw-offscreen-async layer)) (with-buffer layer :front [b] (.drawImage *graphics* ^Image (:image b) 0 0 nil))) - (layer-size [layer] - (Size. width height))) + (geometry [layer] + (->Size width height))) (defn- create-thread-factory [priority] (reify
--- a/src/net/kryshen/indyvon/component.clj Tue May 24 18:43:49 2011 +0400 +++ b/src/net/kryshen/indyvon/component.clj Wed Sep 21 02:27:11 2011 +0300 @@ -22,7 +22,6 @@ (:use net.kryshen.indyvon.core) (:import - (net.kryshen.indyvon.core Size Bounds) (java.awt Graphics Component Dimension Color) (java.awt.geom Rectangle2D$Double) (javax.swing JFrame JPanel JOptionPane))) @@ -49,8 +48,8 @@ (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 [geom (root-geometry layer (font-context this) this)] + (Dimension. (width geom) (height geom)))))] (.setBackground panel (:back-color *theme*)) (add-observer panel layer (fn [w _] ;; Use the first observer argument
--- a/src/net/kryshen/indyvon/core.clj Tue May 24 18:43:49 2011 +0400 +++ b/src/net/kryshen/indyvon/core.clj Wed Sep 21 02:27:11 2011 +0300 @@ -93,18 +93,88 @@ (def ^{:dynamic true} *theme* (default-theme)) -(defrecord Location [x y]) -(defrecord Size [width height]) -(defrecord Bounds [x y width height]) - ;; ;; Core protocols and types ;; (defprotocol Layer "Basic UI element." - (render! [this]) - (layer-size [this])) + (render! [layer] + "Draws layer in the current *graphics* context.") + (geometry [layer] + "Returns the preferred layer Geometry.")) + +(defprotocol Geometry + "Describes geometry of a Layer. 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." + (width [geom] [geom height]) + (height [geom] [geom width]) + (anchor-x [geom h-align width] + "Returns the x coordinate of the anchor point for the specified + horizontal alignment and width, h-align could be :left, :center + or :right.") + (anchor-y [geom v-align height] + "Returns the y coordinate of the anchor point for the specified + vertical alignment and height, v-align could be :top, :center + or :bottom.")) + +(defrecord Size [width height] + Geometry + (width [_] width) + (width [_ _] width) + (height [_] height) + (height [_ _] height) + (anchor-x [_ h-align width] + (case h-align + :left 0 + :center (/ width 2) + :right width)) + (anchor-y [_ v-align height] + (case v-align + :top 0 + :center (/ height 2) + :bottom height))) + +(defrecord FixedGeometry [ax ay width height] + Geometry + (width [_] width) + (width [_ _] width) + (height [_] height) + (height [_ _] height) + (anchor-x [_ _ _] ax) + (anchor-y [_ _ _] ay)) + +(defrecord NestedGeometry [geometry top left bottom right] + Geometry + (width [_] + (+ left right (width geometry))) + (width [_ h] + (+ left right (width geometry (- h top bottom)))) + (height [_] + (+ top bottom (height geometry))) + (height [_ w] + (+ top bottom (height geometry (- w left right)))) + (anchor-x [_ h-align w] + (+ left (anchor-x geometry h-align (- w left right)))) + (anchor-y [_ v-align h] + (+ top (anchor-y geometry v-align (- h top bottom))))) + +(defrecord ScaledGeometry [geometry sx sy] + Geometry + (width [_] + (* sx (width geometry))) + (width [_ h] + (* sx (width geometry (/ h sy)))) + (height [_] + (* sy (height geometry))) + (height [_ w] + (* sy (height geometry (/ w sx)))) + (anchor-x [_ h-align w] + (* sx (anchor-x geometry h-align (/ w sx)))) + (anchor-y [_ v-align h] + (* sy (anchor-y geometry v-align (/ h sy))))) ;; TODO: modifiers (defrecord MouseEvent [id when x y x-on-screen y-on-screen button @@ -128,33 +198,6 @@ "Returns true if the specified handle received the :mouse-entered event and have not yet received :mouse-exited.")) -(defprotocol Anchored - "Provide anchor point for Layers. Used by viewport." - (anchor [this h-align v-align] - "Anchor point: [x y], h-align could be :left, :center or :right, - v-align is :top, :center or :bottom")) - -(defn default-anchor [layer h-align v-align] - (if (and (= h-align :left) - (= v-align :top)) - (Location. 0 0) - (let [size (layer-size layer)] - (Location. - (case h-align - :left 0 - :center (/ (:width size) 2) - :right (:width size)) - (case v-align - :top 0 - :center (/ (:height size) 2) - :bottom (:height size)))))) - -;; Default implementation of Anchored for any Layer. -(extend-protocol Anchored - net.kryshen.indyvon.core.Layer - (anchor [this h-align v-align] - (default-anchor this h-align v-align))) - (defn- assoc-cons [m key val] (->> (get m key) (cons val) (assoc m key))) @@ -408,8 +451,8 @@ ([layer x y] (draw! layer x y true)) ([layer x y clip?] - (let [size (layer-size layer)] - (draw! layer x y (:width size) (:height size) 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?] @@ -417,15 +460,23 @@ (with-bounds* x y width height render! layer) (with-bounds-noclip* x y width height render! layer)))) -(defn draw-anchored! +(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 [anchor (anchor layer h-align v-align)] - (draw! layer (- x (:x anchor)) (- y (:y anchor))))) + (let [geom (geometry layer) + w (width geom) + h (height geom)] + (draw! layer + (- 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 [anchor (anchor layer h-align v-align)] - (draw! layer (- x (:x anchor)) (- y (:y anchor)) w h)))) + (let [geom (geometry layer)] + (draw! layer + (- x (anchor-x geom h-align w)) + (- y (anchor-y geom v-align h)) + w h)))) (defn draw-root! "Draws the root layer." @@ -468,14 +519,14 @@ (remove-observers tmp-watcher) (commit event-dispatcher))))))) -(defn root-size +(defn root-geometry ([layer font-context] - (root-size layer font-context nil)) + (root-geometry layer font-context nil)) ([layer font-context target] (binding [*root* layer *target* target *font-context* font-context] - (layer-size layer)))) + (geometry layer)))) ;; ;; Event handling.
--- a/src/net/kryshen/indyvon/demo.clj Tue May 24 18:43:49 2011 +0400 +++ b/src/net/kryshen/indyvon/demo.clj Wed Sep 21 02:27:11 2011 +0300 @@ -23,7 +23,6 @@ (:use (net.kryshen.indyvon core layers component)) (:import - (net.kryshen.indyvon.core Size) (java.awt Color) (javax.swing JFrame))) @@ -121,10 +120,10 @@ (:mouse-pressed _ (repaint)) (:mouse-released _ (repaint)) (:mouse-clicked _ (apply callback args)))) - (layer-size [button] - (let [face-size (layer-size face)] - (Size. (+ (:width face-size) shadow-offset) - (+ (:height face-size) shadow-offset)))))))) + (geometry [button] + (let [face-geom (geometry face)] + (->Size (+ (width face-geom) shadow-offset) + (+ (height face-geom) shadow-offset)))))))) (def button1 (animated-button (label "Animated button 1") println "Animated button 1 clicked")) @@ -147,8 +146,8 @@ (println e)) (:mouse-moved e (println e)))) - (layer-size [layer] - (Size. 30 20)))) + (geometry [layer] + (->Size 30 20)))) (def test-layer1b (border test-layer1 2 3)) @@ -162,8 +161,8 @@ (with-rotate 0.5 0 0 (draw! test-layer1b 30 25)) (draw! test-layer1 55 5)) - (layer-size [layer] - (Size. 70 65)))) + (geometry [layer] + (->Size 70 65)))) (def test-layer2m (miniature test-layer2 30 30)) @@ -191,8 +190,8 @@ (draw-button! :button (label "Immediate button" :center :center) #(println "Button clicked!")))) - (layer-size [layer] - (Size. 400 300)))) + (geometry [layer] + (->Size 400 300)))) ;; Main viewport (def vp (viewport root)) @@ -205,7 +204,7 @@ (fps-layer (decorate-layer vp [_] (draw! vp) - (draw-anchored! + (draw-aligned! (label (str "Drag mouse to pan," \newline "use mouse wheel to zoom.")) :left :bottom 5 (- *height* 5))
--- a/src/net/kryshen/indyvon/layers.clj Tue May 24 18:43:49 2011 +0400 +++ b/src/net/kryshen/indyvon/layers.clj Wed Sep 21 02:27:11 2011 +0300 @@ -22,7 +22,6 @@ (:use (net.kryshen.indyvon core async)) (:import - (net.kryshen.indyvon.core Size Location) (java.lang.ref SoftReference) (java.awt Font Cursor Image Toolkit Point) java.awt.image.ImageObserver @@ -47,14 +46,13 @@ `(align-xy ~inner ~outer ~align :top :center :bottom)) (defmacro decorate-layer - "Decorate Layer and Anchored replacing render! implementation." + "Decorate Layer replacing render! implementation." [layer & render-tail] - `(reify - Layer - (render! ~@render-tail) - (layer-size [t#] (layer-size ~layer)) - Anchored - (anchor [t# xa# ya#] (anchor ~layer xa# ya#)))) + `(let [layer# ~layer] + (reify + Layer + (render! ~@render-tail) + (geometry [t#] (geometry layer#))))) (defn padding "Decorates layer adding padding." @@ -70,10 +68,8 @@ left top (- *width* left right) (- *height* top bottom))) - (layer-size [l] - (let [s (layer-size content)] - (Size. (+ (:width s) left right) - (+ (:height s) top bottom)))))))) + (geometry [l] + (->NestedGeometry (geometry content) top left bottom right)))))) (defn border "Decorate layer with a border." @@ -110,7 +106,6 @@ shadow-x (+ x-offset x) shadow-y (+ y-offset y)] (reify - ;; TODO: Anchored Layer (render! [_] (let [w (- *width* abs-x) @@ -118,10 +113,9 @@ (with-color :shadow-color (.fillRect *graphics* shadow-x shadow-y w h)) (draw! content x y w h))) - (layer-size [_] - (let [s (layer-size content)] - (Size. (+ (:width s) abs-x) - (+ (:height s) abs-y)))))))) + (geometry [_] + (->NestedGeometry (geometry content) + y x shadow-y shadow-x)))))) (defn panel "Opaque layer using theme's alt-back-color." @@ -141,17 +135,17 @@ (reify Layer (render! [_] - (let [widths (map #(:width (layer-size %)) contents) + (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*)))) - (layer-size [_] - (reduce #(Size. (+ (:width %1) (:width %2)) - (max (:height %1) (:height %2))) - (Size. 0 0) - (map layer-size contents))))) + (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 @@ -160,17 +154,17 @@ (reify Layer (render! [_] - (let [heights (map #(:height (layer-size %)) contents) + (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)))) - (layer-size [_] - (reduce #(Size. (max (:width %1) (:width %2)) - (+ (:height %1) (:height %2))) - (Size. 0 0) - (map layer-size contents))))) + (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))) @@ -223,11 +217,11 @@ x (align-x (.getAdvance layout) w h-align)] (.draw layout *graphics* x (+ y ascent)) (recur (next layouts) (+ y lh))))))) - (layer-size [layer] + (geometry [layer] (let [layouts (layout-text lines (:font *theme*) *font-context*) - width (text-width layouts) - height (text-height layouts)] - (Size. width height))))))) + w (text-width layouts) + h (text-height layouts)] + (->Size w h))))))) (defn- ^ImageObserver image-observer [layer] (reify @@ -250,13 +244,13 @@ (render! [layer] (repaint-on-update layer) (.drawImage *graphics* image 0 0 (image-observer layer))) - (layer-size [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)))))) + (->Size width height)))))) (def ^{:dynamic true} *miniature-thread-priority* 2) @@ -268,24 +262,23 @@ (defn miniature "Creates layer that asynchronously renders view of the content scaled to the specified size." - [content width height] + [content mw mh] (async-layer (reify Layer (render! [this] - (let [size (layer-size content) - s (scaling (:width size) (:height size) width height)] + (let [geom (geometry content) + cw (width geom) + ch (height geom) + s (scaling cw ch mw mh)] (.scale *graphics* s s) (draw! content - (align-x (:width size) (/ width s) :center) - (align-y (:height size) (/ height s) :center) - (:width size) (:height size)))) - (layer-size [this] - (Size. width height))) - ;; (let [size (layer-size content) - ;; s (scaling (:width size) (:height size) width height)] - ;; (Size. (* (:width size) s) (* (:height size) s))))) - width height *miniature-thread-priority*)) + (align-x cw (/ mw s) :center) + (align-y ch (/ mh s) :center) + cw ch))) + (geometry [_] + (->Size mw mh))) + mw mh *miniature-thread-priority*)) ;;(defn- translate [^AffineTransform transform ^double x ^double y] ;; (doto ^AffineTransform (.clone transform) @@ -309,17 +302,22 @@ ;; State (refs) transform fix-x fix-y - last-width last-height last-anchor] + last-width last-height + last-anchor-x last-anchor-y] Layer (render! [layer] (repaint-on-update layer) (with-handlers layer - (let [anchor (anchor content h-align v-align)] + (let [geom (geometry content) + cw (width geom) + ch (height geom) + ax (anchor-x geom h-align cw) + ay (anchor-y geom v-align ch)] (dosync (let [ax1 (align-x @last-width *width* h-align) ay1 (align-y @last-height *height* v-align) - ax2 (- (:x @last-anchor) (:x anchor)) - ay2 (- (:y @last-anchor) (:y anchor))] + ax2 (- @last-anchor-x ax) + ay2 (- @last-anchor-y ay)] (if-not (and (zero? ax1) (zero? ay1) (zero? ax2) (zero? ay2)) (ref-set transform (doto (AffineTransform/getTranslateInstance ax1 ay1) @@ -327,10 +325,11 @@ (.translate ax2 ay2))))) (ref-set last-width *width*) (ref-set last-height *height*) - (ref-set last-anchor anchor)) + (ref-set last-anchor-x ax) + (ref-set last-anchor-y ay)) ;; TODO: notify observers when size changes. (with-transform @transform - (draw! content 0 0 false))) + (draw! content 0 0 cw ch false))) (:mouse-pressed e (dosync (ref-set fix-x (:x-on-screen e)) @@ -364,8 +363,8 @@ *viewport-max-scale*) (ref-set transform scaled)))) (update layer)))) - (layer-size [layer] - (layer-size content))) + (geometry [_] + (geometry content))) (defn viewport "Creates scrollable viewport layer." @@ -376,31 +375,32 @@ (ref (AffineTransform.)) ; transform (ref 0) (ref 0) ; fix-x fix-y (ref 0) (ref 0) ; last-width last-height - (ref (Location. 0 0))))) ; last-anchor + (ref 0) (ref 0)))) ; last-anchor-x last-anchor-y (defn reset-viewport [viewport] (dosync (ref-set (:last-width viewport) 0) (ref-set (:last-height viewport) 0) - (ref-set (:last-anchor viewport) (Location. 0 0)) + (ref-set (:last-anchor-x viewport) 0) + (ref-set (:last-anchor-y viewport) 0) (ref-set (:transform viewport) (AffineTransform.))) (update viewport)) (defn viewport-miniature "Creates miniature view of the viewport's contents." - [viewport width height] - (let [miniature (miniature (:content viewport) width height)] + [viewport m-width m-height] + (let [miniature (miniature (:content viewport) m-width m-height)] (decorate-layer miniature [l] (repaint-on-update viewport) - (let [size (layer-size (:content viewport)) - s (scaling (:width size) (:height size) width height) + (let [geom (geometry (:content viewport)) + s (scaling (width geom) (height geom) m-width m-height) [vp-tr w h] (dosync [@(:transform viewport) @(:last-width viewport) @(:last-height viewport)]) vp-inverse (.createInverse ^AffineTransform vp-tr) - ox (align-x (:width size) (/ width s) :center) - oy (align-y (:height size) (/ height s) :center) + ox (align-x (width geom) (/ m-width s) :center) + oy (align-y (height geom) (/ m-height s) :center) transform (doto (AffineTransform.) (.scale s s) (.translate ox oy) @@ -437,15 +437,10 @@ (repaint-on-update l) (if-let [layer @layer-ref] (render! layer))) - (layer-size [_] + (geometry [_] (if-let [layer @layer-ref] - (layer-size layer) - (Size. 1 1))) - Anchored - (anchor [_ x-align y-align] - (if-let [layer @layer-ref] - (anchor layer x-align y-align) - (Location. 0 0))))] + (geometry layer) + (->Size 1 1))))] (add-watch layer-ref l (fn [_ _ _ _] (update l))) l)) @@ -467,16 +462,12 @@ (apply array-map map-or-keyvals))] (reify Layer - (render! [t] + (render! [_] (with-theme theme (render! layer))) - (layer-size [t] + (geometry [_] (with-theme theme - (layer-size layer))) - Anchored - (anchor [t xa ya] - (with-theme theme - (anchor layer xa ya)))))) + (geometry layer)))))) (defn hinted [layer & map-or-keyvals] (let [hints (if (== (count map-or-keyvals) 1) @@ -553,7 +544,17 @@ ([layer transform x y w h] (let [p (to-graphics-coords transform x y)] (draw! layer (.x p) (.y 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 (- (.x p) (anchor-x geom h-align w)) + y (- (.y 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] @@ -563,6 +564,11 @@ ([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 overlayer [content] (decorate-layer content [_] @@ -570,3 +576,15 @@ (render! content) (doseq [f *above*] (f))))) + +(defn overlayer* + [content] + (decorate-layer content [_] + (binding [*above* []] + (render! content) + (loop [above *above*] + (when (seq above) + (var-set #'*above* []) + (doseq [f above] + (f)) + (recur *above*))))))