Reader small image

You're reading from  Eclipse Plug-in Development Beginner's Guide - Second Edition

Product typeBook
Published inAug 2016
Reading LevelExpert
Publisher
ISBN-139781783980697
Edition2nd Edition
Languages
Tools
Right arrow
Author (1)
Alex Blewitt
Alex Blewitt
author image
Alex Blewitt

contacted on 30 aug 16 _____________ Dr Alex Blewitt has over 20 years of experience in Objective-C and has been using Apple frameworks since NeXTstep 3.0. He upgraded his NeXTstation for a TiBook when Apple released Mac OS X in 2001 and has been developing on it ever since. Alex currently works for an investment bank in London, writes for the on-line technology news site InfoQ and has published two other books for Packt publishing. He also has a number of apps on the Apple AppStore through Bandlem Limited. When he's not working on technology, and if the weather is nice, he likes to go flying from the nearby Cranfield airport. Alex writes regularly at his blog, http://alblue.bandlem.com, as well tweeting regularly from Twitter as @alblue. Acknowledgements This book would not have been possible without the ongoing love and support of my wife Amy, who has helped me through both the highs and lows of life. She gave me the freedom to work during the many late nights and weekends that it takes to produce a book and its associated code repository. She truly is the Lem of my life. I'd also like to thank my parents, Ann and Derek, for their encouragement and support during my formative years. It was this work ethic that allowed me to start my technology career as a teenager and to incorporate my first company before I was 25. I'd also like to congratulate them on their 50th wedding anniversary in 2015, and I look forward to reaching that goal with Amy. Thanks are due especially to the reviewer of this version of the book: Antonio Bello, as well as the previous version of this book: Nate Cook, James Robert and Arvid Gerstmann, who provided excellent feedback on the contents of this book during development and caught many errors in both the text and code. Any remaining errors are my own. I'd also like to thank my children Sam and Holly for inspiring me and hope that they too can achieve anything that they set their minds to. Finally, I'd like to thank Ben Moseley and Eren Kotan, both of whom introduced me to NeXT in the first place and set my career going on a twenty year journey to this book.
Read more about Alex Blewitt

Right arrow

Chapter 2. Creating Views with SWT

SWT – the Standard Widget Toolkit

SWT is the widget toolkit used by Eclipse that gives performant access to the platform's native tools in a portable manner. Unlike Swing, which is rendered with Java native drawing operations, SWT delegates the drawing to the underlying operating system.

In this chapter we will:

  • Create an Eclipse view with SWT widgets

  • Create a custom SWT widget

  • Work with SWT resources and learn how to detect and fix resource leaks

  • Handle focus operations

  • Group components and resize them automatically

  • Create system tray items

  • Display nonrectangular windows

  • Provide scrolling and tabbed navigation

Creating views and widgets


This section introduces views and widgets by creating clocks that can be used to display time zones in Eclipse.

Time for action – creating a view


The Eclipse UI consists of multiple views, which are the rectangular areas that display content, such as the Outline, Console, or Package Explorer. In Eclipse 3.x, views are created by adding an extension point to an existing plug-in, or using a template. A clock.ui plug-in will be created to host the clock widgets and views.

  1. Open the plug-in wizard by navigating to File | New | Other | Plug-in Project. Enter the details as follows:

    1. Set Project name to com.packtpub.e4.clock.ui.

    2. Ensure that Use default location is selected.

    3. Ensure that Create a Java project is selected.

    4. The Eclipse Version should be targeted to 3.5 or greater.

  2. Click on Next again, and fill in the plug-in properties:

    1. Set ID to com.packtpub.e4.clock.ui.

    2. Set Version to 1.0.0.qualifier.

    3. Set Name to Clock.

    4. Set Vendor to PacktPub.

    5. Ensure that Generate an Activator is selected.

    6. Set the Activator to com.packtpub.e4.clock.ui.Activator.

    7. Ensure that This plug-in will make contributions to the UI is selected.

    8. Rich...

Time for action – drawing a custom view


