"Combines multiple borders into one. The first argument will be the outermost,
and so forth.
(defn instructions
"Turns strings, or other objects, using str, into instruction text and returns
a component suitable for displaying these in a Swing UI."
[& strings]
(doto (JTextArea. (apply str strings))
(.setEditable false)
(.setLineWrap true)
(.setWrapStyleWord true)
(.setColumns *instruction-columns*)
(.setOpaque false)
(.setBorder (combine-borders
(lowered-bevel-border)
(empty-border *instruction-inset*)))))
which produces a component that displays essentially a multi-line label with a 10-pixel space around its perimeter; as the name says, useful for putting instruction strings in a dialog box or whatever without worrying about where to put manual line breaks. I've used this in wizards, which often have large chunks of textual instructions as well as a few form fields and back, next, cancel buttons.
Naturally, other code does things like take a passed-in JPanel and pop up a JDialog with a specified title, the JPanel front and center, and OK and Cancel buttons or similar at the bottom, with a supplied block of code to execute when OK is pressed and the behavior that both Cancel and the close button close and dispose the JDialog without doing anything; the macro for this expands into code that evaluates to nil on cancel and whatever the OK code returns on OK.
It can take as little as ten minutes to slap something like one of these things together and another ten to test it. With these kinds of reusable UI fragments, both "framed" elements (like jbutton and instructions generate) and "framing" ones (like the JDialog generator macro), slapping together a GUI in code becomes easy.
You might have noticed indications of my preference for using nested BoxLayouts to build things. I like this because BoxLayout respects a component's preferred size and nested BoxLayouts tend to be well behaved when subjected to window or component resizings. GridBagLayout would be the only other contender for flexibility and size-control of components, but BoxLayout nesting lends itself much better to a compositional style of assembly of the GUI component tree, and thus to a functional style of code and to use of reusable fragments like the above examples.
Last but not least, do keep in mind the need for Swing actions to take place on the EDT. SwingWorker macro in the works, and someone else posted code here a while ago that I shamelessly copied that invokes actions on the EDT. I may have made some modifications:
(defmacro do-on-edt
"Evaluates body on the event dispatch thread. Does not block. Returns nil."
[& body]
`(SwingUtilities/invokeLater (fn [] ~@body)))
(defmacro get-on-edt
"Evaluates body on the event dispatch thread, and returns whatever body
evaluates to. Blocks until body has been evaluated."
[& body]
`(let [ret# (LinkedBlockingDeque.)]
(SwingUtilities/invokeLater (fn [] (.put ret# (do ~@body))))
(.take ret#)))
(defmacro future-on-edt
"Evaluates body on the event dispatch thead and returns a future that will
eventually hold the result of evaluating that body."
[& body]
`(future (get-on-edt ~@body)))
The REPL does NOT run on the EDT, so GUI code tests from the REPL should be wrapped in do-on-edt. That returns nil; the other two furnish the result of evaluating the body code back to the calling thread. All but get-on-edt can be invoked from the EDT safely; get-on-edt will deadlock if called on the EDT, since invokeLater won't do anything until pending events are processed, but one of those pending events will be the code that's blocking on the LinkedBlockingDeque waiting for the invokeLater. It can be called from SwingWorkers, futures, and agents safely though, and directly at the REPL. I should make get-on-edt smarter actually:
(defmacro get-on-edt
"Evaluates body on the event dispatch thread, and returns whatever body
evaluates to. Blocks until body has been evaluated."
[& body]
`(if (SwingUtilities/isEventDispatchThread)
(do ~@body)
(let [ret# (LinkedBlockingDeque.)]
(SwingUtilities/invokeLater (fn [] (.put ret# (do ~@body))))
(.take ret#))))
There, fixx0red.
I have a .clj file full of the most reusable bits of Swing interop code like these. The (ns ...) at the top is chock full of Swing classes. The code that uses this .clj file tends to only need to import one or two, on top of :use-ing that .clj file, so building up a load of little utilities like these in a file even saves on big complex (ns ...) work at the top of your other UI-related source files.
By the way, feel free to use all of the above as public domain code. Well, unless the earlier posted of the foo-on-edt functions complains anyway, but I think he offered them in the same spirit, and I'm not sure code snippets that short are copyrightable anyway.