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.
- Create a new class in the
com.packtpub.e4.clock.ui
package, calledClockWidget
, that extendsCanvas
. - Create a constructor that takes a
Composite
parent and anint style
bits parameter, and pass them to the superclass:public ClockWidget(Composite parent, int style) { super(parent, style); }
- Move the implementation of the
drawClock
method from theClockView
to theClockWidget
. Remove thePaintListener
references from theClockView
class. - In the
ClockWidget
constructor, register aPaintListener
that delegates the call to thedrawClock
method:addPaintListener(this::drawClock);
- Move the
TickTock
thread from theClockView
to theClockWidget
constructor; this will allow theClockWidget
to operate independently. Change any references forclock
to this:Runnable redraw = () -> { while (!this.isDisposed()) { this.getDisplay().asyncExec(() -> this.redraw()); try { Thread.sleep(1000); } catch (InterruptedException e) { return; } } } new Thread(redraw, "TickTock").start();
- Add a
computeSize
method to allow the clock to have a square appearance that is the minimum of the width and height. Note thatSWT.DEFAULT
may be passed in, which has the value-1
, so this needs to be handled explicitly:public Point computeSize(int w, int h, boolean changed) { int size; if (w == SWT.DEFAULT) { size = h; } else if (h == SWT.DEFAULT) { size = w; } else { size = Math.min(w, h); } if (size == SWT.DEFAULT) { size = 50; } return new Point(size, size); }
- Finally, change the
ClockView
to instantiate theClockWidget
instead of theCanvas
in thecreatePartControl
method:new ClockWidget(parent, SWT.NONE);
- Run the target Eclipse instance and the clock should be shown as earlier.
What just happened?
The drawing logic was moved into its own widget, and registered a PaintListener
to a method in the ClockWidget
to render itself. This allows the Clock
to be used as a standalone in any Eclipse or SWT application.
In a real application, the clocks would not have their own thread; it would either be the case that a single Thread
would control updates to all Clock
instances, or they would be set up with repeating Job
instances using the Eclipse jobs framework, which will be covered in Chapter 4, Interacting with the User.
The technique of using a method reference (or anonymous class) to bind a specific listener type to the instance of the class is a common pattern in SWT. When using inner classes, the convention is to use the same method name in the enclosing class; this helps to disambiguate the use.
Tip
Remember to set the listener at startup, as otherwise it can be confusing as to why it's not getting called.
It's also possible for the ClockWidget
to implement PaintListener
directly; in this case, addPaintListener(this)
would be called in the constructor. Modern JITs will optimize the calls to equivalent code paths in any case; it comes down to a style decision as to whether the ClockWidget
class should implement the PaintListener
interface or not.
Finally, the size can be computed based on the hints. This is called by the layout manager to determine what size the widget should be. For widgets with a fixed size (say, a text string or an image), the size can vary depending on the layout. In this case, it returns a square, based on the minimum size of the supplied width and height hints, or 50, whichever is bigger. The SWT.DEFAULT
value is -1
, which has to be dealt with specifically.