# Cleanly preventing flashing an element from corrupting future flashes

For a Seesaw (Swing) app I'm writing, I need to be able to flash a component for a certain duration. Here's the basic function I came up with that accomplishes this:

; Note, this isn't what I want reviewed. See below.
(defn flash-property [component prop-key flash-value duration]
(let [old-value (sc/config component prop-key)]
(sc/config! component prop-key flash-value)

(sc/timer (fn [_]
(sc/config! component prop-key old-value))
:initial-delay duration, :repeats? false)))


It reads the current property value into old-value, sets the new value, then in a timer reverts the old value after duration many milliseconds. If I wanted to create a button that flashes for half a second when pressed, I could write:

(let [btn (sc/button :text "Button")]
(sc/listen btn
:action (fn [_] (flash-property btn :background :red 500)))

btn)


I had a problem that if you start a new flash before the old one has finished, old-value becomes the flash color from the previous flash, and the component gets stuck as the flash color.

To get around this, I had to rely on a "static variable" that keeps track of what is currently being flashed. I tried to extract as much as I could out of the decider function, but I'm still unhappy with it. The atom, while in a restricted scope, still feels like a hack to cover up a bad design. I've never used a let at the top level before, and it feels kind of dirty.

Of course, I could create some kind of a "flash handler" that gets passed back and forth and keeps track of what's being flashed, but that would be it's own flavour of mess. Then the caller would need to maintain it somehow. Considering the flash gets triggered in a button handler, I'd probably have to maintain an atom there anyways.

Can anyone see how to cleanly avoid the "static variable" flashing?-atom?

(ns pet-redo.full-ui.helpers
(:require [seesaw.core :as sc]))

(defn flash-property
"Alters the property at prop-key to flash-value for duration-many milliseconds,
then reverts the property back to the original value.

Will break if a new flash is started before a previous one ends."
[component prop-key flash-value duration component-done-callback]
(let [old-value (sc/config component prop-key)]
(sc/config! component prop-key flash-value)

(sc/timer (fn [_]
(sc/config! component prop-key old-value)
(component-done-callback))
:initial-delay duration, :repeats? false)))

(let [flashing?-atom (atom #{})]
(defn maybe-flash-background!
"Alters the property at prop-key to flash-value for duration-many milliseconds,
then reverts the property back to the original value.

Does nothing if the element is already being flashed.

Returns whether or not a flash was triggered."
[component flash-color duration]
(when-not (@flashing?-atom component)
(swap! flashing?-atom conj component)

(flash-property component :background flash-color duration
#(swap! flashing?-atom disj component))

true)))