Mercurial > hg > indyvon
changeset 139:173616375eb5
Refactoring. Moved viewport functions into separate namespace.
author | Mikhail Kryshen <mikhail@kryshen.net> |
---|---|
date | Mon, 07 Jan 2013 19:52:23 +0400 |
parents | e3eeb1478df1 |
children | 4dd98ea3b475 |
files | src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj src/net/kryshen/indyvon/viewport.clj |
diffstat | 3 files changed, 270 insertions(+), 222 deletions(-) [+] |
line wrap: on
line diff
--- a/src/net/kryshen/indyvon/demo.clj Mon Jan 07 19:51:21 2013 +0400 +++ b/src/net/kryshen/indyvon/demo.clj Mon Jan 07 19:52:23 2013 +0400 @@ -1,5 +1,5 @@ ;; -;; Copyright 2010, 2011 Mikhail Kryshen <mikhail@kryshen.net> +;; Copyright 2010, 2011, 2012 Mikhail Kryshen <mikhail@kryshen.net> ;; ;; This file is part of Indyvon. ;; @@ -21,7 +21,7 @@ "Indyvon demo and experiments." (:gen-class) (:use - (net.kryshen.indyvon core layers component)) + (net.kryshen.indyvon core layers viewport component)) (:import (java.awt Color) (javax.swing JFrame)))
--- a/src/net/kryshen/indyvon/layers.clj Mon Jan 07 19:51:21 2013 +0400 +++ b/src/net/kryshen/indyvon/layers.clj Mon Jan 07 19:52:23 2013 +0400 @@ -22,11 +22,10 @@ (:use (net.kryshen.indyvon core async)) (:import - (java.lang.ref SoftReference) - (java.awt Font Cursor Image Toolkit Point) + (java.awt Font Image Toolkit) java.awt.image.ImageObserver - (java.awt.geom AffineTransform Rectangle2D$Double Point2D$Double - Path2D Path2D$Double) + (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))) @@ -94,12 +93,10 @@ (with-color :border-color (let [w (double *width*) h (double *height*) - outer (Rectangle2D$Double. 0.0 0.0 w h) - inner (Rectangle2D$Double. t t (- w t t) (- h t t))] - (.fill *graphics* - (doto (Path2D$Double. Path2D/WIND_EVEN_ODD) - (.append outer false) - (.append inner false))))))))) + 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 @@ -261,196 +258,6 @@ (def ^:dynamic *miniature-thread-priority* 2) -(defn- scaling - [width height max-width max-height] - (min (/ max-width width) - (/ max-height height))) - -(defn miniature - "Creates layer that asynchronously renders view of the content - scaled to the specified size." - [content mw mh] - (async-layer - (reify - Layer - (render! [this] - (let [geom (geometry content) - cw (width geom) - ch (height geom) - s (scaling cw ch mw mh)] - (.scale *graphics* s s) - (draw! content - (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) -;; (.translate x y))) - -(defn- scale [^AffineTransform transform ^double sx ^double sy] - (doto ^AffineTransform (.clone transform) - (.scale sx sy))) - -(defn- pre-translate [^AffineTransform transform ^double x ^double y] - (if (== 0.0 x y) - transform - (doto (AffineTransform/getTranslateInstance x y) - (.concatenate transform)))) - -(def ^:dynamic *viewport-scaling-step* (double 3/4)) -(def ^:dynamic *viewport-min-scale* 1E-6) -(def ^:dynamic *viewport-max-scale* 1E6) - -(declare scale-viewport) - -(defrecord Viewport [content h-align v-align - ;; State (refs) - transform - fix-x fix-y - last-width last-height - last-anchor-x last-anchor-y] - Layer - (render! [layer] - (repaint-on-update layer) - (with-handlers layer - (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 (- @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) - (.concatenate @transform) - (.translate ax2 ay2))))) - (ref-set last-width *width*) - (ref-set last-height *height*) - (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 cw ch false))) - (:mouse-pressed e - (dosync - (ref-set fix-x (:x-on-screen e)) - (ref-set fix-y (:y-on-screen e))) - (set-cursor! (Cursor. Cursor/MOVE_CURSOR))) - (:mouse-released e - (set-cursor! (Cursor. Cursor/DEFAULT_CURSOR))) - (:mouse-dragged e - (dosync - (alter transform pre-translate - (- (:x-on-screen e) @fix-x) - (- (:y-on-screen e) @fix-y)) - (ref-set fix-x (:x-on-screen e)) - (ref-set fix-y (:y-on-screen e))) - (update layer)) - (:mouse-wheel e - (scale-viewport - layer - (Math/pow *viewport-scaling-step* (:wheel-rotation e)) - true (:x e) (:y e))))) - (geometry [_] - (geometry content))) - -(defn viewport - "Creates scrollable viewport layer." - ([content] - (viewport content :left :top)) - ([content h-align v-align] - (Viewport. content h-align v-align - (ref (AffineTransform.)) ; transform - (ref 0) (ref 0) ; fix-x fix-y - (ref 0) (ref 0) ; last-width last-height - (ref 0) (ref 0)))) ; last-anchor-x last-anchor-y - -(defn scale-viewport - ([viewport s] - (scale-viewport viewport s true)) - ([viewport s relative?] - (scale-viewport viewport s relative? nil nil)) - ([viewport s relative? x y] - (dosync - (let [^AffineTransform tr @(:transform viewport) - sx (if relative? s (/ s (.getScaleX tr))) - sy (if relative? s (/ s (.getScaleY tr))) - x (or x (align-x 0 @(:last-width viewport) (:h-align viewport))) - y (or y (align-y 0 @(:last-height viewport) (:v-align viewport))) - x (- x (* x sx)) - y (- y (* y sy)) - scaled (doto (AffineTransform/getTranslateInstance x y) - (.scale sx sy) - (.concatenate tr)) - sx (.getScaleX scaled) - sy (.getScaleY scaled)] - (if (<= *viewport-min-scale* - (min sx sy) - (max sx sy) - *viewport-max-scale*) - (ref-set (:transform viewport) scaled)))) - (update viewport))) - -(defn reset-viewport [viewport] - (dosync - (ref-set (:last-width viewport) 0) - (ref-set (:last-height viewport) 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 m-width m-height] - (let [miniature (miniature (:content viewport) m-width m-height)] - (decorate-layer miniature [l] - (repaint-on-update viewport) - (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 geom) (/ m-width s) :center) - oy (align-y (height geom) (/ m-height s) :center) - transform (doto (AffineTransform.) - (.scale s s) - (.translate ox oy) - (.concatenate vp-inverse)) - move-vp (fn [x y] - (dosync - (let [x (- (/ x s) ox) - y (- (/ y s) oy) - [x y] (transform-point @(:transform viewport) - x y) - x (- x (/ @(:last-width viewport) 2)) - y (- y (/ @(:last-height viewport) 2))] - (alter (:transform viewport) - pre-translate (- x) (- y)))) - (update viewport))] - (with-color :alt-back-color - (.fillRect *graphics* 0 0 *width* *height*)) - (with-transform transform - (with-color :back-color - (.fillRect *graphics* 0 0 w h))) - (with-handlers l - (draw! miniature) - (:mouse-pressed e (move-vp (:x e) (:y e))) - (:mouse-dragged e (move-vp (:x e) (:y e)))) - (with-transform transform - (with-color :border-color - (.drawRect *graphics* 0 0 w h))))))) - (defn ref-layer [layer-ref] (let [l (reify @@ -552,9 +359,9 @@ (defn- overlay* [f & args] (var-set #'*above* (conj *above* (apply partial f args)))) -(defn- ^Point to-graphics-coords +(defn- ^Point2D to-graphics-coords [^AffineTransform transform x y] - (let [p (Point. x y)] + (let [p (Point2D$Double. x y)] (.transform transform p p) (.transform (.createInverse (.getTransform *graphics*)) p p) p)) @@ -562,10 +369,10 @@ (defn- draw-relative! ([layer transform x y] (let [p (to-graphics-coords transform x y)] - (draw! layer (.x p) (.y p)))) + (draw! layer (.getX p) (.getY p)))) ([layer transform x y w h] (let [p (to-graphics-coords transform x y)] - (draw! layer (.x p) (.y p) w h)))) + (draw! layer (.getX p) (.getY p) w h)))) (defn- draw-relative-aligned! [layer transform h-align v-align x y] @@ -573,8 +380,8 @@ 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))] + 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! @@ -591,22 +398,25 @@ layer (.getTransform *graphics*) h-align v-align x y)) -(defn overlayer - [content] - (decorate-layer content [_] - (binding [*above* []] - (render! content) - (doseq [f *above*] - (f))))) - -(defn overlayer* - [content] - (decorate-layer content [_] - (binding [*above* []] - (render! content) +(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*)))))) + (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))))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/kryshen/indyvon/viewport.clj Mon Jan 07 19:52:23 2013 +0400 @@ -0,0 +1,238 @@ +;; +;; Copyright 2010, 2011, 2012 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.viewport + "Scrollable viewport and miniature." + (:use + (net.kryshen.indyvon core async layers)) + (:import + java.awt.Cursor + java.awt.geom.AffineTransform)) + +;;(defn- translate [^AffineTransform transform ^double x ^double y] +;; (doto ^AffineTransform (.clone transform) +;; (.translate x y))) + +(defn- scale [^AffineTransform transform ^double sx ^double sy] + (doto ^AffineTransform (.clone transform) + (.scale sx sy))) + +(defn- pre-translate [^AffineTransform transform ^double x ^double y] + (if (== 0.0 x y) + transform + (doto (AffineTransform/getTranslateInstance x y) + (.concatenate transform)))) + +(def ^:dynamic *viewport-scaling-step* (double 3/4)) +(def ^:dynamic *viewport-min-scale* 1E-6) +(def ^:dynamic *viewport-max-scale* 1E6) + +(def ^:dynamic *viewport* nil) +(def ^:dynamic ^AffineTransform *viewport-transform*) + +(declare scale-viewport!) + +(defrecord ViewportState [transform + fix-x fix-y + last-width last-height + last-anchor-x last-anchor-y]) + +(defn- update-viewport [state content-geom h-align v-align] + (let [w *width* + h *height* + cw (width content-geom) + ch (height content-geom) + ax (anchor-x content-geom h-align cw) + ay (anchor-y content-geom v-align ch) + ax1 (align-x (:last-width state) w h-align) + ay1 (align-y (:last-height state) h v-align) + ax2 (- (:last-anchor-x state) ax) + ay2 (- (:last-anchor-y state) ay) + transform (:transform state) + transform (if (and (zero? ax1) (zero? ay1) + (zero? ax2) (zero? ay2)) + transform + (doto + (AffineTransform/getTranslateInstance ax1 ay1) + (.concatenate transform) + (.translate ax2 ay2)))] + (assoc state + :last-width w + :last-height h + :last-anchor-x ax + :last-anchor-y ay + :transform transform))) + +(defrecord Viewport [content h-align v-align state] + Layer + (render! [layer] + (repaint-on-update layer) + (with-handlers layer + (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 + *viewport-transform* transform] + (with-transform transform + (draw! content 0 0 (width geom) (height geom) false)))) + (:mouse-pressed e + (swap! state assoc + :fix-x (:x-on-screen e) + :fix-y (:y-on-screen e)) + (set-cursor! (Cursor. Cursor/MOVE_CURSOR))) + (:mouse-released e + (set-cursor! (Cursor. Cursor/DEFAULT_CURSOR))) + (:mouse-dragged e + (swap! state + (fn [s] + (assoc s + :transform (pre-translate + (:transform s) + (- (:x-on-screen e) (:fix-x s)) + (- (:y-on-screen e) (:fix-y s))) + :fix-x (:x-on-screen e) + :fix-y (:y-on-screen e)))) + (update layer)) + (:mouse-wheel e + (scale-viewport! + layer + (Math/pow *viewport-scaling-step* (:wheel-rotation e)) + true (:x e) (:y e))))) + (geometry [_] + (geometry content))) + +(def ^:private viewport-initial-state + (->ViewportState + (AffineTransform.) ; transform + 0 0 ; fix-x fix-y + 0 0 ; last-width last-height + 0 0)) + +(defn viewport + "Creates scrollable viewport layer." + ([content] + (viewport content :left :top)) + ([content h-align v-align] + (->Viewport content h-align v-align (atom viewport-initial-state)))) + +(defn- scale-viewport [state vp s relative? x y] + (let [^AffineTransform tr (:transform state) + sx (if relative? s (/ s (.getScaleX tr))) + sy (if relative? s (/ s (.getScaleY tr))) + x (or x (align-x 0 (:last-width state) (:h-align vp))) + y (or y (align-y 0 (:last-height state) (:v-align vp))) + x (- x (* x sx)) + y (- y (* y sy)) + scaled (doto (AffineTransform/getTranslateInstance x y) + (.scale sx sy) + (.concatenate tr)) + sx (.getScaleX scaled) + sy (.getScaleY scaled)] + (if (<= *viewport-min-scale* + (min sx sy) + (max sx sy) + *viewport-max-scale*) + (assoc state + :transform scaled) + state))) + +(defn scale-viewport! + ([viewport s] + (scale-viewport! viewport s true)) + ([viewport s relative?] + (scale-viewport! viewport s relative? nil nil)) + ([viewport s relative? x y] + (swap! (:state viewport) scale-viewport viewport s relative? x y) + (update viewport))) + +(defn reset-viewport! [viewport] + (reset! (:state viewport) viewport-initial-state) + (update viewport)) + +(defn ^AffineTransform viewport-transform [viewport] + (:transform @(:state viewport))) + +(defn- scaling + [width height max-width max-height] + (min (/ max-width width) + (/ max-height height))) + +(defn miniature + "Creates layer that asynchronously renders view of the content + scaled to the specified size." + [content mw mh] + (async-layer + (reify + Layer + (render! [this] + (let [geom (geometry content) + cw (width geom) + ch (height geom) + s (scaling cw ch mw mh)] + (.scale *graphics* s s) + (draw! content + (align-x cw (/ mw s) :center) + (align-y ch (/ mh s) :center) + cw ch))) + (geometry [_] + (->Size mw mh))) + mw mh *miniature-thread-priority*)) + +(defn viewport-miniature + "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] + (repaint-on-update viewport) + (let [geom (geometry (:content viewport)) + s (scaling (width geom) (height geom) m-width m-height) + vp-state @(:state viewport) + {:keys [transform last-width last-height]} @(:state viewport) + ox (align-x (width geom) (/ m-width s) :center) + oy (align-y (height geom) (/ m-height s) :center) + inverse (.createInverse ^AffineTransform transform) + transform (doto (AffineTransform.) + (.scale s s) + (.translate ox oy) + (.concatenate inverse)) + move-vp (fn [state x y] + (let [x (- (/ x s) ox) + y (- (/ y s) oy) + tr (:transform state) + [x y] (transform-point tr x y) + x (- x (/ (:last-width state) 2)) + y (- y (/ (:last-height state) 2))] + (assoc state + :transform (pre-translate tr (- x) (- y))))) + move-vp! (fn [x y] + (swap! (:state viewport) move-vp x y) + (update viewport))] + (with-color :alt-back-color + (.fillRect *graphics* 0 0 *width* *height*)) + (with-transform transform + (with-color :back-color + (.fillRect *graphics* 0 0 last-width last-height))) + (with-handlers l + (draw! miniature) + (:mouse-pressed e (move-vp! (:x e) (:y e))) + (:mouse-dragged e (move-vp! (:x e) (:y e)))) + (with-transform transform + (with-color :border-color + (.drawRect *graphics* 0 0 last-width last-height)))))))