Every complex GUI uses message dialogs. When using Clojure Swing interop, you will use the
JOptionPane for your message dialogs. Two keys to remember when using
JOptionPane s, is that they should always be called from within the Event Dispatch Thread (EDT). Sometimes the
JOptionPane is shown by an
ActionListener that is already on the EDT, and sometimes you need to call
invokeAndWait , because your current thread is not the EDT.
The other key point to remember with
JOptionPane s is that you can associate the message dialog with a frame, or set the frame to
nil . If a
JOptionPane ‘s frame is set to
nil , then the message dialog will center itself on the screen when it displays. If a
JFrame is provided to the
JOptionPane , then the message dialog will center itself on the
JFrame it is associated with.
Here is a basic example that provides a button to click, and that displays a plain
JOptionPane with a message. The
ActionListener is called from the EDT, so the
JOptionPane is being called from the EDT.
When developing Swing GUIs for Clojure, understanding and using
SwingUtilities/invokeLater is crucial.
Event Dispatch Thread
Don’t confuse “thread” in this case with Clojure threading macros such as
--> . Threading here refers to concurrency.
Swing GUI components are not safe to access concurrently. If two threads modify the same Swing component at the same time, the result is unpredictable. Java solved this problem by introducing the event dispatch thread. All modification Swing displays and component values should happen on the event dispatch thread.
There is only one event dispatch thread. You access it by creating a Java Runnable and passing it to
SwingUtilities/invokeLater . Lucky for us, all Clojure functions are Java Runnables. Here’s the general format.
;; Do some Swing changes here
Remember, ALL the screen rendering and modifications to your Swing components happen on the event dispatch thread. That means, if you tie up the event dispatch thread doing something time consuming, your app will appear to hang. This creates a horrible user experience. Any runnables you create should complete quickly.
On a somewhat related note, any time a Swing component alerts you to an event or a value change, you will want to hand back that thread immediately and use a new thread to do any long tasks associated with the notifications.