Eclipse Plug-in Development:Beginner's Guide(Second Edition)
上QQ阅读APP看书,第一时间看更新

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.

  1. 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.
  2. 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.
  3. 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:
    1. ID: com.packtpub.e4.clock.ui.views.TimeZoneView
    2. Name: Time Zone View
    3. Class: com.packtpub.e4.clock.ui.views.TimeZoneView
    4. Category: com.packtpub.e4.clock.ui
    5. Icon: icons/sample.gif
  4. 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>
  5. Create the TimeZoneView class. The easiest way is to go to the Extensions tab of the plugin.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 the TimeZoneView as a subclass of ViewPart, in the com.packtpub.e4.clock.ui.views package.
  6. Create a class called TimeZoneComparator, which implements Comparator, in a new package com.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. The compare method should use the Id property of the ZoneId and use the String's compareTo method:
    public class TimeZoneComparator implements Comparator<ZoneId> {
      public int compare(ZoneId o1, ZoneId o2) {
          return o1.getId().compareTo(o2.getId());
      }
    }
  7. Add a public static method to the TimeZoneComparator called getTimeZones, which will return a Map of Set instances containing ZoneId instances. The Map will be indexed by the region of the ZoneId (a ZoneId is something like Europe/Milton_Keynes or America/New_York). This will group all the European ZoneId instances together and all ZoneId 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)
        ));
    }
  8. In the TimeZoneView class's createPartControl method, create a CTabFolder and then iterate through the time zones, creating a CTabItem 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);
    }
  9. Run this example, and show the Time Zone View; there should be a populated list of tabs along the bottom:
  10. Inside the while loop, add a Composite to hold multiple ClockWidget instances for each ZoneId group:
    item.setText(region); // from before
    Composite clocks = new Composite(tabs, SWT.NONE);
    clocks.setLayout(new RowLayout());
    item.setControl(clocks);
  11. Now iterate through the ZoneId instances, adding a ClockWidget for each:
    RGB rgb = new RGB(128, 128, 128);
    zones.forEach(zone -> {
      ClockWidget clock = new ClockWidget(clocks, SWT.NONE, rgb);
      clock.setZone(zone);
    }
  12. Run the target Eclipse instance and open the Time Zone View to see all the clocks:
  13. To make the clocks more identifiable, each will be put into a Group with an associated text label so that the view hierarchy goes from CTabItemCompositeClockWidget to CTabItemCompositeGroupClockWidget. Replace the call to create the the ClockWidget 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);
  14. Run it again, and a series of blank elements will be shown:
  15. Since the default layout manager for Composite is null, 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());
  16. Run it again, and now it looks a little bit more sensible:
  17. 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 from CTabItemCompositeGroupClockWidget to CTabItemScrolledCompositeCompositeGroupClockWidget 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());
  18. Run it again, but unfortunately this will be seen:
  19. The problem is that ScrolledComposite has no minimum size. This can be calculated from the clocks container by adding this to the bottom of the while loop, after the contents of the ScrolledComposite have been created:
    Point size = clocks.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    scrolled.setMinSize(size);
    scrolled.setExpandHorizontal(true);
    scrolled.setExpandVertical(true);
  20. Run it again, and the clocks now show up as expected:
  21. The ScrolledComposite has a different background. To change it, add this line after constructing the clock's Composite:
    clocks.setBackground(clocks.getDisplay()
     .getSystemColor(SWT.COLOR_LIST_BACKGROUND));
  22. 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