The org.eclipse.jface.resource package defines classes that help plug-ins manage UI resources such as fonts and icons.
Many of the workbench extension points allow plug-ins to supply icons that can be used to show their contributions in the workbench. Since GUI operating systems support a limited number of images or fonts in memory at once, a plug-in's UI resources must be carefully managed and sometimes shared between widgets.
We've already seen several references to icons in the readme tool plug-in. Some of its icons are specified in the plugin.xml markup.
<extension point="org.eclipse.ui.views"> <category id="org.eclipse.ui.examples.readmetool" name="%Views.category"> </category> <view id="org.eclipse.ui.examples.readmetool.views.SectionsView" name="%Views.ReadmeSections" icon="icons/view16/sections.png" category="org.eclipse.ui.examples.readmetool" class="org.eclipse.ui.examples.readmetool.ReadmeSectionsView"> </view> </extension>
We've also seen code that describes images on the fly. The following is from the readme tool's ReadmeEditorActionBarContributor.
public ReadmeEditorActionBarContributor() { ... action1 = new EditorAction(MessageUtil.getString("Editor_Action1")); action1.setToolTipText(MessageUtil.getString("Readme_Editor_Action1")); action1.setDisabledImageDescriptor(ReadmeImages.EDITOR_ACTION1_IMAGE_DISABLE); action1.setImageDescriptor(ReadmeImages.EDITOR_ACTION1_IMAGE_ENABLE); ...
JFace provides the basic support classes that allow plug-ins to manage their icons and fonts without worrying about when the corresponding platform graphics objects are created and destroyed. These support classes are used directly by plug-ins as shown above, or indirectly when the workbench uses these classes to obtain images that are described in extension point markup.
The SWT Image class represents an image from the operating system's perspective. Because most GUI operating systems have a limit on the number of images that can be open at once, plug-ins should be very careful when creating them, and ensure that they also dispose of them properly when finished using them. By using the JFace ImageDescriptor and ImageRegistry classes instead of the SWT image, plug-ins can generally avoid creating, managing, and disposing these images directly.
The ImageDescriptor class can be used as a lightweight description of an image. It specifies everything that is needed to create an image, such as the URL or filename where the image can be obtained. ImageDescriptors do not allocate an actual platform image unless specifically requested using the createImage() method.
Image descriptors are the best strategy when your code is structured such that it defines all the icons in one place and allocates them as they are needed. Image descriptors can be created at any time without concern for OS resources, making it convenient to create them all in initialization code.
When designing APIs prefer image descriptors over images whenever possible. Clients of your API only hand image descriptors to your API. Your implementation handles the creation and disposal of the images and clients of your API don't need to care about disposing of images. An good example for this approach is the Wizard class.
The following code shows an example for this.
public abstract class MyWizard extends Wizard { ... @Override public void init(IWorkbench workbench, IStructuredSelection selection) { ImageDescriptor imageDesc = MyImageRegistry.getImageRegistry().get(“MyKey”); setDefaultPageImageDescriptor(imageDesc); } ... }
The ImageRegistry class is used to keep a list of named images. Clients can add image descriptors or SWT images directly to the list. When an image is requested by name from the registry, the registry will return the image if it has been created, or create one from the descriptor. This allows clients of the registry to share images.
You should prefer adding image descriptors to the list instead of images whenever possible. This has the advantage that the creation of the images is deferred to the point in time when it's needed.
Images that are added to or retrieved from the registry must not be disposed by any client. The registry is responsible for disposing of the image since the images are shared by multiple clients. The registry will dispose of the images when the platform GUI system shuts down. Disposing images that late in the lifetime of an application may be a disadvantage.
Where possible, specify the icon for your plug-in's UI objects in the plugin.xml file. Many of the workbench extension points include configuration parameters for an icon file. By defining your icons in your extension contribution in the plugin.xml, you leave the image management strategy up the platform. Since the icons are typically kept in your plug-in's directory, this allows you to specify the icons and manage the files all in one place.
The other patterns should only be considered when you can't specify the icon as part of your extension contribution.
Explicitly creating an image is the best strategy when the image is infrequently used and not shared. The image can be created directly in SWT and disposed after it is used.
Images can also be created explicitly using an ImageDescriptor and invoking the createImage() method. As in the first case, the dispose() method for the image must be invoked after the image is no longer needed. For example, if a dialog creates an image when it is opened, it should dispose the image when it is closed.
The following example shows how to tie the image lifecycle to the lifecycle of a related control using a DisposeListener.
public class MyDialog extends TitleAreaDialog ... @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); ... Image image = imageRegistry.get(“MyKey”).createImage(); setTitleImage(image); newShell.addDisposeListener(e -> image.dispose()); } ... }
When an image is used frequently in a plug-in and shared across many different objects in the UI, it is useful to register the image descriptor with an ImageRegistry. The images in the registry will be shared with any object that queries an image by the same name. You must not dispose any images in the registry since they are shared by other objects.
Adding an image to the image registry is the best strategy when the image is used frequently, perhaps through the lifetime of the plug-in, and is shared by many objects. The disadvantage of using the registry is that images in the registry are not disposed until the GUI system shuts down. Since there is a limit on the number of platform (SWT) images that can be open at one time, plug-ins should be careful not to register too many icons in a registry.
The class AbstractUIPlugin includes protocol for creating a plug-in wide image registry.
When an icon is used frequently to display items in a particular viewer, it can be shared among similar items in the viewer using a label provider. Since a label provider is responsible for returning an image for any object in a viewer, it can control the creation of the image and any sharing of images across objects in the viewer.
The label provider can use any of the previously discussed techniques to produce an image. If you browse the various implementations of getImage() in the LabelProvider subclasses, you will see a variety of approaches including caching a single icon for objects and maintaining a table of images by type. Images created by a label provider must be disposed in the provider's dispose() method, which is called when the viewer is disposed.
The following code shows an example for this.
public abstract class MyLabelProvider extends LabelProvider { private final Image myImage = null; ... @Override public void dispose() { if (this.myImage != null) { this.myImage.dispose(); this.myImage = null; } super.dispose(); } @Override public Image getImage(Object element) { if (this.myImage == null)} this.myImage = imageRegistry.get(“MyKey”).createImage(); } setTitleImage(this.myImage); } ... }
Using a label provider is a good compromise between explicit creation and the image registry. It promotes sharing of icons like the image registry, yet still maintains control over the creation and disposal of the actual image.
When fine-tuning a plug-in, it is common to experiment with all of these different image creation patterns. It can be useful to isolate the decision making regarding image creation in a separate class and instruct all clients to use the class to obtain all images. This way, the creation sequence can be tuned to reflect the actual performance characteristics of the plug-in.
The ResourceManager class is used to keep a mapping of ImageDescriptors to Images so that an Image can be reused by referring to it via its descriptor.When an image is requested by descriptor from the registry, the registry will return the image if it has been created, or create one from the descriptor. This allows clients of the registry to share images.
The top level ResourceManager is a DeviceResourceManager which is created on a Display. The ResourceManager defined by JFaceResources.getResources() is a DeviceResourceManager and can be used as the top level ResourceManager. If you need a ResourceManager with a shorter lifecycle than the DeviceResourceManager you can create a LocalResourceManager as a child and dispose of it when you are done with it.
A DeviceResourceManager will be disposed when the Display used to create it is disposed so no special management code is required.
Images that are added to or retrieved from the manager must not be disposed by any client. The manager is responsible for disposing of the image since the images are shared by multiple clients. The registry will dispose of the images when the ResourceManager that holds onto them is disposed.
Fonts are another limited resource in platform operating systems. The creation and disposal issues are the same for fonts as for images, requiring similar speed/space tradeoffs. In general, fonts are allocated in SWT by requesting a font with a platform dependent font name.
The FontRegistry class keeps a table of fonts by their name. It manages the allocation and disposal of the font.
In general, plug-ins should avoid allocating any fonts or describing fonts with platform specific names. Although the font registry is used internally in JFace, it is typically not used by plug-ins. The JFaceResources class should be used to access common fonts.
It is very common to allow users to specify their preferences for the application's fonts in a preference page. In these cases, the FontFieldEditor should be used to obtain the font name from the user, and a FontRegistry may be used to keep the font. The FontFieldEditor is only used in preference pages.
The class JFaceResources controls access to common platform fonts and images. It maintains an internal font and image registry so that clients can share named fonts and images.
There are many techniques used in the workbench and other plug-ins to share images where required. The JFaceResources image registry is not widely used across the workbench and plug-in code.
Use of fonts is much simpler. The workbench and most plug-ins use the JFaceResources class to request fonts by logical name. Methods such as getDialogFont() and getDefaultFont() are provided so that plug-ins can use the expected fonts in their UI.