Time for action – adding a double-click listener
Typically, a tree view is used to show content in a hierarchical manner. However, a tree on its own is not enough to be able to show all the details associated with an object. When the user double-clicks on an element, more details can be shown.
- At the end of the create method in
TimeZoneTreeView
, register a lambda block that implements theIDoubleClickListener
interface with theaddDoubleClickListener
method on thetreeViewer
. As with the example in Chapter 1, Creating Your First Plug-in, this will open a message dialog to verify that it works as expected:treeViewer.addDoubleClickListener(event -> { Viewer viewer = event.getViewer(); Shell shell = viewer.getControl().getShell(); MessageDialog.openInformation(shell, "Double click", "Double click detected"); });
- Run the target Eclipse instance, and open the Time Zone Tree View. Double-click on the tree, and a shell will be displayed with the message Double click detected. The dialog is modal and prevents other components in the user interface from being selected until it is dismissed.
- To find the selected objects, a viewer returns an
ISelection
interface (which only provides anisEmpty
method) and anIStructuredSelection
(which provides an iterator and other accessor methods). There are also a couple of specialized subtypes, such asITreeSelection
, which can be interrogated for the path that led to the selection in the tree. In thecreate
method of theTimeZoneTreeView
class, where thedoubleClick
method of theDoubleClickListener
lambda is present, replace theMessageDialog
as follows:// MessageDialog.openInformation(shell, "Double click", // "Double click detected"); ISelection sel = viewer.getSelection(); Object selectedValue; if (!(sel instanceof IStructuredSelection) || sel.isEmpty()) { selectedValue = null; } else { selectedValue = ((IStructuredSelection)sel).getFirstElement(); } if (selectedValue instanceof ZoneId) { ZoneId timeZone = (ZoneId)selectedValue; MessageDialog.openInformation(shell, timeZone.getId(), timeZone.toString()); }
- Run the Eclipse instance, and open the Time Zone Tree View. Double-click on the tree, and a shell will be displayed with the
ZoneId
string representation. - To display more information about the
ZoneId
in the displayed window, create a subclass ofMessageDialog
calledTimeZoneDialog
in thecom.packtpub.e4.clock.ui.internal
package:public class TimeZoneDialog extends MessageDialog { private ZoneId timeZone; public TimeZoneDialog(Shell parentShell, ZoneId timeZone) { super(parentShell, timeZone.getId(), null, "Time Zone " + timeZone.getId(), INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0); this.timeZone = timeZone; } }
- The
TimeZoneDialog
content is created in thecreateCustomArea
method, which can be implemented as follows:protected Control createCustomArea(Composite parent) { ClockWidget clock = new ClockWidget(parent,SWT.NONE, new RGB(128,255,0)); return parent; }
- Finally, change the
TimeZoneTreeView
call toMessageDialog.open()
to use theTimeZoneDialog
instead:if (selectedValue instanceof ZoneId) { ZoneId timeZone = (ZoneId) selectedValue; // MessageDialog.openInformation(shell, timeZone.getID(), // timeZone.toString()); new TimeZoneDialog(shell, timeZone).open(); }
- Run the Eclipse instance, double-click on a time zone, and the dialog should appear:
What just happened?
A double-click listener was added to the viewer by registering it with addDoubleClickListener
. Initially, a standard information dialog was displayed; but then a custom subclass of MessageDialog
was used which included a ClockWidget
. In order to get the appropriate ZoneId
, it was accessed via the currently selected object from the TreeViewer
.
Selection handled by viewers is managed through an ISelection
interface. The viewer's getSelection
method should always return a non-null value, although it may be isEmpty
. There are two relevant sub-interfaces: IStructuredSelection
and ITreeSelection
.
The ITreeSelection
interface is a subtype of IStructuredSelection
, which adds methods specific to trees. This includes the ability to find out what the selected object(s) and their parents are in the tree.
The IStructuredSelection
interface is the most commonly used interface when dealing with selection types for viewers. If the selection is not empty, it is almost always an instance of an IStructuredSelection
. As a result, the following snippet of code appears regularly:
ISelection sel = viewer.getSelection(); Object selectedValue; if (!(sel instanceof IStructuredSelection) || sel.isEmpty()) { selectedValue = null; } else { selectedValue = ((IStructuredSelection)sel).getFirstElement(); }
This snippet gets the selection from the viewer, and if it's not an IStructuredSelection
, or it's empty, assigns null
to selectedValue
. If it's not empty, it casts it to IStructuredSelection
and calls getFirstElement
to get the single selected value.
Note that the selection may have more than one selected value, in which case the getFirstElement
only returns the first selected element. IStructuredSelection
provides an iterator to step through all selected objects.
Pop quiz – understanding interaction
Q1. How can TreeViewer
instances be made to respond to a click?
Q2. Why are Dialog
subclasses created?