Viewers

Why would you ever want to use a viewer when we have already seen that workbench UI contributions like views, editors, wizards, and dialogs can be implemented directly with SWT widgets?  

Viewers allow you to create widgets while still using your model objects.  If you use an SWT widget directly, you have to convert your objects into the strings and images expected by SWT.  Viewers act as adapters on SWT widgets, handling the common code for handling widget events that you would otherwise have to implement yourself. 

We first saw a viewer in the readme tool's view contribution, inside the ReadmeSectionsView.

   public void createPartControl(Composite parent) {
      viewer = new ListViewer(parent);
      ...
   }
Note:  Viewers can be used to provide the implementation for both workbench views and editors. The term viewer does not imply that they are only useful for implementing views. For example, the TextViewer is used in the implementation in many of the workbench and plug-in editors.

Standard viewers

JFace provides viewers for most of the non-trivial widgets in SWT. Viewers are most commonly used for list, tree, table, and text widgets. 

Each viewer has an associated SWT widget. This widget can be created implicitly by supplying the parent Composite in a convenience viewer constructor, or explicitly by creating it first and supplying it to the viewer in its constructor.

List-oriented viewers

Lists, trees, and tables share many common capabilities from a user's point of view, such as population with objects, selection, sorting, and filtering. 

These viewers keep a list of domain objects (called elements) and display them in their corresponding SWT widget. A list viewer knows how to get a text label from any element in the list.  It obtains the label from an ILabelProvider which can be set on the viewer.  List viewers know how to map from the widget callbacks back into the world of elements known by the viewer client.

Clients that use a plain SWT widget have to operate at the SWT level - where items are strings and events often relate to an index within the list of strings. Viewers provide higher level semantics. Clients are notified of selections and changes to the list using the elements they provided to the viewer. The viewer handles all the grunt work for mapping indexes back to elements, adjusting for a filtered view of the objects, and re-sorting when necessary.

Filtering and sorting capability is handled by designating a viewer sorter (ViewerSorter) and/or viewer filter (ViewerFilter) for the viewer. (These can be specified for tree and table viewers in addition to list viewers.) The client need only provide a class that can compare or filter the objects in the list. The viewer handles the details of populating the list according to the specified order and filter, and maintaining the order and filter as elements are added and removed.

Viewers are not intended to be extended by clients.  To customize a viewer, you can configure it with your own content and label providers.

A ListViewer maps elements in a list to an SWT List control.

A TreeViewer displays hierarchical objects in an SWT Tree widget. It handles the details for expanding and collapsing items. There are several different kinds of tree viewers for different SWT tree controls (plain tree, table tree, checkbox tree).

A TableViewer is very similar to a list viewer, but adds the ability to view multiple columns of information for each element in the table.  Table viewers significantly extend the function of the SWT table widget by introducing the concept of editing a cell. Special cell editors can be used to allow the user to edit a table cell using a combo box, dialog, or text widget. The table viewer handles the creation and placement of these widgets when needed for user editing.  This is done using the CellEditor classes, such as TextCellEditor and CheckboxCellEditor. A virtual table, only populated when viewed, the table viewer only runs a designated number of results regardless of what is actually created. The database "lazily" requests JIT and will only query a predetermined number at a time.

Text viewer

Text widgets have many common semantics such as double click behavior, undo, coloring, and navigating by index or line.  A TextViewer is an adapter for an SWT StyledText widget. Text viewers provide a document model to the client and manage the conversion of the document to the styled text information provided by the text widget.

Text viewers are covered in more detail in Workbench Editors.

Viewer architecture

To understand a viewer, you must become familiar with the relationship between a viewer's input element, its contents, its selection, and the information actually displayed in the widget that it is manipulating.

Input elements

An input element is the main object that the viewer is displaying (or editing). From the viewer's point of view, an input element can be any object at all. It does not assume any particular interface is implemented by the input element. (We'll see why in a moment when we look at content providers.)

A viewer must be able to handle a change of input element. If a new input element is set into a viewer, it must repopulate its widget according to the new element, and disassociate itself from the previous input element. The semantics for registering as a listener on an input element and populating the widget based on the element are different for each kind of viewer.

Content viewers

A content viewer is a viewer that has a well defined protocol for obtaining information from its input element. Content viewers use two specialized helper classes, the IContentProvider and ILabelProvider, to populate their widget and display information about the input element.

IContentProvider provides basic lifecycle protocol for associating a content provider with an input element and handling a change of input element. More specialized content providers are implemented for different kinds of viewers. The most common content provider is IStructuredContentProvider, which can provide a list of objects given an input element. It is used in list-like viewers, such as lists, tables, or trees. In general, the content provider knows how to map between the input element and the expected viewer content.

ILabelProvider goes a step further. Given the content of a viewer (derived from the input element and content provider), it can produce the specific UI elements, such as names and icons, that are needed to display the content in the viewer. Label providers can aid in saving icon resources since they can ensure the same instance of the icon is used for all like types in a viewer.

Note:  Instances of particular content and label providers are not intended to be shared across multiple viewers. Even if all your viewers use the same type of content or label provider, each viewer should be initialized with its own instance of the provider class. The provider life cycle protocol is designed for a 1-to-1 relationship between a provider and its viewer.

Input elements, content providers, and label providers allow viewers to hide most of the implementation details for populating widgets. Clients of a viewer need only worry about populating a viewer with the right kind of input and content provider. The label provider must know how to derive the UI information from the viewer content.

A label provider can show more than just text and an image. JFace provides several classes and interfaces to support extra functionality. The following classes are supported by the TableViewer, AbstractTreeViewer and TableTreeViewer.

It is possible to affect the color of items in a view either from within the view's label provider, or via a decorator. Generally it is better to use the color and font support in label providers, since decorators affect every view that shows a particular type. If you do use a color or font decorator make sure its values can be set in the Colors and Fonts preference page.

Viewers and the workbench

The flexibility provided by viewers, content providers, and label providers can be demonstrated by looking at how the workbench uses them.

The WorkbenchContentProvider is a structured content provider that obtains contents from an input element by asking for its children. The concept of adapters is used again in order to implement generic function. When asked for the list of elements from its input element, the WorkbenchContentProvider obtains an IWorkbenchAdapter for the input element. If an IWorkbenchAdapter has been registered for the input element, then the content provider can safely assume that the element can be queried for its children.  WorkbenchContentProvider also does the work needed to keep its viewer up to date when the workspace changes. 

The WorkbenchLabelProvider is a label provider that obtains an IWorkbenchAdapter from an object in order to find its text and image. The concept of a label provider is particularly helpful for workbench objects because it allows a single label provider to cache images that are commonly used in a viewer. For example, once the WorkbenchLabelProvider obtains an image to use for an IProject, it can cache that image and use it for all IProject objects shown in the viewer.

By defining a common adapter, IWorkbenchAdapter, and registering it for many of the platform types, we make it possible for these types to be represented correctly in many of the common viewers and the workbench views that contain them.