|
|
< Day Day Up > |
|
4.8 PaintingPainting is the standard mechanism that the operating system uses to inform a control that it is time to draw. With the exception of class Canvas (see the section Class Canvas), whose function is to be a general-purpose "drawing area," controls are responsible for drawing themselves. This means that this section is mostly of interest to those programmers who are implementing their own controls or using canvases to draw arbitrary graphics. 4.8.1 Deferred Update StrategyWhen it is time to draw a control, the windowing system issues one or more paint events for the control. Paint events are generated for a variety of reasons. For example, when a shell comes forward, replacing the topmost shell, areas within the shell that were obscured by the previous topmost shell need to be drawn. Similarly, when a control is moved, resized, or hidden, other controls within the shell can be uncovered or exposed and need to be drawn. To minimize the number of paint events needed to draw a control, SWT uses a deferred update strategy. This strategy is implemented directly by most of the underlying windowing systems.[10] Each control maintains a graphics region called the damaged region. When a redraw is necessary, instead of issuing paint events immediately, the newly exposed area is added to the damaged region. When a paint event is finally issued, the damaged area is filled with the background color of the control,[11] and the damaged region is cleared to make it ready for the next redraw request. In this manner, multiple redraws are merged into a single paint event, which is delivered from the event loop.
4.8.2 Paint EventsAn SWT.Paint event is sent when a control needs to be drawn. Controls listen for paint events to draw their contents. Table 4.3 shows the painting events that are provided by SWT.
Table 4.4 shows the relevant event fields during a paint event.
Using SWT.Paint to Draw in a CanvasThe following example uses SWT.Paint to draw an oval in a shell.
public static void main(String[] args) {
Display display = new Display();
final Shell shell = new Shell(display);
shell.addListener(SWT.Paint, new Listener() {
public void handleEvent(Event event) {
GC gc = event.gc;
Rectangle rect = shell.getClientArea();
gc.drawArc(0,0,rect.width,rect.height,0,360);
}
});
shell.setSize(150, 150);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
Notice that the example code uses getClientArea() to determine the bounds of the oval. Drawing occurs in the client area of a control, not the bounds. Figure 4.6 shows the result of running the code. Although you cannot tell from the picture, only the area that needs to be painted is actually drawn, because the GC that is provided in the event is clipped to the damaged region. For more on clipping, regions, and class GC, see the Graphics Fundamentals chapter. Figure 4.6.
4.8.3 Causing a RedrawAs was previously stated, most controls are responsible for drawing themselves. However, for applications that draw their own content (on a canvas, for example), it is possible to exploit the deferred update strategy provided by the operating system to minimize the amount of drawing that is done. Calling one of the following methods indicates that a particular area of the control needs to be painted.
The redraw() method can be used to get rid of multiple drawing operations, reducing flicker. For example, in a simple implementation of a control that just draws a string, if setting the string were to draw right away, the control would flash when the string was set multiple times. If instead, setting the string simply called redraw(), the control would draw the string once, in a paint event. Normally, collapsing multiple drawing operations is a desirable behavior. However, there are circumstances when multiple draws are not only necessary but required for correctness. Consider this code fragment where a text control is used to show progress.
//WRONG – only draws last percentage
text.setText("0 %");
for (int i=1; i<=100; i++) {
try {
Thread.sleep(100);
} catch (Throwable th) {}
text.setText(i + " %");
}
You might expect that running this fragment would cause the text control to draw successive percentages, but this is not the case. Instead, only the last one is drawn. This happens because the text control uses the deferred painting strategy, merging the 100 paint requests into a single request that is delivered when your application returns to its event loop. Fortunately, SWT provides the ability to force paint events to happen outside of the event loop. 4.8.4 Forcing an UpdateThe update() method is used to force paint events for a control (see Updating the Display for information about forcing paint events for the Display).
Using update(), the text control from the previous section can be forced to show each percentage as it occurs.
//CORRECT – draws every percentage
text.setText("0 %");
text.update();
for (int i=1; i<=100; i++) {
try {
Thread.sleep(100);
} catch (Throwable th) {}
text.setText(i + " %");
text.update();
}
4.8.5 Turning off RedrawAlthough it is considered good practice and reduces the number of drawing operations, there is no guarantee that a control will always use the deferred update strategy. For example, to increase performance, many text editors draw each character immediately as it is typed, rather than calling redraw() with the area where the character should appear and drawing only inside paint events. To further reduce the number of drawing operations for controls that draw outside of paint, setRedraw() can be used.
Using setRedraw() in its intended role can be quite tricky. In particular, although the method is available on all platforms, it prevents drawing only on platforms that provide the underlying capability. Furthermore, turning redraw back on ensures that the entire control will redraw, which may look worse than allowing each of the individual graphics operations to occur.
| |||||||||||||||||||||||||||||
|
|
< Day Day Up > |
|