Time for action – groups and tab folders
A new TimeZoneView
will show a list of clocks in time zones around the world. This time, instead of using the plug-in wizard, the extension will be added manually.
Note
The way views are defined for E4 is covered in Chapter 7, Creating Eclipse 4 Applications. This chapter discusses how to do it in Eclipse 3.x and the Eclipse 3.x compatibility model of Eclipse 4.x.
- Right-click on the project and navigate to Plug-in Tools | Open Manifest, or find the
plugin.xml
file in the navigator and double-click on it. - Go to the manifest editor's Extensions tab. The extensions will list
org.eclipse.ui.views
. Expand this, and underneath the Timekeeping (category) the Clock View (view) will be displayed, added via the plug-in wizard. - Right-click on org.eclipse.ui.views and navigate to New | view from the menu. A placeholder entry name (view) will be added to the list, and the right side lists properties such as the id, name, class, and category. Fill in the following:
- ID:
com.packtpub.e4.clock.ui.views.TimeZoneView
- Name:
Time Zone View
- Class:
com.packtpub.e4.clock.ui.views.TimeZoneView
- Category:
com.packtpub.e4.clock.ui
- Icon:
icons/sample.gif
- ID:
- Save the file. This code will be added into the
plugin.xml
file:<view category="com.packtpub.e4.clock.ui" class="com.packtpub.e4.clock.ui.views.TimeZoneView" icon="icons/sample.gif" id="com.packtpub.e4.clock.ui.views.TimeZoneView" name="Time Zone View" restorable="true"> </view>
- Create the
TimeZoneView
class. The easiest way is to go to the Extensions tab of theplugin.xml
file, select the Time Zone View, and click on the hyperlinked class* label next to the class name. Alternatively, navigate to File | New | Class wizard to create theTimeZoneView
as a subclass ofViewPart
, in thecom.packtpub.e4.clock.ui.views
package. - Create a class called
TimeZoneComparator
, which implementsComparator
, in a new packagecom.packtpub.e4.clock.ui.internal
. It is conventional to provide utility classes in an internal package to ensure that the implementation is not visible to others. Thecompare
method should use theId
property of theZoneId
and use the String'scompareTo
method:public class TimeZoneComparator implements Comparator<ZoneId> { public int compare(ZoneId o1, ZoneId o2) { return o1.getId().compareTo(o2.getId()); } }
- Add a
public static
method to theTimeZoneComparator
calledgetTimeZones
, which will return aMap
ofSet
instances containingZoneId
instances. TheMap
will be indexed by the region of theZoneId
(aZoneId
is something likeEurope/Milton_Keynes
orAmerica/New_York
). This will group all the EuropeanZoneId
instances together and allZoneId
instances in America together:public static Map<String, Set<ZoneId>> getTimeZones(){ Supplier<Set<ZoneId>> sortedZones = () -> new TreeSet<>(new TimeZoneComparator()); return ZoneId.getAvailableZoneIds().stream() // stream .filter(s -> s.contains("/")) // with / in them .map(ZoneId::of) // convert String to ZoneId .collect(Collectors.groupingBy( // and group by z -> z.getId().split("/")[0], TreeMap::new, Collectors.toCollection(sortedZones) )); }
- In the
TimeZoneView
class'screatePartControl
method, create aCTabFolder
and then iterate through the time zones, creating aCTabItem
for each one:public void createPartControl(Composite parent) { Map<String, Set<ZoneId>> timeZones = TimeZoneComparator.getTimeZones(); CTabFolder tabs = new CTabFolder(parent, SWT.BOTTOM); timeZones.forEach((region, zones) -> { CTabItem item = new CTabItem(tabs, SWT.NONE); item.setText(region); } tabs.setSelection(0); }
- Run this example, and show the Time Zone View; there should be a populated list of tabs along the bottom:
- Inside the
while
loop, add aComposite
to hold multipleClockWidget
instances for eachZoneId
group:item.setText(region); // from before Composite clocks = new Composite(tabs, SWT.NONE); clocks.setLayout(new RowLayout()); item.setControl(clocks);
- Now iterate through the
ZoneId
instances, adding aClockWidget
for each:RGB rgb = new RGB(128, 128, 128); zones.forEach(zone -> { ClockWidget clock = new ClockWidget(clocks, SWT.NONE, rgb); clock.setZone(zone); }
- Run the target Eclipse instance and open the Time Zone View to see all the clocks:
- To make the clocks more identifiable, each will be put into a
Group
with an associated text label so that the view hierarchy goes fromCTabItem
–Composite
–ClockWidget
toCTabItem
–Composite
–Group
–ClockWidget
. Replace the call to create the theClockWidget
with this:// ClockWidget clock = new ClockWidget(clocks, SWT.NONE, rgb); Group group = new Group(clocks, SWT.SHADOW_ETCHED_IN); group.setText(zone.getId().split("/")[1]); ClockWidget clock = new ClockWidget(group, SWT.NONE, rgb);
- Run it again, and a series of blank elements will be shown:
- Since the default layout manager for
Composite
isnull
,Group
instances don't have a layout manager—and so the clocks are not getting sized appropriately. This can be fixed by setting a layout manager explicitly:group.setLayout(new FillLayout());
- Run it again, and now it looks a little bit more sensible:
- The clocks at the bottom are squashed and the view can't be scrolled even though there are clearly more time zones available. To add scrolling to a widget, the
ScrolledComposite
class can be used. This provides automatic scroll bars and interaction with the user to permit a much larger virtual area to be scrolled. The view hierarchy will change fromCTabItem
–Composite
–Group
–ClockWidget
toCTabItem
–ScrolledComposite
–Composite
–Group
–ClockWidget
instead:// Composite clocks = new Composite(tabs, SWT.NONE); // item.setControl(clocks); ScrolledComposite scrolled = new ScrolledComposite(tabs, SWT.H_SCROLL | SWT.V_SCROLL); Composite clocks = new Composite(scrolled, SWT.NONE); item.setControl(scrolled); scrolled.setContent(clocks); clocks.setLayout(new RowLayout());
- Run it again, but unfortunately this will be seen:
- The problem is that
ScrolledComposite
has no minimum size. This can be calculated from the clocks container by adding this to the bottom of thewhile
loop, after the contents of theScrolledComposite
have been created:Point size = clocks.computeSize(SWT.DEFAULT, SWT.DEFAULT); scrolled.setMinSize(size); scrolled.setExpandHorizontal(true); scrolled.setExpandVertical(true);
- Run it again, and the clocks now show up as expected:
- The
ScrolledComposite
has a different background. To change it, add this line after constructing the clock'sComposite
:clocks.setBackground(clocks.getDisplay() .getSystemColor(SWT.COLOR_LIST_BACKGROUND));
- Now the Time Zone View is complete:
What just happened?
A combination of Composite
types created a tabbed interface using CTabFolder
and CTabItem
instances. Inside each CTabItem
, a ScrolledComposite
contained a Composite
of Group
instances, each of which had a single ClockWidget
. Adding the ScrolledComposite
provided the scrolling for free, and the Group
allowed us to place text above the ClockWidget
to display its time zone.
Note
Some of the components used here are in the org.eclipse.swt.custom
package instead of the org.eclipse.swt.widgets
package. Several of these begin with C as a custom designator to distinguish similarly named widgets. The CTabFolder/Item
is an SWT-implemented class that provides the tab functionality; the corresponding OS widget, TabFolder/Item
, uses a native rendered tab switcher.
Pop quiz: using SWT
Q1. How do you add an icon to the system menu?
Q2. What does the SWT.NO_TRIM
style do for a Shell
?
Q3. How do you make a Shell
transparent?
Q4. What do you need to set to create a non-rectangular Shell
?
Q5. What Composite
allows you to attach a label to a set of related items?
Q6. What is the default layout manager for a Group
?
Q7. How do you add scrolling to an existing widget?
Have a go hero: enhancing the time zones
A set of times are displayed in different time zones, but there is scope for enhancements:
- Switch to the tab with the user's default time zone when the view is created
- Sort the clocks by time zone offset rather than by the name of the region
- Create a favorites tab and allow it to be populated by drag-and-drop
- Improve the speed of updates by sharing a single
Thread
to update all clocks - Improve the sizing of the
ScrollableComposite
so that more than one row is displayed