An SWT Canvas can be used to provide custom rendering for a view. As a starting point for drawing a clock, the Canvas will use drawArc to create a circle.

  1. Remove the content of the ClockView, leaving behind an empty implementation of the setFocus and createPartControl methods.

  2. Run the target Eclipse instance and you will see that the ClockView is now empty.

  3. Create a new method called drawClock that takes a PaintEvent, and use the graphics context gc from the event to draw the circle.

  4. In the createPartControl method, do the following:

    1. Create a new Canvas, which is a drawable widget.

    2. Add a PaintListener to the Canvas that uses a method reference to the drawClock method.

  5. The code will look like this:

    package com.packtpub.e4.clock.ui.views;
    import org.eclipse.swt.*;
    import org.eclipse.swt.events.*;
    import org.eclipse.swt.widgets.*;
    import org.eclipse.ui.part.ViewPart;
    public class ClockView extends ViewPart {
      public void createPartControl(Composite parent) ...

Time for action – drawing a seconds hand


A clock with no hands and no numbers is just a circle. To change this, a second hand will be drawn using a filled arc.

Since arcs are drawn anticlockwise from 0 (on the right, or 3 o'clock) through 90 degrees (12 o'clock), then 180 degrees (9 o'clock), then 270 degrees (6 o'clock), and finally back to 360 degrees (3 o'clock), it is possible to calculate the arc's position for the second hand using the expression (15 – seconds) * 6 % 360.

  1. Go to the drawClock method of the ClockView class.

  2. Add a variable called seconds that is initialized to LocalTime.now().getSecond().

  3. Get the SWT.COLOR_BLUE via the display, and store it in a local variable, blue.

  4. Set the background color of the graphics context to blue.

  5. Draw an arc using the formula mentioned earlier to draw the second hand.

  6. The code should look like this:

    public void paintControl(PaintEvent e) {
      e.gc.drawArc(e.x, e.y, e.width-1, e.height-1, 0, 360);
      int seconds = LocalTime.now().getSecond();
      int arc...

Time for action – animating the second hand


The second hand is drawn with a redraw on the Canvas, but this will need to be run periodically. If it is redrawn once per second, it can emulate a clock ticking.

Eclipse has a jobs plug-in, which would be just right for this task, but this will be covered in Chapter 4, Interacting with the User. So to begin with, a simple Thread will be used to issue the redraw.

  1. Open the ClockView class.

  2. Add the following at the bottom of the createPartControl method:

    Runnable redraw = () -> {
      while (!clock.isDisposed()) {
        clock.redraw();
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          return;
        }
      }
    };
    new Thread(redraw, "TickTock").start();
  3. Relaunch the test Eclipse instance, and open the Clock View.

  4. Open the host Eclipse instance and look in the Console View for the errors.

What just happened?

When the ClockView is shown, a Thread is created and started, which redraws the clock once per second. When it is shown, an exception...

Time for action – running on the UI thread


To execute code on the UI thread, Runnable instances must be posted to the Display via one of two methods, syncExec or asyncExec. The syncExec method runs the code synchronously (the caller blocks until the code has been run) while the asyncExec method runs the code asynchronously (the caller continues while the code is run in the background).

The Display class is SWT's handle to a monitor (so a runtime may have more than one Display object, and each may have its own resolution). To get hold of an instance, call either Display.getCurrent() or Display.getDefault(). However, it's much better to get a Display from an associated view or widget. In this case, the Canvas has an associated Display.

  1. Go to the TickTock thread inside the createPartControl method of the ClockView class.

  2. Inside the redraw lambda, replace the call to clock.redraw() with this:

    // clock.redraw();
    clock.getDisplay().asyncExec(() -> clock.redraw());
  3. Run the target Eclipse instance...

Time for action – creating a reusable widget


Although the ClockView shows a single animated clock, creating an independent widget will allow the clock to be reused in other places.

  1. Create a new class in the com.packtpub.e4.clock.ui package, called ClockWidget, that extends Canvas.

  2. Create a constructor that takes a Composite parent and an int style bits parameter, and pass them to the superclass:

    public ClockWidget(Composite parent, int style) {
      super(parent, style);
    }
  3. Move the implementation of the drawClock method from the ClockView to the ClockWidget. Remove the PaintListener references from the ClockView class.

  4. In the ClockWidget constructor, register a PaintListener that delegates the call to the drawClock method:

    addPaintListener(this::drawClock);
  5. Move the TickTock thread from the ClockView to the ClockWidget constructor; this will allow the ClockWidget to operate independently. Change any references for clock to this:

    Runnable redraw = () -> {
      while (!this.isDisposed()) {
        this.getDisplay...

Time for action – using layouts


Now that the ClockWidget has been created, multiple instances can be added into the ClockView.

  1. Modify the createPartControl method in the ClockView class to create three ClockWidget instances, and assign them to local variables:

    final ClockWidget clock1 = new ClockWidget(parent, SWT.NONE);
    final ClockWidget clock2 = new ClockWidget(parent, SWT.NONE);
    final ClockWidget clock3 = new ClockWidget(parent, SWT.NONE);
  2. Run the target Eclipse instance, and show the Clock View. Three clocks will be shown, counting in seconds:

  3. At the start of the ClockView class's createPartControl method, create a new RowLayout with SWT.HORIZONTAL, and then set it as the layout on the parent Composite:

    public void createPartControl(Composite parent) {
      RowLayout layout = new RowLayout(SWT.HORIZONTAL);
      parent.setLayout(layout);
  4. Run the code again now, and the clocks will be in a horizontal row:

  5. Resize the view; the clocks will flow into different rows:

    Note

    The RowLayout has a number of fields...

Managing resources


One of the challenges in adopting SWT is that native resources must be disposed when they are no longer needed. Unlike AWT or Swing, which perform these operations automatically when an object is garbage-collected, SWT needs manual resource management.

Note

Why does SWT need manual resource management?

A common question asked is why SWT has this rule when Java has had perfectly acceptable garbage collection for many years. In part, it's because SWT pre-dates acceptable garbage collection, but it's also to try and return native resources as soon as they are no longer needed.

From a performance perspective, adding a finalize method to an object also causes the garbage collector to work harder; much of the speed in today's garbage collectors is because they don't need to call methods as they are invariably missing. It also hurts in SWT's case because the object must post its dispose request onto the UI thread, which delays its garbage collection until the object becomes reachable...

Time for action – getting colorful


To add an option for the ClockWidget to have a different color, an instance must be obtained instead of the hardcoded BLUE reference. Since Color objects are Resource objects, they must be disposed correctly when the widget is disposed.

To avoid passing in a Color directly, the constructor will be changed to take an RGB value (which is three int values), and use that to instantiate a Color object to store for later. The lifetime of the Color instance can be tied to the lifetime of the ClockWidget.

  1. Add a private final Color field called color to the ClockWidget:

    private final Color color;
  2. Modify the constructor of the ClockWidget to take an RGB instance, and use it to instantiate a Color object. Note that the color is leaked at this point, and will be fixed later:

    public ClockWidget(Composite parent, int style, RGB rgb) {
      super(parent, style);
      // FIXME color is leaked!
      this.color = new Color(parent.getDisplay(), rgb);
      ...
  3. Modify the drawClock method to...

Time for action – finding the leak


It is necessary to know how many resources are allocated in order to know whether the leak has been plugged or not. Fortunately, SWT provides a mechanism to do this via the Display and the DeviceData class. Normally, this is done by a separate plug-in, but in this example, the ClockView will be modified to show this behavior.

  1. At the start of the ClockView class's createPartControl method, add a call to obtain the number of allocated objects, via the DeviceData of the Display class:

    public void createPartControl(Composite parent) {
      Object[] objects = parent.getDisplay().getDeviceData().objects;
  2. Iterate through the allocated objects, counting how many are instances of Color:

      int count = 0;
      for (int i = 0; i < objects.length; i++) {
        if (objects[i] instanceof Color) {
          count++;
        }
      }
  3. Print the count to the standard error stream:

    System.err.println("There are " + count + " Color instances");
  4. Now run the code in debug mode and show the Clock View...

Time for action – plugging the leak


Now that the leak has been discovered, it needs to be fixed. The solution is to call dispose on the Color once the view itself is removed.

A quick investigation of the ClockWidget suggests that overriding dispose might work, though this is not the correct solution; see later for why.

  1. Create a dispose method in ClockWidget with the following code:

    @Override
    public void dispose() {
      if (color != null && !color.isDisposed())
        color.dispose();
      super.dispose();
    }
  2. Run the target Eclipse application in debug mode (with the tracing enabled, as before) and open and close the view. The output will show something like this:

    There are 87 Color instances
    There are 91 Color instances
    There are 94 Color instances
    There are 98 Color instances
  3. Remove the dispose method (since it doesn't work as intended) and modify the constructor of the ClockWidget to add an anonymous DisposeListener that disposes of the associated Color:

    public ClockWidget(Composite parent, int...

Interacting with the user


The whole point of a user interface is to interact with the user. Having a view that displays information may be useful, but it is often necessary to ask the user for data or respond to user actions.

Time for action – getting in focus


To allow the time zone of the clock widgets to be changed, a drop-down box (known as Combo) as well as a Button will be added to the view. The Combo will be created from a set of ZoneId instances.

  1. Create a timeZones field in the ClockView class:

    private Combo timeZones;
  2. At the end of the createPartControl method, add this snippet to create the drop-down list:

    public void createPartControl(Composite parent) {
      ...
      timeZones = new Combo(parent, SWT.DROP_DOWN);
      timeZones.setVisibleItemCount(5);
      for (String zone : ZoneId.getAvailableZoneIds()) {
        timeZones.add(zone);
      }
    }
  3. Run the target Eclipse and open the Clock View again; a list of time zone names will be shown in a drop-down:

  4. It's conventional to set the focus on a particular widget when a view is opened. Implement the appropriate call in the ClockView method setFocus:

    public void setFocus() {
      timeZones.setFocus();
    }
  5. Run Eclipse and show the Clock View; the time zone drop-down widget will be focused...

Time for action – responding to input


To show the effect of changing the TimeZone, it is necessary to add an hour hand to the clock. When the TimeZone is changed in the drop-down, the hour hand will be updated.

  1. Add a zone field to the ClockWidget along with a setter:

    private ZoneId zone = ZoneId.systemDefault();
    public void setZone(ZoneId zone) {
      this.zone = zone;
    }
  2. Getters and setters can be generated automatically. Once the field is added, navigate to Source | Generate Getters and Setters. It can be used to generate all missing getters and/or setters; in addition, a single getter/setter can be generated by typing set in the class body, followed by Ctrl + Space (Cmd + Space on macOS).

  3. Add an hour hand in the drawClock method using the following:

    e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_BLACK));
    ZonedDateTime now = ZonedDateTime.now(zone);
    int hours = now.getHour();
    arc = (3 - hours) * 30 % 360;
    e.gc.fillArc(e.x, e.y, e.width-1, e.height-1, arc - 5, 10);
  4. To update the clock when...

Using other SWT widgets


SWT contains many widgets other than Canvas, and this section covers some of them. JFace will be covered in the next chapter, which provides a model-view-controller view for designing GUIs, but it's helpful to know the base SWT classes upon which they are built.

Time for action – adding items to the tray


Most operating systems have a concept of tray, as a set of icons visible from the main window that can provide quick access components. On macOS, these are represented as icons across the top menu bar, and on Windows, as icons on the bottom-right near the clock. Linux systems have various approaches that do similar things, and some operating systems have none. Since there is only one tray, it is necessary to add the item only once. The Activator class can be used to ensure that the TrayItem is created at startup and removed at shutdown.

  1. Open the Activator class, and add two private fields:

    private TrayItem trayItem;
    private Image image;
  2. Add the following to the start method:

    final Display display = Display.getDefault();
    display.asyncExec(() -> {
      image = new Image(display,
      Activator.class.getResourceAsStream("/icons/sample.gif"));
      Tray tray = display.getSystemTray();
      if (tray != null && image != null) {
        trayItem = new TrayItem...

Time for action – responding to the user


When the user clicks on the icon, nothing happens. That's because there is no registered listener on the TrayItem itself. There are two listeners that can be registered; a SelectionListener, called when the icon is clicked, and a MenuDetectListener, which can respond to a context-sensitive menu. The former will be used to present a clock in its own window, which in SWT terms is called a Shell.

  1. Open the Activator class.

  2. Go to the lambda inside the asyncExec in the Activator class's start method.

  3. After the creation of the TrayItem, call addSelectionListener with a new anonymous inner subclass of SelectionListener. In the widgetSelected method, a new Shell should be created with a ClockView and then shown:

    trayItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());
        new ClockWidget(shell, SWT.NONE, new RGB(255, 0, 255));
        shell.pack...

Time for action – modal and other effects


There are a number of style bits that are applicable to windows, and some useful methods to affect how the window appears. For example, it might be desirable to make the clock appear semi-transparent, which allows the clock to float above other windows. SWT's Shell has a number of these options that can be set.

  1. Modify the instantiation of the Shell inside the widgetSelected method in the Activator inner class to add SWT.NO_TRIM (no close/minimise/maximise widgets) and SWT.ON_TOP (floating on top of other windows):

    shell = new Shell(trayItem.getDisplay(), SWT.NO_TRIM | SWT.ON_TOP);
  2. Set the alpha value as 128, which is semi-transparent:

    shell.setAlpha(128);
  3. Run the target Eclipse instance, and click on the tray item to see what kind of window is created.

  4. To create a modal window (and thus, prevent interaction on the main window), change the flag to use SWT.APPLICATION_MODAL:

    shell = new Shell(trayItem.getDisplay(), SWT.APPLICATION_MODAL);
  5. To make the application...

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. ...

Summary


In this chapter, we covered how to create views with SWT widgets. We looked at both standard widget types as well as creating our own, and how those widgets can be assembled into groups with Composite and Layout classes. We also looked at how resources are managed within SWT, including following through the debug procedure for detecting and eliminating leaks.

In the next chapter, we will look at how to use a higher level of abstraction, JFace.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Eclipse Plug-in Development Beginner's Guide - Second Edition
Published in: Aug 2016Publisher: ISBN-13: 9781783980697
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Alex Blewitt

contacted on 30 aug 16 _____________ Dr Alex Blewitt has over 20 years of experience in Objective-C and has been using Apple frameworks since NeXTstep 3.0. He upgraded his NeXTstation for a TiBook when Apple released Mac OS X in 2001 and has been developing on it ever since. Alex currently works for an investment bank in London, writes for the on-line technology news site InfoQ and has published two other books for Packt publishing. He also has a number of apps on the Apple AppStore through Bandlem Limited. When he's not working on technology, and if the weather is nice, he likes to go flying from the nearby Cranfield airport. Alex writes regularly at his blog, http://alblue.bandlem.com, as well tweeting regularly from Twitter as @alblue. Acknowledgements This book would not have been possible without the ongoing love and support of my wife Amy, who has helped me through both the highs and lows of life. She gave me the freedom to work during the many late nights and weekends that it takes to produce a book and its associated code repository. She truly is the Lem of my life. I'd also like to thank my parents, Ann and Derek, for their encouragement and support during my formative years. It was this work ethic that allowed me to start my technology career as a teenager and to incorporate my first company before I was 25. I'd also like to congratulate them on their 50th wedding anniversary in 2015, and I look forward to reaching that goal with Amy. Thanks are due especially to the reviewer of this version of the book: Antonio Bello, as well as the previous version of this book: Nate Cook, James Robert and Arvid Gerstmann, who provided excellent feedback on the contents of this book during development and caught many errors in both the text and code. Any remaining errors are my own. I'd also like to thank my children Sam and Holly for inspiring me and hope that they too can achieve anything that they set their minds to. Finally, I'd like to thank Ben Moseley and Eren Kotan, both of whom introduced me to NeXT in the first place and set my career going on a twenty year journey to this book.
Read more about Alex Blewitt