view src/kryshen/indyvon/core.clj @ 45:33d836041cef

Update dependencies.
author Mikhail Kryshen <mikhail@kryshen.net>
date Mon, 19 Jul 2010 15:45:16 +0400
parents 7d67064f0880
children
line source
1 ;;
2 ;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net>
3 ;;
4 ;; This file is part of Indyvon.
5 ;;
7 (ns kryshen.indyvon.core
8 (:import
9 (java.awt Graphics2D Component Color Font AWTEvent)
10 (java.awt.event MouseListener MouseMotionListener)
11 (java.awt.font FontRenderContext)))
13 (def ^Graphics2D *graphics*)
14 (def ^FontRenderContext *font-context*)
15 (def ^Component *target*)
16 (def *bounds*)
17 (def *clip*)
18 (def *update*)
19 (def *event-dispatcher*)
21 (defrecord Theme [fore-color back-color alt-back-color border-color font])
23 (defn default-theme []
24 (Theme. Color/BLACK Color/LIGHT_GRAY Color/WHITE
25 Color/BLUE (Font. "Sans" Font/PLAIN 12)))
27 (def *theme* (default-theme))
29 (defrecord Location [x y])
30 (defrecord Size [width height])
31 (defrecord Bounds [x y width height])
33 (defprotocol Layer
34 "Basic UI element."
35 (render! [this])
36 (layer-size [this]))
38 ;; TODO: modifiers
39 (defrecord MouseEvent [id when x y x-on-screen y-on-screen button])
41 (defprotocol EventDispatcher
42 (listen! [this ^Component component]
43 "Listen for events on the specified AWT Component.")
44 (create-dispatcher [this handle handlers]
45 "Returns new event dispatcher associated with the specified event
46 handlers (an event-id -> handler-fn map). Handle is used to
47 match the contexts between commits.")
48 (commit [this]
49 "Apply the registered handlers for event processing."))
51 (defprotocol Anchored
52 "Provide anchor point for Layers. Used by viewport."
53 (anchor [this h-align v-align]
54 "Anchor point: [x y], h-align could be :left, :center or :right,
55 v-align is :top, :center or :bottom"))
57 ;; Default implementation of Anchored for any Layer.
58 (extend-protocol Anchored
59 kryshen.indyvon.core.Layer
60 (anchor [this h-align v-align]
61 (if (and (= h-align :left)
62 (= v-align :top))
63 (Location. 0 0)
64 (let [size (layer-size this)]
65 (Location.
66 (case h-align
67 :top 0
68 :center (/ (:width size) 2)
69 :right (:width size))
70 (case v-align
71 :left 0
72 :center (/ (:height size) 2)
73 :bottom (:height size)))))))
75 (defn- ^Graphics2D make-graphics [^Graphics2D graphics x y w h]
76 (.create graphics x y w h))
78 (defn- ^Graphics2D apply-theme [^Graphics2D graphics theme]
79 (doto graphics
80 (.setColor (:fore-color theme))
81 (.setFont (:font theme))))
83 (defn intersect
84 ([b1 b2]
85 (let [x1 (:x b1)
86 y1 (:y b1)
87 x2 (:x b2)
88 y2 (:y b2)]
89 (intersect x1 y1 (+ x1 (:width b1)) (+ y1 (:height b1))
90 x2 y2 (+ x2 (:width b2)) (+ y2 (:height b2)))))
91 ([x11 y11 x12 y12, x21 y21 x22 y22]
92 (let [x1 (max x11 x21)
93 y1 (max y11 y21)
94 x2 (min x12 x22)
95 y2 (min y12 y22)]
96 (Bounds. x1 y1 (- x2 x1) (- y2 y1)))))
98 (defn ^Graphics2D create-graphics
99 ([]
100 (create-graphics 0 0 (:width *bounds*) (:height *bounds*)))
101 ([x y w h]
102 (apply-theme (.create *graphics* x y w h) *theme*)))
104 (defmacro with-bounds [x y w h & body]
105 `(let [bounds# (Bounds. (+ ~x (:x *bounds*))
106 (+ ~y (:y *bounds*))
107 ~w ~h)
108 clip# (intersect bounds# *clip*)]
109 (when (and (pos? (:width clip#)) (pos? (:height clip#)))
110 (let [graphics# (create-graphics ~x ~y ~w ~h)]
111 (try
112 (binding [*bounds* bounds#
113 *clip* clip#
114 *graphics* graphics#]
115 ~@body)
116 (finally
117 (.dispose graphics#)))))))
119 (defmacro with-handlers* [handle handlers & body]
120 `(binding
121 [*event-dispatcher*
122 (create-dispatcher *event-dispatcher* ~handle ~handlers)]
123 ~@body))
125 (defmacro with-handlers
126 "specs => (:event-id name & handler-body)*
128 Execute form with the specified event handlers."
129 [handle form & specs]
130 `(with-handlers* ~handle
131 ~(reduce (fn [m spec]
132 (assoc m (first spec)
133 `(fn [~(second spec)]
134 ~@(nnext spec)))) {}
135 specs)
136 ~form))
138 (defn with-theme* [theme f & args]
139 (apply with-bindings* {#'*theme* (merge *theme* theme)}
140 f args))
142 (defmacro with-theme [theme & body]
143 `(binding [*theme* (merge *theme* ~theme)]
144 ~@body))
146 (defmacro with-color [color & body]
147 `(let [color# (.getColor *graphics*)]
148 (try
149 (.setColor *graphics* ~color)
150 ~@body
151 (finally
152 (.setColor *graphics* color#)))))
154 (defn- geometry-vec [geometry]
155 (if (vector? geometry)
156 geometry
157 [(:x geometry) (:y geometry) (:width geometry) (:height geometry)]))
159 (defn draw!
160 ([layer]
161 (let [graphics (create-graphics)]
162 (try
163 (binding [*graphics* graphics]
164 (render! layer))
165 (finally
166 (.dispose graphics)))))
167 ([layer x y]
168 (let [size (layer-size layer)]
169 (draw! layer x y (:width size) (:height size))))
170 ([layer x y width height]
171 (with-bounds x y width height
172 (render! layer))))
174 (defn draw-anchored!
175 "Draw with location relative to the anchor point."
176 ([layer h-align v-align x y]
177 (let [anchor (anchor layer h-align v-align)]
178 (draw! layer (- x (:x anchor)) (- y (:y anchor)))))
179 ([layer h-align v-align x y w h]
180 (let [anchor (anchor layer h-align v-align)]
181 (draw! layer (- x (:x anchor)) (- y (:y anchor)) w h))))
183 ;;
184 ;; EventDispatcher implementation
185 ;;
187 (def awt-events
188 {java.awt.event.MouseEvent/MOUSE_CLICKED :mouse-clicked
189 java.awt.event.MouseEvent/MOUSE_DRAGGED :mouse-dragged
190 java.awt.event.MouseEvent/MOUSE_ENTERED :mouse-entered
191 java.awt.event.MouseEvent/MOUSE_EXITED :mouse-exited
192 java.awt.event.MouseEvent/MOUSE_MOVED :mouse-moved
193 java.awt.event.MouseEvent/MOUSE_PRESSED :mouse-pressed
194 java.awt.event.MouseEvent/MOUSE_RELEASED :mouse-released})
196 (defrecord DispatcherNode [handle handlers parent bounds bindings]
197 EventDispatcher
198 (listen! [this component]
199 (listen! parent component))
200 (create-dispatcher [this handle handlers]
201 (create-dispatcher parent handle handlers))
202 (commit [this]
203 (commit parent)))
205 (defn- make-node [handle handlers]
206 (DispatcherNode. handle handlers *event-dispatcher* *clip*
207 (get-thread-bindings)))
209 (defn- assoc-cons [m key val]
210 (assoc m key (cons val (get m key))))
212 (defn- add-node [tree node]
213 (assoc-cons tree (:parent node) node))
215 (defn- inside?
216 ([x y bounds]
217 (inside? x y (:x bounds) (:y bounds)
218 (:width bounds) (:height bounds)))
219 ([px py x y w h]
220 (and (>= px x)
221 (>= py y)
222 (< px (+ x w))
223 (< py (+ y h)))))
225 (defn- under-cursor
226 "Returns a vector of child nodes under cursor."
227 [x y tree node]
228 (some #(if (inside? x y (:bounds %))
229 (conj (vec (under-cursor x y tree %)) %))
230 (get tree node)))
232 (defn- remove-all [coll1 coll2 pred]
233 (filter #(not (some (partial pred %) coll2)) coll1))
235 (defn- translate-mouse-event [^java.awt.event.MouseEvent event x y id]
236 (MouseEvent. id (.getWhen event)
237 (- (.getX event) x) (- (.getY event) y)
238 (.getXOnScreen event) (.getYOnScreen event)
239 (.getButton event)))
241 (defn- translate-and-dispatch
242 ([nodes first-only ^java.awt.event.MouseEvent event]
243 (translate-and-dispatch nodes first-only
244 event (awt-events (.getID event))))
245 ([nodes first-only event id]
246 (if-let [node (first nodes)]
247 (if-let [handler (get (:handlers node) id)]
248 (do
249 (with-bindings* (:bindings node)
250 handler
251 (translate-mouse-event event
252 (-> node :bounds :x) (-> node :bounds :y) id))
253 (if-not first-only
254 (recur (rest nodes) false event id)))
255 (recur (rest nodes) first-only event id)))))
257 (defn- dispatch-mouse-motion
258 "Dispatches mouse motion events."
259 [hovered-ref tree root ^java.awt.event.MouseEvent event]
260 (let [x (.getX event)
261 y (.getY event)
262 [hovered hovered2] (dosync
263 [@hovered-ref
264 (ref-set hovered-ref
265 (under-cursor x y tree root))])
266 pred #(= (:handle %1) (:handle %2))
267 exited (remove-all hovered hovered2 pred)
268 entered (remove-all hovered2 hovered pred)
269 moved (remove-all hovered2 entered pred)]
270 (translate-and-dispatch exited false event :mouse-exited)
271 (translate-and-dispatch entered false event :mouse-entered)
272 (translate-and-dispatch moved true event :mouse-moved)))
274 (defn- dispatch-mouse-button
275 [picked-ref hovered-ref ^java.awt.event.MouseEvent event]
276 (let [id (awt-events (.getID event))
277 hovered (if (= id :mouse-pressed)
278 (dosync (ref-set picked-ref @hovered-ref))
279 @hovered-ref)]
280 (translate-and-dispatch hovered true event id)))
282 (defn root-event-dispatcher []
283 (let [tree-r (ref {}) ; register
284 tree (ref {}) ; dispatch
285 hovered (ref '())
286 picked (ref '())]
287 (reify
288 EventDispatcher
289 (listen! [this component]
290 (doto component
291 (.addMouseListener this)
292 (.addMouseMotionListener this)))
293 (create-dispatcher [this handle handlers]
294 (let [node (make-node handle handlers)]
295 (dosync (alter tree-r add-node node))
296 node))
297 (commit [this]
298 (dosync (ref-set tree @tree-r)
299 (ref-set tree-r {})))
300 MouseListener
301 (mouseEntered [this event]
302 (dispatch-mouse-motion hovered @tree this event))
303 (mouseExited [this event]
304 (dispatch-mouse-motion hovered @tree this event))
305 (mouseClicked [this event]
306 (dispatch-mouse-button picked hovered event))
307 (mousePressed [this event]
308 (dispatch-mouse-button picked hovered event))
309 (mouseReleased [this event]
310 (translate-and-dispatch @picked true event))
311 ;;(dispatch-mouse-button picked hovered event))
312 MouseMotionListener
313 (mouseDragged [this event]
314 (translate-and-dispatch @picked true event))
315 (mouseMoved [this event]
316 (dispatch-mouse-motion hovered @tree this event)))))
318 ;;
319 ;; ИДЕИ:
320 ;;
321 ;; Контекст: биндинги или запись?
322 ;;
323 ;; Установка обработчиков (в контексте слоя):
324 ;;
325 ;; (listen
326 ;; (:mouse-entered e
327 ;; ...)
328 ;; (:mouse-exited e
329 ;; ...))
330 ;;
331 ;; Не надо IMGUI.
332 ;; Построение сцены путем декорирования слоев:
333 ;;
334 ;; (listener
335 ;; (:action e (println e))
336 ;; (:mouse-dragged e (println e))
337 ;; (theme :font "Helvetica-14"
338 ;; (vbox
339 ;; (button (text-layer "Button 1"))
340 ;; (button (text-layer "Button 2")))))
341 ;;