![]() ![]() |
![]() |
![]() ![]() ![]() ![]() |
![]() |
A GUI toolkit is generally configurable at a variety of levels:
Swing lets you configure the properties of components using the third scenario -- that is, Swing lets you to specify default configuration parameters for an entire toolkit, so you can customize component parameters that are used throughout an application. This specification explains how Swing's L&F-specific component-property defaults work, and provides some hints on how you can customize them in your applications.
As you may recall from the "Component Architecture" specification, Swing components are implemented using a pluggable-L&F design that gives them two parts: a generic part and an L&F-specific part. Because this pluggable-look-and-feel design applies throughout the Swing UI toolkit, Swing component configurations have a generic part and an L&F-specific part. To help you manage the properties of Swing components, the Swing toolkit will supply a class called the SystemDefaults class. The design of the SystemDefaults class stems from a set of requirements so small that it can be boiled down to just two:
It is not a goal of Swing to support fine-grained defaults, as the Xt toolkit does. For example, Swing has no support for defining the font that the OK button uses in Save dialog boxes or the background color of all the TextFields that ScrollPanes contain
Similarly, it is not a goal of Swing to provide a new mechanism for defining or implementing properties in Swing components. Default properties are defined by a simple table that maps from keywords to objects like fonts, colors, icons, and so on. This table is defined as follows by the SystemDefaults class:
class SystemDefaults implements java.io.Serializable { public static final String COLOR_TEXT = "COLOR_TEXT"; public static final String COLOR_CONTROL = "COLOR_CONTROL"; public static final FONT_DEFAULT = "FONT_DEFAULT"; // ... /** * Returns the value of the specified property, null if * the property doesn't exist. Key can't be null. */ public Object getProperty(Object key); /** * Puts a value in the under the specified key. The key * can't be null. If the value is new or different, notify * the propertyChangeListeners. */ public void putProperty(Object key, Object value); /* The following methods just cast the value returned by * getProperty: */ public java.awt.Color getColorProperty(Object key); public java.awt.Font getFontProperty(Object key); public java.awt.Icon getIconProperty(Object key); public java.awt.Fill getFillProperty(Object key); /** * Just a convenience based on getProperty. */ public Object[] getProperties(Object[] keys); /** * Just a convenience based on putProperty except: only one * PropertyChange event is generated. */ public void putProperties(Object[] keyValueArray); /** * Returns true if this property has only been set once, i.e. * its initial value hasn't been overridden. */ public boolean isInitialValue(Object key); /* PropertyChange listeners are notified whenever the table is * modified. */ public addPropertyChangeListener(java.beans.PropertyChangeListener x); public removePropertyChangeListener(java.beans.PropertyChangeListener x); }
The SystemDefaults class defines a String key for every property that's common to most L&Fs. Later, we may add a few more convenience "getXXXProperty" methods for obtaining properties such as Dimension, Rectangle, int, String. Developer comments on this topic are welcome.
Each L&F implementation builds a SystemDefaults subclass that contains values for at least the generic keys. A client can add table entries or override these values and can then synchronize them with the existing Swing component tree.
NOTE: Individual ComponentUI objects could watch a SystemDefaults table and update when changes occur. At this point in the design of Swing, however, this effort doesn't seem to be worth the cost.
Here's an example SystemDefaults implementation:
public class BasicDefaults implements SystemDefaults { static private SystemDefaults defaults = null; public static getBasicDefaultsInstance() { if (defaults == null) { defaults = new BasicDefaults(); } return defaults; } protected BasicDefaults() { Object[] initialProperties = { COLOR_TEXT, SystemColor.textText, // I am not making this up COLOR_CONTROL, SystemColor.control, COLOR_FONT, new Font("Dialog", Font.PLAIN, 12), // ... }; putProperties(initialProperties); } }
An implementation of a "Basic" L&F component would use the preceding table by calling the BasicDefaults.getBasicDefaultsInstance() method:
public class BasicButtonUI implements ComponentUI { protected void getDefaults() { return BasicDefaults.getBasicDefaultsInstance(); } public void paint(Graphics g, JComponent c) { JUButton button = (JButton)c; // ... g.setColor(getDefaults().getColor(COLOR_TEXT)); g.drawString(button.getText(), x, y); } // ... }
Note that in the preceding example, we've inserted a protected method named getDefaults() between the BasicDefaults table and the rest of the ButtonUI implementation. This strategy allows a subclass to shadow the table if it wants to.
There are some existing java.awt.Component properties whose value is is inherited via the component tree if it is not explicitly set. In the example that follows, the first five java.awt.Label children of the panel are rendered in red because the Label objects inherit the panel's foreground color:
Panel panel = new Panel(); panel.setForeground(Color.red); for(int i = 0; i <5; i++) { panel.add(new label("red label" + i)); } bluelabel="new" label("blue label"); bluelabel.setforeground(color.blue); panel.add(bluelabel);
The behavior of the font and background properties is similar. Note that there is no way to determine whether a component is inheriting one of these properties from a parent class or whether the property's value has been explicitly set. Also note that the get methods for these properties return null if an ancestor's property hasn't been explicitly set and the component hasn't been made visible yet. For example:
new Frame().getForeground() => null. /* But */ Frame f = new Frame(); f.show(); f.getForeground() => a valid Color
Although opinions vary about how good a design this is, we would like
to avoid breaking it in Swing. To make it possible for a ComponentUI implementation
to discover if the background, foreground, or font Component properties
have been overridden, we initialize the same properties in JWindow, JDialog,
and JFrame with unique values from the SystemDefaults table. If a component's
font, background, or foreground, is '==' to the value of (say) SystemProperty.getProperty(COMPONENT_FOREGROUND)
,
it is treated as effectively being not overridden.
SystemDefaults contains three methods that simplify looking up one of
these properties and using a L&F-specific value if the property hasn't
been overridden:
class SystemDefaults { // API Presented Earlier /** * If x.getForeground() has been explicitly overridden, return it. * Otherwise return getColorProperty(key). We consider foreground * to have been overridden if its value is not == to the value of * getProperty(COMPONENT_FOREGROUND). */ Color getComponentForeground(String key, JComponent x); /* Likewise for the background, font, properties. */ Color getComponentBackground(String key, JComponent x); Color getComponentFont(String key, JComponent x); }
Note that this approach assumes that the three COMPONENT_* properties have unique objects as values. If someone were to set COMPONENT_FOREGROUND to Color.black, this strategy would often fail. (In JDK 1.2, we'll be able to find out if these properties had been overridden explicitly.)
SystemDefaults defines a BeanInfo class that enumerates the properties in the table. A default's object can be serialized and edited, using a standard Bean tool, separately from the UIFactory that includes it.
There is limited support in the AWT for this kind of thing today. The SystemColor class defines a read-only set of platform-specific colors. Only the colors for the current platform are available, and they can't be overridden.
Version 0.4. Last modified 09/04/97.
Copyright © 1995-97 Sun
Microsystems, Inc. All Rights Reserved.