1

I'd like to have an application that creates a fixed set of Swing objects like instances of Font and Color in a well-defined/central/application-wide location, say GuiFonts.java and GuiColors.java, respectively.

In this way, my application can reference things like: GuiFonts.SMALL_FONT and GuiColors.BACKGROUND, and I can make changes in one place if I should later decide that "small font" means 8 point, instead of 10 point, or that the background should be #FFEF88, instead of #AAAAFFF, for example.

So, how should I write these defaults? In particular, how should I write them to ensure that they are correctly initialized with respect to Swing Event Dispatch Thread (EDT) visibility?

I believe it is correct to create and reference Swing GUI objects on the EDT, and only on the EDT - yes?

So, would something like this be sufficient, or is it somehow insufficient because the code would run on a class loader thread, and not the EDT? Or is it sufficient to run on a non-EDT thread so long as the references are static final?

import java.awt.Font;

public class GuiFonts {
    // not running on EDT, but is it correct anyhow?
    public static final Font = new Font(Font.SANS_SERIF, Font.PLAIN, 16);
}

Or, should I ensure that the objects are created on the EDT, perhaps like this:

import java.awt.*;
import java.util.concurrent.*;
import javax.swing.*;

public class GuiFonts {
    // intended to run on EDT, but is EDT running yet? In all cases?
    public static final Font MEDIUM_FONT = new Callable<Font>() {
        @Override
        public Font call() {
            final RunnableFuture<Font> future = new FutureTask<Font>(new Callable<Font>() {
                    @Override
                    public Font call() {
                        return new Font(Font.SANS_SERIF, Font.PLAIN, 16);
                    }
                });
            try {
                SwingUtilities.invokeAndWait(future); // Could be invokeLater() because of the Future...
                return future.get();
            } catch (final Exception e) {
                throw new RuntimeException(e);
            }
        }
    }.call();
}

Granted, this is verbose, but a lot of it could be packaged into a routine that accepts a Callable and submits it to the EDT:

    public static final Font MEDIUM_FONT = runOnEDT(new Callable<Font>() {
            @Override
            public Font call() {
                return new Font(Font.SANS_SERIF, Font.PLAIN, 16);
            }
        });

In addition to Swing EDT publication issues, this approach has an error-prone initialization ordering aspect to it if constants reference each other in several files: constants that are not initialized with be null.

Note that such a routine like runOnEDT should catch and log all exceptions, in particular, the logging should occur in a catch clause and NOT in some UncaughtExceptionHandler because an UncaughtExceptionHandler is typically already installed on the EDT.

Or, is it preferable that static initialization be abandoned all together. In favor of some lazy-loading scheme in which objects are represented by enum constants, and some accessor creates the GUI objects on the Swing EDT upon first reference?

Greg Mattes
  • 33,090
  • 15
  • 73
  • 105
  • +1 Very good question. Sorry that I don't have any answer for you. – Code-Apprentice Jan 22 '13 at 20:28
  • Swing is unlikely to be initialized enough at top-level class loading time for a static initializer to work reliably, if at all, as far as getting calls onto the EDT. The enum trick you're referring to is also known as the "lazy holder idiom", and it's indeed a reasonable approach, if a bit boilerplatey. You *can* use a static initializer block inside the holder and it will be delayed until you first call a method on the holder. – Chuck Adams Jan 22 '13 at 20:31
  • @ChuckAdams Yes, I've used the "lazy holder idiom" (as described in JCIP) in the past. My question was getting rather long, so I didn't get into the particular details of the `enum` lazy-initialization approach, but that is pretty much what I had in mind... thought I still don't think that it gets around the fundamental dependency problem, could even lead to indefinite loops... – Greg Mattes Jan 22 '13 at 20:40
  • Once your dependencies get that complex, you're probably looking for Dependency Injection. Unfortunately ,that itself does have some wrinkles when it comes to the EDT. This thread might be helpful: http://stackoverflow.com/questions/2728097/guice-creates-swing-components-outside-of-ui-thread-problem – Chuck Adams Jan 22 '13 at 20:49

2 Answers2

1

Font and Color are part of the AWT, not Swing.

Having said that, I've done what you've outlined with fonts.

public class GuiFonts {
    // not running on EDT, but is it correct anyhow?
    public static final Font = new Font(Font.SANS_SERIF, Font.PLAIN, 16);
}

As long as you use this variable on the EDT, you're fine. You're executing the method code on the EDT.

You could put the Font code into a method.

public static Font getNormalFont() {
    return new Font(Font.SANS_SERIF, Font.PLAIN, 12);
}

This way, you're sure that the code is executing on the EDT, when called from the EDT.

Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • Yes, that's a good point, `Font` and `Color` are from AWT, but they're still GUI objects that should be initialized and used exclusively on the EDT, right? The thing about static initializations is that they're not called on the EDT, which is the issue. Wrapping the call in a method with some precondition check for the thread (e.g. throw an exception if SwingUtilities.isEventDispatchThread() produces false) would ensure that the code is running on the EDT. For the static initialization case, some static initializer block preceding the call to "new Font" is needed, and would like be false. – Greg Mattes Jan 22 '13 at 20:46
  • @Greg Mattes: Generally, you call for a Font or a Color when you're constructing Swing components. Hopefully, you're constructing Swing components on the EDT. I don't believe that it's required to construct Font and Color components on the EDT because you couldn't use them in AWT. But I answered the OP's question as best I could. – Gilbert Le Blanc Jan 24 '13 at 14:05
1

Swing already has a "well-defined/central/application-wide location" for these things: the UIManager.

The advantage of using this is that the fonts, colors etc. will be set automatically by Swing, you don't have to set them.

Also see this: Setting the Default Font of Swing Program

EDIT: if you need to create Swing components, they definitely must be created on the EDT. Note that some older documentation says that it is safe to create components on other threads if they are not "realized" yet, but this is outdated advice.

Community
  • 1
  • 1
lbalazscs
  • 17,474
  • 7
  • 42
  • 50
  • This is helpful knowledge for things like Fonts and Colors, which were really just examples for my question. In general, I'm considering the initialization of a non-trivial Swing application with hundreds of GUI objects, JFrames, JDialogs, JButtons, JTextFields, etc. all needing to be initialized and used on the graphics thread. All linked together by dependencies. I'm really wondering whether static final fields guarantee proper concurrent publishing. Your response is making me consider a custom L&F. Thanks! – Greg Mattes Jan 23 '13 at 01:43