I have a couple of questions about the following code:
(import
'(java.awt Color Graphics Dimension)
'(java.awt.image BufferedImage)
'(javax.swing JPanel JFrame))
(def width 900)
(def height 600)
(defn render
[g]
(let [img (new BufferedImage width height
(. BufferedImage TYPE_INT_ARGB))
bg (. img (getGraphics))]
(doto bg
(.setColor (. Color white))
(.fillRect 0 0 (. img (getWidth)) (. img (getHeight)))
(.setColor (. Color red))
(.drawOval 200 200 (rand-int 100) (rand-int 50)))
(. g (drawImage img 0 0 nil))
(. bg (dispose))
))
(def panel (doto (proxy [JPanel] []
(paint [g] (render g)))
(.setPreferredSize (new Dimension
width
height))))
(def frame (doto (new JFrame) (.add panel) .pack .show))
(def animator (agent nil))
(defn animation
[x]
(send-off *agent* #'animation)
(. panel (repaint))
(. Thread (sleep 100)))
(send-off animator animation)
In the animation function - why is #' used before animation in send-off?
Why does send-off at the start of animation function work? Shouldn't it just go the start of animation function again and never execute the repaint and sleep methods?
Is there any disadvantage, as compared to the original, in writing the animation function as:
(defn animation
[x]
(. panel (repaint))
(. Thread (sleep 100))
(send-off *agent* animation))
In the animation function - why is #' used before animation in send-off?
To demonstrate Clojure's dynamic nature.
The form #'animation is a Var, one of Clojure's mutable reference types. The defn macro creates a Var. For convenience, invoking a Var which refers to a function is the same as invoking the function itself. But a Var, unlike a function, can change! We could redefine #'animation at the Clojure REPL and immediately see the effects.
Using (send-off *agent* #'animation) forces Clojure to look up of the current value of the #'animation Var every time. If the code had used (send-off *agent* animation) instead, then Clojure would look up the value only once, and it would not be possible to change the animation function without stopping the loop.
1. This is a little unclear to me as well but seems to be a design decision by Rich. If you notice:
user=> (defn x [y] (+ y 2))
#'user/x
user=> ((var x) 3)
5
If a var is in the function/macro location, it will eventually resolve to the function or macro.
2. One important thing to understand here is the agent model. Agents can be thought of as a worker that operates on a single mutable cell. There is a queue of work (a queue of functions) for that agent to do. send-off and send add work to that queue. Since send-off is only adding work to the queue, it immediately returns. Since the agent only executes the functions serially, the first animation call must finish before executing the next one. Therefore, you achieve basically the same thing regardless of putting send-off first or last.
3. There should be no noticeable difference between the two.
Related
What is a simple way to delay :on-click event to see first if :on-double-click event is triggered?
[:div {:on-click (fn [e]
;; listen to double-click event, within 500ms,
;; if so on-double-click-fn,
;; if not, on-click-fn
)
:on-double-click (fn [e]
;; on-click-fn
)}]
Thanks!
First attempt:
(defn sleep [timeout]
(let [maxtime (+ (.getTime (js/Date.)) timeout)]
(while (< (.getTime (js/Date.)) maxtime))))
[:div {:on-click (fn [e] (sleep 500) (print "single-clicked"))
:on-double-click (fn [e] (print "double-clicked"))}]
Second attempt:
(def state (atom {:click-count 0}))
(defn handle-click [e click-fns-map]
(swap! state update :click-count inc)
(sleep 500)
(let [click-count (get #state :click-count)]
(swap! state assoc :click-count 0)
(cond
(= click-count 1) ((:on-single-click click-fns-map) e)
(> click-count 1) ((:on-double-click click-fns-map) e)))))
[:div
{:on-mouse-down
(fn [e]
(handle-click e {:on-single-click #(print "single-click")
:on-double-click #(print "double-click")}))}]
;;=> "single-click"
;;=> "single-click"
EDIT:
Based on Taylor Wood's answer, here is an abstraction that wraps html element args and overwrites :on-click and :on-double-click for you.
(defn ensure-single-double-click
[{:keys [on-click on-double-click] :as args}]
(let [waiting? (atom false)]
(merge
args
{:on-click (fn [e]
(when (compare-and-set! waiting? false true)
(js/setTimeout
(fn [] (when #waiting?
(on-click %)
(reset! waiting? false)))
300)))
:on-double-click (fn [e]
(reset! waiting? false)
(on-double-click %))})))
[:a (ensure-single-double-click
{:style {:color "blue"} ;; this works
:on-click #(print "single-click")
:on-double-click #(print "double-click")})
"test"]
Here's one way to do it:
(defn slow-link [text single-click-fn double-click-fn]
(let [waiting? (atom false)]
[:a {:on-click #(when (compare-and-set! waiting? false true)
(js/setTimeout (fn [] (when #waiting?
(single-click-fn %)
(reset! waiting? false)))
500))
:on-double-click #(do (reset! waiting? false)
(double-click-fn %))}
text]))
[slow-link "Test" #(prn "single-click") #(prn "double-click")]
This starts a JS timer that will execute a given function after 500ms. The function checks to see if we're still waiting? on another click when the timeout elapses, and if so it executes single-click-fn. If we're not waiting? that means the double-click event has already happened, reset waiting? to false, and called double-click-fn.
The :on-click handler uses compare-and-set to only take action if we're not already in a waiting? state, avoiding some racy behavior for triple/quadruple clicks, etc.
Taylor Wood's answer comes close, but compare-and-set! did not protect me from triple clicks (or even more clicks!), because if, say, three clicks happen within 500ms, waiting? will be set to false again the third time around and a second timeout is scheduled. I think this means a new timeout is technically scheduled upon every odd numbered click.
Luckily, the click event comes with a property called detail, which is set to the number of consecutive clicks. I found it here. The following should solve the OP's problem without allowing for triple clicks:
:on-click
(fn [e]
; This prevents the click handler from running
; a second, third, or other time.
(when (-> e .-detail (= 1))
(reset! waiting? true))
; Wait an appropriate time for the double click
; to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen!
(when #waiting?
(single-click-fn %)
(reset! waiting? false)))
500)))
:on-double-click #(do (reset! waiting? false)
(double-click-fn %))
As exotic as triple clicks may sound, they do serve a purpose: to select an entire line of text, so I wouldn't want the user to miss out on that ability.
The rest is an addendum for those interested in making text selection work on elements that are listening for a single click. I came here from Google looking for how to do this, so maybe it helps someone.
A challenge I ran into is that my application dictated that the user can perform a double click without releasing the second click at first; a bit like "one-and-a-half-clicks". Why? Because I am listening for clicks on a span, and the user may well perform a double click to select a whole word, but then keep the second click pressed and drag his mouse in order to select additional words next to the original one. The problem is that the double click event handler only fires after the user releases the second click, and so waiting? is not set to false on time.
I solved this using an :on-mouse-down handler:
:on-click
(fn [e]
; This prevents the click handler from running
; a second, third, or other time.
(when (-> e .-detail (= 1))
(reset! waiting? true))
; Wait an appropriate time for the double click
; to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen!
(when #waiting?
(single-click-fn %)
(reset! waiting? false)))
500)))
:on-double-click #(double-click-fn %)
:on-mouse-down #(reset! waiting? false)
Remember that the :on-click and :on-double-click handlers only fire upon release (and that the handlers fire in the order of mouse-down, click, double-click), which gives the :on-mouse-down handler an opportunity to set waiting? to false, which is needed if the user hasn't released the mouse yet, because he won't have triggered the :on-double-click event handler.
Note that now you don't even need to set waiting? to false in your double click handler anymore, because the mouse down handler has already done that by the time the double click handler is run.
Lastly, in my particular application, it so happens that a user may want to select text without triggering the click handler. In order to do this, he will click on a piece of text, and, without releasing the mouse, drag the cursor to select more text. When the cursor is released, that should not trigger the click event. So I had to additionally track whether the user had made a selection at any time before he let go of the mouse (a sort of "half-click"). For this case, I had to add a couple more things to the component's state (a boolean atom called selection-made? and an event handler function called selection-handler). This case relies on detection of selection, and, since a selection is made on double click, does not need to check the event's detail property anymore to protect against triple or more clicks.
The whole solution looks like this (but keep in mind that this is specifically for text elements, so just an addition to what the OP asked for):
(defn component
[]
(let [waiting? (r/atom false)
selection-made? (r/atom false)
selection-handler
(fn []
(println "selection-handler running")
(when (seq (.. js/document getSelection toString))
(reset! selection-made? true)))]
(fn []
[:div
; For debugging
[:pre {} "waiting? " (str #waiting?)]
[:pre {} "selection-made? " (str #selection-made?)]
; Your clickable element
[:span
{:on-click
(fn [e]
(println "click handler triggered")
; Remove the selection handler in any case because
; there is a small chance that the selection handler
; was triggered without selecting any text (by
; holding down the mouse on the text for a little
; while without moving it).
(.removeEventListener js/document "selectionchange" selection-handler)
(if #selection-made?
; If a selection was made, only perform cleanup.
(reset! selection-made? false)
; If no selection was made, treat it as a
; simple click for now...
(do
(reset! waiting? true)
; Wait an appropriate amount of time for the
; double click to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen! The mouse-down
; handler would have set waiting? to false
; by now if it had been clicked a second time.
; (Remember that the mouse down handler runs
; before the click handler since the click handler
; runs only once the mouse is released.
(when #waiting?
(single-click-fn e)
(reset! waiting? false)))
500))))
:on-mouse-down
(fn [e]
; Set this for the click handler in case a double
; click is happening.
(reset! waiting? false)
; Only run this if it is a left click, or the event
; listener is not removed until a single click on this
; segment is performed again, and will listen on
; every click everywhere in the window.
(when (-> e .-button zero?)
(js/console.log "mouse down handler running")
(.addEventListener js/document "selectionchange" selection-handler)))
:on-double-click #(double-click-fn %)}
some content here]])))
Consider the following reagent component. It uses a ref function, which updates a local state atom, based on the real size of a span element. This is done in order to re-render the component displaying its own size
(defn show-my-size-comp []
(let [size (r/atom nil)]
(fn []
(.log js/console "log!")
[:div
[:span {:ref (fn [el]
(when el (reset! size (get-real-size el))))}
"Hello, my size is:" ]
[:span (prn-str #size)]])))
If the implementation of get-real-size returns a vector, the log message is printed constantly, meaning the component unnecessarily being re-rendered all the time. If it returns just a number or a string, the log appears only twice - as intended in this scenario.
What's the reason for this? Is it maybe that updating an clojure script atom with a new vector (containing the same values though) internally means putting another JavaScript object there, thus changing the atom? Whereas putting a value produces no observable change? Just speculation...*
Anyways - for the real use case, saving the size of the span in a vector would certainly better.. Are there ways to achieve this?
I cam across this, when trying to enhance the answer given in this question.
* since in JS: ({} === {}) // false
I think I have an answer for why vector behaves differently from string/number. Reagent counts a reagent atom as "changed" (and thus updates a component that depends on it) when identical? returns false between the old and the new values. See the subhead "changed?" in this tutorial:
For ratoms, identical? is used (on the value inside the ratom) to determine if a new value has changed with regard to an old value.
However, it turns out that identical? behaves differently for vectors and for strings/ints. If you fire up either a clj or a cljs repl, you'll see that:
(identical? 1 1)
;; true
(identical? "a" "a")
;; true
(identical? [1] [1])
;; false
(identical? ["a"] ["a"])
;; false
If you look at what identical? does here, you'll see that it tests if its arguments are the same object. I think the underlying internal data representation is such that, in clojure, "a" is always the same object as itself, whereas two vectors containing the same value are not the same object as one another.
Confirmation: with ordinary rather than reagent atoms, we can see that string identity is preserved across atom resets, while vector identity is not.
(def a1 (atom "a"))
(let [aa #a1] (reset! a1 "a") (identical? aa #a1))
;; true
(def a2 (atom ["a"]))
(let [aa #a2] (reset! a2 ["a"]) (identical? aa #a2))
;; false
You can work around the problem with a not= check:
(fn [el]
(when el
(let [s (get-real-size el)]
(when (not= s #size)
(reset! size s)))))
I'm not sure what the reason is for why vectors should differ from other values.
Its rerendering like it is supposed to based on how its written. You are derefing the atom in the same function as you are resetting it. I always keep these separate.
(defn span-size [size]
[:span (prn-str #size)])
(defn show-my-size-comp []
(let [size (r/atom nil)]
(fn []
(.log js/console "log!")
[:div
[:span {:ref (fn [el]
(when el (reset! size (get-real-size el))))}
"Hello, my size is:"]
[span-size]])))
I'm trying to make a game using clojure and libgdx. The information at Using Libgdx with Clojure was immensely helpful but now I'm stuck on the simple step of displaying a sprite. In my screen I have this
(def main-screen
(let [stage (atom nil)]
(proxy [Screen] []
(show []
(reset! stage (Stage.))
(let [style (Label$LabelStyle. (BitmapFont.) (Color. 0 1 1 1))
label (Label. "Hello world!" style)]
(.addActor #stage label)))
(render [delta]
(.glClearColor (Gdx/gl) 0 1 0 1)
(.glClear (Gdx/gl) GL20/GL_COLOR_BUFFER_BIT)
(doto (SpriteBatch.)
(. begin)
(. draw (ship-texture) 10 10)
(. end))
(doto #stage
(.act delta)
(.draw)))
(dispose[])
(hide [])
(pause [])
(resize [w h])
(resume []))))
and that references a simple function above
(defn ship-texture [] (Texture. (.internal Gdx/files "AShip.PNG")))
But no matter where I put the file libgdx can't seem to find it. I've placed it in resources, src, assets and the base directory. I feel like I must be misssing something tremendously simple.
I have a threaded looping sound clip:
(def f
(future
(let [sound-file (java.io.File. "/path/to/file.wav")
sound-in (javax.sound.sampled.AudioSystem/getAudioInputStream sound-file)
format (.getFormat sound-in)
info (javax.sound.sampled.DataLine$Info. javax.sound.sampled.Clip format)
clip (javax.sound.sampled.AudioSystem/getLine info)]
(.open clip sound-in)
(.loop clip javax.sound.sampled.Clip/LOOP_CONTINUOUSLY))))
The problem is that when I try to kill the thread:
(future-cancel f)
it doesn't stop the clip, which plays forever. I found that the only way to stop it was to call (.stop clip) explicitly. My question: what would be the best/idiomatic way of doing this? I'm pretty new to Clojure, so I only experimented with future so far, but maybe an agent would be better suited in this context?
Update: given that the .loop function is non-blocking (as was discussed below), I simplified my design by getting rid of the initial future:
(defn play-loop [wav-fn]
(let [sound-file (java.io.File. wav-fn)
sound-in (javax.sound.sampled.AudioSystem/getAudioInputStream sound-file)
format (.getFormat sound-in)
info (javax.sound.sampled.DataLine$Info. javax.sound.sampled.Clip format)
clip (javax.sound.sampled.AudioSystem/getLine info)]
(.open clip sound-in)
(.loop clip javax.sound.sampled.Clip/LOOP_CONTINUOUSLY)
clip))
along with a controlling atom:
(def ^:dynamic *clip* (atom nil))
with which I start the loop:
(when (nil? #*clip*)
(reset! *clip* (play-loop "/path/to/file.wav")))
and stop it:
(when #*clip*
(future (.stop #*clip*) ; to avoid a slight delay caused by .stop
(reset! *clip* nil)))
You can try something like this:
(def f
(future
(let [sound-file (java.io.File. "/path/to/file.wav")
sound-in (javax.sound.sampled.AudioSystem/getAudioInputStream sound-file)
format (.getFormat sound-in)
info (javax.sound.sampled.DataLine$Info. javax.sound.sampled.Clip format)
clip (javax.sound.sampled.AudioSystem/getLine info)
stop (fn [] (.stop clip))]
(.open clip sound-in)
(.loop clip javax.sound.sampled.Clip/LOOP_CONTINUOUSLY)
stop)))
(def stop-loop #f)
(stop-loop)
While preparing an answer to another question, I created one for myself. Consider the following short program.
(ns net.dneclark.JFrameAndTimerDemo
(:import (javax.swing JLabel JButton JPanel JFrame Timer))
(:gen-class))
(defn timer-action [label counter]
(proxy [java.awt.event.ActionListener] []
(actionPerformed
[e]
(.setText label (str "Counter: " (swap! counter inc))))))
(defn timer-fn []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
timer (Timer. 1000 (timer-action label counter))
panel (doto (JPanel.)
(.add label))]
(.start timer)
(doto (JFrame. "Timer App")
(.setContentPane panel)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setLocation 300 300)
(.setSize 200 200)
(.setVisible true))))
(defn -main []
(timer-fn))
In the action listener, 'timer-action', the value of the 'counter' argument is altered. The actual variable is declared in the 'timer-fn' function, but is altered in the listener. In my earlier experience with languages like Pascal, I would have considered 'counter' to be passed by reference. Is that the case here or is this an example of a closure? Something else?
Thanks for the help.
Yes, it's a closure. The lexical context of the handler function definition is preserved, and when it is later invoked it can access and update variables that "live" there.
I'm not sure how to answer the question, "why?" other than to point out that it's simply the way the language is defined to work.